diff --git a/client.go b/client.go index 11d49ca..469065d 100644 --- a/client.go +++ b/client.go @@ -588,7 +588,7 @@ end: func (client *Client) handleSendLoop() { lastRefresh := time.Time{} - queue := 2 + queue := client.config.SendRate for line := range client.sends { now := time.Now() @@ -600,10 +600,11 @@ func (client *Client) handleSendLoop() { time.Sleep(time.Second - deltaTime) lastRefresh = now - queue = 1 + queue = client.config.SendRate - 1 } } else { lastRefresh = now + queue = client.config.SendRate - 1 } client.Send(line) diff --git a/client_test.go b/client_test.go index 4a127ff..8cb0fd3 100644 --- a/client_test.go +++ b/client_test.go @@ -19,6 +19,7 @@ func TestClient(t *testing.T) { User: "Tester", RealName: "...", Alternatives: []string{"Test2", "Test3", "Test4", "Test768"}, + SendRate: 1000, }) t.Logf("Client.ID = %#+v", client.ID()) @@ -189,6 +190,20 @@ func TestClient(t *testing.T) { {Kind: 'S', Data: ":Test768!~test@127.0.0.1 PRIVMSG #Test :\x01ACTION describes stuff\x01"}, {Kind: 'S', Data: ":Test768!~test@127.0.0.1 PRIVMSG #Test :Hello, World"}, {Kind: 'S', Data: ":Test768!~test@127.0.0.1 PRIVMSG #Test :Hello again"}, + {Callback: func() error { + channel := client.Channel("#Test") + if channel == nil { + return errors.New("Channel #Test not found") + } + + client.EmitInput("/m +N", channel) + client.EmitInput("/npcac Test_NPC stuffs things", channel) + return nil + }}, + {Kind: 'C', Data: "MODE #Test +N"}, + {Kind: 'C', Data: "NPCA #Test Test_NPC :stuffs things"}, + {Kind: 'S', Data: ":Test768!~test@127.0.0.1 MODE #Test +N"}, + {Kind: 'S', Data: ":\x1FTest_NPC\x1F!Test768@npc.fakeuser.invalid PRIVMSG #Test :\x01ACTION stuffs things\x01"}, }, } diff --git a/config.go b/config.go index fdc27b0..dbcad75 100644 --- a/config.go +++ b/config.go @@ -26,6 +26,10 @@ type Config struct { // The Password used upon connection. This is not your NickServ/SASL password! Password string + + // The rate (lines per second) to send with Client.SendQueued. Default is 2, which is how + // clients that don't excess flood does it. + SendRate int } // WithDefaults returns the config with the default values @@ -47,5 +51,9 @@ func (config Config) WithDefaults() Config { } } + if config.SendRate <= 0 { + config.SendRate = 2 + } + return config } diff --git a/event_input.go b/event_input.go index 9b6c988..20c7162 100644 --- a/event_input.go +++ b/event_input.go @@ -12,7 +12,7 @@ func ParseInput(line string) Event { if strings.HasPrefix(line, "/") { split := strings.SplitN(line[1:], " ", 2) - event.verb = split[0] + event.verb = strings.ToLower(split[0]) if len(split) == 2 { event.Text = split[1] } diff --git a/handlers/input.go b/handlers/input.go index 205f76c..df85757 100644 --- a/handlers/input.go +++ b/handlers/input.go @@ -12,6 +12,8 @@ func Input(event *irc.Event, client *irc.Client) { // /msg sends an action to a target specified before the message. case "input.msg": { + event.Kill() + targetName, text := ircutil.ParseArgAndText(event.Text) if targetName == "" || text == "" { client.EmitNonBlocking(irc.NewErrorEvent("input", "Usage: /msg ")) @@ -23,13 +25,13 @@ func Input(event *irc.Event, client *irc.Client) { for _, cut := range cuts { client.Sendf("PRIVMSG %s :%s", targetName, cut) } - - event.Kill() } // /text (or text without a command) sends a message to the target. case "input.text": { + event.Kill() + if event.Text == "" { client.EmitNonBlocking(irc.NewErrorEvent("input", "Usage: /text ")) break @@ -46,13 +48,13 @@ func Input(event *irc.Event, client *irc.Client) { for _, cut := range cuts { client.SendQueuedf("PRIVMSG %s :%s", target.Name(), cut) } - - event.Kill() } // /me and /action sends a CTCP ACTION. case "input.me", "input.action": { + event.Kill() + if event.Text == "" { client.EmitNonBlocking(irc.NewErrorEvent("input", "Usage: /me ")) break @@ -69,13 +71,13 @@ func Input(event *irc.Event, client *irc.Client) { for _, cut := range cuts { client.SendCTCP("ACTION", target.Name(), false, cut) } - - event.Kill() } // /describe sends an action to a target specified before the message, like /msg. case "input.describe": { + event.Kill() + targetName, text := ircutil.ParseArgAndText(event.Text) if targetName == "" || text == "" { client.EmitNonBlocking(irc.NewErrorEvent("input", "Usage: /describe ")) @@ -87,20 +89,20 @@ func Input(event *irc.Event, client *irc.Client) { for _, cut := range cuts { client.SendCTCP("ACTION", targetName, false, cut) } - - event.Kill() } // /m is a shorthand for /mode that targets the current channel case "input.m": { + event.Kill() + if event.Text == "" { client.EmitNonBlocking(irc.NewErrorEvent("input", "Usage: /m ")) break } channel := event.ChannelTarget() - if channel != nil { + if channel == nil { client.EmitNonBlocking(irc.NewErrorEvent("input", "Target is not a channel")) break } diff --git a/handlers/mroleplay.go b/handlers/mroleplay.go index 40e37eb..89da66c 100644 --- a/handlers/mroleplay.go +++ b/handlers/mroleplay.go @@ -49,7 +49,7 @@ func MRoleplay(event *irc.Event, client *irc.Client) { } overhead := ircutil.MessageOverhead("\x1f"+nick+"\x1f", client.Nick(), "npc.fakeuser.invalid", channel.Name(), isAction) - cuts := ircutil.CutMessage(event.Text, overhead) + cuts := ircutil.CutMessage(text, overhead) for _, cut := range cuts { npcCommand := "NPCA" @@ -57,7 +57,7 @@ func MRoleplay(event *irc.Event, client *irc.Client) { npcCommand = "NPC" } - client.SendQueuedf("%s %s :%s", npcCommand, channel.Name(), cut) + client.SendQueuedf("%s %s %s :%s", npcCommand, channel.Name(), nick, cut) } event.Kill()