Skip to content

Instantly share code, notes, and snippets.

@DanyF-github
Created March 24, 2022 10:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DanyF-github/020bc4b217cd6ee36ac565869e2141df to your computer and use it in GitHub Desktop.
Save DanyF-github/020bc4b217cd6ee36ac565869e2141df to your computer and use it in GitHub Desktop.

In this article, you will learn how to build the core feature of a social CRM using Django and Vonage Messages API. Our social CRM will help sales agents and the customer support team communicate with potential customers directly on Facebook in real-time. Let's call it Sales Fox.

Pre-requisites

  1. Create a messages application from your Vonage dashboard. Follow the steps outlined here.
  2. Authorise Vonage to access your Facebook business page and link your application to your Facebook page. Follow the steps outlined here.
  3. Install Redis - If you're using Linux or Mac, follow the instructions here. If you're using Windows, follow the instructions here.
  4. Install Ngrok. Go to Ngrok download page and follow the instructions to set Ngrok up on your computer.

Now that you have the pre-requisites completed. You need to set up your development environment for the tutorial.

Project Set-up

  1. Create and Activate Your Virtual Environment

    Create a directory for your project and change your working directory to the directory you just created. Then, run the following commands to create and activate a virtual environment for your project.

    python3 -m venv sales-env

    source sales-env/bin/activate

  2. Install Required Packages

    To install all required packages at once, create a requirements.txt file in the directory created in step 1. Copy and paste the code snippet below in your requirements.txt file.

    aioredis==1.3.1
    asgiref==3.3.4
    async-timeout==3.0.1
    attrs==21.2.0
    autobahn==21.3.1
    Automat==20.2.0
    certifi==2021.10.8
    cffi==1.14.6
    channels==2.4.0
    channels-redis==2.4.2
    charset-normalizer==2.0.7
    constantly==15.1.0
    cryptography==3.4.7
    daphne==2.5.0
    Django==3.2.2
    djangorestframework==3.12.4
    hiredis==2.0.0
    hyperlink==21.0.0
    idna==3.2
    incremental==21.3.0
    msgpack==0.6.2
    Pillow==8.2.0
    pyasn1==0.4.8
    pyasn1-modules==0.2.8
    pycparser==2.20
    pyOpenSSL==20.0.1
    python-dotenv==0.19.2
    pytz==2021.1
    requests==2.26.0
    service-identity==21.1.0
    six==1.16.0
    sqlparse==0.4.1
    Twisted==21.7.0
    txaio==21.2.1
    typing-extensions==3.10.0.0
    urllib3==1.26.7
    zope.interface==5.4.0
    

    Now, install all the packages in requirements.txt by running the command below in your terminal.

    pip install -r requirements.txt

  3. Create your Django project

  • Run django-admin startproject sales_fox the following to create the Django project named "sales_fox".

  • We will create two apps in sales_fox: The lead_manager app to manage leads and the conversation app for sales agents to communicate with potential customers (known as leads). Now, let's create our two apps by running these commands.

    python manage.py startapp lead_manager
    python manage.py startapp conversation
    

Take note that in this tutorial,

  • I'll be using the words - "leads" and "customers" interchangeably. Leads are potential customers, so it won't hurt to regard them as customers where convenient.
  • I will use the term Project Directory to refer to the directory where you have settings.py. This directory was created when you ran django-admin startproject sales_fox.
  • I will use the term Overall Directory to refer to the directory you created at the beginning of the tutorial. It contains your virtual environment folder, the app directories, and your project directory
  1. Let's get SalesFox ready to use Vonage.

  • Create a .env file in your overall directory. Define FACEBOOK_ID, VONAGE_API_KEY, and VONAGE_API_SECRET. Your .env file should look like this:

    FACEBOOK_ID=YOUR-LINKED-FACEBOOK-ID
    VONAGE_API_KEY=YOUR-VONAGE-API-KEY
    VONAGE_API_SECRET=YOUR-VONAGE-API-SECRET
    

    You can find your Vonage API key and API secret in your Vonage settings page. And your Facebook ID can be found in the Link social channels tab on your application page.

    In your project directory, Go to settings.py, load the variables in your .env file using python-dotenv installed from requirements.txt. Add the following snippet in settings.py to load the .env file:

    from  dotenv  import  load_dotenv	
    import  os
    load_dotenv()
    

