Search code examples
erlangejabberd

erlang, ejabberd, how to deal with [{text, <<>>, <<"hello">>}] formats


So I got a hook in my code that says if an offline message is received, it will start up this function.

offline_msg_received(From, To, Packet) ->

The Packet has code looking like this:

{message,<<"purple2d957668">>,chat,<<>>,undefined,{jid,<<"admin">>,<<"localhost">>,<<"5280">>,<<"admin">>,<<"localhost">>,<<"5280">>},[],[{text,<<>>,<<"Hello">>}],undefined,[{xmlel,<<"active">>,[{<<"xmlns">>,<<"http://jabber.org/protocol/chatstates">>}],[]}],#{}}

What I wanted out of this is the part containing my message. Which is:

[{text,<<>>,<<"Hello">>}]

I could get this part out with a pattern matching against the whole package variable. However, when I tried a pattern matching with my message, it says bad argument. I know that if I specify a variable with just

{text,<<>>,<<"Hello">>}

then I can pattern match against that. From my understanding, seeing [ ] around something means it is a list, or a string.

I can show some of the code here.

{P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11} = Packet, %% P8 is the message I want.
?INFO_MSG("----------------------P8:  ~p~n", P8), %% This shows   {text,<<>>,<<"Hello">>}
{A, B, C} = P8,  %% This gives me a badmatch error.

Now, if I were to instead do this manually in the code shell:

{text,<<>>,<<"Hello">>} = P8,
{A, B, C} = P8,

Then the code works, and puts <<"Hello">> in the C variable.

My guess is that the [ ] around it in the "Package" variable has something to do with my badmatch error, but for some reason those symbols don't appear when I print out P8. Does anyone have a good suggestion why it doesn't work? I am guessing it has something to do with the hidden [ ], since otherwise it looks identical to my shell code test.

Thanks. This is my first time asking questions here, so I hope I didn't do anything wrong.


Solution

  • The reason you're not seeing the square brackets around the value in your INFO_MSG call is that INFO_MSG expects a list of values, not just a single value. In the code above it happens to work out, since the format string only contains a single directive, and the value is a list of one element, so that's why the brackets are silently stripped out.

    The INFO_MSG macro works the same as io:format, so you could try this out in the shell:

    %% two arguments
    io:format("~p ~p\n", [foo, bar]).
    %% one argument - we still need square brackets
    io:format("~p\n", [foo]).
    

    You're doing this the hard way :) message and text are records defined in xmpp_codec.hrl. If you don't have the record definitions loaded in the shell, records look like a tuple with lots of elements, but if you load the record definitions, your message looks like this instead:

    1> rr("include/xmpp_codec.hrl").
    [address,addresses,adhoc_actions,adhoc_command,adhoc_note,
     bind,block,block_list,bob_data,bookmark_conference,
     bookmark_storage,bookmark_url,bytestreams,caps,
     carbons_disable,carbons_enable,carbons_private,
     carbons_received,carbons_sent,chatstate,client_id,compress,
     compress_failure,compressed,compression,csi,db_feature,
     db_result,db_verify|...]
    2> {message,<<"purple2d957668">>,chat,<<>>,undefined,{jid,<<"admin">>,<<"localhost">>,<<"5280">>,<<"admin">>,<<"localhost">>,<<"5280">>},[],[{text,<<>>,<<"Hello">>}],undefined,[{xmlel,<<"active">>,[{<<"xmlns">>,<<"http://jabber.org/protocol/chatstates">>}],[]}],#{}}. 
    #message{id = <<"purple2d957668">>,type = chat,lang = <<>>,
             from = undefined,
             to = {jid,<<"admin">>,<<"localhost">>,<<"5280">>,
                       <<"admin">>,<<"localhost">>,<<"5280">>},
             subject = [],
             body = [#text{lang = <<>>,data = <<"Hello">>}],
             thread = undefined,
             sub_els = [{xmlel,<<"active">>,
                               [{<<"xmlns">>,
                                 <<"http://jabber.org/protocol/chatstates">>}],
                               []}],
             meta = #{}}
    

    The data you are looking for is in the body field.

    Let's assign this record to the variable M and extract the body field:

    4> Body = M#message.body.
    [#text{lang = <<>>,data = <<"Hello">>}]
    

    So that's a list containing one text record. Let's extract the data field from that:

    6> [#text{data = Data}] = Body.
    [#text{lang = <<>>,data = <<"Hello">>}]
    7> Data.
    <<"Hello">>
    

    That's how it works in the shell - load the record definitions with the rr function. When writing a module that uses record definitions, you'd need to include the hrl file:

    -include_lib("xmpp/include/xmpp_codec.hrl").
    

    The -include_lib directory will look for the xmpp application on the load path, and search for the file xmpp_codec.hrl inside its include directory. You may need to specify the code path to the compiler, something like erlc -pa path/to/xmpp/ebin my_module.erl - note that the ebin directory inside the application should be on the code path.