diff --git a/channel.go b/channel.go index 9c13d12..6f433e6 100644 --- a/channel.go +++ b/channel.go @@ -8,11 +8,17 @@ import ( // A Channel is a target that manages the userlist type Channel struct { + id string name string userlist *list.List parted bool } +// ID returns a unique ID for the channel target. +func (channel *Channel) ID() string { + return channel.id +} + // Kind returns "channel" func (channel *Channel) Kind() string { return "channel" diff --git a/client.go b/client.go index 38ecf4c..107c3f8 100644 --- a/client.go +++ b/client.go @@ -87,9 +87,8 @@ type Client struct { isupport isupport.ISupport values map[string]interface{} - status *Status - targets []Target - targetIds map[Target]string + status *Status + targets []Target handlers []Handler } @@ -105,13 +104,12 @@ func New(ctx context.Context, config Config) *Client { capEnabled: make(map[string]bool), capData: make(map[string]string), config: config.WithDefaults(), - targetIds: make(map[Target]string, 16), - status: &Status{}, + status: &Status{id: generateClientID("T")}, } client.ctx, client.cancel = context.WithCancel(ctx) - _, _ = client.AddTarget(client.status) + _ = client.AddTarget(client.status) go client.handleEventLoop() go client.handleSendLoop() @@ -220,7 +218,7 @@ func (client *Client) State() ClientState { for _, target := range client.targets { tstate := target.State() - tstate.ID = client.targetIds[target] + tstate.ID = target.ID() state.Targets = append(state.Targets, tstate) } @@ -484,7 +482,7 @@ func (client *Client) EmitInput(line string, target Target) context.Context { event := ParseInput(line) client.mutex.RLock() - if target != nil && client.targetIds[target] == "" { + if target != nil && client.TargetByID(target.ID()) == nil { client.EmitNonBlocking(NewErrorEvent("invalid_target", "Target does not exist.")) ctx, cancel := context.WithCancel(context.Background()) @@ -496,12 +494,10 @@ func (client *Client) EmitInput(line string, target Target) context.Context { if target != nil { client.mutex.RLock() event.targets = append(event.targets, target) - event.targetIds[target] = client.targetIds[target] client.mutex.RUnlock() } else { client.mutex.RLock() event.targets = append(event.targets, client.status) - event.targetIds[client.status] = client.targetIds[client.status] client.mutex.RUnlock() } @@ -594,6 +590,20 @@ func (client *Client) Target(kind string, name string) Target { return nil } +// TargetByID gets a target by kind and name +func (client *Client) TargetByID(id string) Target { + client.mutex.RLock() + defer client.mutex.RUnlock() + + for _, target := range client.targets { + if target.ID() == id { + return target + } + } + + return nil +} + // Targets gets all targets of the given kinds. func (client *Client) Targets(kinds ...string) []Target { if len(kinds) == 0 { @@ -658,7 +668,7 @@ func (client *Client) Query(name string) *Query { } // AddTarget adds a target to the client, generating a unique ID for it. -func (client *Client) AddTarget(target Target) (id string, err error) { +func (client *Client) AddTarget(target Target) (err error) { client.mutex.Lock() defer client.mutex.Unlock() @@ -672,14 +682,11 @@ func (client *Client) AddTarget(target Target) (id string, err error) { } } - id = generateClientID("T") client.targets = append(client.targets, target) - client.targetIds[target] = id event := NewEvent("hook", "add_target") - event.Args = []string{client.targetIds[target], target.Kind(), target.Name()} + event.Args = []string{target.ID(), target.Kind(), target.Name()} event.targets = []Target{target} - event.targetIds[target] = id client.EmitNonBlocking(event) return @@ -696,15 +703,14 @@ func (client *Client) RemoveTarget(target Target) (id string, err error) { for i := range client.targets { if target == client.targets[i] { - id = client.targetIds[target] + id = target.ID() event := NewEvent("hook", "remove_target") - event.Args = []string{client.targetIds[target], target.Kind(), target.Name()} + event.Args = []string{target.ID(), target.Kind(), target.Name()} client.EmitNonBlocking(event) client.targets[i] = client.targets[len(client.targets)-1] client.targets = client.targets[:len(client.targets)-1] - delete(client.targetIds, target) // Ensure the channel has been parted if channel, ok := target.(*Channel); ok && !channel.parted { @@ -1218,8 +1224,12 @@ func (client *Client) handleEvent(event *Event) { var channel *Channel if event.Nick == client.nick { - channel = &Channel{name: event.Arg(0), userlist: list.New(&client.isupport)} - _, _ = client.AddTarget(channel) + channel = &Channel{ + id: generateClientID("T"), + name: event.Arg(0), + userlist: list.New(&client.isupport), + } + _ = client.AddTarget(channel) } else { channel = client.Channel(event.Arg(0)) } @@ -1317,14 +1327,20 @@ func (client *Client) handleEvent(event *Event) { if targetName == client.nick { target := client.Target("query", targetName) if target == nil { - query := &Query{user: list.User{ - Nick: event.Nick, - User: event.User, - Host: event.Host, - }} + query := &Query{ + id: client.id, + user: list.User{ + Nick: event.Nick, + User: event.User, + Host: event.Host, + }, + } + if accountTag, ok := event.Tags["account"]; ok { + query.user.Account = accountTag + } - id, _ := client.AddTarget(query) - event.RenderTags["spawned"] = id + _ = client.AddTarget(query) + event.RenderTags["spawned"] = query.id target = query } @@ -1406,7 +1422,6 @@ func (client *Client) handleEvent(event *Event) { channels = append(channels, channel.Name()) rejoinEvent.targets = append(rejoinEvent.targets, target) - rejoinEvent.targetIds[target] = client.targetIds[target] } } client.mutex.RUnlock() @@ -1452,7 +1467,6 @@ func (client *Client) handleInTargets(nick string, event *Event) { target.Handle(event, client) event.targets = append(event.targets, target) - event.targetIds[target] = client.targetIds[target] } case *Query: { @@ -1460,7 +1474,6 @@ func (client *Client) handleInTargets(nick string, event *Event) { target.Handle(event, client) event.targets = append(event.targets, target) - event.targetIds[target] = client.targetIds[target] } } case *Status: @@ -1469,7 +1482,6 @@ func (client *Client) handleInTargets(nick string, event *Event) { target.Handle(event, client) event.targets = append(event.targets, target) - event.targetIds[target] = client.targetIds[target] } } } @@ -1488,7 +1500,6 @@ func (client *Client) handleInTarget(target Target, event *Event) { target.Handle(event, client) event.targets = append(event.targets, target) - event.targetIds[target] = client.targetIds[target] client.mutex.RUnlock() } diff --git a/cmd/ircrepl/main.go b/cmd/ircrepl/main.go index 53326db..65aa9b1 100644 --- a/cmd/ircrepl/main.go +++ b/cmd/ircrepl/main.go @@ -44,7 +44,7 @@ func main() { err := client.Connect(*flagServer, *flagSsl) if err != nil { - fmt.Fprintf(os.Stderr, "Failed to connect: %s", err) + _, _ = fmt.Fprintf(os.Stderr, "Failed to connect: %s", err) os.Exit(1) } diff --git a/event.go b/event.go index 64feb2f..e11f82d 100644 --- a/event.go +++ b/event.go @@ -27,8 +27,7 @@ type Event struct { preventedDefault bool hidden bool - targets []Target - targetIds map[Target]string + targets []Target } // NewEvent makes a new event with Kind, Verb, Time set and Args and Tags initialized. @@ -42,8 +41,6 @@ func NewEvent(kind, verb string) Event { Args: make([]string, 0, 4), Tags: make(map[string]string), - targetIds: make(map[Target]string), - RenderTags: make(map[string]string), } } @@ -92,7 +89,7 @@ func (event *Event) Context() context.Context { // the current event handler returns. // // A use case for this is to prevent the default input handler from firing -// on an already prcoessed input event. +// on an already processed input event. func (event *Event) PreventDefault() { event.preventedDefault = true } @@ -177,8 +174,8 @@ func (event *Event) StatusTarget() *Status { func (event *Event) TargetIDs() []string { ids := make([]string, len(event.targets)) - for _, value := range event.targetIds { - ids = append(ids, value) + for _, target := range event.targets { + ids = append(ids, target.ID()) } return ids @@ -202,7 +199,7 @@ func (event *Event) MarshalJSON() ([]byte, error) { data.Targets = make([]string, 0, len(event.targets)) for _, target := range event.targets { - data.Targets = append(data.Targets, event.targetIds[target]) + data.Targets = append(data.Targets, target.ID()) } return json.Marshal(data) diff --git a/query.go b/query.go index 34fccf5..7daf217 100644 --- a/query.go +++ b/query.go @@ -6,9 +6,15 @@ import ( // A Query is a target for direct messages to and from a specific nick. type Query struct { + id string user list.User } +// ID returns a unique ID for the channel target. +func (query *Query) ID() string { + return query.id +} + // Kind returns "channel" func (query *Query) Kind() string { return "query" diff --git a/status.go b/status.go index a0b03f3..3532a23 100644 --- a/status.go +++ b/status.go @@ -2,6 +2,12 @@ package irc // A Status contains type Status struct { + id string +} + +// ID returns a unique ID for the status target. +func (status *Status) ID() string { + return status.id } // Kind returns "status" diff --git a/target.go b/target.go index e445ec2..530235f 100644 --- a/target.go +++ b/target.go @@ -3,6 +3,7 @@ package irc // A Target is a handler for a message meant for a limited part of the client, like a channel or // query type Target interface { + ID() string Kind() string Name() string Handle(event *Event, client *Client)