load_dotenv loads all variables in our .env file as environment variable.Now, define FACEBOOK_ID, VONAGE_API_KEY, VONAGE_API_SECRET, VONAGE_MESSAGES_ENDPOINT in your settings.py file. Simply copy and paste the snippet below.

https://gist.github.com/8cfbde008f13e4f71c40ff8999bd4388

  1. Setup Static Files

In settings.py, find STATIC_URL variable and add the STATICFILES_DIRS and STATIC_FILES beneath STATIC_URL, You should have something like:

https://gist.github.com/5e1229272a96b43500d3ae7e2c1a48ea

Go to your overall directory and create a folder named static. This is where you will keep all your static files. Note that you should only do this for a development environment. In a production environment, you should set up an external store like an AWS S3 bucket to serve your static files.

  1. Update Installed Apps and define channel layer

We need to add channels and the apps we created (lead_manager and conversation) to INSTALLED_APPS in the project's settings.py. Your INSTALLED_APPS in settings.py file should look like this:

https://gist.github.com/5bce895c4cedcc1d42001690dea2cb70

Django channels help us include WebSocket support to Sales Fox. A channel layer introduces the use of channels and groups in SalesFox. It helps us build distributed features into our application. You can read more about channel layers here. For this project, I will be using Redis as our channel layer. We have installed channels-redis from requirements.txt. Now, let's add CHANNEL_LAYER to settings.py. Copy and paste the code snippet below:

https://gist.github.com/0362be69df2014111b9af01e51c84f2e

The Brass Tacks

Now, let's get to the real deal.

Create models for the lead_manager app. Here, we will add models for Lead and Agent. The model Lead will represent customers and prospective customers. The model Agent will represent SalesFox salespersons who will be in touch with the customers. Copy and paste the following code snippet into lead_manager/models.py:

https://gist.github.com/d0a6f8354591adb05700a19035600b9b

We created a User model to represent every user in SalesFox - This could be community managers, region representatives, customer support, etc. However, to keep SalesFox as lean as possible, the only kind of users we have are the agents.

The Lead model represents potential customers reaching out from their Facebook account. The facebook_id field represents the ID of a customer's Facebook account. It is the field we need for agents to send a direct message to customers on Facebook. The Lead model also has a preferred_medium field. It holds the customer's preferred means of communication. We will only focus on communicating via Facebook.

https://gist.github.com/45a1076c0b4279e63239404215ff314d

Now, let's create a Message model in the conversation app. The Message model represents a single message sent from/to SalesFox. Copy and Paste the following code snippet into models.py of the conversation app.

https://gist.github.com/cd76f02fa5537f044b109cd25c7acf6d

In our Message model, we have two generic relations to identify the sender and recipient of the message. The sender and receiver can either be a lead or an agent. It means only Agents or Leads can send or receive messages. Visit here to learn more about generic relations in Django.

Create a property method messages for the Lead model in lead_manager/models.py. This method returns all the incoming and outgoing messages of a lead.

In lead_manager/models.py, paste the following import statements.

Under the Lead model, create the property method - "messages" as in the snippet below:

https://gist.github.com/92ac1931b2ea3f8411139ddb37bb06f3

Let's get started with the views.

In lead_manager, we will create views to perform CRUD operations on the Lead model. Go to the lead_manager app folder, then copy and paste the following code in views.py to create the views:

https://gist.github.com/80659679fd4e6bab847636ef820f8fc8

In the views above, we override the dispatch method to handle permissions for each view.

Create forms.py inside the lead_manager app directory. In forms.py, define LeadForm:

