diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index beb9074..12776e1 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -1,2 +1,2 @@
# Global rule:
-* @Bandwidth-Samples/dx
+* @Bandwidth-Samples/band-swi @Bandwidth-Samples/band-devx
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..5a29fba
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,19 @@
+version: 2
+
+updates:
+ - package-ecosystem: nuget
+ directory: /AutoRespond
+ schedule:
+ interval: monthly
+ open-pull-requests-limit: 5
+ ignore:
+ - dependency-name: "Bandwidth.Sdk"
+ update-types:
+ - version-update:semver-minor
+ - version-update:semver-patch
+ groups:
+ dependencies:
+ patterns:
+ - "*"
+ exclude-patterns:
+ - "Bandwidth.Sdk"
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000..5c5263c
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,16 @@
+name: Test
+
+on:
+ pull_request:
+ branches:
+ - main
+ schedule:
+ - cron: "0 9 * * 1"
+
+jobs:
+ contract-test:
+ name: Contract Test
+ uses: Bandwidth-Samples/sample-app-tooling/.github/workflows/csharp.yml@main
+ with:
+ sample: messaging-auto-respond
+ working-directory: AutoRespond
diff --git a/SendAutoRespond/SendAutoRespond.csproj b/AutoRespond/AutoRespond.csproj
similarity index 59%
rename from SendAutoRespond/SendAutoRespond.csproj
rename to AutoRespond/AutoRespond.csproj
index 4b5a048..fd311df 100644
--- a/SendAutoRespond/SendAutoRespond.csproj
+++ b/AutoRespond/AutoRespond.csproj
@@ -1,13 +1,13 @@
- net7.0
+ net8.0enableenable
-
+
diff --git a/AutoRespond/Program.cs b/AutoRespond/Program.cs
new file mode 100644
index 0000000..46c4cb4
--- /dev/null
+++ b/AutoRespond/Program.cs
@@ -0,0 +1,119 @@
+using Bandwidth.Standard.Api;
+using Bandwidth.Standard.Client;
+using Bandwidth.Standard.Model;
+using Newtonsoft.Json;
+
+//Setting up environment variables
+string? BW_ACCOUNT_ID = Environment.GetEnvironmentVariable("BW_ACCOUNT_ID");
+string? BW_CLIENT_ID = Environment.GetEnvironmentVariable("BW_CLIENT_ID");
+string? BW_CLIENT_SECRET = Environment.GetEnvironmentVariable("BW_CLIENT_SECRET");
+string? BW_MESSAGING_APPLICATION_ID = Environment.GetEnvironmentVariable("BW_MESSAGING_APPLICATION_ID");
+string? BW_NUMBER = Environment.GetEnvironmentVariable("BW_NUMBER");
+string? LOCAL_PORT = Environment.GetEnvironmentVariable("LOCAL_PORT");
+
+if (BW_ACCOUNT_ID == null || BW_CLIENT_ID == null || BW_CLIENT_SECRET == null || BW_MESSAGING_APPLICATION_ID == null
+ || BW_NUMBER == null || !int.TryParse(LOCAL_PORT, out int port))
+{
+ Console.WriteLine("Please set the environmental variables defined in the README");
+ Environment.Exit(1);
+ return;
+}
+
+var builder = WebApplication.CreateBuilder(args);
+builder.WebHost.UseUrls($"http://localhost:{port}");
+var app = builder.Build();
+
+Configuration configuration = new Configuration();
+configuration.OAuthClientId = BW_CLIENT_ID;
+configuration.OAuthClientSecret = BW_CLIENT_SECRET;
+
+var statusMessages = new Dictionary
+{
+ [StatusCallbackTypeEnum.Sending] = "message-sending type is only for MMS.",
+ [StatusCallbackTypeEnum.Delivered] = "Your message has been handed off to the Bandwidth's MMSC network, but has not been confirmed at the downstream carrier.",
+ [StatusCallbackTypeEnum.Failed] = "For MMS and Group Messages, you will only receive this callback if you have enabled delivery receipts on MMS."
+};
+
+var replies = new Dictionary
+{
+ ["stop"] = "STOP: OK, you'll no longer receive messages from us.",
+ ["quit"] = "QUIT: OK, you'll no longer receive messages from us.",
+ ["help"] = "Valid words are: STOP, QUIT, HELP, and INFO. Reply STOP or QUIT to opt out.",
+ ["info"] = "INFO: This is the test responder service. Reply STOP or QUIT to opt out."
+};
+const string DEFAULT_REPLY = "Please respond with a valid word. Reply HELP for help.";
+
+async Task ReadBody(HttpContext context)
+{
+ try
+ {
+ using var streamReader = new StreamReader(context.Request.Body);
+ return JsonConvert.DeserializeObject(await streamReader.ReadToEndAsync());
+ }
+ catch (JsonException)
+ {
+ return default;
+ }
+}
+
+string AutoReply(string? text)
+{
+ return replies.GetValueOrDefault((text ?? "").Trim().ToLower(), DEFAULT_REPLY);
+}
+
+async Task SendReply(string to, string text)
+{
+ MessageRequest messageRequest = new MessageRequest(
+ applicationId: BW_MESSAGING_APPLICATION_ID,
+ to: new List { to },
+ from: BW_NUMBER,
+ text: text
+ );
+
+ try
+ {
+ MessagesApi apiInstance = new MessagesApi(configuration);
+ Message message = await apiInstance.CreateMessageAsync(BW_ACCOUNT_ID, messageRequest);
+ Console.WriteLine($"Sent reply {message.Id}\nTo: {to}\nText: {text}");
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine($"Failed to send reply: {e.Message}");
+ }
+}
+
+app.MapPost("/callbacks/outbound/messaging/status", async (HttpContext context) =>
+{
+ var callbacks = await ReadBody>(context);
+ if (callbacks == null || !callbacks.All(callback => statusMessages.ContainsKey(callback.Type)))
+ {
+ return Results.BadRequest();
+ }
+
+ foreach (var callback in callbacks)
+ {
+ Console.WriteLine(statusMessages[callback.Type]);
+ }
+
+ return Results.NoContent();
+});
+
+app.MapPost("/callbacks/inbound/messaging", async (HttpContext context) =>
+{
+ var callbacks = await ReadBody>(context);
+ if (callbacks == null || !callbacks.All(callback => callback.Type == InboundCallbackTypeEnum.MessageReceived))
+ {
+ return Results.BadRequest();
+ }
+
+ foreach (var callback in callbacks)
+ {
+ var message = callback.Message;
+ Console.WriteLine($"{callback.Description}\nTo: {message.To[0]}\nFrom: {message.From}\nText: {message.Text}");
+ await SendReply(message.From, AutoReply(message.Text));
+ }
+
+ return Results.NoContent();
+});
+
+app.Run();
diff --git a/SendAutoRespond/Properties/launchSettings.json b/AutoRespond/Properties/launchSettings.json
similarity index 83%
rename from SendAutoRespond/Properties/launchSettings.json
rename to AutoRespond/Properties/launchSettings.json
index a6cf4a4..46a8914 100644
--- a/SendAutoRespond/Properties/launchSettings.json
+++ b/AutoRespond/Properties/launchSettings.json
@@ -12,7 +12,6 @@
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
- "applicationUrl": "http://localhost:5001",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
@@ -21,7 +20,6 @@
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
- "applicationUrl": "https://localhost:7278;http://localhost:5001",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
diff --git a/SendAutoRespond/appsettings.Development.json b/AutoRespond/appsettings.Development.json
similarity index 100%
rename from SendAutoRespond/appsettings.Development.json
rename to AutoRespond/appsettings.Development.json
diff --git a/SendAutoRespond/appsettings.json b/AutoRespond/appsettings.json
similarity index 100%
rename from SendAutoRespond/appsettings.json
rename to AutoRespond/appsettings.json
diff --git a/LICENSE b/LICENSE
index c85e706..ddc77f4 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2022 Bandwidth Samples
+Copyright (c) 2026 Bandwidth Samples
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index 3fdc41f..703bf39 100644
--- a/README.md
+++ b/README.md
@@ -1,54 +1,56 @@
-# Send Auto Response C#
+# Messaging Auto Respond
-
+
- # Table of Contents
+# Table of Contents
-- [Send Auto Response C#](#send-auto-response-c)
-- [Table of Contents](#table-of-contents)
-- [Description](#description)
-- [Pre-Requisites](#pre-requisites)
-- [Environmental Variables](#environmental-variables)
-- [Running the Application](#running-the-application)
-- [Callback URLs](#callback-urls)
- - [Ngrok](#ngrok)
+* [Description](#description)
+* [Pre-Requisites](#pre-requisites)
+* [Running the Application](#running-the-application)
+* [Environmental Variables](#environmental-variables)
+* [Callback URLs](#callback-urls)
+ * [Ngrok](#ngrok)
# Description
-This app automatically responds to texts sent to the associated Bandwidth number. Any texts sent to the `BW_NUMBER` will be auto-responded to using the auto response map. Valid words are: `STOP`, `QUIT`, `HELP`, and `INFO`.
+This sample app automatically replies to texts sent to your Bandwidth number. The reply depends on the text received: `STOP`, `QUIT`, `HELP` and `INFO` each get their own reply, and anything else gets a prompt to reply `HELP`. Keywords are matched against the whole message and are not case sensitive.
-To use this app, you must check the "Use multiple callback URLs" box on the application page in Dashboard. Then in Dashboard, set the INBOUND CALLBACK to `/callbacks/inbound/messaging` and the STATUS CALLBACK to `/callbacks/outbound/messaging/status`. The same can be accomplished via the Dashboard API by setting InboundCallbackUrl and OutboundCallbackUrl respectively.
+The `STOP` and `QUIT` replies are for demonstration only. This app doesn't keep track of opt-outs, so it will keep replying to a number that has sent `STOP`.
-Inbound callbacks are sent notifying you of a received message on a Bandwidth number, this app sends a custom response to those messages based on their content. Outbound callbacks are status updates for messages sent from a Bandwidth number, this app has a dedicated response for each type of status update.
+In order to use the correct endpoints, you must check the "Use multiple callback URLs" box on the application page in Dashboard. Then in Dashboard, set the INBOUND CALLBACK to `/callbacks/inbound/messaging` and the STATUS CALLBACK to `/callbacks/outbound/messaging/status`. The same can be accomplished via the Dashboard API by setting `InboundCallbackUrl` and `OutboundCallbackUrl` respectively.
+
+Inbound callbacks are sent notifying you of a received message on a Bandwidth number. This app prints the phone numbers involved and the text received, then sends the reply. Outbound callbacks are status updates for messages sent from a Bandwidth number, this app has a dedicated response for each type of status update.
# Pre-Requisites
In order to use the Bandwidth API users need to set up the appropriate application at the [Bandwidth Dashboard](https://dashboard.bandwidth.com/) and create API tokens.
-To create an application log into the [Bandwidth Dashboard](https://dashboard.bandwidth.com/) and navigate to the `Applications` tab. Fill out the **New Application** form selecting the service that the application will be used for (this sample app uses a messaging application). All Bandwidth services require publicly accessible Callback URLs, for more information on how to set one up see [Callback URLs](#callback-urls).
+To create an application log into the [Bandwidth Dashboard](https://dashboard.bandwidth.com/) and navigate to the `Applications` tab. Fill out the **New Application** form selecting the service (Messaging or Voice) that the application will be used for. All Bandwidth services require publicly accessible Callback URLs, for more information on how to set one up see [Callback URLs](#callback-urls).
For more information about API credentials see our [Account Credentials](https://dev.bandwidth.com/docs/account/credentials) page.
-# Environmental Variables
+# Running the Application
-The sample app uses the below environmental variables.
+Use the following command to run the application:
```sh
-BW_ACCOUNT_ID # Your Bandwidth Account Id
-BW_USERNAME # Your Bandwidth API Username
-BW_PASSWORD # Your Bandwidth API Password
-BW_MESSAGING_APPLICATION_ID # Your Messaging Application Id created in the dashboard
+cd AutoRespond/
+dotnet run
```
-# Running the Application
+# Environmental Variables
-Use the following command to run the application:
+The sample app uses the below environmental variables.
```sh
-cd SendAutoRespond/
-dotnet run
+BW_ACCOUNT_ID # Your Bandwidth Account Id
+BW_CLIENT_ID # Your Bandwidth API OAuth Client Id
+BW_CLIENT_SECRET # Your Bandwidth API OAuth Client Secret
+BW_NUMBER # The Bandwidth phone number involved with this application
+BW_MESSAGING_APPLICATION_ID # Your Messaging Application Id created in the dashboard
+LOCAL_PORT # The port number you wish to run the sample on
```
# Callback URLs
@@ -62,11 +64,10 @@ Below are the callback paths:
## Ngrok
A simple way to set up a local callback URL for testing is to use the free tool [ngrok](https://ngrok.com/).
-After you have downloaded and installed `ngrok` run the following command to open a public tunnel to your port (in this case port 5001).
+After you have downloaded and installed `ngrok` run the following command to open a public tunnel to your port (`$LOCAL_PORT`)
-```cmd
-ngrok http 5001
+```sh
+ngrok http $LOCAL_PORT
```
-You can view your public URL at `http://127.0.0.1:4040` after ngrok is running. You can also view the status of the tunnel and requests/responses here.
-*Note: If you would like to change your port number feel free to do so. However, if you do change the port you will also need to change the number appended to the application URL in the `launchSettings.json` file located in `SendAutoRespond/Properties/`*
+You can view your public URL at `http://127.0.0.1:4040` after ngrok is running. You can also view the status of the tunnel and requests/responses here. Once your public ngrok url has been created, use it as the base of the callback URLs in the messaging application created in the [Pre-Requisites](#pre-requisites) section.
diff --git a/SendAutoRespond/Program.cs b/SendAutoRespond/Program.cs
deleted file mode 100644
index 33773cc..0000000
--- a/SendAutoRespond/Program.cs
+++ /dev/null
@@ -1,122 +0,0 @@
-using Bandwidth.Standard.Api;
-using Bandwidth.Standard.Client;
-using Bandwidth.Standard.Model;
-using Newtonsoft.Json;
-
-var builder = WebApplication.CreateBuilder(args);
-var app = builder.Build();
-
-string BW_USERNAME;
-string BW_PASSWORD;
-string BW_MESSAGING_APPLICATION_ID;
-string BW_ACCOUNT_ID;
-
-//Setting up environment variables
-try
-{
- BW_USERNAME = Environment.GetEnvironmentVariable("BW_USERNAME");
- BW_PASSWORD = Environment.GetEnvironmentVariable("BW_PASSWORD");
- BW_MESSAGING_APPLICATION_ID = Environment.GetEnvironmentVariable("BW_MESSAGING_APPLICATION_ID");
- BW_ACCOUNT_ID = Environment.GetEnvironmentVariable("BW_ACCOUNT_ID");
-}
-catch (Exception)
-{
- Console.WriteLine("Please set the environmental variables defined in the README");
- throw;
-}
-
-Configuration configuration = new Configuration();
-configuration.Username = BW_USERNAME;
-configuration.Password = BW_PASSWORD;
-
-app.MapPost("/callbacks/inbound/messaging", async (HttpContext context) =>
-{
- var requestBody = new List