DEV Community

Cover image for GIFs For All Your Chat Messages! Power-Up Your Appwrite Applications Using Python and NLP
Vincent Ge for Appwrite

Posted on

GIFs For All Your Chat Messages! Power-Up Your Appwrite Applications Using Python and NLP

Appwrite’s Functions service is a powerful tool that allows your applications to execute code written in a variety of languages in remote, secure, and isolated environments. These code snippets can be triggered by events, scheduled jobs, or through Appwrite’s API. This is great for handling niche tasks that are better handled outside of your frontend application. With the release of Appwrite 0.13, the Functions service received even faster response times, synchronous and asynchronous execution, and improved dependency management and developer experience.

To show off the new features found in Appwrite 0.13, we’ll add a function to an Appwrite application using Python and NLTK (Natural Language Toolkit) to perform some basic sentiment analysis to generate GIFs from chat messages. This is a perfect use case for Appwrite’s Functions service. The Python script is very simple so it doesn’t justify being developed into a stand alone service. We also can’t use the NLTK library and its NLP models natively in a JavaScript frontend. Appwrite Functions solves these challenges by providing a simple way to integrate code in a variety of language with your application.

The Python NLP Script

The Python script uses NLTK and VADER, which is a “lexicon and rule-based sentiment analysis tool that is specifically attuned to sentiments expressed in social media.” This script allows us to detect the strength of emotion displayed in a message, and identify which words carry the most “sentiment” or emotion. We can generate GIFs that represent a chat message’s emotion by querying GIPHY using the most sentimental word.

import nltk

# Download VADER
nltk.download('vader_lexicon')
from nltk.sentiment.vader import SentimentIntensityAnalyzer

# Initialize sentiment intensity analyzer
sid = SentimentIntensityAnalyzer()
def analyze_sentiment_by_word(sentence):
    """
    Returns sentiment intensity of each word in the sentence.
    :type sentence: str
    """
    words = sentence.split()
    polarity_scores = [sid.polarity_scores(word)["compound"] for word in words]
    return words, polarity_scores

def pick_max_sentiment_words(sentence):
    """
    Returns word with the strongest sentiment intensity.
    :type sentence: str
    """
    words, polarity_scores = analyze_sentiment_by_word(sentence)
    if max(polarity_scores) < 0.05:
        # If the max polarity is < 0.01, the chat message is neutral, we can just return the entire message
        return sentence
    return words[polarity_scores.index(max(polarity_scores, key=abs))]
Enter fullscreen mode Exit fullscreen mode

If there are any words carrying a notable emotion, pick_max_sentiment_words() will return that word, other wise the entire sentence is returned.

Installing the Appwrite CLI

The quickest way to create, manage, and deploy your functions is with the updated CLI released with Appwrite 0.13.

You can install the Appwrite CLI using the command below:

npm install -g appwrite-cli
Enter fullscreen mode Exit fullscreen mode

To verify the installation, open a new terminal and enter the following command:

appwrite -v
Enter fullscreen mode Exit fullscreen mode

You should see the version number of your CLI installation printed to the console. Then, set up your Appwrite CLI by specifying an Appwrite API endpoint with appwrite client --endpoint <your endpoint> then login with appwrite login.

The CLI is all setup, you’re ready to create your first Appwrite function!

Creating a Function

With Appwrite 0.13’s new CLI, we can manage functions among other services in a config file called appwrite.json. We can initialize our config file with the following CLI command:

appwrite init project
Enter fullscreen mode Exit fullscreen mode

You can either create a new Appwrite project or link to an existing project. After setting up your Appwrite project, you should see a file named appwrite.json generated by the CLI with something like this inside:

{
    "projectId": "<project id>",
    "projectName": "<project name>"
}
Enter fullscreen mode Exit fullscreen mode

Now you’re ready create a function with the following command:

appwrite init function
Enter fullscreen mode Exit fullscreen mode

Your appwrite.json should be updated to something like this:

{
        ...,
    "functions": [
        {
            "$id": "<function id>",
            "name": "chat-to-gif",
            "runtime": "python-3.10",
            "path": "chat-to-gif",
            "entrypoint": "src/index.py"
        }
    ]
}
Enter fullscreen mode Exit fullscreen mode

The CLI should have also generated a template function in <function-name>/src/index.py. This is a stub of a Python function. When the Python function is executed, the main function is used as the entry point. Data passed to the executed function can be accessed through req.payload and responses can be sent using res.json() for JSON response or res.send() for plain text response.

def main(req, res):
    ...
    req.json({
    "areDevelopersAwesome": "true"
  })
Enter fullscreen mode Exit fullscreen mode

To deploy and activate the function, run the following commands:

appwrite deploy function
Enter fullscreen mode Exit fullscreen mode

You can verify that the function is deployed and activated by executing the function:

appwrite functions createExecution --functionId <function id> \
    --async false
Enter fullscreen mode Exit fullscreen mode

Adding NLP to Our Function

Now we’ve verified out stub function can be executed, we can incorporate our NLP script into the generated function. First we need to make sure we specify the required pip packages.

