DEV Community

Matheus Cardoso
Matheus Cardoso

Posted on • Originally published at getstream.io

16 13

Moderate Chat Content with Swift on AWS Lambda

Most of the time, when building a chat application, it's essential to have some level of control over what your users can share and say to each other.

In this tutorial, we'll use Swift Lambda and Stream's powerful chat API to build a content moderation system that can prevent users from sending unwanted content. In this case, we'll filter out possible credit card data to prevent our users from sharing it with scammers. However, you can adapt it to other sensitive data or to block bad words.

Animation shows a chat screen with a message containing credit card information being typed and then redacted after submission

You can find the completed project on GitHub in the Samples folder inside the Swift Lambda repository.

Requirements

Set the Presend Webhook URL

After you've built your iOS chat app and the Swift Lambda is up and running, you need to configure the Presend Webhook URL using the Stream Chat REST API. To do this, you'll need your API Key, a server-side JWT, and your AWS Lambda URL.

To get the API Key, go to the Stream Chat dashboard. It will be there, similar to the image below.

Image shows a Stream application with the API Key and Secret in the Stream Dashboard

Also, copy the secret, as it is needed to generate the server-side JWT. You can use jwt.io to generate it. Just paste the secret inside the text field on the right side and copy the JWT on the left side.

Image shows JWT.io website with arrows pointing to the generated JWT and the text field where the secret is pasted

Finally, to set the Presend Webhook URL, you can run the curl command below in the terminal. Make sure to replace [api_key], [jwt], and [aws_lambda_endpoint_url].

curl --location --request PATCH 'https://chat-us-east-1.stream-io-api.com/app?api_key=[api_key]' \
--header 'Stream-Auth-Type: jwt' \
--header 'Authorization: [jwt]' \
--header 'Content-Type: application/json' \
--data-raw '{"before_message_send_hook_url": "[aws_lambda_endpoint_url]"}'
view raw Presend.sh hosted with ❤ by GitHub

Configure Swift Lambda

At first, your Swift Lambda will handle GET requests. Change it to process POST requests instead, which can be done by editing the serverless.yml file and swapping the line method: get with method: post.

Filter out credit card numbers

Now that the Swift Lambda is configured, we can get to the code part.

First, we need a function to redact credit card numbers from a message by replacing the numbers with asterisks. The code below does just that by using regular expressions to match valid numbers of known credit card operators.

func redactCreditCardNumbers(from text: String) -> String {
var text = text
let americanExpress = "(?:3[47][0-9]{13})";
let dinersClub = "(?:3(?:0[0-5]|[68][0-9])[0-9]{11})";
let discover = "(?:6(?:011|5[0-9]{2})(?:[0-9]{12}))";
let jcb = "(?:(?:2131|1800|35\\d{3})\\d{11})";
let maestro = "(?:(?:5[0678]\\d\\d|6304|6390|67\\d\\d)\\d{8,15})";
let mastercard = "(?:(?:5[1-5][0-9]{2}|222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12})";
let visa = "(?:4[0-9]{12})(?:[0-9]{3})?";
let all = [americanExpress, dinersClub, discover, jcb, maestro, mastercard, visa].joined(separator: "|")
let regex = try! NSRegularExpression(pattern: all)
let res = regex.matches(in: text, options: [], range: NSRange(location: 0, length: text.count))
res.forEach {
text = text.replacingCharacters(
in: Range($0.range, in: text)!,
with: String(repeating: "*", count: $0.range.length)
)
}
return text
}
view raw main.swift hosted with ❤ by GitHub

You should add that to the main.swift file.

Handle the message

When the POST request hits your lambda, it will contain a JSON object describing the new message. To see which fields you can expect in this payload, see the documentation.

In the main.swift file, paste the code below.

import AWSLambdaEvents
import AWSLambdaRuntime
import NIO
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
Lambda.run { (context: Lambda.Context, event: APIGateway.Request, callback: @escaping (Result<APIGateway.Response, Error>) -> Void) in
guard
let data = event.body?.data(using: .utf8),
let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
var message = json["message"] as? [String: Any]
else {
callback(.success(APIGateway.Response(statusCode: .ok)))
return
}
// rewrite message's text with redacted version
message["text"] = redactCreditCardNumbers(from: text)
let body = ["message": message]
let bodyData = try! JSONSerialization.data(withJSONObject: body)
let string = String(data: bodyData, encoding: .utf8)
callback(.success(APIGateway.Response(statusCode: .ok, body: string)))
}
view raw main.swift hosted with ❤ by GitHub

That code will parse the JSON body into a dictionary and modify the message object by running its "text" field through the credit card redaction function. As specified in the documentation, we return this modified message object to submit the redacted version.

Deploying and testing the content moderation

The main advantage of using Swift Lambda is that it's possible to iterate fast with it. After writing the moderation code, just run the ./Scripts/deploy.sh script again and wait a few seconds.

This animation shows the deploy script running and finishing successfully

After the upload is finished, you can run the chat app and type something in any channel. If it contains a seemingly valid credit card number such as 6011881485017922, it should be replaced with asterisks.

Image shows a chat UI running on iPhone simulator with a chat screen containing redacted messages

Next steps with Swift Lambda

Congratulations! You've just uploaded a real Swift backend to AWS. This moderation code is simple and made to demonstrate a usage of Swift Lambda to empower your chat apps. There are many ways to build even richer use cases with Swift Lambda. To find out more, check out the chatbot tutorial with Swift Lambda and Stream Chat's documentation.

Top comments (1)

Collapse
 
starpebble profile image
starpebble

Neat. I'd try this tutorial however I'm running on empty fumes. My feedback: I'm thinking about presets for these type of chat filters.

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Dive into an ocean of knowledge with this thought-provoking post, revered deeply within the supportive DEV Community. Developers of all levels are welcome to join and enhance our collective intelligence.

Saying a simple "thank you" can brighten someone's day. Share your gratitude in the comments below!

On DEV, sharing ideas eases our path and fortifies our community connections. Found this helpful? Sending a quick thanks to the author can be profoundly valued.

Okay