start working on ctcp handling
This commit is contained in:
parent
3364fbdbe6
commit
3abefe033c
15
client.go
15
client.go
@ -524,6 +524,21 @@ func (c *Client) PartMessage(channel, message string) error {
|
|||||||
return c.Send(&Event{Command: JOIN, Params: []string{channel}, Trailing: message})
|
return c.Send(&Event{Command: JOIN, Params: []string{channel}, Trailing: message})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendCTCP sends a CTCP request to target.
|
||||||
|
func (c *Client) SendCTCP(target, ctcpType, message string) error {
|
||||||
|
out := encodeCTCPRaw(ctcpType, message)
|
||||||
|
if out == "" {
|
||||||
|
return errors.New("invalid CTCP")
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Message(target, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendCTCPf sends a CTCP request to target using a specific format.
|
||||||
|
func (c *Client) SendCTCPf(target, ctcpType, format string, a ...interface{}) error {
|
||||||
|
return c.SendCTCP(target, ctcpType, fmt.Sprintf(format, a...))
|
||||||
|
}
|
||||||
|
|
||||||
// Message sends a PRIVMSG to target (either channel, service, or user).
|
// Message sends a PRIVMSG to target (either channel, service, or user).
|
||||||
func (c *Client) Message(target, message string) error {
|
func (c *Client) Message(target, message string) error {
|
||||||
if !IsValidNick(target) && !IsValidChannel(target) {
|
if !IsValidNick(target) && !IsValidChannel(target) {
|
||||||
|
12
contants.go
12
contants.go
@ -4,6 +4,18 @@
|
|||||||
|
|
||||||
package girc
|
package girc
|
||||||
|
|
||||||
|
// Standard CTCP based constants
|
||||||
|
const (
|
||||||
|
CTCP_PING = "PING"
|
||||||
|
CTCP_PONG = "PONG"
|
||||||
|
CTCP_VERSION = "VERSION"
|
||||||
|
CTCP_USERINFO = "USERINFO"
|
||||||
|
CTCP_CLIENTINFO = "CLIENTINFO"
|
||||||
|
CTCP_FINGER = "FINGER"
|
||||||
|
CTCP_SOURCE = "SOURCE"
|
||||||
|
CTCP_TIME = "TIME"
|
||||||
|
)
|
||||||
|
|
||||||
// Misc constants for use with the client.
|
// Misc constants for use with the client.
|
||||||
const (
|
const (
|
||||||
ALLEVENTS = "*" // trigger on all events
|
ALLEVENTS = "*" // trigger on all events
|
||||||
|
151
ctcp.go
Normal file
151
ctcp.go
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
// Copyright 2016 Liam Stanley <me@liamstanley.io>. All rights reserved.
|
||||||
|
// Use of this source code is governed by the MIT license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
package girc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
const ctcpDelim byte = 0x01 // Prefix and suffix for CTCP messages.
|
||||||
|
|
||||||
|
type CTCPEvent struct {
|
||||||
|
Source *Source
|
||||||
|
Command string
|
||||||
|
Text string
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeCTCP(e *Event) *CTCPEvent {
|
||||||
|
if len(e.Params) != 1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Command != "PRIVMSG" || !IsValidNick(e.Params[0]) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Trailing[0] != ctcpDelim || e.Trailing[len(e.Trailing)-1] != ctcpDelim {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
text := e.Trailing[1 : len(e.Trailing)-1]
|
||||||
|
|
||||||
|
s := strings.IndexByte(text, space)
|
||||||
|
|
||||||
|
// Check to see if it only contains a tag.
|
||||||
|
if s < 0 {
|
||||||
|
for i := 0; i < len(text); i++ {
|
||||||
|
// Check for A-Z, 0-9.
|
||||||
|
if (text[i] < 0x41 || text[i] > 0x5A) && (text[i] < 0x30 || text[i] > 0x39) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &CTCPEvent{Source: e.Source, Command: text}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop through checking the tag first.
|
||||||
|
for i := 0; i < s; i++ {
|
||||||
|
// Check for A-Z, 0-9.
|
||||||
|
if (text[i] < 0x41 || text[i] > 0x5A) && (text[i] < 0x30 || text[i] > 0x39) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &CTCPEvent{Source: e.Source, Command: text[1:s], Text: text[s+1 : len(text)-1]}
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeCTCP(ctcp *CTCPEvent) (out string) {
|
||||||
|
if ctcp == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return encodeCTCPRaw(ctcp.Command, ctcp.Text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeCTCPRaw(cmd, text string) (out string) {
|
||||||
|
if len(cmd) <= 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
out = string(ctcpDelim) + cmd
|
||||||
|
|
||||||
|
if len(text) > 0 {
|
||||||
|
out += string(space) + text
|
||||||
|
}
|
||||||
|
|
||||||
|
return out + string(ctcpDelim)
|
||||||
|
}
|
||||||
|
|
||||||
|
type CTCP struct {
|
||||||
|
// mu is the mutex that should be used when accessing callbacks.
|
||||||
|
mu sync.RWMutex
|
||||||
|
// handlers is a map of CTCP message -> functions.
|
||||||
|
handlers map[string]CTCPHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCTCP() *CTCP {
|
||||||
|
return &CTCP{handlers: map[string]CTCPHandler{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CTCP) Call(event *CTCPEvent, client *Client) {
|
||||||
|
c.mu.RLock()
|
||||||
|
if _, ok := c.handlers[event.Command]; !ok {
|
||||||
|
c.mu.RUnlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
go c.handlers[event.Command](client, event)
|
||||||
|
c.mu.RUnlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CTCP) parseCMD(cmd string) string {
|
||||||
|
cmd = strings.ToUpper(cmd)
|
||||||
|
|
||||||
|
for i := 0; i < len(cmd); i++ {
|
||||||
|
// Check for A-Z, 0-9.
|
||||||
|
if (cmd[i] < 0x41 || cmd[i] > 0x5A) && (cmd[i] < 0x30 || cmd[i] > 0x39) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CTCP) Set(cmd string, handler func(client *Client, ctcp *CTCPEvent)) {
|
||||||
|
if cmd = c.parseCMD(cmd); cmd == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.mu.Lock()
|
||||||
|
c.handlers[cmd] = CTCPHandler(handler)
|
||||||
|
c.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CTCP) Clear(cmd string) {
|
||||||
|
if cmd = c.parseCMD(cmd); cmd == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.mu.Lock()
|
||||||
|
delete(c.handlers, cmd)
|
||||||
|
c.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CTCP) ClearAll() {
|
||||||
|
c.mu.Lock()
|
||||||
|
c.handlers = map[string]CTCPHandler{}
|
||||||
|
c.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CTCPHandler is a type that represents the function necessary to
|
||||||
|
// implement a CTCP handler.
|
||||||
|
type CTCPHandler func(client *Client, ctcp *CTCPEvent)
|
||||||
|
|
||||||
|
func (c *CTCP) addDefaultHandlers() {}
|
||||||
|
|
||||||
|
func handleCTCPPing(client *Client, ctcp *CTCPEvent) {
|
||||||
|
client.SendCTCP(ctcp.Source.Name, CTCP_PING, "")
|
||||||
|
}
|
4
event.go
4
event.go
@ -34,7 +34,7 @@ func cutCRFunc(r rune) bool {
|
|||||||
// CR or LF>
|
// CR or LF>
|
||||||
// <crlf> :: CR LF
|
// <crlf> :: CR LF
|
||||||
type Event struct {
|
type Event struct {
|
||||||
*Source // The source of the event.
|
Source *Source // The source of the event.
|
||||||
Command string // the IRC command, e.g. JOIN, PRIVMSG, KILL.
|
Command string // the IRC command, e.g. JOIN, PRIVMSG, KILL.
|
||||||
Params []string // parameters to the command. Commonly nickname, channel, etc.
|
Params []string // parameters to the command. Commonly nickname, channel, etc.
|
||||||
Trailing string // any trailing data. e.g. with a PRIVMSG, this is the message text.
|
Trailing string // any trailing data. e.g. with a PRIVMSG, this is the message text.
|
||||||
@ -240,7 +240,7 @@ func (e *Event) IsAction() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if !strings.HasPrefix(e.Trailing, "\001ACTION") || !strings.HasSuffix(e.Trailing, "\001") {
|
if !strings.HasPrefix(e.Trailing, "\001ACTION") || e.Trailing[len(e.Trailing)-1] != ctcpDelim {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user