diff -r b0162227c921 TODO --- a/TODO Wed Dec 12 21:43:17 2018 -0600 +++ b/TODO Thu Dec 13 14:36:21 2018 -0700 @@ -2,7 +2,7 @@ :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 @@ * 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 -r b0162227c921 commands.go --- a/commands.go Wed Dec 12 21:43:17 2018 -0600 +++ b/commands.go Thu Dec 13 14:36:21 2018 -0700 @@ -41,7 +41,17 @@ 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 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 @@ //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 -r b0162227c921 config.go --- a/config.go Wed Dec 12 21:43:17 2018 -0600 +++ b/config.go Thu Dec 13 14:36:21 2018 -0700 @@ -7,8 +7,7 @@ "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 @@ 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 @@ //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 @@ 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 @@ 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 @@ /* 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 -r b0162227c921 helper.go --- a/helper.go Wed Dec 12 21:43:17 2018 -0600 +++ b/helper.go Thu Dec 13 14:36:21 2018 -0700 @@ -1,6 +1,9 @@ package main import ( + "bufio" + "fmt" + "github.com/bwmarrin/discordgo" "io" "log" "os" @@ -8,16 +11,13 @@ "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 @@ //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 @@ 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 @@ if *enableNotify == false { return } - Channel, err := State.Session.DiscordGo.Channel(m.ChannelID) - if err != nil { - Msg(ErrorMsg, "(NOT) Channel 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) } - 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 switch runtime.GOOS { case "plan9": pr, pw := io.Pipe() @@ -91,32 +101,33 @@ 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 @@ 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 @@ // /* 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 -r b0162227c921 main.go --- a/main.go Wed Dec 12 21:43:17 2018 -0600 +++ b/main.go Thu Dec 13 14:36:21 2018 -0700 @@ -3,13 +3,14 @@ package main import ( + "bitbucket.org/henesy/disco/DiscordState" + "bufio" "flag" + "fmt" "log" + "os" "regexp" - "bitbucket.org/henesy/disco/DiscordState" - "fmt" - "bufio" - "os" + "strings" ) //Global Message Types @@ -88,9 +89,9 @@ 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 @@ ShowContent() } -//ShowContent shows defaulth Channel content +//ShowContent shows default Channel content func ShowContent() { Header() if Config.MessageDefault { @@ -122,31 +123,54 @@ //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 +//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 +} - SizeByte := len(input) - InputString := string(input[1:SizeByte]) - - 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 -r b0162227c921 menu.go --- a/menu.go Wed Dec 12 21:43:17 2018 -0600 +++ b/menu.go Thu Dec 13 14:36:21 2018 -0700 @@ -1,20 +1,20 @@ package main import ( + "bufio" "fmt" "log" + "os" "strconv" - "bufio" - "os" ) -/* 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 @@ 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 @@ State.Channel = UserChannels[ResponseInteger] ShowContent() End: -}*/ +} -/* FIXME -- PM's broken //SelectDeletePrivateMenu deletes a user channel func SelectDeletePrivateMenu() { @@ -99,7 +109,18 @@ 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 @@ 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 @@ 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 @@ //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