https://gist.github.com/01d973db198f773c12b3ffb564af07f7

Create views.py file inside a sub-folder in lead_manager named agent. And define your AgentLoginView and AgentDashboardView views.

https://gist.github.com/7fe44bc2631182cb3b3e60b964b5ca84

Let us create lead_manager/urls.py and lead_manager/agent/urls.py.

Go to the lead_manager directory and create a urls.py file. Now, define URL patterns for lead_manager views.

https://gist.github.com/6a3bb2cf1cfd716a0c9e8c026685223a

In your lead_manager directory, go to agent folder and create a file named urls.py. Define URL patterns for agent views as in the snippet below:

https://gist.github.com/733f328a954e70996d58702c89622a0e

From the two urls.py in the lead_manager app, you can confirm that all the views we created in the lead_manager app have corresponding URL configurations.

Now, let's inform Django of the login URL, login redirect URL, and logout redirect URL. Add the following to settings.py

https://gist.github.com/80f3f5ce2796d5bfc92ec1b085764403

Now, let's move to the conversation app.

Besides views and URL configuration, you will also set up a web-socket consumer in the conversation app. It will enable communication between SalesFox agents and leads in real-time.

Let's create the lead_conversation_room view for the conversation room. Go to views.py in the conversation folder and paste the code snippet below

https://gist.github.com/e75356971f62598cff46cd911586b579

The lead_conversation_room view handles requests made by agents to open a conversation room with a customer.

Now, create send_outbound function. send_outbound function is responsible for sending messages from SalesFox to customers on Facebook Messenger. It takes the message to be sent and the lead facebook ID as arguments.

https://gist.github.com/2a854089c5105d41466cdc82dcc43a40

Because we want real-time communication between leads and agents in the conversation room, we need to create a WebSocket on the client-side and set up a WebSocket consumer on the backend.

Right in the conversation app folder, create a consumers.py folder. In the consumers.py, create a WebSocket consumer class - ConversationConsumer.

https://gist.github.com/e7c080e78700859ca4a438e4d9e51e65

To explain the methods - For every agent that opens the conversation page, there is a call to ConversationConsumer. It results in a new channel for the agent.

  • connect(): is called when a WebSocket connection is received. Here, we add the agent's channel to a conversation and then accept the connection.
  • disconnect(): Here, we remove the agent's channel from the conversation.
  • receive(): Here, we receive a new message from the client. After which we call save_message which saves the message to our database. We then send the message as a Facebook direct message to the lead by calling send_outbound. The message is then sent back to the conversation room. At the end of the receive method, the message will be sent to every agent in the conversation room.
  • save_message(): We save the agent's message to the database here. This is called in receive
  • send_to_conversation(): We use this to broadcast the agent's message to the conversation room so that every agent in the room can see the message.

Now, let's set up routing for our ConversationConsumer.

Create routing.py in the conversation app directory and paste the following:

https://gist.github.com/f85b668758ecc4ad62cb5e30d60a9b02

Create a routing.py file in your project directory. This file holds the global routing configuration for the project.

https://gist.github.com/baf19b59a6dad33198ce476ca5dadcc0

Now, reference application in settings.py as ASGI application to be executed when Sales-Fox is served through asynchronous server gateway interface:

https://gist.github.com/673771fadfa094ad01d15602fc52cf28

Let's create an inbound view. The inbound view receives a customer's message from Vonage, saves the message, and sends it to agents in the conversation room.

https://gist.github.com/091591b2f4e7afe0ff733f888cdbd151

Vonage sends message status updates via the status endpoint.

Since we will not be using the status information in this tutorial, let's create a simple status view to write the request body in a status.txt file.

In the views.py file on the conversation app, copy the following to create the status view.

https://gist.github.com/ff63349263efaf8b058edad14eb764f4

Let's create URL configurations for the conversation app. Go to the conversation app directory and create urls.py file. Then copy and paste the code snippet below:

