Search code examples
erlangmongoose-im

MongooseIM simple module event not getting handled


I am new in erlang and ejabbered/mongooseIM. I am trying to write a very simple mongoose module that will add an extra child element to the packets before sending. Below is my code:

-module(mod_test).

-behavior(gen_mod).


-export([start/2, stop/1]).
-export([add_child/1]).

-include("ejabberd.hrl").

start(Host, Opts) ->
    ejabberd_hooks:add(filter_packet, Host, ?MODULE, add_child, 0),
    ?DEBUG(" MOD_TEST Started",[]),
    ok.

stop(Host) ->
    ejabberd_hooks:delete(filter_packet, Host, ?MODULE, add_child, 0),
    ok.

add_child({From, To, XML} = Packet) ->
    Tag = {"a","b"},
    NewPacket = xml:append_subtags(Packet, [Tag]),
    ?INFO_MSG("  To party: ~p~n",[To]),
    NewPacket.

I can compile the code with few warnings

mod_test.erl:3: Warning: behaviour gen_mod undefined
mod_test.erl:11: Warning: variable 'Opts' is unused
mod_test.erl:20: Warning: variable 'From' is unused
mod_test.erl:20: Warning: variable 'XML' is unused

Then when I add the module and run it it gets started but it doesnt make any changes to the packets and doesnt generate any logs either.

Another issue is, if I add a log within my start function, it gets compiled but I see errors while starting the module

2015-03-03 16:36:34.772 [critical] <0.200.0>@gen_mod:start_module:94 Problem starting the module mod_test for host <<"localhost">>
 options: []
 error: undef
[{lager,info,["  mod_test starting ...",[[]]],[]},
 {mod_test,start,2,[{file,"mod_test.erl"},{line,13}]},
 {gen_mod,start_module,3,[{file,"src/gen_mod.erl"},{line,83}]},
 {lists,foreach,2,[{file,"lists.erl"},{line,1336}]},
 {ejabberd_app,start,2,[{file,"src/ejabberd_app.erl"},{line,69}]},
 {application_master,start_it_old,4,
                     [{file,"application_master.erl"},{line,272}]}]
2015-03-03 16:36:34.773 [critical] <0.200.0>@gen_mod:start_module:99 ejabberd initialization was aborted because a module start failed.
The trace is [{lager,info,["  mod_test starting ...",[[]]],[]},{mod_test,start,2,[{file,"mod_test.erl"},{line,13}]},{gen_mod,start_module,3,[{file,"src/gen_mod.erl"},{line,83}]},{lists,foreach,2,[{file,"lists.erl"},{line,1336}]},{ejabberd_app,start,2,[{file,"src/ejabberd_app.erl"},{line,69}]},{application_master,start_it_old,4,[{file,"application_master.erl"},{line,272}]}].

Crash dump was written to: erl_crash.dump
Problem starting the module mod_test for host <<"localhost">>
 options: []
 error: undef
[{lager,info,["  mod_xyz starting ...",[[]]],[]},
 {mod_test,start,2,[{file,"mod_timetagg

What wrong am I doing?


Solution

  • So your example is actually a bit tricky, because of how the filter_packet hook works. You picked the worst hook to register for on your first attempt ;)

    If you look into ejabberd_router:do_route, you'll see that filter_packet is run without a Host parameter -- it is a global hook, so when you register your add_child function for a particular Host, it will essentially be ignored.

    Try the following:

    -module(mod_test).
    -behavior(gen_mod).
    
    -export([start/2, stop/1]).
    -export([add_child/1]).
    
    -include_lib("ejabberd/include/ejabberd.hrl").
    -include_lib("exml/include/exml.hrl").
    
    start(Host, Opts) ->
        ejabberd_loglevel:set_custom(?MODULE, 5),
        ejabberd_hooks:add(filter_local_packet, Host, ?MODULE, add_child, 1),
        ?DEBUG(" MOD_TEST Started",[]),
        ok.
    
    stop(Host) ->
        ejabberd_hooks:delete(filter_local_packet, Host, ?MODULE, add_child, 1),
        ok.
    
    add_child({From, To, Element} = HookData) ->
        ?DEBUG("Filtering ~p~n", [HookData]),
        case Element#xmlel.name of
            <<"message">> ->
                Tag = #xmlel{name = <<"added-tag">>, attrs = [], children = []},
                NewElement = xml:append_subtags(Element, [Tag]),
                ?DEBUG("will return new el: ~p", [NewElement]),
                {From, To, NewElement};
            _ ->
                ?DEBUG("will pass old el: ~p", [Element]),
                HookData
        end.
    

    Registering for filter_local_packet on your given Host will now work, and all incoming stanzas will be passed to your function. It is important to remember that adding spurious tags to all stanzas may break things, so the above code will only add a <added-tag> element to <message> stanzas.

    Use the example above and work from there.

    Good luck!