Elixir Sips: ejabberd with Elixir – Part 1

Mickaël Rémond
· 3 min read
Send by email

Elixir Sips is an Elixir screencast website providing great tutorials to learn Elixir but also to help you build extraordinary pieces of code quickly with Elixir.

They produced a great series of videos on programming ejabberd with Elixir. Here is the material for the first part. We will publish more material on ProcessOne blog soon. Stay tuned !

Most of all, please, give us feedback in the comments on what you would like to see covered regarding ejabberd and Elixir… Or even better, come to see us live at upcoming ejabberd San Francisco Bay Area Meetup !

Here is the first ejabberd / Elixir tutorial (Part 1) by Josh Adams.

Please note that part 2 of this tutorial is also available: Extending ejabberd with Elixir – Part 2.

Introduction

ejabberd is an XMPP server that is widely used to power either vanilla XMPP installs or XMPP-backed applications. It’s one of Erlang’s success stories, and it recently added Elixir support. Here is the blog post announcing Elixir support: ejabberd joins the Elixir revolution.

Setup

I followed the blog post regarding compiling ejabberd with Elixir support.
Let’s see the support in action:

cd ~/my-ejabberd
./sbin/ejabberdctl iexlive
iex(ejabberd@localhost)1> IO.puts "ejabberdSips!"
ejabberdSips!
:ok
iex(ejabberd@localhost)2> :ejabberd_auth.try_register("elixirsips", "localhost", "mypass")
{:atomic, :ok}

Alright, so we’ve registered a elixirsips user locally. Let’s see if our XMPP client can connect:

((( open up gajim, add account )))

So there, we’ve connected to the account with the user we added from Elixir.
Now the blog post says for us to build the smallest possible module, but they have already provided it for us.

cd ~/erlang/ejabberd
vim lib/mod_presence_demo.ex
defmodule ModPresenceDemo do
  import Ejabberd.Logger # this allow using info, error, etc for logging
  @behaviour :gen_mod

  def start(host, _opts) do
    info('Starting ejabberd module Presence Demo')
    Ejabberd.Hooks.add(:set_presence_hook, host, __ENV__.module, :on_presence, 50)
    :ok
  end

  def stop(host) do
    info('Stopping ejabberd module Presence Demo')
    Ejabberd.Hooks.delete(:set_presence_hook, host, __ENV__.module, :on_presence, 50)
    :ok
  end

  def on_presence(user, _server, _resource, _packet) do
    info('Receive presence for #{user}')
    :none
  end
end

Here you can see that, when enabled, this will announce presence for every user when they change presence. We didn’t see that happen when I connected, so it must not be enabled. Let’s open up the yaml configuration file:

cd ~/my-ejabberd
vim etc/ejabberd/ejabberd.yml

In the modules section, add:

ModPresenceDemo: {}

Now we’ll restart and connect again. Now every time we change presence, an update gets logged.

This is the end of the introductory blog post. Let’s see if we can take it a little bit further. Open up the module again:

  def on_presence(user, _server, _resource, packet) do
    info('Receive presence for #{user}')
    # We'll inspect the packet that we're sent in this event
    info(inspect packet)
    :none
  end

Now recompile everything and reinstall, and start it back up. Now I’ll connect again with my client and we’ll see what a presence packet looks like.

((( connect, mark yourself away with a given status )))

19:54:04.160 [info] Receive presence for elixirsips
19:54:04.161 [info] {:xmlel, "presence", [{"xml:lang", "en"}, {"id", "56"}], [{:xmlel, "priority", [], [xmlcdata: "40"]}, {:xmlel, "show", [], [xmlcdata: "away"]}, {:xmlel, "x", [{"xmlns", "vcard-temp:x:update"}], [{:xmlel, "photo", [], []}]}, {:xmlel, "c", [{"xmlns", "https://jabber.org/protocol/caps"}, {"node", "https://gajim.org"}, {"ver", "47EPEmSc9oqPGwcrbNtpKcYyJcE="}, {"hash", "sha-1"}], []}, {:xmlel, "status", [], [xmlcdata: "asdf"]}]}

So here we can see what an away presence message with a status looks like.
Let’s log it a bit nicer:

  def on_presence(user, _server, _resource, packet) do
    info('Receive presence for #{user}')
    info(inspect(:xml.get_subtag(packet, "show")))
    :none
  end

Now I’ll do my common happy-path “,t” thing and map that key combination to build and install the new plugin, so we can easily do that part, then restart the server every time we want to see a change:

:map ,t :!make && make install<cr>

Let’s use it, and restart the server. Now change our status a few times.

We can see that there’s no “show” subtag when we’re just available, but otherwise it maps to various defined states. Let’s also log the status if it exists:

  def on_presence(user, _server, _resource, packet) do
    info('Receive presence for #{user}')
    info(inspect(:xml.get_subtag(packet, "show")))
    info(inspect(:xml.get_subtag(packet, "status")))
    :none
  end

We could do a bit more, but this shows a decent start. In the next episode, we’ll look at how you can filter packets as they pass through the system. See you soon!

Resources

Learn more about ejabberd development at upcoming ejabberd San Francisco Bay Area Meetup !