rewrite capability ack functionality
This commit is contained in:
parent
a95be3ad67
commit
aeef5a8774
122
cap.go
122
cap.go
@ -11,14 +11,14 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
var possibleCap = []string{
|
var possibleCap = map[string][]string{
|
||||||
"account-notify",
|
"account-notify": nil,
|
||||||
"account-tag",
|
"account-tag": nil,
|
||||||
"away-notify",
|
"away-notify": nil,
|
||||||
"batch",
|
"batch": nil,
|
||||||
"cap-notify",
|
"cap-notify": nil,
|
||||||
"chghost",
|
"chghost": nil,
|
||||||
"message-tags",
|
"message-tags": nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) listCAP() error {
|
func (c *Client) listCAP() error {
|
||||||
@ -31,13 +31,46 @@ func (c *Client) listCAP() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func possibleCapList(c *Client) map[string][]string {
|
||||||
|
out := make(map[string][]string)
|
||||||
|
|
||||||
|
for k := range c.Config.SupportedCaps {
|
||||||
|
out[k] = c.Config.SupportedCaps[k]
|
||||||
|
}
|
||||||
|
|
||||||
|
for k := range possibleCap {
|
||||||
|
out[k] = possibleCap[k]
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseCap(raw string) map[string][]string {
|
||||||
|
out := make(map[string][]string)
|
||||||
|
parts := strings.Split(raw, " ")
|
||||||
|
|
||||||
|
var val int
|
||||||
|
|
||||||
|
for i := 0; i < len(parts); i++ {
|
||||||
|
val = strings.IndexByte(parts[i], prefixTagValue) // =
|
||||||
|
|
||||||
|
// No value splitter, or has splitter but no trailing value.
|
||||||
|
if val < 1 || len(parts[i]) < val+1 {
|
||||||
|
// The capability doesn't contain a value.
|
||||||
|
out[parts[i]] = []string{}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
out[parts[i][:val]] = strings.Split(parts[i][val+1:], ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// handleCAP attempts to find out what IRCv3 capabilities the server supports.
|
// handleCAP attempts to find out what IRCv3 capabilities the server supports.
|
||||||
// This will lock further registration until we have acknowledged the
|
// This will lock further registration until we have acknowledged the
|
||||||
// capabilities.
|
// capabilities.
|
||||||
func handleCAP(c *Client, e Event) {
|
func handleCAP(c *Client, e Event) {
|
||||||
possible := c.Config.SupportedCaps
|
|
||||||
possible = append(possible, possibleCap...)
|
|
||||||
|
|
||||||
if len(e.Params) >= 2 && (e.Params[1] == CAP_NEW || e.Params[1] == CAP_DEL) {
|
if len(e.Params) >= 2 && (e.Params[1] == CAP_NEW || e.Params[1] == CAP_DEL) {
|
||||||
c.listCAP()
|
c.listCAP()
|
||||||
return
|
return
|
||||||
@ -46,32 +79,51 @@ func handleCAP(c *Client, e Event) {
|
|||||||
// We can assume there was a failure attempting to enable a capability.
|
// We can assume there was a failure attempting to enable a capability.
|
||||||
if len(e.Params) == 2 && e.Params[1] == CAP_NAK {
|
if len(e.Params) == 2 && e.Params[1] == CAP_NAK {
|
||||||
// Let the server know that we're done.
|
// Let the server know that we're done.
|
||||||
c.Send(&Event{Command: CAP, Params: []string{CAP_END}})
|
c.write(&Event{Command: CAP, Params: []string{CAP_END}})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
possible := possibleCapList(c)
|
||||||
|
|
||||||
if len(e.Params) >= 2 && len(e.Trailing) > 1 && e.Params[1] == CAP_LS {
|
if len(e.Params) >= 2 && len(e.Trailing) > 1 && e.Params[1] == CAP_LS {
|
||||||
c.state.mu.Lock()
|
c.state.mu.Lock()
|
||||||
// Loop through and check if it's one we support.
|
|
||||||
for _, cap := range strings.Split(e.Trailing, " ") {
|
caps := parseCap(e.Trailing)
|
||||||
for i := 0; i < len(possible); i++ {
|
|
||||||
// Check if it's one that we support.
|
for k := range caps {
|
||||||
if possibleCap[i] == cap {
|
if _, ok := possible[k]; !ok {
|
||||||
// Ensure that there are no duplicates.
|
continue
|
||||||
var isin bool
|
}
|
||||||
for j := 0; j < len(c.state.tmpCap); j++ {
|
|
||||||
if c.state.tmpCap[j] == cap {
|
if len(possible[k]) == 0 || len(caps[k]) == 0 {
|
||||||
isin = true
|
c.state.tmpCap = append(c.state.tmpCap, k)
|
||||||
break
|
continue
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if !isin {
|
var contains bool
|
||||||
c.state.tmpCap = append(c.state.tmpCap, cap)
|
for i := 0; i < len(caps[k]); i++ {
|
||||||
|
for j := 0; j < len(possible[k]); j++ {
|
||||||
|
if caps[k][i] == possible[k][j] {
|
||||||
|
// Assume we have a matching split value.
|
||||||
|
contains = true
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if contains {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if contains {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !contains {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
c.state.tmpCap = append(c.state.tmpCap, k)
|
||||||
}
|
}
|
||||||
c.state.mu.Unlock()
|
c.state.mu.Unlock()
|
||||||
|
|
||||||
@ -80,12 +132,12 @@ func handleCAP(c *Client, e Event) {
|
|||||||
if len(e.Params) == 2 {
|
if len(e.Params) == 2 {
|
||||||
// If we support no caps, just ack the CAP message and END.
|
// If we support no caps, just ack the CAP message and END.
|
||||||
if len(c.state.tmpCap) == 0 {
|
if len(c.state.tmpCap) == 0 {
|
||||||
c.Send(&Event{Command: CAP, Params: []string{CAP_END}})
|
c.write(&Event{Command: CAP, Params: []string{CAP_END}})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Let them know which ones we'd like to enable.
|
// Let them know which ones we'd like to enable.
|
||||||
c.Send(&Event{Command: CAP, Params: []string{CAP_REQ}, Trailing: strings.Join(c.state.tmpCap, " ")})
|
c.write(&Event{Command: CAP, Params: []string{CAP_REQ}, Trailing: strings.Join(c.state.tmpCap, " ")})
|
||||||
|
|
||||||
// Re-initialize the tmpCap, so if we get multiple 'CAP LS' requests
|
// Re-initialize the tmpCap, so if we get multiple 'CAP LS' requests
|
||||||
// due to cap-notify, we can re-evaluate what we can support.
|
// due to cap-notify, we can re-evaluate what we can support.
|
||||||
@ -101,7 +153,7 @@ func handleCAP(c *Client, e Event) {
|
|||||||
c.state.mu.Unlock()
|
c.state.mu.Unlock()
|
||||||
|
|
||||||
// Let the server know that we're done.
|
// Let the server know that we're done.
|
||||||
c.Send(&Event{Command: CAP, Params: []string{CAP_END}})
|
c.write(&Event{Command: CAP, Params: []string{CAP_END}})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -210,20 +262,14 @@ func ParseTags(raw string) (t Tags) {
|
|||||||
for i := 0; i < len(parts); i++ {
|
for i := 0; i < len(parts); i++ {
|
||||||
hasValue = strings.IndexByte(parts[i], prefixTagValue)
|
hasValue = strings.IndexByte(parts[i], prefixTagValue)
|
||||||
|
|
||||||
if hasValue < 1 {
|
// The tag doesn't contain a value or has a splitter with no value.
|
||||||
|
if hasValue < 1 || len(parts[i]) < hasValue+1 {
|
||||||
// The tag doesn't contain a value.
|
// The tag doesn't contain a value.
|
||||||
t[parts[i]] = ""
|
t[parts[i]] = ""
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// May have equals sign and no value as well.
|
|
||||||
if len(parts[i]) < hasValue+1 {
|
|
||||||
t[parts[i]] = ""
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
t[parts[i][:hasValue]] = parts[i][hasValue+1:]
|
t[parts[i][:hasValue]] = parts[i][hasValue+1:]
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return t
|
return t
|
||||||
|
@ -78,7 +78,8 @@ type Config struct {
|
|||||||
// SupportedCaps are the IRCv3 capabilities you would like the client to
|
// SupportedCaps are the IRCv3 capabilities you would like the client to
|
||||||
// support. Only use this if DisableTracking and DisableCapTracking are
|
// support. Only use this if DisableTracking and DisableCapTracking are
|
||||||
// not enabled, otherwise you will need to handle CAP negotiation yourself.
|
// not enabled, otherwise you will need to handle CAP negotiation yourself.
|
||||||
SupportedCaps []string
|
// The keys value gets passed to the server if supported.
|
||||||
|
SupportedCaps map[string][]string
|
||||||
// Version is the application version information that will be used in
|
// Version is the application version information that will be used in
|
||||||
// response to a CTCP VERSION, if default CTCP replies have not been
|
// response to a CTCP VERSION, if default CTCP replies have not been
|
||||||
// overwritten or a VERSION handler was already supplied.
|
// overwritten or a VERSION handler was already supplied.
|
||||||
@ -292,7 +293,7 @@ func (c *Client) Reconnect() (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if c.Config.ReconnectDelay < (10 * time.Second) {
|
if c.Config.ReconnectDelay < (10 * time.Second) {
|
||||||
c.Config.ReconnectDelay = 10 * time.Second
|
c.Config.ReconnectDelay = 25 * time.Second
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.IsConnected() {
|
if c.IsConnected() {
|
||||||
|
4
state.go
4
state.go
@ -45,8 +45,8 @@ type state struct {
|
|||||||
// last capability list command from the server.
|
// last capability list command from the server.
|
||||||
tmpCap []string
|
tmpCap []string
|
||||||
// serverOptions are the standard capabilities and configurations
|
// serverOptions are the standard capabilities and configurations
|
||||||
// supported by the server at connection time. This also includes ISUPPORT
|
// supported by the server at connection time. This also includes
|
||||||
// entries.
|
// RPL_ISUPPORT entries.
|
||||||
serverOptions map[string]string
|
serverOptions map[string]string
|
||||||
// motd is the servers message of the day.
|
// motd is the servers message of the day.
|
||||||
motd string
|
motd string
|
||||||
|
Loading…
Reference in New Issue
Block a user