Skip to content

Echo Bot Example

A simple bot that echoes back any message it receives.

Overview

This is the most basic example of a Signal bot. It demonstrates:

  • Receiving messages
  • Sending responses
  • Basic error handling
  • Graceful shutdown

Code

csharp
public static void Main(string[] args)
{
    // Configuration
    const string apiUrl = "http://localhost:8080";
    const string botNumber = "+1234567890"; // Replace with your Signal number

    // Create client
    var client = new SignalBotClient(x => x.WithBaseUrl(apiUrl).WithNumber(botNumber));

    // Cancellation token for graceful shutdown
    using var cts = new CancellationTokenSource();

    // Handle Ctrl+C
    Console.CancelKeyPress += (sender, e) =>
    {
        Console.WriteLine("\nStopping bot...");
        e.Cancel = true;
        cts.Cancel();
    };

    Console.WriteLine("Echo Bot is starting...");
    Console.WriteLine($"Bot number: {botNumber}");
    Console.WriteLine("Press Ctrl+C to stop\n");

    // Start receiving messages
    client.StartReceiving(
        updateHandler: HandleMessage,
        errorHandler: HandleError,
        cancellationToken:
        cts.Token
    );

    Console.WriteLine("Bot stopped");
}

public static async Task HandleMessage(ISignalBotClient client, ReceivedMessageEnvelope message, CancellationToken token)
{
    var envelope = message.Envelope!;
    // Get the text message
    var text = envelope.DataMessage?.Message;

    // Ignore empty messages
    if (string.IsNullOrWhiteSpace(text))
    {
        return;
    }

    // Get sender
    var sender = message.Envelope?.SourceNumber ?? string.Empty;

    // Log received message
    Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] From {sender}: {text}");

    // Echo back with a prefix
    var echoMessage = $"You said: {text}";

    try
    {
        // Send the echo
        await client.SendMessageAsync(builder => builder.WithMessage(echoMessage).WithRecipient(sender),
            cancellationToken: token);

        Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] Echoed back to {sender}");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Error sending echo: {ex.Message}");
    }
}

public static async Task HandleError(ISignalBotClient client, Error err, CancellationToken cancellationToken)
{
    Console.WriteLine($"[ERROR] {err.Exception?.GetType().Name}: {err.Exception?.Message}");

    // Wait a bit before continuing to avoid tight error loops
    await Task.Delay(1000, cancellationToken);
}

Configuration

Before running, update these values:

csharp
var apiUrl = "http://localhost:8080";  // Your signal-cli-rest-api URL
var botNumber = "+1234567890";         // Your registered Signal number

Running the Bot

  1. Make sure signal-cli-rest-api is running:
bash
docker ps | grep signal-api
  1. Run the bot:
bash
dotnet run
  1. Send a message to your bot's number from another Signal account

  2. The bot will echo back your message with "You said: " prefix

Expected Output

Echo Bot is starting...
Bot number: +1234567890
Press Ctrl+C to stop

[14:23:15] From +0987654321: Hello!
[14:23:15] Echoed back to +0987654321
[14:23:20] From +0987654321: How are you?
[14:23:20] Echoed back to +0987654321

Enhancements

1. Ignore Bot's Own Messages

csharp
static async Task HandleMessage(
    SignalBotClient client,
    SignalMessage message,
    CancellationToken cancellationToken)
{
    var botNumber = "+1234567890";
    
    // Ignore messages from the bot itself
    if (message.Source == botNumber)
        return;
    
    // ... rest of handler
}

2. Add Typing Indicator

csharp
// Show typing before replying
await client.SendTypingIndicatorAsync(
    number: botNumber,
    recipient: sender,
    cancellationToken: cancellationToken
);

// Simulate thinking time
await Task.Delay(500, cancellationToken);

// Send the echo
await client.SendMessageAsync(...);

3. Handle Group Messages

csharp
static async Task HandleMessage(
    SignalBotClient client,
    SignalMessage message,
    CancellationToken cancellationToken)
{
    var text = message.DataMessage?.Message;
    if (string.IsNullOrWhiteSpace(text)) return;

    var sender = message.Source;
    var groupId = message.DataMessage?.GroupId;

    // Handle group messages
    if (!string.IsNullOrEmpty(groupId))
    {
        await client.SendMessageAsync(
            number: botNumber,
            message: $"@{sender} said: {text}",
            groupId: groupId,
            cancellationToken: cancellationToken
        );
    }
    else
    {
        // Handle direct messages
        await client.SendMessageAsync(
            number: botNumber,
            message: $"You said: {text}",
            recipients: new[] { sender },
            cancellationToken: cancellationToken
        );
    }
}

Troubleshooting

Bot not receiving messages?

  • Check signal-cli-rest-api is running: curl http://localhost:8080/v1/about
  • Verify your number is registered
  • Check console for errors

Bot not responding?

  • Check the bot number in SendMessageAsync matches your registered number
  • Verify network connectivity to signal-cli-rest-api
  • Check signal-cli-rest-api logs: docker logs signal-api

Messages received multiple times?

  • Normal behavior - messages stay in queue until acknowledged
  • They'll stop appearing after being processed

Next Steps