Skip to content

Instantly share code, notes, and snippets.

@stevenlivz
Created March 31, 2016 16:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save stevenlivz/d666200595a23c42b2a662fef87bf281 to your computer and use it in GitHub Desktop.
Save stevenlivz/d666200595a23c42b2a662fef87bf281 to your computer and use it in GitHub Desktop.
-module(mod_echobot).
-author('Steven Livingstone-Perez').
-behavior(gen_server).
-behavior(gen_mod).
%% Interface
-export([start_link/2]).
-export([start/2,
stop/1,
init/1,
handle_call/3,
handle_cast/2,
handle_info/2,
terminate/2,
code_change/3]).
-export([route/3]).
-include("ejabberd.hrl").
-include("logger.hrl").
-define(PROCNAME, ejabberd_mod_bot).
-define(BOTNAME, echo_bot).
%% Implementation
start_link(Host, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []).
start(Host, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
ChildSpec = {Proc,
{?MODULE, start_link, [Host, Opts]},
temporary,
1000,
worker,
[?MODULE]},
supervisor:start_child(ejabberd_sup, ChildSpec).
stop(Host) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
gen_server:call(Proc, stop),
supervisor:terminate_child(ejabberd_sup, Proc),
supervisor:delete_child(ejabberd_sup, Proc).
init([Host, Opts]) ->
error_logger:info_msg("ECHO_BOT: Starting echo_bot", []),
%?DEBUG("ECHO_BOT: Starting echo_bot", []),
% add a new virtual host / subdomain "echo".example.com
MyHost = gen_mod:get_opt_host(Host, Opts, <<"echo.@HOST@">>),
ejabberd_router:register_route(MyHost, {apply, ?MODULE, route}),
{ok, Host}.
handle_call(stop, _From, Host) ->
{stop, normal, ok, Host}.
handle_cast(_Msg, Host) ->
{noreply, Host}.
handle_info(_Msg, Host) ->
{noreply, Host}.
terminate(_Reason, Host) ->
ejabberd_router:unregister_route(Host),
ok.
code_change(_OldVsn, Host, _Extra) ->
{ok, Host}.
% Checks a presence /subscription/ is a part of this.
% we may want to impliment blacklisting / some kind of
% protection here to prevent malicious users
%route(From, #jid{luser = ?BOTNAME} = To, {xmlelement, "presence", _, _} = Packet) ->
route(From, To, {xmlelement, "presence", _, _} = Packet) ->
case xml:get_tag_attr_s("type", Packet) of
"subscribe" ->
send_presence(To, From, "subscribe");
"subscribed" ->
send_presence(To, From, "subscribed"),
send_presence(To, From, "");
"unsubscribe" ->
send_presence(To, From, "unsubscribed"),
send_presence(To, From, "unsubscribe");
"unsubscribed" ->
send_presence(To, From, "unsubscribed");
"" ->
send_presence(To, From, "");
"unavailable" ->
ok;
"probe" ->
send_presence(To, From, "");
_Other ->
error_logger:info_msg("Other kind of presence~n~p", [Packet])
%?INFO_MSG("Other kind of presence~n~p", [Packet])
end,
ok;
%route(From, #jid{luser = ?BOTNAME} = To, {xmlelement, "message", _, _} = Packet) ->
route(From, To, {xmlelement, "message", _, _} = Packet) ->
case xml:get_subtag_cdata(Packet, "body") of
"" ->
ok;
Body ->
case xml:get_tag_attr_s("type", Packet) of
"error" ->
error_logger:info_msg("Received error message~n~p -> ~p~n~p", [From, To, Packet]);
%?ERROR_MSG("Received error message~n~p -> ~p~n~p", [From, To, Packet]);
_ ->
echo(To, From, strip_bom(Body))
end
end,
ok.
%% HELPER FUNCTIONS
strip_bom([239,187,191|C]) -> C;
strip_bom(C) -> C.
send_presence(From, To, "") ->
ejabberd_router:route(From, To, {xmlelement, "presence", [], []});
send_presence(From, To, TypeStr) ->
ejabberd_router:route(From, To, {xmlelement, "presence", [{"type", TypeStr}], []}).
echo(From, To, Body) ->
send_message(From, To, "chat", Body).
send_message(From, To, TypeStr, BodyStr) ->
XmlBody = {xmlelement, "message",
[{"type", TypeStr},
{"from", jlib:jid_to_string(From)},
{"to", jlib:jid_to_string(To)}],
[{xmlelement, "body", [],
[{xmlcdata, BodyStr}]}]},
ejabberd_router:route(From, To, XmlBody).
@andreabenini
Copy link

Hi Steven
I'm stumbling upon the same problem with echo bots like you. I've tried to debug and get a working version on internet and I have even found your question on stackoverflow. Now with your code (the same above) I have a bot but I cannot send anything to it.
I have the ejabberd domain/host: chat.domain.local, I'm using a common xmpp client (psi) and after a service discovery I have added "echo.chat.domain.local" to my service Agent/Transport list in the xmpp client. I can send messages but I cannot see anything back.
I'm using ejabberd v16.02 (x64) on a CentOS 6.7 machine.
Here're the error messages I can see in my ejabberd.log

2016-04-21 17:53:02.327 [error] <0.6289.0>@ejabberd_router:route:77 {function_clause,[{mod_echobot,route,[{jid,<<"ben">>,<<"chat.domain.local">>,<<"MyCustomResourceID">>,<<"ben">>,<<"chat.domain.local">>,<<"MyCustomResourceID">>},{jid,<<>>,<<"echo.chat.domain.local">>,<<>>,<<>>,<<"echo.chat.domain.local">>,<<>>},{xmlel,<<"message">>,[{<<"xml:lang">>,<<"en">>},{<<"type">>,<<"chat">>},{<<"to">>,<<"echo.chat.domain.local">>},{<<"id">>,<<"aae0a">>}],[{xmlcdata,<<"\n">>},{xmlel,<<"body">>,[],[{xmlcdata,<<"test 123">>}]},{xmlcdata,<<"\n">>},{xmlel,<<"active">>,[{<<"xmlns">>,<<"http://jabber.org/protocol/chatstates">>}],[]},{xmlcdata,<<"\n">>},{xmlel,<<"nick">>,[{<<"xmlns">>,<<"http://jabber.org/protocol/nick">>}],[{xmlcdata,<<"Ben">>}]},{xmlcdata,<<"\n">>}]}],[{file,"src/mod_echobot.erl"},{line,105}]},{ejabberd_router,route,3,[{file,"src/ejabberd_router.erl"},{line,75}]},{ejabberd_c2s,check_privacy_route,5,[{file,"src/ejabberd_c2s.erl"},{line,2113}]},{ejabberd_c2s,session_established2,2,[{file,"src/ejabberd_c2s.erl"},{line,1278}]},{p1_fsm,handle_msg,10,[{file,"src/p1_fsm.erl"},{line,582}]},{proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,237}]}]}
when processing: {{jid,<<"ben">>,<<"chat.domain.local">>,<<"MyCustomResourceID">>,<<"ben">>,<<"chat.domain.local">>,<<"MyCustomResourceID">>},{jid,<<>>,<<"echo.chat.domain.local">>,<<>>,<<>>,<<"echo.chat.domain.local">>,<<>>},{xmlel,<<"message">>,[{<<"xml:lang">>,<<"en">>},{<<"type">>,<<"chat">>},{<<"to">>,<<"echo.chat.domain.local">>},{<<"id">>,<<"aae0a">>}],[{xmlcdata,<<"\n">>},{xmlel,<<"body">>,[],[{xmlcdata,<<"test 123">>}]},{xmlcdata,<<"\n">>},{xmlel,<<"active">>,[{<<"xmlns">>,<<"http://jabber.org/protocol/chatstates">>}],[]},{xmlcdata,<<"\n">>},{xmlel,<<"nick">>,[{<<"xmlns">>,<<"http://jabber.org/protocol/nick">>}],[{xmlcdata,<<"Ben">>}]},{xmlcdata,<<"\n">>}]}}

It seems there's a problem with the route() function but I cannot understand why, are you able to test it or can you tell me something more about it ? It's running with your own code (downloaded on 2016/04/21)

Cheers
Ben

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