C# backend for web-based chat application that features Catapult SMS and MMS capabilities.
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
csharp-web-sms-chat
.editorconfig
.gitattributes
.gitignore
CODE_OF_CONDUCT.md
CONTRIBUTING.md
ISSUE_TEMPLATE.md
LICENSE
PULLREQUEST.md
PULL_REQUEST_TEMPLATE.md
README.md
azuredeploy.json
csharp-web-sms-chat.sln

README.md

C Sharp Web SMS Chat

This application is outdated, but will be updated soon!

C# backend for web-based chat application that features Catapult SMS and MMS capabilities

Prerequisites

  • Configured Machine with Ngrok/Port Forwarding -OR- Azure Account
  • Visual Studio 2015
  • Git
  • Common Azure Tools for Visual Studio (they are preinstalled with Visual Studio)

Build and Deploy

Azure One Click

Deploy to Azure

How it works

Routes.cs

Routes.cs contains, well, the routes for the application.

There are two main endpoints:

  • post /upload add media to Catapult
  • post /{userId}/callback callback URL for catapult events

Grab client and upload media to catapult

Post["/upload", true] = async (c, t) =>
{
  Debug.Print("Uploading file");
  var file = Request.Files.First();
  var fileName = Guid.NewGuid().ToString("N");
  var serializer = new JavaScriptSerializer();
  var auth = serializer.Deserialize<Dictionary<string, string>>(Request.Headers.Authorization);
  var client = new Client(auth["UserId"], auth["ApiToken"], auth["ApiSecret"]);
  await client.Media.UploadAsync(new UploadMediaFileData
  {
    MediaName = fileName,
    Stream = file.Value,
    ContentType = file.ContentType
  });
  return new Dictionary<string, string>
  {
    {"fileName", fileName}
  };
};

Each user has their own callback path from catapult

Post["/{userId}/callback", true] = async (c, t) =>
{
  var userId = (string) c.UserId;
  Debug.Print("Handling Catapult callback for user Id {0}", userId);
  using (var reader = new StreamReader(Request.Body, Encoding.UTF8))
  {
    var json = await reader.ReadToEndAsync();
    Debug.Print("Data from Catapult for {0}: {1}", userId, json);
    var serializer = new JavaScriptSerializer();
    var data = serializer.DeserializeObject(json);
    WebSocketHandler[] sockets;
    lock (WebSocketSmsChatHandler.ActiveSockets)
    {
      sockets = WebSocketSmsChatHandler.ActiveSockets.ToArray();
    }
    foreach (var socket in sockets.Where(s => (string) s.WebSocketContext.Items["userId"] == userId))
    {
      Debug.Print("Sending Catapult data to websocket client");
      socket.EmitEvent("message", data);
    }
    return "";
  }
};

WebSocketSmsChatHandler.cs

WebSocketSmsChatHandler.cs contains all the information shuttled between the client and Server

Check if application already exists for user

var applicationId =
            (client.Application.List(new ApplicationQuery {Size = 1000})
               .FirstOrDefault(a => a.Name == applicationName) ?? new Application()).Id;
if (applicationId == null)
{
  applicationId = await client.Application.CreateAsync(new CreateApplicationData
  {
    Name = applicationName,
    IncomingMessageUrl = $"{baseUrl}{userId}/callback"
  });
}

Check if phone number exists for user, if not create new number

Debug.Print("Getting phone number");
var phoneNumber = (client.PhoneNumber.List(new PhoneNumberQuery
{
  ApplicationId = applicationId,
  Size = 1
}).FirstOrDefault() ?? new PhoneNumber()).Number;
var number = "";
if (phoneNumber == null)
{
  Debug.Print("Reserving new phone number");
  number = (await client.AvailableNumber.SearchLocalAsync(new LocalNumberQuery
  {
    City = "Cary",
    State = "NC",
    Quantity = 1
  })).First().Number;
  await client.PhoneNumber.CreateAsync(new CreatePhoneNumberData
  {
    ApplicationId = applicationId,
    Number = number
  });
}

Messages are stored on catapult, so we need to fetch those

{"getMessages", async (message, socket) =>
  {
    var client = GetCatapultClient(message);
    var data = (Dictionary<string, object>) message["Data"];
    var outMessages = client.Message.List(new MessageQuery
    {
      Size = 1000,
      Direction = MessageDirection.Out,
      From = data["PhoneNumber"] as string
    });
    var inMessages = client.Message.List(new MessageQuery
    {
      Size = 1000,
      Direction = MessageDirection.In,
      To = data["PhoneNumber"] as string
    });
    var messages = inMessages.Concat(outMessages).OrderBy(m => m.Time).Select(m => m.ToDictionary());
    socket.WebSocketContext.Items["userId"] = (string)((Dictionary<string, object>)message["Auth"])["UserId"];
    return messages;
  }
},

How to send a messages

{"sendMessage", async (message, socket) =>
  {
    var client = GetCatapultClient(message);
    var data = (Dictionary<string, object>) message["Data"];
    var messageId = await client.Message.SendAsync(new MessageData
    {
      From = data["from"] as string,
      To = data["to"] as string,
      Text = data["text"] as string,
      Media = data["media"] as string[]
    });
    var newMessage = await client.Message.GetAsync(messageId);
    socket.WebSocketContext.Items["userId"] = (string)((Dictionary<string, object>)message["Auth"])["UserId"];
    return newMessage.ToDictionary();
  }
}

Locally

Clone the web application.

git clone https://github.com/BandwidthExamples/csharp-web-sms-chat.git

Open solution file in Visual Studio and build it.

You can run compiled C# code with IIS Express on local machine if you have ability to handle external requests or use any external hosting (like Azure).

Note: If you are going to use Azure as hosting please after deployment open this site in Azure Management Console, select app settings and switch on Web Sockets. Otherwise the app will not work correctly.