https://gist.github.com/2b1fac57a8ea53ae9bc225c4852648e0

Go to the project directory and find the urls.py file. This file is in the same directory as settings.py. Now, copy and paste the following code:

https://gist.github.com/51a420c47210633b5a4dbae2243d3e5a

Now that we're through with the backend of our project. Let's create the frontend files.

Go to your static folder in the overall directory and create a folder named css. In css folder, create two files style.css and chat.css.

In styles.css, copy and paste the following styles

https://gist.github.com/2505e56b2fc20567ad021f92f2903f83

In chat.css, copy and paste the following styles:

https://gist.github.com/76e36b11c9f451d6cc5e116e43b324ab

We will use the chat.css file for conversation room.html while we use styles.css for other pages. Now, in your overall directory, create a folder named templates and create two HTML files - base.html and index.html. You will extend base.html in every other HTML file except in conversation room.html.

In base.html, copy and paste the following

https://gist.github.com/53c5d9d84309fa116bae803388c55e58

In index.html (Home page), copy and paste the following

https://gist.github.com/bf31b1b49fb01bf3488c2a0f3f945a1e

Now, go to the lead_manager application directory. Create a folder templates and in templates, create another folder lead_manager. In lead_manager/templates/lead_manager, create five html files - lead_list.html, lead_create.html, lead_update.html, agent_login.html, agent_dashboard.html.

lead_list.html,

https://gist.github.com/72eddb0d7c94b67810144903ccae8a80

lead_create.html,

https://gist.github.com/c22f012bab14186668b6977e597c9e4c

lead_update.html,

https://gist.github.com/e268676d5cc223fc2d2139c6158b850d

agent_login.html

https://gist.github.com/41c7ce0b9ea704c9ab75cfb11d224d98

agent_dashboard.html

https://gist.github.com/c1ddc427b26896ea3274c7370926a735

In the conversation app directory, create a folder templates and in templates folder create a sub-folder conversation.

Inside "conversation/templates/conversation" folder, create a room.html file. Copy and paste the following:

https://gist.github.com/0cc247b2dc973b59970d482368393607

Before the closing tag for the body element in room.html, we have a script that handles the WebSocket operation and message rendering in the conversation room.

Get SalesFox Running

We have now completed the development of SalesFox.

Follow these steps to get SalesFox running locally.

  1. Run redis-server to start Redis. You can safely stop the Redis server by running redis-cli shutdown

  2. Create an HTTP tunnel with Ngrok that forwards request to the port from which you're running SalesFox. This provides you with a public available URL for your SalesFox localhost:port. Learn more about this here.

  3. Go to the .env file in your overall directory. Define a new env variable called HOST set to your Ngrok tunnel URL.

    HOST=4339-197-210-53-35.ngrok.io
    
  4. Add HOST from .env file to ALLOWED_HOST in settings.py. ALLOWED_HOST definition in settings.py should look like this:

    ALLOWED_HOSTS = [os.getenv('HOST'), "localhost", "127.0.0.1"]
    
  5. Recall that we filled in dummy URLs as inbound and status URLs in our Vonage application page. Now, we will replace these URLs with the correct values. Because my tunnel host is http://4339-197-210-53-35.ngrok.io, my inbound URL will be http://4339-197-210-53-35.ngrok.io/conversation/inbound and my status URL will be http://4339-197-210-53-35.ngrok.io/conversation/status.

    Go to your Vonage application page and update the Inbound URL and Status URL fields.

  6. Now, go to your terminal (ensure that you're in the overall directory). Then, run python manage.py runserver to serve SalesFox on port 8000.

    python manage.py runserver 9000.

Conclusion

If you got here, Thank you for building this project with me. In the course of building SalesFox, we have stuck to the minimum possible features and design. However, You can do so much more by creating more features upon SalesFox.

You can add more preferred_medium options for leads. Vonage provides varieties of communication APIs, some of which you can develop SalesFox to support. It would be worth checking them out here.

Cheers!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment