< Back To Home

Build a Chatbot with Phoenix and Api.ai - Part 2

Published at December 27, 2019

Part 2

The goal of this section is to load and send messages. I think we can do it. Let’s get started. Setting up the Channel

We can see that our endpoint is already set up for us in lib/goldfish_web/endpoint.ex:

defmodule GoldfishWeb.Endpoint do
  use Phoenix.Endpoint, otp_app: :goldfish
  socket "/socket", GoldfishWeb.UserSocket
  # ...
end

We need to also go into our user socket and uncomment the “room:*”. This will allow us to connect to room channels.

defmodule HelloWeb.UserSocket do
  use Phoenix.Socket
  ## Channels
  channel "room:*", HelloWeb.RoomChannel
  ...

Joining the RoomChannel

defmodule GoldfishWeb.RoomChannel do
  use GoldfishWeb, :channel
  def join("room:" <> session_id, payload, socket) do
    {:ok, socket}
  end
end

With that code for our backend, it is now time to get to work connecting to our channel using javascript. Open up assets/js/socket.js:

// assets/js/socket.js
...
socket.connect()
// Now that you are connected, you can join channels with a topic:
let channel = socket.channel("room:lobby", {})
channel.join()
  .receive("ok", resp => { console.log("Joined successfully", resp) })
  .receive("error", resp => { console.log("Unable to join", resp) })
export default socket;

We also need to make sure that we uncommented the line in assets/js/app.js to import our socket code.

import socket from “./socket”;

Creating a message

We need to make a few changes to make this happen. Let’s first open pages/index.html.eex and add:

<div id="messages"></div>
<input id="chat-input" type="text"></input>

The messages div will be a container for messages we send in this session. The chat-input will be our input field for typing out these messages. Alright, now let’s get to a bit of javascript. Open up assets/js/socket.js and add:

...
let channel           = socket.channel(`room:${sessionId}`, {});
let chatInput         = document.querySelector("#chat-input");
let messagesContainer = document.querySelector("#messages");

chatInput.addEventListener("keypress", event => {
  if (event.keyCode === 13){
    channel.push("new_msg", {body: chatInput.value});
    chatInput.value = "";
  }
})

channel.join()
  .receive("ok", resp => { console.log("Joined successfully", resp) })
  .receive("error", resp => { console.log("Unable to join", resp) })

export default socket

We also need a way to get the session_id to the front end. Right now it is stored as a cookie that our javascript does not have access to. An easy way to get this cookie is to set it as a global javascript variable. Open app.html.eex and add in the :

<head>
...
  <script>window.sessionId = "<%= assigns[:session_id] %>";</script>
</head>

Handling incoming events

defmodule GoldfishWeb.RoomChannel do
  use GoldfishWeb, :channel

  def join("room:" <> room_id, payload, socket) do
    {:ok, socket}
  end

  def handle_in("new_msg", %{"body" => body}, socket) do
    broadcast!(socket, "new_msg", %{body: body})
    {:noreply, socket}
  end
end

Loading messages on page index

We are going to be loading messages by the session_id which we set in the last part.

defmodule Goldfish.Chat do
  ...

  @doc """
  Returns the list of messages.

  ## Examples

      iex> list_messages(session_id)
      [%Message{}, ...]

  """
  def list_messages(session_id) do
    query = from m in Message,
      where: m.room_id == ^room_id,
      order_by: [desc: m.inserted_at]
    Repo.all(query)
  end
end
Avatar of Author

Evan Dancer

Engineering guy who wrote this.


Evan Dancer

Evan Dancer

Founder of Hailey.
Previously, at Apartment List.
Before, I started Cribspot (YC W15).
I've been running a lot recently.