Receiving Messages
Signal.Bot provides a convenient StartReceiving method that handles all the complexity of polling for new messages.
Basic Message Receiving
The simplest way to start receiving messages:
csharp
var client = new SignalBotClient("http://localhost:8080");
var botNumber = "+1234567890";
using var cts = new CancellationTokenSource();
await client.StartReceiving(
botNumber,
handleMessage: async (botClient, message, ct) =>
{
Console.WriteLine($"Received: {message.DataMessage?.Message}");
},
handleError: async (botClient, exception, ct) =>
{
Console.WriteLine($"Error: {exception.Message}");
},
cancellationToken: cts.Token
);
// Keep running
Console.ReadKey();
cts.Cancel();1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Message Structure
Messages in Signal.Bot follow this structure:
csharp
public class SignalMessage
{
public Envelope Envelope { get; set; }
public string Source { get; set; } // Sender's phone number
public DataMessage DataMessage { get; set; } // Contains the actual message
public long Timestamp { get; set; }
// ... other properties
}
public class DataMessage
{
public string Message { get; set; } // Text content
public List<Attachment> Attachments { get; set; }
public string GroupId { get; set; }
public List<Mention> Mentions { get; set; }
public Quote Quote { get; set; } // If replying to a message
// ... other properties
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Handling Different Message Types
Text Messages
csharp
await client.StartReceiving(
botNumber,
handleMessage: async (botClient, message, ct) =>
{
if (message.DataMessage?.Message != null)
{
var text = message.DataMessage.Message;
var sender = message.Source;
Console.WriteLine($"{sender}: {text}");
}
},
handleError: HandleError,
cancellationToken: cts.Token
);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Messages with Attachments
csharp
await client.StartReceiving(
botNumber,
handleMessage: async (botClient, message, ct) =>
{
if (message.DataMessage?.Attachments?.Any() == true)
{
foreach (var attachment in message.DataMessage.Attachments)
{
Console.WriteLine($"Received attachment: {attachment.ContentType}");
Console.WriteLine($"Filename: {attachment.Filename}");
Console.WriteLine($"ID: {attachment.Id}");
}
}
},
handleError: HandleError,
cancellationToken: cts.Token
);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Group Messages
csharp
await client.StartReceiving(
botNumber,
handleMessage: async (botClient, message, ct) =>
{
if (!string.IsNullOrEmpty(message.DataMessage?.GroupId))
{
Console.WriteLine($"Group message in: {message.DataMessage.GroupId}");
Console.WriteLine($"From: {message.Source}");
Console.WriteLine($"Text: {message.DataMessage.Message}");
}
},
handleError: HandleError,
cancellationToken: cts.Token
);1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
Reactions
csharp
await client.StartReceiving(
botNumber,
handleMessage: async (botClient, message, ct) =>
{
if (message.DataMessage?.Reaction != null)
{
var reaction = message.DataMessage.Reaction;
Console.WriteLine($"{message.Source} reacted with {reaction.Emoji}");
Console.WriteLine($"To message from: {reaction.TargetAuthor}");
}
},
handleError: HandleError,
cancellationToken: cts.Token
);1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
Command Handling
Build a simple command system:
csharp
await client.StartReceiving(
botNumber,
handleMessage: async (botClient, message, ct) =>
{
var text = message.DataMessage?.Message;
if (string.IsNullOrEmpty(text)) return;
var sender = message.Source;
// Parse command
if (text.StartsWith("/"))
{
var parts = text.Split(' ', 2);
var command = parts[0].ToLower();
var args = parts.Length > 1 ? parts[1] : string.Empty;
switch (command)
{
case "/start":
await botClient.SendMessageAsync(
number: botNumber,
message: "Welcome! Use /help to see available commands.",
recipients: new[] { sender },
cancellationToken: ct
);
break;
case "/help":
await botClient.SendMessageAsync(
number: botNumber,
message: @"Available commands:
/start - Start the bot
/help - Show this message
/time - Get current time
/echo <text> - Echo back your message",
recipients: new[] { sender },
cancellationToken: ct
);
break;
case "/time":
await botClient.SendMessageAsync(
number: botNumber,
message: $"Current time: {DateTime.Now:HH:mm:ss}",
recipients: new[] { sender },
cancellationToken: ct
);
break;
case "/echo":
if (!string.IsNullOrEmpty(args))
{
await botClient.SendMessageAsync(
number: botNumber,
message: args,
recipients: new[] { sender },
cancellationToken: ct
);
}
break;
}
}
},
handleError: HandleError,
cancellationToken: cts.Token
);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
Advanced Pattern: Message Router
Create a more structured message handling system:
csharp
public class MessageRouter
{
private readonly SignalBotClient _client;
private readonly string _botNumber;
private readonly Dictionary<string, Func<SignalBotClient, SignalMessage, CancellationToken, Task>> _handlers;
public MessageRouter(SignalBotClient client, string botNumber)
{
_client = client;
_botNumber = botNumber;
_handlers = new Dictionary<string, Func<SignalBotClient, SignalMessage, CancellationToken, Task>>();
}
public void OnCommand(string command, Func<SignalBotClient, SignalMessage, CancellationToken, Task> handler)
{
_handlers[command.ToLower()] = handler;
}
public async Task HandleMessage(SignalBotClient botClient, SignalMessage message, CancellationToken ct)
{
var text = message.DataMessage?.Message;
if (string.IsNullOrEmpty(text) || !text.StartsWith("/")) return;
var command = text.Split(' ')[0].ToLower();
if (_handlers.TryGetValue(command, out var handler))
{
await handler(botClient, message, ct);
}
}
public async Task Start(CancellationToken cancellationToken)
{
await _client.StartReceiving(
_botNumber,
handleMessage: HandleMessage,
handleError: async (client, ex, ct) => Console.WriteLine($"Error: {ex.Message}"),
cancellationToken: cancellationToken
);
}
}
// Usage
var router = new MessageRouter(client, botNumber);
router.OnCommand("/start", async (client, message, ct) =>
{
await client.SendMessageAsync(
number: botNumber,
message: "Welcome!",
recipients: new[] { message.Source },
cancellationToken: ct
);
});
router.OnCommand("/help", async (client, message, ct) =>
{
await client.SendMessageAsync(
number: botNumber,
message: "Available commands: /start, /help",
recipients: new[] { message.Source },
cancellationToken: ct
);
});
await router.Start(cts.Token);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
Filtering Messages
Ignore Bot Messages
csharp
await client.StartReceiving(
botNumber,
handleMessage: async (botClient, message, ct) =>
{
// Ignore messages from the bot itself
if (message.Source == botNumber) return;
// Process message...
},
handleError: HandleError,
cancellationToken: cts.Token
);1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
Group-Only Messages
csharp
await client.StartReceiving(
botNumber,
handleMessage: async (botClient, message, ct) =>
{
// Only handle group messages
if (string.IsNullOrEmpty(message.DataMessage?.GroupId)) return;
// Process group message...
},
handleError: HandleError,
cancellationToken: cts.Token
);1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
Private Messages Only
csharp
await client.StartReceiving(
botNumber,
handleMessage: async (botClient, message, ct) =>
{
// Only handle private messages
if (!string.IsNullOrEmpty(message.DataMessage?.GroupId)) return;
// Process private message...
},
handleError: HandleError,
cancellationToken: cts.Token
);1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
Error Handling
Properly handle errors during message processing:
csharp
async Task HandleError(SignalBotClient client, Exception exception, CancellationToken ct)
{
Console.WriteLine($"Error occurred: {exception.GetType().Name}");
Console.WriteLine($"Message: {exception.Message}");
if (exception is HttpRequestException httpEx)
{
Console.WriteLine("Network error - check signal-cli-rest-api connection");
}
else if (exception is TaskCanceledException)
{
Console.WriteLine("Operation was cancelled");
}
else
{
Console.WriteLine($"Stack trace: {exception.StackTrace}");
}
// Optionally notify admin
// await NotifyAdmin(exception.Message);
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Polling Configuration
The StartReceiving method polls for new messages at regular intervals. While the default settings work for most cases, you can customize the polling behavior if needed.
TIP
The current implementation uses a simple polling mechanism. For high-traffic bots, consider implementing exponential backoff or rate limiting.
Best Practices
1. Always Use Cancellation Tokens
csharp
var cts = new CancellationTokenSource();
// Handle Ctrl+C gracefully
Console.CancelKeyPress += (sender, e) =>
{
e.Cancel = true;
cts.Cancel();
};
await client.StartReceiving(..., cancellationToken: cts.Token);1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
2. Separate Concerns
csharp
// ✅ Good - separate message handling logic
async Task HandleTextMessage(SignalBotClient client, SignalMessage message, CancellationToken ct)
{
// Handle text message
}
async Task HandleAttachment(SignalBotClient client, SignalMessage message, CancellationToken ct)
{
// Handle attachment
}
// ❌ Bad - everything in one handler
await client.StartReceiving(..., handleMessage: async (c, m, ct) => {
// 500 lines of code here
});1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
3. Log Everything Important
csharp
await client.StartReceiving(
botNumber,
handleMessage: async (botClient, message, ct) =>
{
_logger.LogInformation(
"Received message from {Sender} at {Time}",
message.Source,
DateTimeOffset.FromUnixTimeMilliseconds(message.Timestamp)
);
// Process message...
},
handleError: async (botClient, exception, ct) =>
{
_logger.LogError(exception, "Error processing message");
},
cancellationToken: cts.Token
);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Next Steps
- Learn about working with groups
- Explore attachment handling
- Check out complete examples
