forked from jshiffer/go-xmpp
Logging.
Added menus. Can now send raw stanzas.
This commit is contained in:
parent
1ba2add651
commit
f0179ad90e
@ -9,4 +9,7 @@ Client :
|
||||
|
||||
Contacts : "testuser1@localhost;testuser3@localhost"
|
||||
|
||||
LogStanzas:
|
||||
- logger_on: "true"
|
||||
- logfile_path: "./logs"
|
||||
|
||||
|
@ -1,15 +1,48 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/awesome-gocui/gocui"
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
chatLogWindow = "clw"
|
||||
inputWindow = "iw"
|
||||
menuWindow = "menw"
|
||||
// Windows
|
||||
chatLogWindow = "clw" // Where (received and sent) messages are logged
|
||||
chatInputWindow = "iw" // Where messages are written
|
||||
rawInputWindow = "rw" // Where raw stanzas are written
|
||||
contactsListWindow = "cl" // Where the contacts list is shown, and contacts are selectable
|
||||
menuWindow = "mw" // Where the menu is shown
|
||||
|
||||
// Menu options
|
||||
disconnect = "Disconnect"
|
||||
askServerForRoster = "Ask server for roster"
|
||||
rawMode = "Switch to Send Raw Mode"
|
||||
messageMode = "Switch to Send Message Mode"
|
||||
contactList = "Contacts list"
|
||||
backFromContacts = "<- Go back"
|
||||
)
|
||||
|
||||
// To store names of views on top
|
||||
type viewsState struct {
|
||||
input string // Which input view is on top
|
||||
side string // Which side view is on top
|
||||
contacts []string // Contacts list
|
||||
currentContact string // Contact we are currently messaging
|
||||
}
|
||||
|
||||
var (
|
||||
// Which window is on top currently on top of the other.
|
||||
// This is the init setup
|
||||
viewState = viewsState{
|
||||
input: chatInputWindow,
|
||||
side: menuWindow,
|
||||
}
|
||||
menuOptions = []string{contactList, rawMode, askServerForRoster, disconnect}
|
||||
// Errors
|
||||
servConnFail = errors.New("failed to connect to server. Check your configuration ? Exiting")
|
||||
)
|
||||
|
||||
func setCurrentViewOnTop(g *gocui.Gui, name string) (*gocui.View, error) {
|
||||
@ -31,7 +64,7 @@ func layout(g *gocui.Gui) error {
|
||||
v.Autoscroll = true
|
||||
}
|
||||
|
||||
if v, err := g.SetView(menuWindow, 0, 0, maxX/5-1, 5*maxY/6-1, 0); err != nil {
|
||||
if v, err := g.SetView(contactsListWindow, 0, 0, maxX/5-1, 5*maxY/6-2, 0); err != nil {
|
||||
if !gocui.IsUnknownView(err) {
|
||||
return err
|
||||
}
|
||||
@ -40,7 +73,29 @@ func layout(g *gocui.Gui) error {
|
||||
v.Autoscroll = true
|
||||
}
|
||||
|
||||
if v, err := g.SetView(inputWindow, 0, 5*maxY/6-1, maxX/1-1, maxY-1, 0); err != nil {
|
||||
if v, err := g.SetView(menuWindow, 0, 0, maxX/5-1, 5*maxY/6-2, 0); err != nil {
|
||||
if !gocui.IsUnknownView(err) {
|
||||
return err
|
||||
}
|
||||
v.Title = "Menu"
|
||||
v.Wrap = true
|
||||
v.Autoscroll = true
|
||||
fmt.Fprint(v, strings.Join(menuOptions, "\n"))
|
||||
if _, err = setCurrentViewOnTop(g, menuWindow); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if v, err := g.SetView(rawInputWindow, 0, 5*maxY/6-1, maxX/1-1, maxY-1, 0); err != nil {
|
||||
if !gocui.IsUnknownView(err) {
|
||||
return err
|
||||
}
|
||||
v.Title = "Write or paste a raw stanza. Press \"Ctrl+E\" to send :"
|
||||
v.Editable = true
|
||||
v.Wrap = true
|
||||
}
|
||||
|
||||
if v, err := g.SetView(chatInputWindow, 0, 5*maxY/6-1, maxX/1-1, maxY-1, 0); err != nil {
|
||||
if !gocui.IsUnknownView(err) {
|
||||
return err
|
||||
}
|
||||
@ -48,7 +103,7 @@ func layout(g *gocui.Gui) error {
|
||||
v.Editable = true
|
||||
v.Wrap = true
|
||||
|
||||
if _, err = setCurrentViewOnTop(g, inputWindow); err != nil {
|
||||
if _, err = setCurrentViewOnTop(g, chatInputWindow); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -60,50 +115,83 @@ func quit(g *gocui.Gui, v *gocui.View) error {
|
||||
return gocui.ErrQuit
|
||||
}
|
||||
|
||||
// Sends an input line from the user to the backend while also printing it in the chatlog window.
|
||||
// Sends an input text from the user to the backend while also printing it in the chatlog window.
|
||||
// KeyEnter is viewed as "\n" by gocui, so messages should only be one line, whereas raw sending has a different key
|
||||
// binding and therefor should work with this too (for multiple lines stanzas)
|
||||
func writeInput(g *gocui.Gui, v *gocui.View) error {
|
||||
log, _ := g.View(chatLogWindow)
|
||||
for _, line := range v.ViewBufferLines() {
|
||||
textChan <- line
|
||||
fmt.Fprintln(log, "Me : ", line)
|
||||
}
|
||||
chatLogWindow, _ := g.View(chatLogWindow)
|
||||
|
||||
input := strings.Join(v.ViewBufferLines(), "\n")
|
||||
|
||||
fmt.Fprintln(chatLogWindow, "Me : ", input)
|
||||
textChan <- input
|
||||
|
||||
v.Clear()
|
||||
v.EditDeleteToStartOfLine()
|
||||
return nil
|
||||
}
|
||||
|
||||
func setKeyBindings(g *gocui.Gui) {
|
||||
// ==========================
|
||||
// All views
|
||||
if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
|
||||
if err := g.SetKeybinding(inputWindow, gocui.KeyEnter, gocui.ModNone, writeInput); err != nil {
|
||||
// ==========================
|
||||
// Chat input
|
||||
if err := g.SetKeybinding(chatInputWindow, gocui.KeyEnter, gocui.ModNone, writeInput); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
|
||||
if err := g.SetKeybinding(inputWindow, gocui.KeyCtrlSpace, gocui.ModNone, nextView); err != nil {
|
||||
log.Panicln(err)
|
||||
|
||||
}
|
||||
if err := g.SetKeybinding(menuWindow, gocui.KeyCtrlSpace, gocui.ModNone, nextView); err != nil {
|
||||
if err := g.SetKeybinding(chatInputWindow, gocui.KeyCtrlSpace, gocui.ModNone, nextView); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
|
||||
// ==========================
|
||||
// Raw input
|
||||
if err := g.SetKeybinding(rawInputWindow, gocui.KeyCtrlE, gocui.ModNone, writeInput); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
|
||||
if err := g.SetKeybinding(rawInputWindow, gocui.KeyCtrlSpace, gocui.ModNone, nextView); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
|
||||
// ==========================
|
||||
// Menu
|
||||
if err := g.SetKeybinding(menuWindow, gocui.KeyArrowDown, gocui.ModNone, cursorDown); err != nil {
|
||||
log.Panicln(err)
|
||||
|
||||
}
|
||||
if err := g.SetKeybinding(menuWindow, gocui.KeyArrowUp, gocui.ModNone, cursorUp); err != nil {
|
||||
log.Panicln(err)
|
||||
|
||||
}
|
||||
if err := g.SetKeybinding(menuWindow, gocui.KeyCtrlSpace, gocui.ModNone, nextView); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
if err := g.SetKeybinding(menuWindow, gocui.KeyEnter, gocui.ModNone, getLine); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
|
||||
// ==========================
|
||||
// Contacts list
|
||||
if err := g.SetKeybinding(contactsListWindow, gocui.KeyArrowDown, gocui.ModNone, cursorDown); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
if err := g.SetKeybinding(contactsListWindow, gocui.KeyArrowUp, gocui.ModNone, cursorUp); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
if err := g.SetKeybinding(contactsListWindow, gocui.KeyEnter, gocui.ModNone, getLine); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
if err := g.SetKeybinding(contactsListWindow, gocui.KeyCtrlSpace, gocui.ModNone, nextView); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// When we select a new correspondent, we change it in the client, and we display a message window confirming the change.
|
||||
// General
|
||||
// Used to handle menu selections and navigations
|
||||
func getLine(g *gocui.Gui, v *gocui.View) error {
|
||||
var l string
|
||||
var err error
|
||||
@ -112,34 +200,107 @@ func getLine(g *gocui.Gui, v *gocui.View) error {
|
||||
if l, err = v.Line(cy); err != nil {
|
||||
l = ""
|
||||
}
|
||||
// Updating the current correspondent, back-end side.
|
||||
CorrespChan <- l
|
||||
if viewState.side == menuWindow {
|
||||
if l == contactList {
|
||||
cv, _ := g.View(contactsListWindow)
|
||||
viewState.side = contactsListWindow
|
||||
g.SetViewOnTop(contactsListWindow)
|
||||
g.SetCurrentView(contactsListWindow)
|
||||
if len(cv.ViewBufferLines()) == 0 {
|
||||
printContactsToWindow(g, viewState.contacts)
|
||||
}
|
||||
} else if l == disconnect || l == askServerForRoster {
|
||||
chlw, _ := g.View(chatLogWindow)
|
||||
fmt.Fprintln(chlw, infoFormat+" Not yet implemented !")
|
||||
} else if l == rawMode {
|
||||
mw, _ := g.View(menuWindow)
|
||||
viewState.input = rawInputWindow
|
||||
g.SetViewOnTop(rawInputWindow)
|
||||
g.SetCurrentView(rawInputWindow)
|
||||
menuOptions[1] = messageMode
|
||||
v.Clear()
|
||||
v.EditDeleteToStartOfLine()
|
||||
fmt.Fprintln(mw, strings.Join(menuOptions, "\n"))
|
||||
message := "Now sending in raw stanza mode"
|
||||
clv, _ := g.View(chatLogWindow)
|
||||
fmt.Fprintln(clv, infoFormat+message)
|
||||
} else if l == messageMode {
|
||||
mw, _ := g.View(menuWindow)
|
||||
viewState.input = chatInputWindow
|
||||
g.SetViewOnTop(chatInputWindow)
|
||||
g.SetCurrentView(chatInputWindow)
|
||||
menuOptions[1] = rawMode
|
||||
v.Clear()
|
||||
v.EditDeleteToStartOfLine()
|
||||
fmt.Fprintln(mw, strings.Join(menuOptions, "\n"))
|
||||
message := "Now sending in messages mode"
|
||||
clv, _ := g.View(chatLogWindow)
|
||||
fmt.Fprintln(clv, infoFormat+message)
|
||||
}
|
||||
} else if viewState.side == contactsListWindow {
|
||||
if l == backFromContacts {
|
||||
viewState.side = menuWindow
|
||||
g.SetViewOnTop(menuWindow)
|
||||
g.SetCurrentView(menuWindow)
|
||||
} else if l == "" {
|
||||
return nil
|
||||
} else {
|
||||
// Updating the current correspondent, back-end side.
|
||||
CorrespChan <- l
|
||||
viewState.currentContact = l
|
||||
// Showing the selected contact in contacts list
|
||||
cl, _ := g.View(contactsListWindow)
|
||||
cts := cl.ViewBufferLines()
|
||||
cl.Clear()
|
||||
printContactsToWindow(g, cts)
|
||||
// Showing a message to the user, and switching back to input after the new contact is selected.
|
||||
message := "Now sending messages to : " + l + " in a private conversation"
|
||||
clv, _ := g.View(chatLogWindow)
|
||||
fmt.Fprintln(clv, infoFormat+message)
|
||||
g.SetCurrentView(chatInputWindow)
|
||||
}
|
||||
}
|
||||
|
||||
// Showing a message to the user, and switching back to input after the new contact is selected.
|
||||
message := "Now sending messages to : " + l + " in a private conversation"
|
||||
clv, _ := g.View(chatLogWindow)
|
||||
fmt.Fprintln(clv, infoFormat+message)
|
||||
g.SetCurrentView(inputWindow)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Changing view between input and "menu" (= basically contacts only right now) when pressing the specific key.
|
||||
func printContactsToWindow(g *gocui.Gui, contactsList []string) {
|
||||
cl, _ := g.View(contactsListWindow)
|
||||
for _, c := range contactsList {
|
||||
c = strings.ReplaceAll(c, " *", "")
|
||||
if c == viewState.currentContact {
|
||||
fmt.Fprintf(cl, c+" *\n")
|
||||
} else {
|
||||
fmt.Fprintf(cl, c+"\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Changing view between input and "menu/contacts" when pressing the specific key.
|
||||
func nextView(g *gocui.Gui, v *gocui.View) error {
|
||||
if v == nil || v.Name() == inputWindow {
|
||||
_, err := g.SetCurrentView(menuWindow)
|
||||
if v == nil || v.Name() == chatInputWindow || v.Name() == rawInputWindow {
|
||||
_, err := g.SetCurrentView(viewState.side)
|
||||
return err
|
||||
} else if v.Name() == menuWindow || v.Name() == contactsListWindow {
|
||||
_, err := g.SetCurrentView(viewState.input)
|
||||
return err
|
||||
}
|
||||
_, err := g.SetCurrentView(inputWindow)
|
||||
|
||||
// Should not be reached right now
|
||||
_, err := g.SetCurrentView(chatInputWindow)
|
||||
return err
|
||||
}
|
||||
|
||||
func cursorDown(g *gocui.Gui, v *gocui.View) error {
|
||||
if v != nil {
|
||||
cx, cy := v.Cursor()
|
||||
// Avoid going below the list of contacts
|
||||
// Avoid going below the list of contacts. Although lines are stored in the view as a slice
|
||||
// in the used lib. Therefor, if the number of lines is too big, the cursor will go past the last line since
|
||||
// increasing slice capacity is done by doubling it. Last lines will be "nil" and reachable by the cursor
|
||||
// in a dynamic context (such as contacts list)
|
||||
cv := g.CurrentView()
|
||||
h := cv.LinesHeight()
|
||||
if cy+1 >= h-1 {
|
||||
if cy+1 >= h {
|
||||
return nil
|
||||
}
|
||||
// Lower cursor
|
||||
|
@ -2,10 +2,10 @@ package main
|
||||
|
||||
/*
|
||||
xmpp_chat_client is a demo client that connect on an XMPP server to chat with other members
|
||||
Note that this example sends to a very specific user. User logic is not implemented here.
|
||||
*/
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/awesome-gocui/gocui"
|
||||
@ -14,6 +14,9 @@ import (
|
||||
"gosrc.io/xmpp"
|
||||
"gosrc.io/xmpp/stanza"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@ -24,6 +27,8 @@ const (
|
||||
|
||||
configFileName = "config"
|
||||
configType = "yaml"
|
||||
logStanzasOn = "logger_on"
|
||||
logFilePath = "logfile_path"
|
||||
// Keys in config
|
||||
serverAddressKey = "full_address"
|
||||
clientJid = "jid"
|
||||
@ -34,16 +39,22 @@ const (
|
||||
var (
|
||||
CorrespChan = make(chan string, 1)
|
||||
textChan = make(chan string, 5)
|
||||
rawTextChan = make(chan string, 5)
|
||||
killChan = make(chan struct{}, 1)
|
||||
errChan = make(chan error)
|
||||
|
||||
logger *log.Logger
|
||||
)
|
||||
|
||||
type config struct {
|
||||
Server map[string]string `mapstructure:"server"`
|
||||
Client map[string]string `mapstructure:"client"`
|
||||
Contacts string `string:"contact"`
|
||||
Server map[string]string `mapstructure:"server"`
|
||||
Client map[string]string `mapstructure:"client"`
|
||||
Contacts string `string:"contact"`
|
||||
LogStanzas map[string]string `mapstructure:"logstanzas"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
// ============================================================
|
||||
// Parse the flag with the config directory path as argument
|
||||
flag.String("c", defaultConfigFilePath, "Provide a path to the directory that contains the configuration"+
|
||||
@ -55,6 +66,22 @@ func main() {
|
||||
// Read configuration
|
||||
c := readConfig()
|
||||
|
||||
//================================
|
||||
// Setup logger
|
||||
on, err := strconv.ParseBool(c.LogStanzas[logStanzasOn])
|
||||
if err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
if on {
|
||||
f, err := os.OpenFile(path.Join(c.LogStanzas[logFilePath], "logs.txt"), os.O_RDWR|os.O_APPEND|os.O_CREATE, 0666)
|
||||
if err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
logger = log.New(f, "", log.Lshortfile|log.Ldate|log.Ltime)
|
||||
logger.SetOutput(f)
|
||||
defer f.Close()
|
||||
}
|
||||
|
||||
// ==========================
|
||||
// Create TUI
|
||||
g, err := gocui.NewGui(gocui.OutputNormal, true)
|
||||
@ -70,7 +97,6 @@ func main() {
|
||||
|
||||
// ==========================
|
||||
// Run TUI
|
||||
errChan := make(chan error)
|
||||
go func() {
|
||||
errChan <- g.MainLoop()
|
||||
}()
|
||||
@ -107,6 +133,10 @@ func startClient(g *gocui.Gui, config *config) {
|
||||
|
||||
handlerWithGui := func(_ xmpp.Sender, p stanza.Packet) {
|
||||
msg, ok := p.(stanza.Message)
|
||||
if logger != nil {
|
||||
logger.Println(msg)
|
||||
}
|
||||
|
||||
v, err := g.View(chatLogWindow)
|
||||
if !ok {
|
||||
fmt.Fprintf(v, "%sIgnoring packet: %T\n", infoFormat, p)
|
||||
@ -120,8 +150,11 @@ func startClient(g *gocui.Gui, config *config) {
|
||||
_, err := fmt.Fprintf(v, "Error from server : %s : %s \n", msg.Error.Reason, msg.XMLName.Space)
|
||||
return err
|
||||
}
|
||||
_, err := fmt.Fprintf(v, "%s : %s \n", msg.From, msg.Body)
|
||||
return err
|
||||
if len(strings.TrimSpace(msg.Body)) != 0 {
|
||||
_, err := fmt.Fprintf(v, "%s : %s \n", msg.From, msg.Body)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
@ -140,6 +173,8 @@ func startClient(g *gocui.Gui, config *config) {
|
||||
fmt.Fprintf(v, msg)
|
||||
return err
|
||||
})
|
||||
fmt.Println("Failed to connect to server. Exiting...")
|
||||
errChan <- servConnFail
|
||||
return
|
||||
}
|
||||
|
||||
@ -147,24 +182,42 @@ func startClient(g *gocui.Gui, config *config) {
|
||||
// Start working
|
||||
//askForRoster(client, g)
|
||||
updateRosterFromConfig(g, config)
|
||||
// Sending the default contact in a channel. Default value is the first contact in the list from the config.
|
||||
viewState.currentContact = strings.Split(config.Contacts, configContactSep)[0]
|
||||
// Informing user of the default contact
|
||||
clw, _ := g.View(chatLogWindow)
|
||||
fmt.Fprintf(clw, infoFormat+"Now sending messages to "+viewState.currentContact+" in a private conversation\n")
|
||||
CorrespChan <- viewState.currentContact
|
||||
startMessaging(client, config)
|
||||
}
|
||||
|
||||
func startMessaging(client xmpp.Sender, config *config) {
|
||||
var text string
|
||||
// Update this with a channel. Default value is the first contact in the list from the config.
|
||||
correspondent := strings.Split(config.Contacts, configContactSep)[0]
|
||||
var correspondent string
|
||||
for {
|
||||
select {
|
||||
case <-killChan:
|
||||
return
|
||||
case text = <-textChan:
|
||||
reply := stanza.Message{Attrs: stanza.Attrs{To: correspondent}, Body: text}
|
||||
reply := stanza.Message{Attrs: stanza.Attrs{To: correspondent, From: config.Client[clientJid], Type: stanza.MessageTypeChat}, Body: text}
|
||||
if logger != nil {
|
||||
raw, _ := xml.Marshal(reply)
|
||||
logger.Println(string(raw))
|
||||
}
|
||||
err := client.Send(reply)
|
||||
if err != nil {
|
||||
fmt.Printf("There was a problem sending the message : %v", reply)
|
||||
return
|
||||
}
|
||||
case text = <-rawTextChan:
|
||||
if logger != nil {
|
||||
logger.Println(text)
|
||||
}
|
||||
err := client.SendRaw(text)
|
||||
if err != nil {
|
||||
fmt.Printf("There was a problem sending the message : %v", text)
|
||||
return
|
||||
}
|
||||
case crrsp := <-CorrespChan:
|
||||
correspondent = crrsp
|
||||
}
|
||||
@ -172,6 +225,7 @@ func startMessaging(client xmpp.Sender, config *config) {
|
||||
}
|
||||
}
|
||||
|
||||
// Only reads and parses the configuration
|
||||
func readConfig() *config {
|
||||
viper.SetConfigName(configFileName) // name of config file (without extension)
|
||||
viper.BindPFlags(pflag.CommandLine)
|
||||
@ -184,6 +238,7 @@ func readConfig() *config {
|
||||
log.Panicln(err)
|
||||
}
|
||||
}
|
||||
|
||||
viper.SetConfigType(configType)
|
||||
var config config
|
||||
err = viper.Unmarshal(&config)
|
||||
@ -191,6 +246,20 @@ func readConfig() *config {
|
||||
panic(fmt.Errorf("Unable to decode Config: %s \n", err))
|
||||
}
|
||||
|
||||
// Check if we have contacts to message
|
||||
if len(strings.TrimSpace(config.Contacts)) == 0 {
|
||||
log.Panicln("You appear to have no contacts to message !")
|
||||
}
|
||||
// Check logging
|
||||
config.LogStanzas[logFilePath] = path.Clean(config.LogStanzas[logFilePath])
|
||||
on, err := strconv.ParseBool(config.LogStanzas[logStanzasOn])
|
||||
if err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
if d, e := isDirectory(config.LogStanzas[logFilePath]); (e != nil || !d) && on {
|
||||
log.Panicln("The log file path could not be found or is not a directory.")
|
||||
}
|
||||
|
||||
return &config
|
||||
}
|
||||
|
||||
@ -203,45 +272,19 @@ func errorHandler(err error) {
|
||||
// Read the client roster from the config. This does not check with the server that the roster is correct.
|
||||
// If user tries to send a message to someone not registered with the server, the server will return an error.
|
||||
func updateRosterFromConfig(g *gocui.Gui, config *config) {
|
||||
g.Update(func(g *gocui.Gui) error {
|
||||
menu, _ := g.View(menuWindow)
|
||||
for _, contact := range strings.Split(config.Contacts, configContactSep) {
|
||||
fmt.Fprintln(menu, contact)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
viewState.contacts = append(strings.Split(config.Contacts, configContactSep), backFromContacts)
|
||||
}
|
||||
|
||||
// Updates the menu panel of the view with the current user's roster.
|
||||
// Need to add support for Roster IQ stanzas to make this work.
|
||||
func askForRoster(client *xmpp.Client, g *gocui.Gui) {
|
||||
//ctx, _ := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
//iqReq := stanza.NewIQ(stanza.Attrs{Type: stanza.IQTypeGet, From: currentUserJid, To: "localhost", Lang: "en"})
|
||||
//disco := iqReq.DiscoInfo()
|
||||
//iqReq.Payload = disco
|
||||
//
|
||||
//// Handle a possible error
|
||||
//errChan := make(chan error)
|
||||
//errorHandler := func(err error) {
|
||||
// errChan <- err
|
||||
//}
|
||||
//client.ErrorHandler = errorHandler
|
||||
//res, err := client.SendIQ(ctx, iqReq)
|
||||
//if err != nil {
|
||||
// t.Errorf(err.Error())
|
||||
//}
|
||||
//
|
||||
//select {
|
||||
//case <-res:
|
||||
//}
|
||||
|
||||
//roster := []string{"testuser1", "testuser2", "testuser3@localhost"}
|
||||
//
|
||||
//g.Update(func(g *gocui.Gui) error {
|
||||
// menu, _ := g.View(menuWindow)
|
||||
// for _, contact := range roster {
|
||||
// fmt.Fprintln(menu, contact)
|
||||
// }
|
||||
// return nil
|
||||
//})
|
||||
// Not implemented yet !
|
||||
}
|
||||
|
||||
func isDirectory(path string) (bool, error) {
|
||||
fileInfo, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return fileInfo.IsDir(), err
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user