From 1f273b8ee7d44d4aaffa382444ccc1c58a77f8ec Mon Sep 17 00:00:00 2001 From: Sean Hinchee Date: Thu, 13 Dec 2018 15:04:40 -0600 Subject: [PATCH] partially fix pm's ;; fix @'s ;; fix emoji printing ;; add :u ;; --- TODO | 10 ++--- commands.go | 27 +++++++------ config.go | 25 ++++++------ helper.go | 75 ++++++++++++++++++++---------------- main.go | 66 +++++++++++++++++++++----------- menu.go | 107 +++++++++++++++++++++++++++++++--------------------- 6 files changed, 187 insertions(+), 123 deletions(-) diff --git a/TODO b/TODO index 149542f1ab0ee7369a01a8fa93555f93aaebf373..c3436da28f322fddf69c467229471a40ee2f8bec 100644 --- a/TODO +++ b/TODO @@ -2,7 +2,7 @@ Commands: :r -- register via Register(email) -:u -- change username via UserUpdate +:u [done] -- change username via UserUpdate :d [n] [file] -- dump backlog of last 'n' msgs to a file 'file' @@ -20,17 +20,17 @@ Features: * if ``` entered, allow multi-line message (composition) -* replace <:emoji:3lk4j12:> garbage with :emoji: +* [done] replace <:emoji:3lk4j12:> garbage with :emoji: -* fix @'s +* [done] fix @'s * flag for larger backlog loading * CAPTCHA solution -- http-proxy is a bad one -* change > to → or whtever desired +* [done] change > to → or whtever desired -* fix pm's +* [done] fix pm's * aux/statusmsg backgrounds and doesn't steal input diff --git a/commands.go b/commands.go index 210c7c4eb66a473ccd6e5cf287dababb59986141..f7c61fd8fa06809550e2b83c19a40ec9d426cd85 100644 --- a/commands.go +++ b/commands.go @@ -41,7 +41,17 @@ func ParseForCommands(line string) string { PrintMessages(Amount) line = "" } - + if strings.HasPrefix(line, ":u") { + session := State.Session + user := session.User + newName := strings.TrimPrefix(line, ":u ") + _, err := State.Session.DiscordGo.UserUpdate(user.Email, session.Password, newName, user.Avatar, "") + if err != nil { + Msg(ErrorMsg, "[:u] Argument Error: %s\n", err) + } + line = "" + } + return line } @@ -49,10 +59,8 @@ func ParseForCommands(line string) string { func SelectGuild() { State.Enabled = false SelectGuildMenu() - /* this causes a segfault, investigate later */ - //if !State.Channel.IsPrivate { + // Segfaults would happen here SelectChannelMenu() - //} State.Enabled = true ShowContent() } @@ -76,18 +84,15 @@ func SelectChannel() { //SelectPrivate a private channel func SelectPrivate() { State.Enabled = false - /* FIXME -- PM's broken - SelectPrivateMenu()*/ + SelectPrivateMenu() State.Enabled = true + ShowContent() } //SelectDeletePrivate a private channel func SelectDeletePrivate() { State.Enabled = false - /* FIXME -- PM's broken - SelectDeletePrivateMenu()*/ + SelectDeletePrivateMenu() State.Enabled = true - if State.Channel != nil { - ShowContent() - } + ShowContent() } diff --git a/config.go b/config.go index 763141f4796632cd5810b24f7a2cdb8c74ff50a9..e43b9702abe9c5c9b064a529e1aec58ad7a44107 100644 --- a/config.go +++ b/config.go @@ -7,8 +7,7 @@ import ( "log" "os" "os/user" - -// "golang.org/x/crypto/ssh/terminal" + // "golang.org/x/crypto/ssh/terminal" ) //Configuration is a struct that contains all configuration fields @@ -17,6 +16,8 @@ type Configuration struct { Password string `json:"password"` MessageDefault bool `json:"messagedefault"` Messages int `json:"messages"` + CompletionChar string `json:"completionchar"` + TimeCompChar string `json:"timecompchar"` } // Config is the global configuration of discord-cli @@ -24,7 +25,7 @@ var Config Configuration //GetConfig retrieves configuration file from $home/lib/disco.cfg, if it doesn't exist it calls CreateConfig() func GetConfig() { -//Get User + //Get User Start: usr, err := user.Current() if err != nil { @@ -65,7 +66,7 @@ func CreateConfig() { EmptyStruct.Username = scan.Text() fmt.Print("Input your password: ") //password, err := terminal.ReadPassword(0) - + plan9 := true /* Plan 9 raw mode for rio */ consctl, err := Rawon() @@ -73,13 +74,13 @@ func CreateConfig() { fmt.Println("Failed to set rawon") plan9 = false } - - password := ""; - + + password := "" + if plan9 { - + password = GetCons() - + err = RawOff(consctl) if err != nil { fmt.Println("\nFailed to set rawoff") @@ -89,13 +90,15 @@ func CreateConfig() { /* Maybe put linux terminal raw mode in here one day */ fmt.Println("Skipping raw input for Plan 9") } - + EmptyStruct.Password = string(password) EmptyStruct.Messages = 10 EmptyStruct.MessageDefault = true + EmptyStruct.CompletionChar = ">" + EmptyStruct.TimeCompChar = ">" //Create File - os.Mkdir(usr.HomeDir + "/lib", 0775) + os.Mkdir(usr.HomeDir+"/lib", 0775) file, err := os.Create(usr.HomeDir + "/lib/disco.cfg") if err != nil { log.Fatalln(err) diff --git a/helper.go b/helper.go index 3fcf46da2a7ab0c68ace11b9fc0919ad5d2b5e1f..400f37d98783dc94f3f3d36a705b230da091009b 100644 --- a/helper.go +++ b/helper.go @@ -1,6 +1,9 @@ package main import ( + "bufio" + "fmt" + "github.com/bwmarrin/discordgo" "io" "log" "os" @@ -8,16 +11,13 @@ import ( "runtime" "strings" "time" - "fmt" - "github.com/bwmarrin/discordgo" - "bufio" ) //HexColor is a struct gives RGB values type HexColor struct { - R int - G int - B int + R int + G int + B int } //Msg is a composition of Color.New printf functions @@ -28,13 +28,18 @@ func Msg(MsgType, format string, a ...interface{}) { //Header simply prints a header containing state/session information func Header() { Msg(InfoMsg, "Welcome, %s!\n\n", State.Session.User.Username) - /* FIXME -- PM's won't work - if State.Channel.IsPrivate { - Msg(InfoMsg, "Channel: %s\n", State.Channel.Recipient.Username) - } else { - */ + switch State.Channel.Type { + case discordgo.ChannelTypeGuildText: Msg(InfoMsg, "Guild: %s, Channel: %s\n", State.Guild.Name, State.Channel.Name) - /*}*/ + case discordgo.ChannelTypeDM: + Msg(InfoMsg, "Channel: %s\n", State.Channel.Recipients[0].Username) + case discordgo.ChannelTypeGroupDM: + var nicklist string + for _, user := range State.Channel.Recipients { + nicklist += user.Username + } + Msg(InfoMsg, "Channel: %s\n", nicklist) + } } //ReceivingMessageParser parses receiving message for mentions, images and MultiLine and returns string array @@ -57,7 +62,6 @@ func PrintMessages(Amount int) { for Key, m := range State.Messages { if Key >= len(State.Messages)-Amount { Messages := ReceivingMessageParser(m) - for _, Msg := range Messages { //log.Printf("> %s > %s\n", UserName(m.Author.Username), Msg) MessagePrint(string(m.Timestamp), m.Author.Username, Msg) @@ -72,15 +76,21 @@ func Notify(m *discordgo.Message) { if *enableNotify == false { return } - Channel, err := State.Session.DiscordGo.Channel(m.ChannelID) - if err != nil { - Msg(ErrorMsg, "(NOT) Channel Error: %s\n", err) - } - Guild, err := State.Session.DiscordGo.Guild(Channel.GuildID) - if err != nil { - Msg(ErrorMsg, "(NOT) Guild Error: %s\n", err) + var Title string + switch State.Channel.Type { + case discordgo.ChannelTypeGuildText: + Channel, err := State.Session.DiscordGo.Channel(m.ChannelID) + if err != nil { + Msg(ErrorMsg, "(NOT) Channel Error: %s\n", err) + } + Guild, err := State.Session.DiscordGo.Guild(Channel.GuildID) + if err != nil { + Msg(ErrorMsg, "(NOT) Guild Error: %s\n", err) + } + Title = "@" + m.Author.Username + " : " + Guild.Name + "/" + Channel.Name + case discordgo.ChannelTypeDM: + Title = fmt.Sprintf("%s (pm)\n", m.Author.Username) } - Title := "@" + m.Author.Username + " : " + Guild.Name + "/" + Channel.Name switch runtime.GOOS { case "plan9": pr, pw := io.Pipe() @@ -91,32 +101,33 @@ func Notify(m *discordgo.Message) { fmt.Fprintf(pw, "%s\n", m.ContentWithMentionsReplaced()) cmd.Wait() }() - err = cmd.Start() + err := cmd.Start() if err != nil { Msg(ErrorMsg, "%s\n", err) } - + default: cmd := exec.Command("notify-send", Title, m.ContentWithMentionsReplaced()) - err = cmd.Start() + err := cmd.Start() if err != nil { Msg(ErrorMsg, "(NOT) Check if libnotify is installed, or disable notifications.\n") } } - } //MessagePrint prints one correctly formatted Message to stdout func MessagePrint(Time, Username, Content string) { + //Clean up emoji + content := ParseForEmoji(Content) //var Color color.Attribute log.SetFlags(0) if *hideTimeStamp { - log.Printf("%s > %s\n", Username, Content) + log.Printf("%s %s %s\n", Username, Config.CompletionChar, content) } else { TimeStamp, _ := time.Parse(time.RFC3339, Time) LocalTime := TimeStamp.Local().Format("2006/01/02 15:04:05") - log.Printf("%s > %s > %s\n", LocalTime, Username, Content) + log.Printf("%s %s %s %s %s\n", LocalTime, Config.TimeCompChar, Username, Config.CompletionChar, content) } log.SetFlags(log.LstdFlags) @@ -126,21 +137,21 @@ func dis(a, b int) float64 { return float64((a - b) * (a - b)) } -func Rawon() (*os.File, error){ +func Rawon() (*os.File, error) { consctl, err := os.OpenFile("/dev/consctl", os.O_WRONLY, 0200) if err != nil { /* not on Plan 9 */ fmt.Println("\nNot running on Plan 9") return consctl, err } - + rawon := []byte("rawon") _, err = consctl.Write(rawon) if err != nil { consctl.Close() return consctl, err } - + return consctl, nil } @@ -150,14 +161,14 @@ func RawOff(consctl *os.File) error { // /* not on Plan 9 */ // return err //} - + rawoff := []byte("rawoff") _, err := consctl.Write(rawoff) if err != nil { consctl.Close() return err } - + consctl.Close() return nil } diff --git a/main.go b/main.go index 7e7af762b55b76562c80401adb35a00b79c00134..a0df9822fbc2ff4bf9ad408c79d3615b78bf0685 100644 --- a/main.go +++ b/main.go @@ -3,13 +3,14 @@ package main import ( - "flag" - "log" - "regexp" "bitbucket.org/henesy/disco/DiscordState" - "fmt" "bufio" + "flag" + "fmt" + "log" "os" + "regexp" + "strings" ) //Global Message Types @@ -88,9 +89,9 @@ func main() { line = ParseForCommands(line) line = ParseForMentions(line) - + if line != "" { - _ ,err := State.Session.DiscordGo.ChannelMessageSend(State.Channel.ID, line) + _, err := State.Session.DiscordGo.ChannelMessageSend(State.Channel.ID, line) if err != nil { fmt.Print("Error: ", err, "\n") } @@ -110,7 +111,7 @@ func InitWindow() { ShowContent() } -//ShowContent shows defaulth Channel content +//ShowContent shows default Channel content func ShowContent() { Header() if Config.MessageDefault { @@ -122,31 +123,54 @@ func ShowContent() { //ShowEmptyContent shows an empty channel func ShowEmptyContent() { Header() + } //ParseForMentions parses input string for mentions func ParseForMentions(line string) string { - r, err := regexp.Compile("\\@\\w+") + r, err := regexp.Compile("@\\w+") if err != nil { Msg(ErrorMsg, "Regex Error: ", err) } - lineByte := r.ReplaceAllFunc([]byte(line), ReplaceMentions) + lineByte := r.ReplaceAllStringFunc(line, ReplaceMentions) - return string(lineByte[:]) + return lineByte } -//ReplaceMentions replaces mentions to ID -func ReplaceMentions(input []byte) []byte { - var OutputString string - - SizeByte := len(input) - InputString := string(input[1:SizeByte]) +//ReplaceMentions replaces mentions to ID +func ReplaceMentions(input string) string { + // Check for guild members that match + channel := State.Guild.Members + for _, member := range channel { + if member.Nick == input[1:] { + return member.User.Mention() + } + if strings.HasPrefix(member.User.Username, input[1:]) { + return member.User.Mention() + } + } + // Walk all PM channels + userChannels, err := Session.DiscordGo.UserChannels() + if err != nil { + return input + } + for _, channel := range userChannels { + for _, recipient := range channel.Recipients { + if strings.HasPrefix(input[1:], recipient.Username) { + fmt.Println("usermatch") + return recipient.Mention() + } + } + } + return input +} - if Member, ok := State.Members[InputString]; ok { - OutputString = "<@" + Member.User.ID + ">" - } else { - OutputString = "@" + InputString +//Parse for guild-specific emoji +func ParseForEmoji(line string) string { + r, err := regexp.Compile("<(:\\w+:)[0-9]+>") + if err != nil { + Msg(ErrorMsg, "Regex Error: ", err) } - return []byte(OutputString) + return r.ReplaceAllString(line, "$1") } diff --git a/menu.go b/menu.go index 9e3a12a4bbb537e196b4ff397948d6151c7705cf..6914e0546ae0d85a97110156f75e655c9f4ef7c6 100644 --- a/menu.go +++ b/menu.go @@ -1,20 +1,20 @@ package main import ( + "bufio" "fmt" "log" - "strconv" - "bufio" "os" + "strconv" ) -/* FIXME -- PM's broken + //SelectPrivateMenu is a menu item that changes to a private channel func SelectPrivateMenu() { Start: Msg(InfoMsg, "Select a Member:\n") - + // List of PMs UserChannels, err := Session.DiscordGo.UserChannels() if err != nil { @@ -26,7 +26,18 @@ Start: for _, user := range UserChannels { UserMap[SelectID] = user.ID - Msg(TextMsg, "[%d] %s\n", SelectID, UserChannels[SelectID].Recipient.Username) + // We have to loop through all recipients + recipients := UserChannels[SelectID].Recipients + for _, recipient := range recipients { + if recipient.ID == user.ID { + continue + } + if recipient.Username == "" { + continue + } + Msg(TextMsg, "[%d] %s\n", SelectID, recipient.Username) + break + } SelectID++ } Msg(TextMsg, "[b] Extra Options\n") @@ -78,9 +89,8 @@ Start: State.Channel = UserChannels[ResponseInteger] ShowContent() End: -}*/ +} -/* FIXME -- PM's broken //SelectDeletePrivateMenu deletes a user channel func SelectDeletePrivateMenu() { @@ -99,7 +109,18 @@ Start: for _, user := range UserChannels { UserMap[SelectID] = user.ID - Msg(TextMsg, "[%d] %s\n", SelectID, UserChannels[SelectID].Recipient.Username) + // We have to loop through all recipients + recipients := UserChannels[SelectID].Recipients + for _, recipient := range recipients { + if recipient.ID == user.ID { + continue + } + if recipient.Username == "" { + continue + } + Msg(TextMsg, "[%d] %s\n", SelectID, recipient.Username) + break + } SelectID++ } var response string @@ -118,7 +139,7 @@ Start: Session.DiscordGo.ChannelDelete(UserChannels[ResponseInteger].ID) -} */ +} //SelectGuildMenu is a menu item that creates a new State on basis of Guild selection func SelectGuildMenu() { @@ -245,7 +266,7 @@ Start: response, _ := reader.ReadString('\n') response = response[:len(response)-1] fmt.Println(response, " ", len(response)) - + //fmt.Scanf("%s\n", &response) if response == "y" { Session.DiscordGo.InviteAccept(Invite.Code) @@ -281,44 +302,44 @@ func ExtraPrivateMenuOptions() { //AddUserChannelMenu takes a user from the current guild and adds them to a private message. WILL RETURN ERROR IF IN USER CHANNEL. func AddUserChannelMenu() { - /* FIXME -- PM's broken - if State.Channel.IsPrivate { + + if State.Channel.GuildID == "" { Msg(ErrorMsg, "Currently in a user channel, move to a guild with :g\n") - } else {*/ - SelectMap := make(map[int]string) - Start: - SelectID := 0 - for _, Member := range State.Members { - SelectMap[SelectID] = Member.User.ID - Msg(TextMsg, "[%d] %s\n", SelectID, Member.User.Username) - SelectID++ - } - var response string - fmt.Scanf("%s\n", &response) + } else { + SelectMap := make(map[int]string) +Start: + SelectID := 0 + for _, Member := range State.Members { + SelectMap[SelectID] = Member.User.ID + Msg(TextMsg, "[%d] %s\n", SelectID, Member.User.Username) + SelectID++ + } + var response string + fmt.Scanf("%s\n", &response) - if response == "b" { - return - } + if response == "b" { + return + } - ResponseInteger, err := strconv.Atoi(response) - if err != nil { - Msg(ErrorMsg, "(CH) Conversion Error: %s\n", err) - goto Start - } + ResponseInteger, err := strconv.Atoi(response) + if err != nil { + Msg(ErrorMsg, "(CH) Conversion Error: %s\n", err) + goto Start + } - if ResponseInteger > SelectID-1 || ResponseInteger < 0 { - Msg(ErrorMsg, "(CH) Error: ID is out of bound\n") - goto Start - } - Chan, err := Session.DiscordGo.UserChannelCreate(SelectMap[ResponseInteger]) + if ResponseInteger > SelectID-1 || ResponseInteger < 0 { + Msg(ErrorMsg, "(CH) Error: ID is out of bound\n") + goto Start + } + Chan, err := Session.DiscordGo.UserChannelCreate(SelectMap[ResponseInteger]) - if Chan.LastMessageID == "" { - var firstMessage string - fmt.Scanf("%s\n", &firstMessage) - Session.DiscordGo.ChannelMessageSend(Chan.ID, "Test") - } - State.Channel = Chan - /*}*/ + if Chan.LastMessageID == "" { + var firstMessage string + fmt.Scanf("%s\n", &firstMessage) + Session.DiscordGo.ChannelMessageSend(Chan.ID, "Test") + } + State.Channel = Chan + } } //LeaveServerMenu is a copy of SelectGuildMenu that leaves instead of selects