M TODO => TODO +5 -5
@@ 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
M commands.go => commands.go +16 -11
@@ 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()
}
M config.go => config.go +14 -11
@@ 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)
M helper.go => helper.go +43 -32
@@ 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
}
M main.go => main.go +45 -21
@@ 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")
}
M => +64 -43
@@ 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