implement commands
This commit is contained in:
parent
9cc4ff46a2
commit
27efadda57
15
builtin.go
15
builtin.go
@ -92,12 +92,12 @@ func handleConnect(c *Client, e Event) {
|
||||
// nickCollisionHandler helps prevent the client from having conflicting
|
||||
// nicknames with another bot, user, etc.
|
||||
func nickCollisionHandler(c *Client, e Event) {
|
||||
c.Nick(c.GetNick() + "_")
|
||||
c.Commands.Nick(c.GetNick() + "_")
|
||||
}
|
||||
|
||||
// handlePING helps respond to ping requests from the server.
|
||||
func handlePING(c *Client, e Event) {
|
||||
c.Pong(e.Trailing)
|
||||
c.Commands.Pong(e.Trailing)
|
||||
}
|
||||
|
||||
// handleJOIN ensures that the state has updated users and channels.
|
||||
@ -247,14 +247,13 @@ func handleKICK(c *Client, e Event) {
|
||||
// handleNICK ensures that users are renamed in state, or the client name is
|
||||
// up to date.
|
||||
func handleNICK(c *Client, e Event) {
|
||||
if len(e.Params) != 1 {
|
||||
// Something erronous was sent to us.
|
||||
return
|
||||
}
|
||||
|
||||
c.state.mu.Lock()
|
||||
// renameUser updates the LastActive time automatically.
|
||||
c.state.renameUser(e.Source.Name, e.Params[0])
|
||||
if len(e.Params) == 1 {
|
||||
c.state.renameUser(e.Source.Name, e.Params[0])
|
||||
} else if len(e.Trailing) > 0 {
|
||||
c.state.renameUser(e.Source.Name, e.Trailing)
|
||||
}
|
||||
c.state.mu.Unlock()
|
||||
}
|
||||
|
||||
|
334
client.go
334
client.go
@ -34,7 +34,8 @@ type Client struct {
|
||||
// Handlers is a handler which manages internal and external handlers.
|
||||
Handlers *Caller
|
||||
// CTCP is a handler which manages internal and external CTCP handlers.
|
||||
CTCP *CTCP
|
||||
CTCP *CTCP
|
||||
Commands *Commands
|
||||
|
||||
// conn is a net.Conn reference to the IRC server.
|
||||
conn *ircConn
|
||||
@ -176,6 +177,8 @@ func New(config Config) *Client {
|
||||
initTime: time.Now(),
|
||||
}
|
||||
|
||||
c.Commands = &Commands{c: c}
|
||||
|
||||
if c.Config.Debugger == nil {
|
||||
c.debug = log.New(ioutil.Discard, "", 0)
|
||||
} else {
|
||||
@ -566,20 +569,6 @@ func (c *Client) GetNick() (nick string) {
|
||||
return nick
|
||||
}
|
||||
|
||||
// Nick changes the client nickname.
|
||||
func (c *Client) Nick(name string) error {
|
||||
if !IsValidNick(name) {
|
||||
return &ErrInvalidTarget{Target: name}
|
||||
}
|
||||
|
||||
c.state.mu.Lock()
|
||||
c.state.nick = name
|
||||
err := c.Send(&Event{Command: NICK, Params: []string{name}})
|
||||
c.state.mu.Unlock()
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Channels returns the active list of channels that the client is in.
|
||||
// Panics if tracking is disabled.
|
||||
func (c *Client) Channels() []string {
|
||||
@ -614,321 +603,6 @@ func (c *Client) IsInChannel(channel string) bool {
|
||||
return inChannel
|
||||
}
|
||||
|
||||
// Join attempts to enter a list of IRC channels, at bulk if possible to
|
||||
// prevent sending extensive JOIN commands.
|
||||
func (c *Client) Join(channels ...string) error {
|
||||
// We can join multiple channels at once, however we need to ensure that
|
||||
// we are not exceeding the line length. (see maxLength)
|
||||
max := maxLength - len(JOIN) - 1
|
||||
|
||||
var buffer string
|
||||
var err error
|
||||
|
||||
for i := 0; i < len(channels); i++ {
|
||||
if !IsValidChannel(channels[i]) {
|
||||
return &ErrInvalidTarget{Target: channels[i]}
|
||||
}
|
||||
|
||||
if len(buffer+","+channels[i]) > max {
|
||||
err = c.Send(&Event{Command: JOIN, Params: []string{buffer}})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buffer = ""
|
||||
continue
|
||||
}
|
||||
|
||||
if len(buffer) == 0 {
|
||||
buffer = channels[i]
|
||||
} else {
|
||||
buffer += "," + channels[i]
|
||||
}
|
||||
|
||||
if i == len(channels)-1 {
|
||||
return c.Send(&Event{Command: JOIN, Params: []string{buffer}})
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// JoinKey attempts to enter an IRC channel with a password.
|
||||
func (c *Client) JoinKey(channel, password string) error {
|
||||
if !IsValidChannel(channel) {
|
||||
return &ErrInvalidTarget{Target: channel}
|
||||
}
|
||||
|
||||
return c.Send(&Event{Command: JOIN, Params: []string{channel, password}})
|
||||
}
|
||||
|
||||
// Part leaves an IRC channel.
|
||||
func (c *Client) Part(channel, message string) error {
|
||||
if !IsValidChannel(channel) {
|
||||
return &ErrInvalidTarget{Target: channel}
|
||||
}
|
||||
|
||||
return c.Send(&Event{Command: JOIN, Params: []string{channel}})
|
||||
}
|
||||
|
||||
// PartMessage leaves an IRC channel with a specified leave message.
|
||||
func (c *Client) PartMessage(channel, message string) error {
|
||||
if !IsValidChannel(channel) {
|
||||
return &ErrInvalidTarget{Target: channel}
|
||||
}
|
||||
|
||||
return c.Send(&Event{Command: JOIN, Params: []string{channel}, Trailing: message})
|
||||
}
|
||||
|
||||
// SendCTCP sends a CTCP request to target. Note that this method uses
|
||||
// PRIVMSG specifically.
|
||||
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. Note that
|
||||
// this method uses PRIVMSG specifically.
|
||||
func (c *Client) SendCTCPf(target, ctcpType, format string, a ...interface{}) error {
|
||||
return c.SendCTCP(target, ctcpType, fmt.Sprintf(format, a...))
|
||||
}
|
||||
|
||||
// SendCTCPReplyf sends a CTCP response to target using a specific format.
|
||||
// Note that this method uses NOTICE specifically.
|
||||
func (c *Client) SendCTCPReplyf(target, ctcpType, format string, a ...interface{}) error {
|
||||
return c.SendCTCPReply(target, ctcpType, fmt.Sprintf(format, a...))
|
||||
}
|
||||
|
||||
// SendCTCPReply sends a CTCP response to target. Note that this method uses
|
||||
// NOTICE specifically.
|
||||
func (c *Client) SendCTCPReply(target, ctcpType, message string) error {
|
||||
out := encodeCTCPRaw(ctcpType, message)
|
||||
if out == "" {
|
||||
return errors.New("invalid CTCP")
|
||||
}
|
||||
|
||||
return c.Notice(target, out)
|
||||
}
|
||||
|
||||
// Message sends a PRIVMSG to target (either channel, service, or user).
|
||||
func (c *Client) Message(target, message string) error {
|
||||
if !IsValidNick(target) && !IsValidChannel(target) {
|
||||
return &ErrInvalidTarget{Target: target}
|
||||
}
|
||||
|
||||
return c.Send(&Event{Command: PRIVMSG, Params: []string{target}, Trailing: message})
|
||||
}
|
||||
|
||||
// Messagef sends a formated PRIVMSG to target (either channel, service, or
|
||||
// user).
|
||||
func (c *Client) Messagef(target, format string, a ...interface{}) error {
|
||||
return c.Message(target, fmt.Sprintf(format, a...))
|
||||
}
|
||||
|
||||
// Action sends a PRIVMSG ACTION (/me) to target (either channel, service,
|
||||
// or user).
|
||||
func (c *Client) Action(target, message string) error {
|
||||
if !IsValidNick(target) && !IsValidChannel(target) {
|
||||
return &ErrInvalidTarget{Target: target}
|
||||
}
|
||||
|
||||
return c.Send(&Event{
|
||||
Command: PRIVMSG,
|
||||
Params: []string{target},
|
||||
Trailing: fmt.Sprintf("\001ACTION %s\001", message),
|
||||
})
|
||||
}
|
||||
|
||||
// Actionf sends a formated PRIVMSG ACTION (/me) to target (either channel,
|
||||
// service, or user).
|
||||
func (c *Client) Actionf(target, format string, a ...interface{}) error {
|
||||
return c.Action(target, fmt.Sprintf(format, a...))
|
||||
}
|
||||
|
||||
// Notice sends a NOTICE to target (either channel, service, or user).
|
||||
func (c *Client) Notice(target, message string) error {
|
||||
if !IsValidNick(target) && !IsValidChannel(target) {
|
||||
return &ErrInvalidTarget{Target: target}
|
||||
}
|
||||
|
||||
return c.Send(&Event{Command: NOTICE, Params: []string{target}, Trailing: message})
|
||||
}
|
||||
|
||||
// Noticef sends a formated NOTICE to target (either channel, service, or
|
||||
// user).
|
||||
func (c *Client) Noticef(target, format string, a ...interface{}) error {
|
||||
return c.Notice(target, fmt.Sprintf(format, a...))
|
||||
}
|
||||
|
||||
// SendRaw sends a raw string back to the server, without carriage returns
|
||||
// or newlines.
|
||||
func (c *Client) SendRaw(raw string) error {
|
||||
e := ParseEvent(raw)
|
||||
if e == nil {
|
||||
return errors.New("invalid event: " + raw)
|
||||
}
|
||||
|
||||
return c.Send(e)
|
||||
}
|
||||
|
||||
// SendRawf sends a formated string back to the server, without carriage
|
||||
// returns or newlines.
|
||||
func (c *Client) SendRawf(format string, a ...interface{}) error {
|
||||
return c.SendRaw(fmt.Sprintf(format, a...))
|
||||
}
|
||||
|
||||
// Topic sets the topic of channel to message. Does not verify the length
|
||||
// of the topic.
|
||||
func (c *Client) Topic(channel, message string) error {
|
||||
return c.Send(&Event{Command: TOPIC, Params: []string{channel}, Trailing: message})
|
||||
}
|
||||
|
||||
// Who sends a WHO query to the server, which will attempt WHOX by default.
|
||||
// See http://faerion.sourceforge.net/doc/irc/whox.var for more details. This
|
||||
// sends "%tcuhnr,2" per default. Do not use "1" as this will conflict with
|
||||
// girc's builtin tracking functionality.
|
||||
func (c *Client) Who(target string) error {
|
||||
if !IsValidNick(target) && !IsValidChannel(target) && !IsValidUser(target) {
|
||||
return &ErrInvalidTarget{Target: target}
|
||||
}
|
||||
|
||||
return c.Send(&Event{Command: WHO, Params: []string{target, "%tcuhnr,2"}})
|
||||
}
|
||||
|
||||
// Whois sends a WHOIS query to the server, targeted at a specific user.
|
||||
// as WHOIS is a bit slower, you may want to use WHO for brief user info.
|
||||
func (c *Client) Whois(nick string) error {
|
||||
if !IsValidNick(nick) {
|
||||
return &ErrInvalidTarget{Target: nick}
|
||||
}
|
||||
|
||||
return c.Send(&Event{Command: WHOIS, Params: []string{nick}})
|
||||
}
|
||||
|
||||
// Ping sends a PING query to the server, with a specific identifier that
|
||||
// the server should respond with.
|
||||
func (c *Client) Ping(id string) error {
|
||||
return c.Send(&Event{Command: PING, Params: []string{id}})
|
||||
}
|
||||
|
||||
// Pong sends a PONG query to the server, with an identifier which was
|
||||
// received from a previous PING query received by the client.
|
||||
func (c *Client) Pong(id string) error {
|
||||
return c.Send(&Event{Command: PONG, Params: []string{id}})
|
||||
}
|
||||
|
||||
// Oper sends a OPER authentication query to the server, with a username
|
||||
// and password.
|
||||
func (c *Client) Oper(user, pass string) error {
|
||||
return c.Send(&Event{Command: OPER, Params: []string{user, pass}, Sensitive: true})
|
||||
}
|
||||
|
||||
// Kick sends a KICK query to the server, attempting to kick nick from
|
||||
// channel, with reason. If reason is blank, one will not be sent to the
|
||||
// server.
|
||||
func (c *Client) Kick(channel, nick, reason string) error {
|
||||
if !IsValidChannel(channel) {
|
||||
return &ErrInvalidTarget{Target: channel}
|
||||
}
|
||||
|
||||
if !IsValidNick(nick) {
|
||||
return &ErrInvalidTarget{Target: nick}
|
||||
}
|
||||
|
||||
if reason != "" {
|
||||
return c.Send(&Event{Command: KICK, Params: []string{channel, nick}, Trailing: reason})
|
||||
}
|
||||
|
||||
return c.Send(&Event{Command: KICK, Params: []string{channel, nick}})
|
||||
}
|
||||
|
||||
// Invite sends a INVITE query to the server, to invite nick to channel.
|
||||
func (c *Client) Invite(channel, nick string) error {
|
||||
if !IsValidChannel(channel) {
|
||||
return &ErrInvalidTarget{Target: channel}
|
||||
}
|
||||
|
||||
if !IsValidNick(nick) {
|
||||
return &ErrInvalidTarget{Target: nick}
|
||||
}
|
||||
|
||||
return c.Send(&Event{Command: INVITE, Params: []string{nick, channel}})
|
||||
}
|
||||
|
||||
// Away sends a AWAY query to the server, suggesting that the client is no
|
||||
// longer active. If reason is blank, Client.Back() is called. Also see
|
||||
// Client.Back().
|
||||
func (c *Client) Away(reason string) error {
|
||||
if reason == "" {
|
||||
return c.Back()
|
||||
}
|
||||
|
||||
return c.Send(&Event{Command: AWAY, Params: []string{reason}})
|
||||
}
|
||||
|
||||
// Back sends a AWAY query to the server, however the query is blank,
|
||||
// suggesting that the client is active once again. Also see Client.Away().
|
||||
func (c *Client) Back() error {
|
||||
return c.Send(&Event{Command: AWAY})
|
||||
}
|
||||
|
||||
// List sends a LIST query to the server, which will list channels and topics.
|
||||
// Supports multiple channels at once, in hopes it will reduce extensive
|
||||
// LIST queries to the server. Supply no channels to run a list against the
|
||||
// entire server (warning, that may mean LOTS of channels!)
|
||||
func (c *Client) List(channels ...string) error {
|
||||
if len(channels) == 0 {
|
||||
return c.Send(&Event{Command: LIST})
|
||||
}
|
||||
|
||||
// We can LIST multiple channels at once, however we need to ensure that
|
||||
// we are not exceeding the line length. (see maxLength)
|
||||
max := maxLength - len(JOIN) - 1
|
||||
|
||||
var buffer string
|
||||
var err error
|
||||
|
||||
for i := 0; i < len(channels); i++ {
|
||||
if !IsValidChannel(channels[i]) {
|
||||
return &ErrInvalidTarget{Target: channels[i]}
|
||||
}
|
||||
|
||||
if len(buffer+","+channels[i]) > max {
|
||||
err = c.Send(&Event{Command: LIST, Params: []string{buffer}})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buffer = ""
|
||||
continue
|
||||
}
|
||||
|
||||
if len(buffer) == 0 {
|
||||
buffer = channels[i]
|
||||
} else {
|
||||
buffer += "," + channels[i]
|
||||
}
|
||||
|
||||
if i == len(channels)-1 {
|
||||
return c.Send(&Event{Command: LIST, Params: []string{buffer}})
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Whowas sends a WHOWAS query to the server. amount is the amount of results
|
||||
// you want back.
|
||||
func (c *Client) Whowas(nick string, amount int) error {
|
||||
if !IsValidNick(nick) {
|
||||
return &ErrInvalidTarget{Target: nick}
|
||||
}
|
||||
|
||||
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 (or RPL_PROTOCTL).
|
||||
// Will panic if used when tracking has been disabled. Examples of usage:
|
||||
|
@ -48,12 +48,12 @@ func Example_simple() {
|
||||
})
|
||||
|
||||
client.Handlers.Add(girc.CONNECTED, func(c *girc.Client, e girc.Event) {
|
||||
c.Join("#dev")
|
||||
c.Commands.Join("#dev")
|
||||
})
|
||||
|
||||
client.Handlers.Add(girc.PRIVMSG, func(c *girc.Client, e girc.Event) {
|
||||
if strings.Contains(e.Trailing, "hello") {
|
||||
c.Message(e.Params[0], "hello world!")
|
||||
c.Commands.Message(e.Params[0], "hello world!")
|
||||
}
|
||||
})
|
||||
|
||||
@ -88,12 +88,12 @@ func Example_commands() {
|
||||
})
|
||||
|
||||
client.Handlers.Add(girc.CONNECTED, func(c *girc.Client, e girc.Event) {
|
||||
c.Join("#channel", "#other-channel")
|
||||
c.Commands.Join("#channel", "#other-channel")
|
||||
})
|
||||
|
||||
client.Handlers.Add(girc.PRIVMSG, func(c *girc.Client, e girc.Event) {
|
||||
if strings.HasPrefix(e.Trailing, "!hello") {
|
||||
c.Message(e.Params[0], "hello world!")
|
||||
c.Commands.Message(e.Params[0], "hello world!")
|
||||
return
|
||||
}
|
||||
|
||||
|
340
commands.go
Normal file
340
commands.go
Normal file
@ -0,0 +1,340 @@
|
||||
// Copyright (c) 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 (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Commands holds a large list of useful methods to interact with the server,
|
||||
// and wrappers for common events.
|
||||
type Commands struct {
|
||||
c *Client
|
||||
}
|
||||
|
||||
// Nick changes the client nickname.
|
||||
func (cmd *Commands) Nick(name string) error {
|
||||
if !IsValidNick(name) {
|
||||
return &ErrInvalidTarget{Target: name}
|
||||
}
|
||||
|
||||
return cmd.c.Send(&Event{Command: NICK, Params: []string{name}})
|
||||
}
|
||||
|
||||
// Join attempts to enter a list of IRC channels, at bulk if possible to
|
||||
// prevent sending extensive JOIN commands.
|
||||
func (cmd *Commands) Join(channels ...string) error {
|
||||
// We can join multiple channels at once, however we need to ensure that
|
||||
// we are not exceeding the line length. (see maxLength)
|
||||
max := maxLength - len(JOIN) - 1
|
||||
|
||||
var buffer string
|
||||
var err error
|
||||
|
||||
for i := 0; i < len(channels); i++ {
|
||||
if !IsValidChannel(channels[i]) {
|
||||
return &ErrInvalidTarget{Target: channels[i]}
|
||||
}
|
||||
|
||||
if len(buffer+","+channels[i]) > max {
|
||||
err = cmd.c.Send(&Event{Command: JOIN, Params: []string{buffer}})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buffer = ""
|
||||
continue
|
||||
}
|
||||
|
||||
if len(buffer) == 0 {
|
||||
buffer = channels[i]
|
||||
} else {
|
||||
buffer += "," + channels[i]
|
||||
}
|
||||
|
||||
if i == len(channels)-1 {
|
||||
return cmd.c.Send(&Event{Command: JOIN, Params: []string{buffer}})
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// JoinKey attempts to enter an IRC channel with a password.
|
||||
func (cmd *Commands) JoinKey(channel, password string) error {
|
||||
if !IsValidChannel(channel) {
|
||||
return &ErrInvalidTarget{Target: channel}
|
||||
}
|
||||
|
||||
return cmd.c.Send(&Event{Command: JOIN, Params: []string{channel, password}})
|
||||
}
|
||||
|
||||
// Part leaves an IRC channel.
|
||||
func (cmd *Commands) Part(channel, message string) error {
|
||||
if !IsValidChannel(channel) {
|
||||
return &ErrInvalidTarget{Target: channel}
|
||||
}
|
||||
|
||||
return cmd.c.Send(&Event{Command: JOIN, Params: []string{channel}})
|
||||
}
|
||||
|
||||
// PartMessage leaves an IRC channel with a specified leave message.
|
||||
func (cmd *Commands) PartMessage(channel, message string) error {
|
||||
if !IsValidChannel(channel) {
|
||||
return &ErrInvalidTarget{Target: channel}
|
||||
}
|
||||
|
||||
return cmd.c.Send(&Event{Command: JOIN, Params: []string{channel}, Trailing: message})
|
||||
}
|
||||
|
||||
// SendCTCP sends a CTCP request to target. Note that this method uses
|
||||
// PRIVMSG specifically.
|
||||
func (cmd *Commands) SendCTCP(target, ctcpType, message string) error {
|
||||
out := encodeCTCPRaw(ctcpType, message)
|
||||
if out == "" {
|
||||
return errors.New("invalid CTCP")
|
||||
}
|
||||
|
||||
return cmd.Message(target, out)
|
||||
}
|
||||
|
||||
// SendCTCPf sends a CTCP request to target using a specific format. Note that
|
||||
// this method uses PRIVMSG specifically.
|
||||
func (cmd *Commands) SendCTCPf(target, ctcpType, format string, a ...interface{}) error {
|
||||
return cmd.SendCTCP(target, ctcpType, fmt.Sprintf(format, a...))
|
||||
}
|
||||
|
||||
// SendCTCPReplyf sends a CTCP response to target using a specific format.
|
||||
// Note that this method uses NOTICE specifically.
|
||||
func (cmd *Commands) SendCTCPReplyf(target, ctcpType, format string, a ...interface{}) error {
|
||||
return cmd.SendCTCPReply(target, ctcpType, fmt.Sprintf(format, a...))
|
||||
}
|
||||
|
||||
// SendCTCPReply sends a CTCP response to target. Note that this method uses
|
||||
// NOTICE specifically.
|
||||
func (cmd *Commands) SendCTCPReply(target, ctcpType, message string) error {
|
||||
out := encodeCTCPRaw(ctcpType, message)
|
||||
if out == "" {
|
||||
return errors.New("invalid CTCP")
|
||||
}
|
||||
|
||||
return cmd.Notice(target, out)
|
||||
}
|
||||
|
||||
// Message sends a PRIVMSG to target (either channel, service, or user).
|
||||
func (cmd *Commands) Message(target, message string) error {
|
||||
if !IsValidNick(target) && !IsValidChannel(target) {
|
||||
return &ErrInvalidTarget{Target: target}
|
||||
}
|
||||
|
||||
return cmd.c.Send(&Event{Command: PRIVMSG, Params: []string{target}, Trailing: message})
|
||||
}
|
||||
|
||||
// Messagef sends a formated PRIVMSG to target (either channel, service, or
|
||||
// user).
|
||||
func (cmd *Commands) Messagef(target, format string, a ...interface{}) error {
|
||||
return cmd.Message(target, fmt.Sprintf(format, a...))
|
||||
}
|
||||
|
||||
// Action sends a PRIVMSG ACTION (/me) to target (either channel, service,
|
||||
// or user).
|
||||
func (cmd *Commands) Action(target, message string) error {
|
||||
if !IsValidNick(target) && !IsValidChannel(target) {
|
||||
return &ErrInvalidTarget{Target: target}
|
||||
}
|
||||
|
||||
return cmd.c.Send(&Event{
|
||||
Command: PRIVMSG,
|
||||
Params: []string{target},
|
||||
Trailing: fmt.Sprintf("\001ACTION %s\001", message),
|
||||
})
|
||||
}
|
||||
|
||||
// Actionf sends a formated PRIVMSG ACTION (/me) to target (either channel,
|
||||
// service, or user).
|
||||
func (cmd *Commands) Actionf(target, format string, a ...interface{}) error {
|
||||
return cmd.Action(target, fmt.Sprintf(format, a...))
|
||||
}
|
||||
|
||||
// Notice sends a NOTICE to target (either channel, service, or user).
|
||||
func (cmd *Commands) Notice(target, message string) error {
|
||||
if !IsValidNick(target) && !IsValidChannel(target) {
|
||||
return &ErrInvalidTarget{Target: target}
|
||||
}
|
||||
|
||||
return cmd.c.Send(&Event{Command: NOTICE, Params: []string{target}, Trailing: message})
|
||||
}
|
||||
|
||||
// Noticef sends a formated NOTICE to target (either channel, service, or
|
||||
// user).
|
||||
func (cmd *Commands) Noticef(target, format string, a ...interface{}) error {
|
||||
return cmd.Notice(target, fmt.Sprintf(format, a...))
|
||||
}
|
||||
|
||||
// SendRaw sends a raw string back to the server, without carriage returns
|
||||
// or newlines.
|
||||
func (cmd *Commands) SendRaw(raw string) error {
|
||||
e := ParseEvent(raw)
|
||||
if e == nil {
|
||||
return errors.New("invalid event: " + raw)
|
||||
}
|
||||
|
||||
return cmd.c.Send(e)
|
||||
}
|
||||
|
||||
// SendRawf sends a formated string back to the server, without carriage
|
||||
// returns or newlines.
|
||||
func (cmd *Commands) SendRawf(format string, a ...interface{}) error {
|
||||
return cmd.SendRaw(fmt.Sprintf(format, a...))
|
||||
}
|
||||
|
||||
// Topic sets the topic of channel to message. Does not verify the length
|
||||
// of the topic.
|
||||
func (cmd *Commands) Topic(channel, message string) error {
|
||||
return cmd.c.Send(&Event{Command: TOPIC, Params: []string{channel}, Trailing: message})
|
||||
}
|
||||
|
||||
// Who sends a WHO query to the server, which will attempt WHOX by default.
|
||||
// See http://faerion.sourceforge.net/doc/irc/whox.var for more details. This
|
||||
// sends "%tcuhnr,2" per default. Do not use "1" as this will conflict with
|
||||
// girc's builtin tracking functionality.
|
||||
func (cmd *Commands) Who(target string) error {
|
||||
if !IsValidNick(target) && !IsValidChannel(target) && !IsValidUser(target) {
|
||||
return &ErrInvalidTarget{Target: target}
|
||||
}
|
||||
|
||||
return cmd.c.Send(&Event{Command: WHO, Params: []string{target, "%tcuhnr,2"}})
|
||||
}
|
||||
|
||||
// Whois sends a WHOIS query to the server, targeted at a specific user.
|
||||
// as WHOIS is a bit slower, you may want to use WHO for brief user info.
|
||||
func (cmd *Commands) Whois(nick string) error {
|
||||
if !IsValidNick(nick) {
|
||||
return &ErrInvalidTarget{Target: nick}
|
||||
}
|
||||
|
||||
return cmd.c.Send(&Event{Command: WHOIS, Params: []string{nick}})
|
||||
}
|
||||
|
||||
// Ping sends a PING query to the server, with a specific identifier that
|
||||
// the server should respond with.
|
||||
func (cmd *Commands) Ping(id string) error {
|
||||
return cmd.c.Send(&Event{Command: PING, Params: []string{id}})
|
||||
}
|
||||
|
||||
// Pong sends a PONG query to the server, with an identifier which was
|
||||
// received from a previous PING query received by the client.
|
||||
func (cmd *Commands) Pong(id string) error {
|
||||
return cmd.c.Send(&Event{Command: PONG, Params: []string{id}})
|
||||
}
|
||||
|
||||
// Oper sends a OPER authentication query to the server, with a username
|
||||
// and password.
|
||||
func (cmd *Commands) Oper(user, pass string) error {
|
||||
return cmd.c.Send(&Event{Command: OPER, Params: []string{user, pass}, Sensitive: true})
|
||||
}
|
||||
|
||||
// Kick sends a KICK query to the server, attempting to kick nick from
|
||||
// channel, with reason. If reason is blank, one will not be sent to the
|
||||
// server.
|
||||
func (cmd *Commands) Kick(channel, nick, reason string) error {
|
||||
if !IsValidChannel(channel) {
|
||||
return &ErrInvalidTarget{Target: channel}
|
||||
}
|
||||
|
||||
if !IsValidNick(nick) {
|
||||
return &ErrInvalidTarget{Target: nick}
|
||||
}
|
||||
|
||||
if reason != "" {
|
||||
return cmd.c.Send(&Event{Command: KICK, Params: []string{channel, nick}, Trailing: reason})
|
||||
}
|
||||
|
||||
return cmd.c.Send(&Event{Command: KICK, Params: []string{channel, nick}})
|
||||
}
|
||||
|
||||
// Invite sends a INVITE query to the server, to invite nick to channel.
|
||||
func (cmd *Commands) Invite(channel, nick string) error {
|
||||
if !IsValidChannel(channel) {
|
||||
return &ErrInvalidTarget{Target: channel}
|
||||
}
|
||||
|
||||
if !IsValidNick(nick) {
|
||||
return &ErrInvalidTarget{Target: nick}
|
||||
}
|
||||
|
||||
return cmd.c.Send(&Event{Command: INVITE, Params: []string{nick, channel}})
|
||||
}
|
||||
|
||||
// Away sends a AWAY query to the server, suggesting that the client is no
|
||||
// longer active. If reason is blank, Client.Back() is called. Also see
|
||||
// Client.Back().
|
||||
func (cmd *Commands) Away(reason string) error {
|
||||
if reason == "" {
|
||||
return cmd.Back()
|
||||
}
|
||||
|
||||
return cmd.c.Send(&Event{Command: AWAY, Params: []string{reason}})
|
||||
}
|
||||
|
||||
// Back sends a AWAY query to the server, however the query is blank,
|
||||
// suggesting that the client is active once again. Also see Client.Away().
|
||||
func (cmd *Commands) Back() error {
|
||||
return cmd.c.Send(&Event{Command: AWAY})
|
||||
}
|
||||
|
||||
// List sends a LIST query to the server, which will list channels and topics.
|
||||
// Supports multiple channels at once, in hopes it will reduce extensive
|
||||
// LIST queries to the server. Supply no channels to run a list against the
|
||||
// entire server (warning, that may mean LOTS of channels!)
|
||||
func (cmd *Commands) List(channels ...string) error {
|
||||
if len(channels) == 0 {
|
||||
return cmd.c.Send(&Event{Command: LIST})
|
||||
}
|
||||
|
||||
// We can LIST multiple channels at once, however we need to ensure that
|
||||
// we are not exceeding the line length. (see maxLength)
|
||||
max := maxLength - len(JOIN) - 1
|
||||
|
||||
var buffer string
|
||||
var err error
|
||||
|
||||
for i := 0; i < len(channels); i++ {
|
||||
if !IsValidChannel(channels[i]) {
|
||||
return &ErrInvalidTarget{Target: channels[i]}
|
||||
}
|
||||
|
||||
if len(buffer+","+channels[i]) > max {
|
||||
err = cmd.c.Send(&Event{Command: LIST, Params: []string{buffer}})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buffer = ""
|
||||
continue
|
||||
}
|
||||
|
||||
if len(buffer) == 0 {
|
||||
buffer = channels[i]
|
||||
} else {
|
||||
buffer += "," + channels[i]
|
||||
}
|
||||
|
||||
if i == len(channels)-1 {
|
||||
return cmd.c.Send(&Event{Command: LIST, Params: []string{buffer}})
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Whowas sends a WHOWAS query to the server. amount is the amount of results
|
||||
// you want back.
|
||||
func (cmd *Commands) Whowas(nick string, amount int) error {
|
||||
if !IsValidNick(nick) {
|
||||
return &ErrInvalidTarget{Target: nick}
|
||||
}
|
||||
|
||||
return cmd.c.Send(&Event{Command: WHOWAS, Params: []string{nick, string(amount)}})
|
||||
}
|
14
ctcp.go
14
ctcp.go
@ -146,7 +146,7 @@ func (c *CTCP) call(client *Client, event *CTCPEvent) {
|
||||
if _, ok := c.handlers[event.Command]; !ok {
|
||||
// Send a ERRMSG reply, if we know who sent it.
|
||||
if event.Source != nil && IsValidNick(event.Source.Name) {
|
||||
client.SendCTCPReply(event.Source.Name, CTCP_ERRMSG, "that is an unknown CTCP query")
|
||||
client.Commands.SendCTCPReply(event.Source.Name, CTCP_ERRMSG, "that is an unknown CTCP query")
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -237,7 +237,7 @@ func handleCTCPPing(client *Client, ctcp CTCPEvent) {
|
||||
if ctcp.Reply {
|
||||
return
|
||||
}
|
||||
client.SendCTCPReply(ctcp.Source.Name, CTCP_PING, ctcp.Text)
|
||||
client.Commands.SendCTCPReply(ctcp.Source.Name, CTCP_PING, ctcp.Text)
|
||||
}
|
||||
|
||||
// handleCTCPPong replies with a pong.
|
||||
@ -245,7 +245,7 @@ func handleCTCPPong(client *Client, ctcp CTCPEvent) {
|
||||
if ctcp.Reply {
|
||||
return
|
||||
}
|
||||
client.SendCTCPReply(ctcp.Source.Name, CTCP_PONG, "")
|
||||
client.Commands.SendCTCPReply(ctcp.Source.Name, CTCP_PONG, "")
|
||||
}
|
||||
|
||||
// handleCTCPVersion replies with the name of the client, Go version, as well
|
||||
@ -253,11 +253,11 @@ func handleCTCPPong(client *Client, ctcp CTCPEvent) {
|
||||
// arm, etc).
|
||||
func handleCTCPVersion(client *Client, ctcp CTCPEvent) {
|
||||
if client.Config.Version != "" {
|
||||
client.SendCTCPReply(ctcp.Source.Name, CTCP_VERSION, client.Config.Version)
|
||||
client.Commands.SendCTCPReply(ctcp.Source.Name, CTCP_VERSION, client.Config.Version)
|
||||
return
|
||||
}
|
||||
|
||||
client.SendCTCPReplyf(
|
||||
client.Commands.SendCTCPReplyf(
|
||||
ctcp.Source.Name, CTCP_VERSION,
|
||||
"girc (github.com/lrstanley/girc) using %s (%s, %s)",
|
||||
runtime.Version(), runtime.GOOS, runtime.GOARCH,
|
||||
@ -266,11 +266,11 @@ func handleCTCPVersion(client *Client, ctcp CTCPEvent) {
|
||||
|
||||
// handleCTCPSource replies with the public git location of this library.
|
||||
func handleCTCPSource(client *Client, ctcp CTCPEvent) {
|
||||
client.SendCTCPReply(ctcp.Source.Name, CTCP_SOURCE, "https://github.com/lrstanley/girc")
|
||||
client.Commands.SendCTCPReply(ctcp.Source.Name, CTCP_SOURCE, "https://github.com/lrstanley/girc")
|
||||
}
|
||||
|
||||
// handleCTCPTime replies with a RFC 1123 (Z) formatted version of Go's
|
||||
// local time.
|
||||
func handleCTCPTime(client *Client, ctcp CTCPEvent) {
|
||||
client.SendCTCPReply(ctcp.Source.Name, CTCP_TIME, ":"+time.Now().Format(time.RFC1123Z))
|
||||
client.Commands.SendCTCPReply(ctcp.Source.Name, CTCP_TIME, ":"+time.Now().Format(time.RFC1123Z))
|
||||
}
|
||||
|
5
event.go
5
event.go
@ -95,10 +95,7 @@ func ParseEvent(raw string) (e *Event) {
|
||||
j++
|
||||
|
||||
// Find prefix for trailer.
|
||||
i = bytes.Index([]byte(raw[j:]), []byte{eventSpace, messagePrefix})
|
||||
if i != -1 {
|
||||
i++
|
||||
}
|
||||
i = strings.IndexByte(raw[j:], messagePrefix)
|
||||
|
||||
if i < 0 || raw[j+i-1] != eventSpace {
|
||||
// No trailing argument.
|
||||
|
5
state.go
5
state.go
@ -267,6 +267,11 @@ func (s *state) renameUser(from, to string) {
|
||||
return
|
||||
}
|
||||
|
||||
// Update our nickname.
|
||||
if from == s.nick {
|
||||
s.nick = to
|
||||
}
|
||||
|
||||
for k := range s.channels {
|
||||
// Check to see if they're in this channel.
|
||||
if _, ok := s.channels[k].users[from]; !ok {
|
||||
|
Loading…
Reference in New Issue
Block a user