Skip to content

Commit e7a68d2

Browse files
stho32claude
andcommitted
fix: Bot private message command processing
- Bot now correctly processes private messages without "/" prefix - Added ExecuteWithoutPrefix method to ClientCommandCollection - Private messages (e.g., "exec calculate.ps1") now work properly - Added comprehensive unit tests for command processing This fixes the issue where commands like "/msg CalculatorBot exec calculate.ps1 '7 + 4'" were received but not executed by the bot. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 11a3e90 commit e7a68d2

File tree

4 files changed

+148
-20
lines changed

4 files changed

+148
-20
lines changed

Source/LocalNetAppChat/LocalNetAppChat.Bot/Program.cs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -88,15 +88,16 @@ public static async Task Main(string[] args)
8888
{
8989
output.WriteLine(message);
9090

91-
if (CommandMessageTokenizer.IsCommandMessage(message.Message.Text))
91+
if (IsAPrivateMessage(message))
9292
{
93-
if (IsAPrivateMessage(message))
94-
{
95-
Result<string> result = privateClientCommands.Execute(message.Message.Text);
96-
await SendResultBack(lnacClient, message.Message.Name, result);
97-
continue;
98-
}
93+
// Private messages don't need the "/" prefix
94+
Result<string> result = privateClientCommands.ExecuteWithoutPrefix(message.Message.Text);
95+
await SendResultBack(lnacClient, message.Message.Name, result);
96+
continue;
97+
}
9998

99+
if (CommandMessageTokenizer.IsCommandMessage(message.Message.Text))
100+
{
100101
if (publicClientCommands.IsAKnownCommand(message.Message.Text))
101102
{
102103
await SendResultBack(
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
using LocalNetAppChat.Domain.Bots.ClientCommands;
2+
using NUnit.Framework;
3+
4+
namespace LocalNetAppChat.Domain.Tests.Bots.ClientCommands
5+
{
6+
[TestFixture]
7+
public class ClientCommandCollectionTests
8+
{
9+
private class TestCommand : IClientCommand
10+
{
11+
private readonly string _keyword;
12+
private readonly string _response;
13+
14+
public TestCommand(string keyword, string response)
15+
{
16+
_keyword = keyword;
17+
_response = response;
18+
}
19+
20+
public bool IsReponsibleFor(string keyword)
21+
{
22+
return keyword == _keyword;
23+
}
24+
25+
public string Execute(string arguments)
26+
{
27+
return $"{_response} {arguments}".Trim();
28+
}
29+
}
30+
31+
[Test]
32+
public void Execute_WithSlashPrefix_ExecutesCommand()
33+
{
34+
// Arrange
35+
var collection = new ClientCommandCollection();
36+
collection.Add(new TestCommand("test", "executed"));
37+
38+
// Act
39+
var result = collection.Execute("/test arg1 arg2");
40+
41+
// Assert
42+
Assert.IsTrue(result.IsSuccess);
43+
Assert.AreEqual("executed arg1 arg2", result.Value);
44+
}
45+
46+
[Test]
47+
public void Execute_WithoutSlashPrefix_ReturnsFailure()
48+
{
49+
// Arrange
50+
var collection = new ClientCommandCollection();
51+
collection.Add(new TestCommand("test", "executed"));
52+
53+
// Act
54+
var result = collection.Execute("test arg1 arg2");
55+
56+
// Assert
57+
Assert.IsFalse(result.IsSuccess);
58+
Assert.AreEqual("Invalid Command!", result.Error);
59+
}
60+
61+
[Test]
62+
public void ExecuteWithoutPrefix_WithoutSlashPrefix_ExecutesCommand()
63+
{
64+
// Arrange
65+
var collection = new ClientCommandCollection();
66+
collection.Add(new TestCommand("exec", "executed"));
67+
68+
// Act
69+
var result = collection.ExecuteWithoutPrefix("exec calculate.ps1 \"7 + 4\"");
70+
71+
// Assert
72+
Assert.IsTrue(result.IsSuccess);
73+
Assert.AreEqual("executed calculate.ps1 \"7 + 4\"", result.Value);
74+
}
75+
76+
[Test]
77+
public void ExecuteWithoutPrefix_UnknownCommand_ReturnsFailure()
78+
{
79+
// Arrange
80+
var collection = new ClientCommandCollection();
81+
collection.Add(new TestCommand("exec", "executed"));
82+
83+
// Act
84+
var result = collection.ExecuteWithoutPrefix("unknown arg1 arg2");
85+
86+
// Assert
87+
Assert.IsFalse(result.IsSuccess);
88+
Assert.AreEqual("Unknown command: unknown", result.Error);
89+
}
90+
91+
[Test]
92+
public void IsAKnownCommand_WithSlashPrefix_ReturnsTrue()
93+
{
94+
// Arrange
95+
var collection = new ClientCommandCollection();
96+
collection.Add(new TestCommand("test", "executed"));
97+
98+
// Act
99+
var result = collection.IsAKnownCommand("/test");
100+
101+
// Assert
102+
Assert.IsTrue(result);
103+
}
104+
105+
[Test]
106+
public void IsAKnownCommand_WithoutSlashPrefix_ReturnsFalse()
107+
{
108+
// Arrange
109+
var collection = new ClientCommandCollection();
110+
collection.Add(new TestCommand("test", "executed"));
111+
112+
// Act
113+
var result = collection.IsAKnownCommand("test");
114+
115+
// Assert
116+
Assert.IsFalse(result);
117+
}
118+
}
119+
}

Source/LocalNetAppChat/LocalNetAppChat.Domain/Bots/ClientCommands/ClientCommandCollection.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,22 @@ public Result<string> Execute(string command)
3030
return Result<string>.Failure("Invalid commmand.");
3131
}
3232

33+
public Result<string> ExecuteWithoutPrefix(string command)
34+
{
35+
var rest = command;
36+
var keyWord = CommandMessageTokenizer.GetToken(ref rest);
37+
38+
foreach (var clientCommand in _clientCommands)
39+
{
40+
if (clientCommand.IsReponsibleFor(keyWord))
41+
{
42+
return Result<string>.Success(clientCommand.Execute(rest));
43+
}
44+
}
45+
46+
return Result<string>.Failure($"Unknown command: {keyWord}");
47+
}
48+
3349
public bool IsAKnownCommand(string command)
3450
{
3551
if (!CommandMessageTokenizer.IsCommandMessage(command))

docs/scenarios/math-bots/quickstart-math-bots.md

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,14 @@ LocalNetAppChat.Server --port 5000 --key "demo"
2121

2222
**Terminal 2 - Emitter (Generator):**
2323
```bash
24-
LocalNetAppChat.ConsoleClient emitter \
25-
--server localhost --port 5000 --key "demo" \
26-
--clientName "MathGenerator" \
27-
--command "python -u python/generator-simple.py"
24+
LocalNetAppChat.ConsoleClient emitter --server localhost --port 5000 --key "demo" --clientName "MathGenerator" --command "python -u python/generator-simple.py"
2825
```
2926

3027
**Wichtig**: Das `-u` Flag ist essentiell! Ohne dieses Flag puffert Python die Ausgabe und der Emitter sendet nichts.
3128

3229
**Terminal 3 - Beobachter:**
3330
```bash
34-
LocalNetAppChat.ConsoleClient listener \
35-
--server localhost --port 5000 --key "demo" \
36-
--clientName "Observer"
31+
LocalNetAppChat.ConsoleClient listener --server localhost --port 5000 --key "demo" --clientName "Observer"
3732
```
3833

3934
### Was Sie sehen werden
@@ -66,20 +61,17 @@ LocalNetAppChat.Server --port 5000 --key "demo"
6661

6762
**Terminal 2 - Calculator Bot:**
6863
```bash
69-
LocalNetAppChat.Bot --server localhost --port 5000 --key "demo" \
70-
--clientName "CalculatorBot" --scriptspath "./scripts"
64+
LocalNetAppChat.Bot --server localhost --port 5000 --key "demo" --clientName "CalculatorBot" --scriptspath "./scripts"
7165
```
7266

7367
**Terminal 3 - Observer:**
7468
```bash
75-
LocalNetAppChat.ConsoleClient listener --server localhost --port 5000 --key "demo" \
76-
--clientName "Observer"
69+
LocalNetAppChat.ConsoleClient listener --server localhost --port 5000 --key "demo" --clientName "Observer"
7770
```
7871

7972
**Terminal 4 - Chat (Controller):**
8073
```bash
81-
LocalNetAppChat.ConsoleClient chat --server localhost --port 5000 --key "demo" \
82-
--clientName "Teacher"
74+
LocalNetAppChat.ConsoleClient chat --server localhost --port 5000 --key "demo" --clientName "Teacher"
8375
```
8476

8577
### Bots verwenden

0 commit comments

Comments
 (0)