mirror of
https://github.com/gissleh/irc.git
synced 2026-10-15 19:32:52 +00:00
clean up code, add client-specific handlers.
This commit is contained in:
@@ -43,15 +43,15 @@ var ErrNoConnection = errors.New("irc: no connection")
|
|||||||
// added to the client.
|
// added to the client.
|
||||||
var ErrTargetAlreadyAdded = errors.New("irc: target already added")
|
var ErrTargetAlreadyAdded = errors.New("irc: target already added")
|
||||||
|
|
||||||
// ErrTargetConflict is returned by Clinet.AddTarget if there already exists a target
|
// ErrTargetConflict is returned by Client.AddTarget if there already exists a target
|
||||||
// matching the name and kind.
|
// matching the name and kind.
|
||||||
var ErrTargetConflict = errors.New("irc: target name and kind match existing target")
|
var ErrTargetConflict = errors.New("irc: target name and kind match existing target")
|
||||||
|
|
||||||
// ErrTargetNotFound is returned by Clinet.RemoveTarget if the target is not part of
|
// ErrTargetNotFound is returned by Client.RemoveTarget if the target is not part of
|
||||||
// the client's target list
|
// the client's target list
|
||||||
var ErrTargetNotFound = errors.New("irc: target not found")
|
var ErrTargetNotFound = errors.New("irc: target not found")
|
||||||
|
|
||||||
// ErrTargetIsStatus is returned by Clinet.RemoveTarget if the target is the client's
|
// ErrTargetIsStatus is returned by Client.RemoveTarget if the target is the client's
|
||||||
// status target
|
// status target
|
||||||
var ErrTargetIsStatus = errors.New("irc: cannot remove status target")
|
var ErrTargetIsStatus = errors.New("irc: cannot remove status target")
|
||||||
|
|
||||||
@@ -85,6 +85,8 @@ type Client struct {
|
|||||||
status *Status
|
status *Status
|
||||||
targets []Target
|
targets []Target
|
||||||
targetIds map[Target]string
|
targetIds map[Target]string
|
||||||
|
|
||||||
|
handlers []Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new client. The context can be context.Background if you want manually to
|
// New creates a new client. The context can be context.Background if you want manually to
|
||||||
@@ -102,7 +104,7 @@ func New(ctx context.Context, config Config) *Client {
|
|||||||
status: &Status{},
|
status: &Status{},
|
||||||
}
|
}
|
||||||
|
|
||||||
client.AddTarget(client.status)
|
_, _ = client.AddTarget(client.status)
|
||||||
|
|
||||||
client.ctx, client.cancel = context.WithCancel(ctx)
|
client.ctx, client.cancel = context.WithCancel(ctx)
|
||||||
|
|
||||||
@@ -174,6 +176,7 @@ func (client *Client) Ready() bool {
|
|||||||
|
|
||||||
func (client *Client) State() ClientState {
|
func (client *Client) State() ClientState {
|
||||||
client.mutex.RLock()
|
client.mutex.RLock()
|
||||||
|
|
||||||
state := ClientState{
|
state := ClientState{
|
||||||
Nick: client.nick,
|
Nick: client.nick,
|
||||||
User: client.user,
|
User: client.user,
|
||||||
@@ -190,6 +193,7 @@ func (client *Client) State() ClientState {
|
|||||||
state.Caps = append(state.Caps, key)
|
state.Caps = append(state.Caps, key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
sort.Strings(state.Caps)
|
||||||
|
|
||||||
for _, target := range client.targets {
|
for _, target := range client.targets {
|
||||||
tstate := target.State()
|
tstate := target.State()
|
||||||
@@ -200,8 +204,6 @@ func (client *Client) State() ClientState {
|
|||||||
|
|
||||||
client.mutex.RUnlock()
|
client.mutex.RUnlock()
|
||||||
|
|
||||||
sort.Strings(state.Caps)
|
|
||||||
|
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,7 +212,7 @@ func (client *Client) Connect(addr string, ssl bool) (err error) {
|
|||||||
var conn net.Conn
|
var conn net.Conn
|
||||||
|
|
||||||
if client.Connected() {
|
if client.Connected() {
|
||||||
client.Disconnect()
|
_ = client.Disconnect()
|
||||||
}
|
}
|
||||||
|
|
||||||
client.isupport.Reset()
|
client.isupport.Reset()
|
||||||
@@ -219,7 +221,7 @@ func (client *Client) Connect(addr string, ssl bool) (err error) {
|
|||||||
client.quit = false
|
client.quit = false
|
||||||
client.mutex.Unlock()
|
client.mutex.Unlock()
|
||||||
|
|
||||||
client.EmitSync(context.Background(), NewEvent("client", "connecting"))
|
_ = client.EmitSync(context.Background(), NewEvent("client", "connecting"))
|
||||||
|
|
||||||
if ssl {
|
if ssl {
|
||||||
conn, err = tls.Dial("tcp", addr, &tls.Config{
|
conn, err = tls.Dial("tcp", addr, &tls.Config{
|
||||||
@@ -283,9 +285,7 @@ func (client *Client) Disconnect() error {
|
|||||||
|
|
||||||
client.quit = true
|
client.quit = true
|
||||||
|
|
||||||
err := client.conn.Close()
|
return client.conn.Close()
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connected returns true if the client has a connection
|
// Connected returns true if the client has a connection
|
||||||
@@ -314,7 +314,7 @@ func (client *Client) Send(line string) error {
|
|||||||
_, err := conn.Write([]byte(line))
|
_, err := conn.Write([]byte(line))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
client.EmitNonBlocking(NewErrorEvent("network", err.Error()))
|
client.EmitNonBlocking(NewErrorEvent("network", err.Error()))
|
||||||
client.Disconnect()
|
_ = client.Disconnect()
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
@@ -402,7 +402,7 @@ func (client *Client) Emit(event Event) context.Context {
|
|||||||
return event.ctx
|
return event.ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
// EmitNonBlocking is just like emit, but it will spin off a goroutine if the channel is full.
|
// EmitNonBlocking is just like emitInGlobalHandlers, but it will spin off a goroutine if the channel is full.
|
||||||
// This lets it be called from other handlers without ever blocking. See Emit for what the
|
// This lets it be called from other handlers without ever blocking. See Emit for what the
|
||||||
// returned context is for.
|
// returned context is for.
|
||||||
func (client *Client) EmitNonBlocking(event Event) context.Context {
|
func (client *Client) EmitNonBlocking(event Event) context.Context {
|
||||||
@@ -476,7 +476,7 @@ func (client *Client) SetValue(key string, value interface{}) {
|
|||||||
// Destroy destroys the client, which will lead to a disconnect. Cancelling the
|
// Destroy destroys the client, which will lead to a disconnect. Cancelling the
|
||||||
// parent context will do the same.
|
// parent context will do the same.
|
||||||
func (client *Client) Destroy() {
|
func (client *Client) Destroy() {
|
||||||
client.Disconnect()
|
_ = client.Disconnect()
|
||||||
client.cancel()
|
client.cancel()
|
||||||
close(client.sends)
|
close(client.sends)
|
||||||
close(client.events)
|
close(client.events)
|
||||||
@@ -668,6 +668,14 @@ func (client *Client) FindUser(nick string) (u list.User, ok bool) {
|
|||||||
return list.User{}, false
|
return list.User{}, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddHandler adds a handler. This is thread safe, unlike adding global handlers.
|
||||||
|
func (client *Client) AddHandler(handler Handler) {
|
||||||
|
client.mutex.Lock()
|
||||||
|
client.handlers = append(client.handlers[:0], client.handlers...)
|
||||||
|
client.handlers = append(client.handlers, handler)
|
||||||
|
client.mutex.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
func (client *Client) handleEventLoop() {
|
func (client *Client) handleEventLoop() {
|
||||||
ticker := time.NewTicker(time.Second * 30)
|
ticker := time.NewTicker(time.Second * 30)
|
||||||
|
|
||||||
@@ -680,7 +688,6 @@ func (client *Client) handleEventLoop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
client.handleEvent(event)
|
client.handleEvent(event)
|
||||||
emit(event, client)
|
|
||||||
|
|
||||||
// Turn an unhandled input into a raw command.
|
// Turn an unhandled input into a raw command.
|
||||||
if event.kind == "input" && !event.preventedDefault {
|
if event.kind == "input" && !event.preventedDefault {
|
||||||
@@ -695,7 +702,6 @@ func (client *Client) handleEventLoop() {
|
|||||||
event.ctx, event.cancel = context.WithCancel(client.ctx)
|
event.ctx, event.cancel = context.WithCancel(client.ctx)
|
||||||
|
|
||||||
client.handleEvent(&event)
|
client.handleEvent(&event)
|
||||||
emit(&event, client)
|
|
||||||
|
|
||||||
event.cancel()
|
event.cancel()
|
||||||
}
|
}
|
||||||
@@ -710,13 +716,12 @@ end:
|
|||||||
|
|
||||||
ticker.Stop()
|
ticker.Stop()
|
||||||
|
|
||||||
client.Disconnect()
|
_ = client.Disconnect()
|
||||||
|
|
||||||
event := NewEvent("client", "destroy")
|
event := NewEvent("client", "destroy")
|
||||||
event.ctx, event.cancel = context.WithCancel(client.ctx)
|
event.ctx, event.cancel = context.WithCancel(client.ctx)
|
||||||
|
|
||||||
client.handleEvent(&event)
|
client.handleEvent(&event)
|
||||||
emit(&event, client)
|
|
||||||
|
|
||||||
event.cancel()
|
event.cancel()
|
||||||
}
|
}
|
||||||
@@ -742,7 +747,7 @@ func (client *Client) handleSendLoop() {
|
|||||||
queue = client.config.SendRate - 1
|
queue = client.config.SendRate - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
client.Send(line)
|
_ = client.Send(line)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -771,7 +776,7 @@ func (client *Client) handleEvent(event *Event) {
|
|||||||
client.mutex.RUnlock()
|
client.mutex.RUnlock()
|
||||||
|
|
||||||
if lastSend > time.Second*120 {
|
if lastSend > time.Second*120 {
|
||||||
client.Sendf("PING :%x%x%x", mathRand.Int63(), mathRand.Int63(), mathRand.Int63())
|
_ = client.Sendf("PING :%x%x%x", mathRand.Int63(), mathRand.Int63(), mathRand.Int63())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "packet.ping":
|
case "packet.ping":
|
||||||
@@ -784,7 +789,7 @@ func (client *Client) handleEvent(event *Event) {
|
|||||||
message += " :" + event.Text
|
message += " :" + event.Text
|
||||||
}
|
}
|
||||||
|
|
||||||
client.Send(message)
|
_ = client.Send(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client Registration
|
// Client Registration
|
||||||
@@ -796,11 +801,11 @@ func (client *Client) handleEvent(event *Event) {
|
|||||||
delete(client.capEnabled, key)
|
delete(client.capEnabled, key)
|
||||||
}
|
}
|
||||||
client.mutex.Unlock()
|
client.mutex.Unlock()
|
||||||
client.Send("CAP LS 302")
|
_ = client.Send("CAP LS 302")
|
||||||
|
|
||||||
// Send server password if configured.
|
// Send server password if configured.
|
||||||
if client.config.Password != "" {
|
if client.config.Password != "" {
|
||||||
client.Sendf("PASS :%s", client.config.Password)
|
_ = client.Sendf("PASS :%s", client.config.Password)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reuse nick or get from config
|
// Reuse nick or get from config
|
||||||
@@ -812,19 +817,22 @@ func (client *Client) handleEvent(event *Event) {
|
|||||||
client.mutex.RUnlock()
|
client.mutex.RUnlock()
|
||||||
|
|
||||||
// Start registration.
|
// Start registration.
|
||||||
client.Sendf("NICK %s", nick)
|
_ = client.Sendf("NICK %s", nick)
|
||||||
client.Sendf("USER %s 8 * :%s", client.config.User, client.config.RealName)
|
_ = client.Sendf("USER %s 8 * :%s", client.config.User, client.config.RealName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Welcome message
|
||||||
case "packet.001":
|
case "packet.001":
|
||||||
{
|
{
|
||||||
client.mutex.Lock()
|
client.mutex.Lock()
|
||||||
client.nick = event.Args[0]
|
client.nick = event.Args[0]
|
||||||
client.mutex.Unlock()
|
client.mutex.Unlock()
|
||||||
|
|
||||||
client.Sendf("WHO %s", event.Args[0])
|
// Send a WHO right away to gather enough client information for precise message cutting.
|
||||||
|
_ = client.Sendf("WHO %s", event.Args[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Nick rotation
|
||||||
case "packet.431", "packet.432", "packet.433", "packet.436":
|
case "packet.431", "packet.432", "packet.433", "packet.436":
|
||||||
{
|
{
|
||||||
client.mutex.RLock()
|
client.mutex.RLock()
|
||||||
@@ -839,7 +847,7 @@ func (client *Client) handleEvent(event *Event) {
|
|||||||
sent := false
|
sent := false
|
||||||
for _, alt := range client.config.Alternatives {
|
for _, alt := range client.config.Alternatives {
|
||||||
if nick == prev {
|
if nick == prev {
|
||||||
client.Sendf("NICK %s", alt)
|
_ = client.Sendf("NICK %s", alt)
|
||||||
sent = true
|
sent = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -849,7 +857,7 @@ func (client *Client) handleEvent(event *Event) {
|
|||||||
|
|
||||||
if !sent {
|
if !sent {
|
||||||
// "LastAlt" -> "Nick23962"
|
// "LastAlt" -> "Nick23962"
|
||||||
client.Sendf("NICK %s%05d", client.config.Nick, mathRand.Int31n(99999))
|
_ = client.Sendf("NICK %s%05d", client.config.Nick, mathRand.Int31n(99999))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -918,9 +926,9 @@ func (client *Client) handleEvent(event *Event) {
|
|||||||
requestedCaps := strings.Join(client.capsRequested, " ")
|
requestedCaps := strings.Join(client.capsRequested, " ")
|
||||||
client.mutex.RUnlock()
|
client.mutex.RUnlock()
|
||||||
|
|
||||||
client.Send("CAP REQ :" + requestedCaps)
|
_ = client.Send("CAP REQ :" + requestedCaps)
|
||||||
} else {
|
} else {
|
||||||
client.Send("CAP END")
|
_ = client.Send("CAP END")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -934,7 +942,7 @@ func (client *Client) handleEvent(event *Event) {
|
|||||||
client.mutex.Unlock()
|
client.mutex.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
client.Send("CAP END")
|
_ = client.Send("CAP END")
|
||||||
}
|
}
|
||||||
case "NAK":
|
case "NAK":
|
||||||
{
|
{
|
||||||
@@ -954,7 +962,7 @@ func (client *Client) handleEvent(event *Event) {
|
|||||||
requestedCaps := strings.Join(client.capsRequested, " ")
|
requestedCaps := strings.Join(client.capsRequested, " ")
|
||||||
client.mutex.RUnlock()
|
client.mutex.RUnlock()
|
||||||
|
|
||||||
client.Send("CAP REQ :" + requestedCaps)
|
_ = client.Send("CAP REQ :" + requestedCaps)
|
||||||
}
|
}
|
||||||
case "NEW":
|
case "NEW":
|
||||||
{
|
{
|
||||||
@@ -969,7 +977,7 @@ func (client *Client) handleEvent(event *Event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(requests) > 0 {
|
if len(requests) > 0 {
|
||||||
client.Send("CAP REQ :" + strings.Join(requests, " "))
|
_ = client.Send("CAP REQ :" + strings.Join(requests, " "))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "DEL":
|
case "DEL":
|
||||||
@@ -1021,7 +1029,7 @@ func (client *Client) handleEvent(event *Event) {
|
|||||||
|
|
||||||
if event.Nick == client.nick {
|
if event.Nick == client.nick {
|
||||||
channel = &Channel{name: event.Arg(0), userlist: list.New(&client.isupport)}
|
channel = &Channel{name: event.Arg(0), userlist: list.New(&client.isupport)}
|
||||||
client.AddTarget(channel)
|
_, _ = client.AddTarget(channel)
|
||||||
} else {
|
} else {
|
||||||
channel = client.Channel(event.Arg(0))
|
channel = client.Channel(event.Arg(0))
|
||||||
}
|
}
|
||||||
@@ -1038,7 +1046,7 @@ func (client *Client) handleEvent(event *Event) {
|
|||||||
|
|
||||||
if event.Nick == client.nick {
|
if event.Nick == client.nick {
|
||||||
channel.parted = true
|
channel.parted = true
|
||||||
client.RemoveTarget(channel)
|
_, _ = client.RemoveTarget(channel)
|
||||||
} else {
|
} else {
|
||||||
client.handleInTarget(channel, event)
|
client.handleInTarget(channel, event)
|
||||||
}
|
}
|
||||||
@@ -1053,7 +1061,7 @@ func (client *Client) handleEvent(event *Event) {
|
|||||||
|
|
||||||
if event.Arg(1) == client.nick {
|
if event.Arg(1) == client.nick {
|
||||||
channel.parted = true
|
channel.parted = true
|
||||||
client.RemoveTarget(channel)
|
_, _ = client.RemoveTarget(channel)
|
||||||
} else {
|
} else {
|
||||||
client.handleInTarget(channel, event)
|
client.handleInTarget(channel, event)
|
||||||
}
|
}
|
||||||
@@ -1095,9 +1103,8 @@ func (client *Client) handleEvent(event *Event) {
|
|||||||
// Message parsing
|
// Message parsing
|
||||||
case "packet.privmsg", "ctcp.action":
|
case "packet.privmsg", "ctcp.action":
|
||||||
{
|
{
|
||||||
// Target the mssage
|
// Target the message
|
||||||
target := Target(client.status)
|
target := Target(client.status)
|
||||||
spawned := false
|
|
||||||
targetName := event.Arg(0)
|
targetName := event.Arg(0)
|
||||||
if targetName == client.nick {
|
if targetName == client.nick {
|
||||||
target := client.Target("query", targetName)
|
target := client.Target("query", targetName)
|
||||||
@@ -1108,9 +1115,8 @@ func (client *Client) handleEvent(event *Event) {
|
|||||||
Host: event.Host,
|
Host: event.Host,
|
||||||
}}
|
}}
|
||||||
|
|
||||||
client.AddTarget(query)
|
id, _ := client.AddTarget(query)
|
||||||
|
event.RenderTags["spawned"] = id
|
||||||
spawned = true
|
|
||||||
|
|
||||||
target = query
|
target = query
|
||||||
}
|
}
|
||||||
@@ -1128,10 +1134,6 @@ func (client *Client) handleEvent(event *Event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
client.handleInTarget(target, event)
|
client.handleInTarget(target, event)
|
||||||
|
|
||||||
if spawned {
|
|
||||||
// TODO: Message has higher importance // 0:Normal, 1:Important, 2:Highlight
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case "packet.notice":
|
case "packet.notice":
|
||||||
@@ -1202,7 +1204,7 @@ func (client *Client) handleEvent(event *Event) {
|
|||||||
client.mutex.RUnlock()
|
client.mutex.RUnlock()
|
||||||
|
|
||||||
if len(channels) > 0 {
|
if len(channels) > 0 {
|
||||||
client.Sendf("JOIN %s", strings.Join(channels, ","))
|
_ = client.Sendf("JOIN %s", strings.Join(channels, ","))
|
||||||
client.EmitNonBlocking(rejoinEvent)
|
client.EmitNonBlocking(rejoinEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1217,6 +1219,17 @@ func (client *Client) handleEvent(event *Event) {
|
|||||||
if len(event.targets) == 0 {
|
if len(event.targets) == 0 {
|
||||||
client.handleInTarget(client.status, event)
|
client.handleInTarget(client.status, event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client.mutex.RLock()
|
||||||
|
clientHandlers := client.handlers
|
||||||
|
client.mutex.RUnlock()
|
||||||
|
|
||||||
|
for _, handler := range globalHandlers {
|
||||||
|
handler(event, client)
|
||||||
|
}
|
||||||
|
for _, handler := range clientHandlers {
|
||||||
|
handler(event, client)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) handleInTargets(nick string, event *Event) {
|
func (client *Client) handleInTargets(nick string, event *Event) {
|
||||||
|
|||||||
+4
-4
@@ -35,7 +35,7 @@ func TestClient(t *testing.T) {
|
|||||||
{Client: "NICK Test"},
|
{Client: "NICK Test"},
|
||||||
{Client: "USER Tester 8 * :..."},
|
{Client: "USER Tester 8 * :..."},
|
||||||
{Server: ":testserver.example.com CAP * LS :multi-prefix chghost userhost-in-names vendorname/custom-stuff echo-message =malformed vendorname/advanced-custom-stuff=things,and,items"},
|
{Server: ":testserver.example.com CAP * LS :multi-prefix chghost userhost-in-names vendorname/custom-stuff echo-message =malformed vendorname/advanced-custom-stuff=things,and,items"},
|
||||||
{Client: "CAP REQ :multi-prefix chghost userhost-in-names"},
|
{Client: "CAP REQ :multi-prefix chghost userhost-in-names echo-message"},
|
||||||
{Server: ":testserver.example.com CAP * ACK :multi-prefix userhost-in-names"},
|
{Server: ":testserver.example.com CAP * ACK :multi-prefix userhost-in-names"},
|
||||||
{Client: "CAP END"},
|
{Client: "CAP END"},
|
||||||
{Callback: func() error {
|
{Callback: func() error {
|
||||||
@@ -377,8 +377,8 @@ func TestParenthesesBug(t *testing.T) {
|
|||||||
Nick: "Stuff",
|
Nick: "Stuff",
|
||||||
})
|
})
|
||||||
|
|
||||||
irc.AddHandler(func(event *irc.Event, client *irc.Client) {
|
client.AddHandler(func(event *irc.Event, client *irc.Client) {
|
||||||
if event.Name() == "packet.privmsg" || event.Nick == "Dante" {
|
if event.Name() == "packet.privmsg" || event.Nick == "Beans" {
|
||||||
gotMessage = true
|
gotMessage = true
|
||||||
|
|
||||||
if event.Text != "((Remove :01 goofs!*))" {
|
if event.Text != "((Remove :01 goofs!*))" {
|
||||||
@@ -388,7 +388,7 @@ func TestParenthesesBug(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
packet, err := irc.ParsePacket("@example/tag=32; :Dante!TheBeans@captain.purple.beans PRIVMSG Stuff :((Remove :01 goofs!*))")
|
packet, err := irc.ParsePacket("@example/tag=32; :Beans!beans@beans.example.com PRIVMSG Stuff :((Remove :01 goofs!*))")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("Parse", err)
|
t.Error("Parse", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,25 +4,17 @@ package irc
|
|||||||
// events.
|
// events.
|
||||||
type Handler func(event *Event, client *Client)
|
type Handler func(event *Event, client *Client)
|
||||||
|
|
||||||
var eventHandler struct {
|
var globalHandlers = make([]Handler, 0, 8)
|
||||||
handlers []Handler
|
|
||||||
}
|
|
||||||
|
|
||||||
func emit(event *Event, client *Client) {
|
|
||||||
for _, handler := range eventHandler.handlers {
|
|
||||||
handler(event, client)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddHandler adds a new handler to the irc handling. The handler may be called from multiple threads at the same
|
// AddHandler adds a new handler to the irc handling. The handler may be called from multiple threads at the same
|
||||||
// time, so external resources should be locked if there are multiple clients. Adding handlers is not thread
|
// time, so external resources should be locked if there are multiple clients. Adding handlers is not thread
|
||||||
// safe and should be done prior to clients being created.AddHandler. Also, this handler will block the individual
|
// safe and should be done prior to clients being created. Also, this handler will block the individual
|
||||||
// client's event loop, so long operations that include network requests and the like should be done in a
|
// client's event loop, so long operations that include network requests and the like should be done in a
|
||||||
// goroutine with the needed data **copied** from the handler function.
|
// goroutine with the needed data **copied** from the handler function.
|
||||||
func AddHandler(handler Handler) {
|
func AddHandler(handler Handler) {
|
||||||
eventHandler.handlers = append(eventHandler.handlers, handler)
|
globalHandlers = append(globalHandlers, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
eventHandler.handlers = make([]Handler, 0, 8)
|
globalHandlers = make([]Handler, 0, 8)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ func (interaction *Interaction) Listen() (addr string, err error) {
|
|||||||
line := lines[i]
|
line := lines[i]
|
||||||
|
|
||||||
if line.Server != "" {
|
if line.Server != "" {
|
||||||
|
_ = conn.SetWriteDeadline(time.Now().Add(time.Second * 2))
|
||||||
_, err := conn.Write(append([]byte(line.Server), '\r', '\n'))
|
_, err := conn.Write(append([]byte(line.Server), '\r', '\n'))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
interaction.Failure = &InteractionFailure{
|
interaction.Failure = &InteractionFailure{
|
||||||
@@ -58,7 +59,7 @@ func (interaction *Interaction) Listen() (addr string, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else if line.Client != "" {
|
} else if line.Client != "" {
|
||||||
conn.SetReadDeadline(time.Now().Add(time.Second * 2))
|
_ = conn.SetReadDeadline(time.Now().Add(time.Second * 2))
|
||||||
input, err := reader.ReadString('\n')
|
input, err := reader.ReadString('\n')
|
||||||
if err != nil {
|
if err != nil {
|
||||||
interaction.Failure = &InteractionFailure{
|
interaction.Failure = &InteractionFailure{
|
||||||
|
|||||||
@@ -49,12 +49,12 @@ func (isupport *ISupport) ParsePrefixedNick(fullnick string) (nick, modes, prefi
|
|||||||
isupport.lock.RLock()
|
isupport.lock.RLock()
|
||||||
defer isupport.lock.RUnlock()
|
defer isupport.lock.RUnlock()
|
||||||
|
|
||||||
if fullnick == "" || isupport.state.Prefixes == nil {
|
if fullnick == "" || isupport.state.PrefixMap == nil {
|
||||||
return fullnick, "", ""
|
return fullnick, "", ""
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, ch := range fullnick {
|
for i, ch := range fullnick {
|
||||||
if mode, ok := isupport.state.Prefixes[ch]; ok {
|
if mode, ok := isupport.state.PrefixMap[ch]; ok {
|
||||||
modes += string(mode)
|
modes += string(mode)
|
||||||
prefixes += string(ch)
|
prefixes += string(ch)
|
||||||
} else {
|
} else {
|
||||||
@@ -163,7 +163,7 @@ func (isupport *ISupport) Mode(prefix rune) rune {
|
|||||||
isupport.lock.RLock()
|
isupport.lock.RLock()
|
||||||
defer isupport.lock.RUnlock()
|
defer isupport.lock.RUnlock()
|
||||||
|
|
||||||
return isupport.state.Prefixes[prefix]
|
return isupport.state.PrefixMap[prefix]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefix gets the prefix for the mode. It's a bit slower
|
// Prefix gets the prefix for the mode. It's a bit slower
|
||||||
@@ -173,7 +173,7 @@ func (isupport *ISupport) Prefix(mode rune) rune {
|
|||||||
isupport.lock.RLock()
|
isupport.lock.RLock()
|
||||||
defer isupport.lock.RUnlock()
|
defer isupport.lock.RUnlock()
|
||||||
|
|
||||||
for prefix, mappedMode := range isupport.state.Prefixes {
|
for prefix, mappedMode := range isupport.state.PrefixMap {
|
||||||
if mappedMode == mode {
|
if mappedMode == mode {
|
||||||
return prefix
|
return prefix
|
||||||
}
|
}
|
||||||
@@ -182,7 +182,7 @@ func (isupport *ISupport) Prefix(mode rune) rune {
|
|||||||
return rune(0)
|
return rune(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefixes gets the prefixes in the order of the modes, skipping any
|
// PrefixMap gets the prefixes in the order of the modes, skipping any
|
||||||
// invalid modes.
|
// invalid modes.
|
||||||
func (isupport *ISupport) Prefixes(modes string) string {
|
func (isupport *ISupport) Prefixes(modes string) string {
|
||||||
result := ""
|
result := ""
|
||||||
@@ -280,9 +280,9 @@ func (isupport *ISupport) Set(key, value string) {
|
|||||||
|
|
||||||
isupport.state.PrefixOrder = split[1]
|
isupport.state.PrefixOrder = split[1]
|
||||||
isupport.state.ModeOrder = split[0]
|
isupport.state.ModeOrder = split[0]
|
||||||
isupport.state.Prefixes = make(map[rune]rune, len(split[0]))
|
isupport.state.PrefixMap = make(map[rune]rune, len(split[0]))
|
||||||
for i, ch := range split[0] {
|
for i, ch := range split[0] {
|
||||||
isupport.state.Prefixes[rune(split[1][i])] = ch
|
isupport.state.PrefixMap[rune(split[1][i])] = ch
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "CHANMODES": // CHANMODES=eIbq,k,flj,CFLNPQcgimnprstz
|
case "CHANMODES": // CHANMODES=eIbq,k,flj,CFLNPQcgimnprstz
|
||||||
@@ -304,7 +304,7 @@ func (isupport *ISupport) Reset() {
|
|||||||
isupport.lock.Lock()
|
isupport.lock.Lock()
|
||||||
isupport.state.PrefixOrder = ""
|
isupport.state.PrefixOrder = ""
|
||||||
isupport.state.ModeOrder = ""
|
isupport.state.ModeOrder = ""
|
||||||
isupport.state.Prefixes = nil
|
isupport.state.PrefixMap = nil
|
||||||
isupport.state.ChannelModes = nil
|
isupport.state.ChannelModes = nil
|
||||||
|
|
||||||
for key := range isupport.state.Raw {
|
for key := range isupport.state.Raw {
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@ package isupport
|
|||||||
|
|
||||||
type State struct {
|
type State struct {
|
||||||
Raw map[string]string `json:"raw"`
|
Raw map[string]string `json:"raw"`
|
||||||
Prefixes map[rune]rune `json:"-"`
|
PrefixMap map[rune]rune `json:"prefixMap"`
|
||||||
ModeOrder string `json:"modeOrder"`
|
ModeOrder string `json:"modeOrder"`
|
||||||
PrefixOrder string `json:"prefixOrder"`
|
PrefixOrder string `json:"prefixOrder"`
|
||||||
ChannelModes []string `json:"channelModes"`
|
ChannelModes []string `json:"channelModes"`
|
||||||
|
|||||||
Reference in New Issue
Block a user