mtfosbot/vendor/github.com/go-irc/irc/client_handlers.go
2018-11-16 11:10:19 +08:00

152 lines
3.5 KiB
Go

package irc
import (
"fmt"
"strings"
)
type clientFilter func(*Client, *Message)
// clientFilters are pre-processing which happens for certain message
// types. These were moved from below to keep the complexity of each
// component down.
var clientFilters = map[string]clientFilter{
"001": handle001,
"433": handle433,
"437": handle437,
"PING": handlePing,
"PONG": handlePong,
"NICK": handleNick,
"CAP": handleCap,
}
// From rfc2812 section 5.1 (Command responses)
//
// 001 RPL_WELCOME
// "Welcome to the Internet Relay Network
// <nick>!<user>@<host>"
func handle001(c *Client, m *Message) {
c.currentNick = m.Params[0]
c.connected = true
}
// From rfc2812 section 5.2 (Error Replies)
//
// 433 ERR_NICKNAMEINUSE
// "<nick> :Nickname is already in use"
//
// - Returned when a NICK message is processed that results
// in an attempt to change to a currently existing
// nickname.
func handle433(c *Client, m *Message) {
// We only want to try and handle nick collisions during the initial
// handshake.
if c.connected {
return
}
c.currentNick = c.currentNick + "_"
c.Writef("NICK :%s", c.currentNick)
}
// From rfc2812 section 5.2 (Error Replies)
//
// 437 ERR_UNAVAILRESOURCE
// "<nick/channel> :Nick/channel is temporarily unavailable"
//
// - Returned by a server to a user trying to join a channel
// currently blocked by the channel delay mechanism.
//
// - Returned by a server to a user trying to change nickname
// when the desired nickname is blocked by the nick delay
// mechanism.
func handle437(c *Client, m *Message) {
// We only want to try and handle nick collisions during the initial
// handshake.
if c.connected {
return
}
c.currentNick = c.currentNick + "_"
c.Writef("NICK :%s", c.currentNick)
}
func handlePing(c *Client, m *Message) {
reply := m.Copy()
reply.Command = "PONG"
c.WriteMessage(reply)
}
func handlePong(c *Client, m *Message) {
if c.incomingPongChan != nil {
select {
case c.incomingPongChan <- m.Trailing():
default:
// Note that this return isn't really needed, but it helps some code
// coverage tools actually see this line.
return
}
}
}
func handleNick(c *Client, m *Message) {
if m.Prefix.Name == c.currentNick && len(m.Params) > 0 {
c.currentNick = m.Params[0]
}
}
var capFilters = map[string]clientFilter{
"LS": handleCapLs,
"ACK": handleCapAck,
"NAK": handleCapNak,
}
func handleCap(c *Client, m *Message) {
if c.remainingCapResponses <= 0 || len(m.Params) <= 2 {
return
}
if filter, ok := capFilters[m.Params[1]]; ok {
filter(c, m)
}
if c.remainingCapResponses <= 0 {
for key, cap := range c.caps {
if cap.Required && !cap.Enabled {
c.sendError(fmt.Errorf("CAP %s requested but not accepted", key))
return
}
}
c.Write("CAP END")
}
}
func handleCapLs(c *Client, m *Message) {
for _, key := range strings.Split(m.Trailing(), " ") {
cap := c.caps[key]
cap.Available = true
c.caps[key] = cap
}
c.remainingCapResponses--
}
func handleCapAck(c *Client, m *Message) {
for _, key := range strings.Split(m.Trailing(), " ") {
cap := c.caps[key]
cap.Enabled = true
c.caps[key] = cap
}
c.remainingCapResponses--
}
func handleCapNak(c *Client, m *Message) {
// If we got a NAK and this REQ was required, we need to bail
// with an error.
for _, key := range strings.Split(m.Trailing(), " ") {
if c.caps[key].Required {
c.sendError(fmt.Errorf("CAP %s requested but was rejected", key))
return
}
}
c.remainingCapResponses--
}