To add NLTK as a dependency, add it to requirements.txt:

# requirements.txt
appwrite
nltk
Enter fullscreen mode Exit fullscreen mode

When we deploy our functions, the build worker in Appwrite will install the dependencies specified in requirements.txt.

Then we incorporate the NLP script to [index.py](http://index.py) and add a function fetch_gif() to get GIF links from GIPHY’s API:

# src/index.py
import json
from urllib import parse, request
import nltk

# Download the VADER lexicon 
nltk.download('vader_lexicon')
from nltk.sentiment.vader import SentimentIntensityAnalyzer

# Initialize sentiment intensity analyzer
sid = SentimentIntensityAnalyzer()

# Specify the GIPHY API url
url = "http://api.giphy.com/v1/gifs/search"

def fetch_gif(search_phrase):
    """
    Queries the Giphy search API and returns links to GIFs.
    :type search_phrase: str
    """
    params = parse.urlencode({
        "q": search_phrase,
        "api_key": "<GIPHY API KEY>",
        "limit": "1"
    })

    with request.urlopen("".join((url, "?", params))) as response:
        data = json.loads(response.read())

    return data["data"][0]["images"]["downsized"]["url"]

def analyze_sentiment_by_word(sentence):
    """
    Returns sentiment intensity of each word in the sentence.
    :type sentence: str
    """
    words = sentence.split()
    polarity_scores = [sid.polarity_scores(word)["compound"] for word in words]
    return words, polarity_scores

def pick_max_sentiment_words(sentence):
    """
    Returns word with the strongest sentiment intensity.
    :type sentence: str
    """
    words, polarity_scores = analyze_sentiment_by_word(sentence)
    if max(polarity_scores) < 0.05:
        # If the max polarity is < 0.01, the chat message is neutral, we can just return the entire message
        return sentence
    return words[polarity_scores.index(max(polarity_scores, key=abs))]

def main(req, res):

    """
    The limit for queries is 50 char. If it's a long message, and non of the words have sentimental value,
    the parsed message should be clipped.
    """
    parsed_message = pick_max_sentiment_words(req.payload)[:50]

    return res.json({
        "gif-generated": fetch_gif(parsed_message)
    })
Enter fullscreen mode Exit fullscreen mode

Executing the New Function

Deploy and activate the updated function like before, and execute it with the following command:

appwrite functions createExecution --functionId <function id> \
    --async false --data "Functions and Python make me excited"
Enter fullscreen mode Exit fullscreen mode

The Python function should identify that the word “excited” conveys the most emotion, and query GIPHY for an “excited” GIF. This should output something similar to:

$id : 621d70c183e001e62f1f
$read
[
  "user:<user id>"
]
functionId : <function id>
dateCreated : 1646096577
trigger : http
status : completed
statusCode : 200
stdout : {"gif-generated":"https://media4.giphy.com/media/oF5oUYTOhvFnO/giphy.gif?cid=8eda20a7b1ltjcv2u3nxxf8d5hpt6pr6dpqo26b6axldy2xe&rid=giphy.gif&ct=g"}

stderr : 
time : 0.015591144561767578
 Success
Enter fullscreen mode Exit fullscreen mode

In my case, it returned this GIF of SpongeBob.

SpongeBob Image

Integrating Functions With a Web App.

Appwrite supports SDKs for Web, Android, Apple, and Flutter on the client side, and a wide variety of server side languages. Appwrite’s Functions service can be accessed using any of these SDKs.

For example, the Web SDK provides the following function to invoke executions:

appwrite.functions.createExecution(functionId, data, async)
Enter fullscreen mode Exit fullscreen mode

We can synchronously execute our Python function to generate GIPHY urls by passing in a chat message in data and setting async to false:

const result = await appwrite.functions.createExecution('<function id>', 
        "Functions and Python make me excited", false);
console.log(JSON.parse(result["stdout"]))
Enter fullscreen mode Exit fullscreen mode

Which will output the following to the console:

{
    "gif-generated": "https://media0.giphy.com/media/oF5oUYTOhvFnO/giphy.gif?cid=8eda20a7oe3xgviextw3h70z6vtkxyeckhjaydxoo0wnd6hh&rid=giphy.gif&ct=g"
}
Enter fullscreen mode Exit fullscreen mode

Final Remarks

More Powerful Together

Appwrite’s Functions service is a simple way to power-up your frontend application with server-side logic written in a variety of languages. With the release of Appwrite 0.13, these functions are faster than ever, making them practical to invoke synchronously though the SDK. With support for an ever growing list of runtimes like Node, Python, Swift, Ruby and more, the capabilities of these different languages can be integrated seamlessly into your frontend application.

In release 0.13, Appwrite also gained many other upgrades like Storage Buckets, S3 compatible Storage Adapters, support for large files, and more. You can find more on our full 0.13 release notes. If this is your first time hearing about Appwrite, you can learn more from these resources:

The following resources can help you learn more about Appwrite:

🚀 Getting Started Tutorial

🚀 Appwrite GitHub

📜 Appwrite Docs

💬 Discord Community

Top comments (0)