mirror of
https://github.com/gissleh/irc.git
synced 2026-10-15 19:32:52 +00:00
add ctcp handlers, and improve destroyed handling.
continuous-integration/drone/push Build is failing
continuous-integration/drone/push Build is failing
This commit is contained in:
@@ -60,6 +60,9 @@ var ErrTargetNotFound = errors.New("irc: target not found")
|
|||||||
// status target
|
// status target
|
||||||
var ErrTargetIsStatus = errors.New("irc: cannot remove status target")
|
var ErrTargetIsStatus = errors.New("irc: cannot remove status target")
|
||||||
|
|
||||||
|
// ErrDestroyed is returned by Client.Connect if you try to connect a destroyed client.
|
||||||
|
var ErrDestroyed = errors.New("irc: client destroyed")
|
||||||
|
|
||||||
// A Client is an IRC client. You need to use New to construct it
|
// A Client is an IRC client. You need to use New to construct it
|
||||||
type Client struct {
|
type Client struct {
|
||||||
id string
|
id string
|
||||||
@@ -114,6 +117,8 @@ func New(ctx context.Context, config Config) *Client {
|
|||||||
go client.handleEventLoop()
|
go client.handleEventLoop()
|
||||||
go client.handleSendLoop()
|
go client.handleSendLoop()
|
||||||
|
|
||||||
|
client.EmitNonBlocking(NewEvent("client", "create"))
|
||||||
|
|
||||||
return client
|
return client
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,15 +254,26 @@ func (client *Client) Connect(addr string, ssl bool) (err error) {
|
|||||||
InsecureSkipVerify: client.config.SkipSSLVerification,
|
InsecureSkipVerify: client.config.SkipSSLVerification,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if !client.Destroyed() {
|
||||||
|
client.EmitNonBlocking(NewErrorEvent("connect", "Connect failed: "+err.Error()))
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
conn, err = net.Dial("tcp", addr)
|
conn, err = net.Dial("tcp", addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if !client.Destroyed() {
|
||||||
|
client.EmitNonBlocking(NewErrorEvent("connect", "Connect failed: "+err.Error()))
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if client.Destroyed() {
|
||||||
|
_ = conn.Close()
|
||||||
|
return ErrDestroyed
|
||||||
|
}
|
||||||
|
|
||||||
client.EmitNonBlocking(NewEvent("client", "connect"))
|
client.EmitNonBlocking(NewEvent("client", "connect"))
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
@@ -435,6 +451,11 @@ func (client *Client) Describef(targetName string, format string, a ...interface
|
|||||||
// wait for the event, or the client's destruction.
|
// wait for the event, or the client's destruction.
|
||||||
func (client *Client) Emit(event Event) context.Context {
|
func (client *Client) Emit(event Event) context.Context {
|
||||||
event.ctx, event.cancel = context.WithCancel(client.ctx)
|
event.ctx, event.cancel = context.WithCancel(client.ctx)
|
||||||
|
if client.Destroyed() {
|
||||||
|
event.cancel()
|
||||||
|
return event.ctx
|
||||||
|
}
|
||||||
|
|
||||||
client.events <- &event
|
client.events <- &event
|
||||||
|
|
||||||
return event.ctx
|
return event.ctx
|
||||||
@@ -445,6 +466,10 @@ func (client *Client) Emit(event Event) context.Context {
|
|||||||
// returned context is for.
|
// returned context is for.
|
||||||
func (client *Client) EmitNonBlocking(event Event) context.Context {
|
func (client *Client) EmitNonBlocking(event Event) context.Context {
|
||||||
event.ctx, event.cancel = context.WithCancel(client.ctx)
|
event.ctx, event.cancel = context.WithCancel(client.ctx)
|
||||||
|
if client.Destroyed() {
|
||||||
|
event.cancel()
|
||||||
|
return event.ctx
|
||||||
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case client.events <- &event:
|
case client.events <- &event:
|
||||||
@@ -1374,7 +1399,7 @@ func (client *Client) handleEvent(event *Event) {
|
|||||||
client.handleInTarget(channel, event)
|
client.handleInTarget(channel, event)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Try to target by mentioned channel name
|
// Try to target by mentioned channel name.
|
||||||
for _, token := range strings.Fields(event.Text) {
|
for _, token := range strings.Fields(event.Text) {
|
||||||
if client.isupport.IsChannel(token) {
|
if client.isupport.IsChannel(token) {
|
||||||
channel := client.Channel(token)
|
channel := client.Channel(token)
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gissleh/irc"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CTCP implements the widely used CTCP commands (CLIENTINFO, VERSION, TIME, and PING), as well as the /ping command.
|
||||||
|
// It does not implement DCC.
|
||||||
|
//
|
||||||
|
// For every other CTCP command supported, you should expand the `ctcp.clientinfo.reply` client value like above.
|
||||||
|
func CTCP(event *irc.Event, client *irc.Client) {
|
||||||
|
switch event.Name() {
|
||||||
|
case "client.create":
|
||||||
|
if r, ok := client.Value("ctcp.clientinfo.reply").(string); ok {
|
||||||
|
if !strings.Contains(r, "ACTION PING TIME VERSION") {
|
||||||
|
client.SetValue("ctcp.clientinfo.reply", r+" ACTION PING TIME VERSION")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
client.SetValue("ctcp.clientinfo.reply", "ACTION PING TIME VERSION")
|
||||||
|
}
|
||||||
|
case "ctcp.clientinfo":
|
||||||
|
{
|
||||||
|
response, ok := client.Value("ctcp.clientinfo.reply").(string)
|
||||||
|
if !ok {
|
||||||
|
response = "ACTION PING TIME VERSION"
|
||||||
|
}
|
||||||
|
|
||||||
|
client.SendCTCP("CLIENTINFO", event.Nick, true, response)
|
||||||
|
}
|
||||||
|
case "ctcp.version":
|
||||||
|
{
|
||||||
|
version := "github.com/gissleh/irc v1.0"
|
||||||
|
if v, ok := client.Value("ctcp.version.reply").(string); ok {
|
||||||
|
version = v
|
||||||
|
}
|
||||||
|
|
||||||
|
client.SendCTCP("VERSION", event.Nick, true, version)
|
||||||
|
}
|
||||||
|
case "ctcp.time":
|
||||||
|
{
|
||||||
|
client.SendCTCP("TIME", event.Nick, true, time.Now().Local().Format(time.RFC1123))
|
||||||
|
}
|
||||||
|
case "ctcp.ping":
|
||||||
|
{
|
||||||
|
client.SendCTCP("PING", event.Nick, true, event.Text)
|
||||||
|
}
|
||||||
|
case "input.ping":
|
||||||
|
{
|
||||||
|
args := strings.SplitN(event.Text, " ", 2)
|
||||||
|
targetName := args[0]
|
||||||
|
if targetName == "" {
|
||||||
|
client.EmitNonBlocking(irc.NewErrorEvent("ctcp.pingarg", "/ping needs an argument"))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
client.SendCTCP("PING", targetName, false, strconv.FormatInt(time.Now().UnixNano()/1000000, 10))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -99,7 +99,7 @@ func MRoleplay(event *irc.Event, client *irc.Client) {
|
|||||||
case "input.scenec", "input.narratorc":
|
case "input.scenec", "input.narratorc":
|
||||||
{
|
{
|
||||||
if event.Text == "" {
|
if event.Text == "" {
|
||||||
client.EmitNonBlocking(irc.NewErrorEvent("input", "Usage: /scenec <text...>"))
|
client.EmitNonBlocking(irc.NewErrorEvent("input", "Usage: /"+event.Verb()+" <text...>"))
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user