implement GetServerOption, ServerName, NetworkName, ServerVersion, and ISUPPORT tracking; update todos

This commit is contained in:
Liam Stanley 2017-01-06 08:30:09 -05:00
parent 0870acadcb
commit 687e2753a1
4 changed files with 97 additions and 3 deletions

View File

@ -28,7 +28,6 @@
- [ ] ensure types `User` and `Channel` don't have any unexported fields, and that when they are given publically, it's not a pointer to internal state
- [ ] track with `NAMES` as well? would require rewrite of user existance logic, could also help track user modes
- [ ] write more function-specific examples as the api becomes much more stable
- [ ] would be cool to track things like `SERVERNAME`, `VERSION`, `UMODES`, `CMODES`, etc. also see `Config.DisableCapTracking`. [e.g. here](https://github.com/lrstanley/Code/blob/master/core/triggers.py#L40-L67)
- [ ] client should support ping tracking (sending `PING`'s to the server)
- [ ] with this, we can potentially find lag. `Client.Lag()` would be useful
- [ ] users need to be exposed in state somehow (other than `GetChannels()`)

View File

@ -707,3 +707,45 @@ func (c *Client) Whowas(nick string, amount int) error {
return c.Send(&Event{Command: WHOWAS, Params: []string{nick, string(amount)}})
}
// GetServerOption retrieves a server capability setting that was retrieved
// during client connection. This is also known as ISUPPORT. Examples of usage:
//
// nickLen, success := GetServerOption("MAXNICKLEN")
//
func (c *Client) GetServerOption(key string) (result string, success bool) {
if c.Config.DisableTracking {
panic("GetServerOption() used when tracking is disabled")
}
c.state.mu.Lock()
result, success = c.state.serverOptions[key]
c.state.mu.Unlock()
return result, success
}
// ServerName returns the server host/name that the server itself identifies
// as. May be empty if the server does not support RPL_MYINFO.
func (c *Client) ServerName() (name string) {
name, _ = c.GetServerOption("SERVER")
return name
}
// NetworkName returns the network identifier. E.g. "EsperNet", "ByteIRC".
// May be empty if the server does not support RPL_ISUPPORT.
func (c *Client) NetworkName() (name string) {
name, _ = c.GetServerOption("NETWORK")
return name
}
// ServerVersion returns the server software version, if the server has
// supplied this information during connection. May be empty if the server
// does not support RPL_MYINFO.
func (c *Client) ServerVersion() (version string) {
version, _ = c.GetServerOption("VERSION")
return version
}

View File

@ -4,7 +4,10 @@
package girc
import "time"
import (
"strings"
"time"
)
// registerHandlers sets up built-in callbacks/helpers, based on client
// configuration.
@ -32,6 +35,8 @@ func (c *Client) registerHandlers() {
// Other misc. useful stuff.
c.Callbacks.register(true, TOPIC, CallbackFunc(handleTOPIC))
c.Callbacks.register(true, RPL_TOPIC, CallbackFunc(handleTOPIC))
c.Callbacks.register(true, RPL_MYINFO, CallbackFunc(handleMYINFO))
c.Callbacks.register(true, RPL_ISUPPORT, CallbackFunc(handleISUPPORT))
}
// Nickname collisions.
@ -64,7 +69,7 @@ func handleConnect(c *Client, e Event) {
c.state.nick = e.Params[0]
}
time.Sleep(1 * time.Second)
time.Sleep(2 * time.Second)
c.Events <- &Event{Command: CONNECTED}
}
@ -226,3 +231,46 @@ func handleQUIT(c *Client, e Event) {
c.state.deleteUser(e.Source.Name)
c.state.mu.Unlock()
}
func handleMYINFO(c *Client, e Event) {
// Malformed or odd output. As this can differ strongly between networks,
// just skip it.
if len(e.Params) < 3 {
return
}
c.state.mu.Lock()
c.state.serverOptions["SERVER"] = e.Params[1]
c.state.serverOptions["VERSION"] = e.Params[2]
c.state.mu.Unlock()
}
func handleISUPPORT(c *Client, e Event) {
// Must be a ISUPPORT-based message. 005 is also used for server bounce
// related things, so this callback may be triggered during other
// situations.
if !strings.HasSuffix(e.Trailing, "this server") {
return
}
// Must have at least one configuration.
if len(e.Params) < 2 {
return
}
c.state.mu.Lock()
// Skip the first parameter, as it's our nickname.
for i := 1; i < len(e.Params); i++ {
j := strings.IndexByte(e.Params[i], 0x3D) // =
if j < 1 || (j+1) == len(e.Params[i]) {
c.state.serverOptions[e.Params[i]] = ""
continue
}
name := e.Params[i][0:j]
val := e.Params[i][j+1:]
c.state.serverOptions[name] = val
}
c.state.mu.Unlock()
}

View File

@ -44,6 +44,10 @@ type state struct {
// last capability check. These will get sent once we have received the
// last capability list command from the server.
tmpCap []string
// serverOptions are the standard capabilities and configurations
// supported by the server at connection time. This also includes ISUPPORT
// entries.
serverOptions map[string]string
}
// User represents an IRC user and the state attached to them.
@ -168,6 +172,7 @@ func newState() *state {
s := &state{}
s.channels = make(map[string]*Channel)
s.serverOptions = make(map[string]string)
s.connected = false
return s