forked from jshiffer/go-xmpp
		
	Logging.
Added menus. Can now send raw stanzas.
This commit is contained in:
		| @@ -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 | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 CORNIERE Rémi
					CORNIERE Rémi