GIFs For All Your Chat Messages! Power-Up Your Appwrite Applications Using Python and NLP
Tags: python, nlp, appwrite, tutorial
Published: 2022-03-14 15:53:19

[Appwrite’s](https://appwrite.io/) 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](https://dev.to/appwrite/announcing-appwrite-013-with-major-upgrades-to-storage-and-functions-3hpf), 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](https://www.nltk.org/) and [VADER](https://github.com/cjhutto/vaderSentiment), 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](https://developers.giphy.com/docs/api/) using the most sentimental word.

```python
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))]
```

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:

```bash
npm install -g appwrite-cli
```

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

```bash
appwrite -v
```

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:

```bash
appwrite init project
```

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:

```json
{
    "projectId": "<project id>",
    "projectName": "<project name>"
}
```

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

```bash
appwrite init function
```

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

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

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.

```python
def main(req, res):
	...
	req.json({
    "areDevelopersAwesome": "true"
  })
```

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

```bash
appwrite deploy function
```

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

```json
appwrite functions createExecution --functionId <function id> \
	--async false
```

## 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:`

```bash
# requirements.txt
appwrite
nltk
```

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:

```python
# 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)
    })
```

## Executing the New Function

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

```bash
appwrite functions createExecution --functionId <function id> \
	--async false --data "Functions and Python make me excited"
```

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:

```json
$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
```

In my case, it returned this GIF of SpongeBob.

![SpongeBob Image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wmyboveku8xojlxo0bwn.gif)

## Integrating Functions With a Web App.

Appwrite supports SDKs for [Web](https://appwrite.io/docs/getting-started-for-web), [Android](https://appwrite.io/docs/getting-started-for-android), [Apple](https://appwrite.io/docs/getting-started-for-apple), and [Flutter](https://appwrite.io/docs/getting-started-for-flutter) on the client side, and a wide variety of [server side languages](https://appwrite.io/docs/sdks#server). Appwrite’s Functions service can be accessed using any of these SDKs.

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

```jsx
appwrite.functions.createExecution(functionId, data, async)
```

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

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

Which will output the following to the console:

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

## Final Remarks


![More Powerful Together](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1aqyukwyrapxsey32orw.gif)

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](https://github.com/appwrite/appwrite/blob/master/CHANGES.md). 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](https://appwrite.io/docs/getting-started-for-server)

🚀 [Appwrite GitHub](https://github.com/appwrite/appwrite)

📜 [Appwrite Docs](https://appwrite.io/docs)

💬 [Discord Community](https://discord.gg/appwrite)