diff --git a/README.md b/README.md index 9fe1180..74e7302 100644 --- a/README.md +++ b/README.md @@ -20,10 +20,10 @@ - [ ] Should Client.Message() an other similar methods support errors? - [ ] along with this, should we forcefully check to ensure that the target/events are valid? -- [ ] track connection time (`conntime`? in state) - - [ ] with conntime, find lag. `Client.Lag()` would be useful + - [ ] should we not allow methods like `Action()` and `SendRaw()` when not connected? - [ ] would be cool to track things like `SERVERNAME`, `VERSION`, `UMODES`, `CMODES`, etc. also see `Config.DisableCapTracking`. [e.g. here](https://github.com/Liamraystanley/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()`) - [ ] `User.Age()`? (`FirstActive()`?) (time since first seen) - [ ] add `Client.IsInChannel()`? and/or basic channel list diff --git a/main.go b/main.go index c92545e..fe26463 100644 --- a/main.go +++ b/main.go @@ -101,6 +101,13 @@ type Config struct { // ErrCallbackTimedout is used when we need to wait for temporary callbacks. var ErrCallbackTimedout = errors.New("callback timed out while waiting for response from the server") +// ErrNotConnected is returned if a method is used when the client isn't +// connected. +var ErrNotConnected = errors.New("client is not connected") + +// ErrAlreadyConnecting implies that a connection attempt is already happening. +var ErrAlreadyConnecting = errors.New("a connection attempt is alreayd occurring") + // New creates a new IRC client with the specified server, name and // config. func New(config Config) *Client { @@ -150,8 +157,8 @@ func (c *Client) Stop() { c.quitChan <- struct{}{} } -// Uptime returns the amount of time that has passed since the -// client was created. +// Uptime returns the amount of time that has passed since the client was +// created. func (c *Client) Uptime() time.Duration { return time.Since(c.initTime) } @@ -222,12 +229,45 @@ func (c *Client) Connect() error { go c.readLoop() // Consider the connection a success at this point. - c.state.connected = true c.tries = 0 + c.state.m.Lock() + ctime := time.Now() + c.state.connTime = &ctime + c.state.connected = true + c.state.m.Unlock() + return nil } +// ConnTime is the time at which the client successfully connected to the +// server. +func (c *Client) ConnTime() (*time.Time, error) { + c.state.m.RLock() + defer c.state.m.RUnlock() + + if !c.state.connected { + return nil, ErrNotConnected + } + + return c.state.connTime, nil +} + +// ConnSince is the duration that has past since the client successfully +// connected to the server. +func (c *Client) ConnSince() (*time.Duration, error) { + c.state.m.RLock() + defer c.state.m.RUnlock() + + if !c.state.connected { + return nil, ErrNotConnected + } + + since := time.Since(*c.state.connTime) + + return &since, nil +} + // connectMessages is a list of IRC messages to send when attempting to // connect to the IRC server. func (c *Client) connectMessages() (events []*Event) { @@ -257,7 +297,7 @@ func (c *Client) connectMessages() (events []*Event) { // to the server. func (c *Client) Reconnect() (err error) { if c.state.reconnecting { - return errors.New("a reconnect is already occurring") + return ErrAlreadyConnecting } c.state.reconnecting = true diff --git a/state.go b/state.go index 9c2b4b0..8226f91 100644 --- a/state.go +++ b/state.go @@ -27,6 +27,8 @@ type state struct { // connected is true if we're actively connected to a server. connected bool + // connTime is the time at which the client has connected to a server. + connTime *time.Time // hasQuit is used to determine if we've finished quitting/cleaning up. hasQuit bool // reconnecting lets the internal state know a reconnect is occurring.