Ejabberd:处理脱机消息的简单模块出错

时间:2017-01-25 12:53:37

标签: push-notification erlang xmpp ejabberd

我有一个Ejabberd 17.01安装,我需要在收件人离线时推送通知。这似乎是一项常见的任务,使用定制的Ejabberd模块的解决方案随处可见。但是,我只是不让它运行。首先,这是我的脚本:

    -module(mod_offline_push).
    -behaviour(gen_mod).

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

    -include("ejabberd.hrl").
    -include("logger.hrl").
    -include("jlib.hrl").

    start(Host, _Opts) ->
            ?INFO_MSG("mod_offline_push loading", []),
            ejabberd_hooks:add(offline_message_hook, Host, ?MODULE, push_message, 10),
            ok.

    stop(Host) ->
            ?INFO_MSG("mod_offline_push stopping", []),
            ejabberd_hooks:add(offline_message_hook, Host, ?MODULE, push_message, 10),
            ok.

    push_message(From, To, Packet) ->
            ?INFO_MSG("mod_offline_push -> push_message", [To]),
            Type = fxml:get_tag_attr_s(<<"type">>, Packet), % Supposedly since 16.04
            %Type = xml:get_tag_attr_s(<<"type">>, Packet), % Supposedly since 13.XX
            %Type = xml:get_tag_attr_s("type", Packet),
            %Type = xml:get_tag_attr_s(list_to_binary("type"), Packet),
            ?INFO_MSG("mod_offline_push -> push_message", []),
            ok.

问题是方法Type = ...中的行push_message行;如果没有该行,则记录最后的信息消息(因此钩子肯定有效)。在线浏览时,我可以找到各种函数调用来从Packet中提取元素。据我所知,随着时间的推移,新版本发生了变化。但它并不好,所有变种都会导致某种错误。目前的方式返回:

2017-01-25 20:38:08.701 [error] <0.21678.0>@ejabberd_hooks:run1:332 {function_clause,[{fxml,get_tag_attr_s,[<<"type">>,{message,<<>>,normal,<<>>,{jid,<<"homer">>,<<"xxx.xxx.xxx.xxx">>,<<"conference">>,<<"homer">>,<<"xxx.xxx.xxx.xxx">>,<<"conference">>},{jid,<<"carl">>,<<"xxx.xxx.xxx.xxx">>,<<>>,<<"carl">>,<<"xxx.xxx.xxx.xxx">>,<<>>},[],[{text,<<>>,<<"sfsdfsdf">>}],undefined,[],#{}}],[{file,"src/fxml.erl"},{line,169}]},{mod_offline_push,push_message,3,[{file,"mod_offline_push.erl"},{line,33}]},{ejabberd_hooks,safe_apply,3,[{file,"src/ejabberd_hooks.erl"},{line,382}]},{ejabberd_hooks,run1,3,[{file,"src/ejabberd_hooks.erl"},{line,329}]},{ejabberd_sm,route,3,[{file,"src/ejabberd_sm.erl"},{line,126}]},{ejabberd_local,route,3,[{file,"src/ejabberd_local.erl"},{line,110}]},{ejabberd_router,route,3,[{file,"src/ejabberd_router.erl"},{line,87}]},{ejabberd_c2s,check_privacy_route,5,[{file,"src/ejabberd_c2s.erl"},{line,1886}]}]}

running hook: {offline_message_hook,[{jid,<<"homer">>,<<"xxx.xxx.xxx.xxx">>,<<"conference">>,<<"homer">>,<<"xxx.xxx.xxx.xxx">>,<<"conference">>},{jid,<<"carl">>,<<"xxx.xxx.xxx.xxx">>,<<>>,<<"carl">>,<<"xxx.xxx.xxx.xxx">>,<<>>},{message,<<>>,normal,<<>>,{jid,<<"homer">>,<<"xxx.xxx.xxx.xxx">>,<<"conference">>,<<"homer">>,<<"xxx.xxx.xxx.xxx">>,<<"conference">>},{jid,<<"carl">>,<<"xxx.xxx.xxx.xxx">>,<<>>,<<"carl">>,<<"xxx.xxx.xxx.xxx">>,<<>>},[],[{text,<<>>,<<"sfsdfsdf">>}],undefined,[],#{}}]}

我是新的Ejabberd和Erlang,所以我无法解释错误,但{mod_offline_push,push_message,3,[{file,"mod_offline_push.erl"}, {line,33}]}中提到的第33行肯定是调用get_tag_attr_s的行。

更新2017/01/27:因为这让我很头疼 - 而且我仍然不是很开心 - 我在这里发布我目前的工作模块,希望它可能有所帮助其他。我的设置是在Ubuntu 16.04上运行的Ejabberd 17.01。对于旧版本的Ejabberd来说,我尝试过的大多数东西都失败了:

-module(mod_fcm_fork).
-behaviour(gen_mod).

%% public methods for this module
-export([start/2, stop/1]).
-export([push_notification/3]).

%% included for writing to ejabberd log file
-include("ejabberd.hrl").
-include("logger.hrl").
-include("xmpp_codec.hrl").

%% Copied this record definition from jlib.hrl
%% Including "xmpp_codec.hrl" and "jlib.hrl" resulted in errors ("XYZ already defined")
-record(jid, {user = <<"">> :: binary(),
              server = <<"">> :: binary(),
              resource = <<"">> :: binary(),
              luser = <<"">> :: binary(),
              lserver = <<"">> :: binary(),
              lresource = <<"">> :: binary()}).


start(Host, _Opts) ->
    ?INFO_MSG("mod_fcm_fork loading", []),
    % Providing the most basic API to the clients and servers that are part of the Inets application
    inets:start(),
    % Add hook to handle message to user who are offline
    ejabberd_hooks:add(offline_message_hook, Host, ?MODULE, push_notification, 10),
    ok.


stop(Host) ->
    ?INFO_MSG("mod_fcm_fork stopping", []),
    ejabberd_hooks:add(offline_message_hook, Host, ?MODULE, push_notification, 10),
    ok.


push_notification(From, To, Packet) ->
    % Generate JID of sender and receiver
    FromJid = lists:concat([binary_to_list(From#jid.user), "@", binary_to_list(From#jid.server), "/", binary_to_list(From#jid.resource)]),
    ToJid = lists:concat([binary_to_list(To#jid.user), "@", binary_to_list(To#jid.server), "/", binary_to_list(To#jid.resource)]),
    % Get message body
    MessageBody = Packet#message.body,
    % Check of MessageBody is not empty
    case MessageBody/=[] of
        true ->
            % Get first element (no idea when this list can have more elements)
            [First | _ ] = MessageBody,
            % Get message data and convert to string
            MessageBodyText = binary_to_list(First#text.data),
            send_post_request(FromJid, ToJid, MessageBodyText);
        false ->
            ?INFO_MSG("mod_fcm_fork -> push_notification: MessageBody is empty",[])
    end,    
    ok.


send_post_request(FromJid, ToJid, MessageBodyText) ->
    %?INFO_MSG("mod_fcm_fork -> send_post_request -> MessageBodyText = ~p", [Demo]),    
    Method = post,
    PostURL = gen_mod:get_module_opt(global, ?MODULE, post_url,fun(X) -> X end, all),
    % Add data as query string. Not nice, query body would be preferable
    % Problem: message body itself can be in a JSON string, and I couldn't figure out the correct encoding.
    URL = lists:concat([binary_to_list(PostURL), "?", "fromjid=", FromJid,"&tojid=", ToJid,"&body=", edoc_lib:escape_uri(MessageBodyText)]),   
    Header = [],
    ContentType = "application/json",
    Body = [],
    ?INFO_MSG("mod_fcm_fork -> send_post_request -> URL = ~p", [URL]),    
    % ADD SSL CONFIG BELOW!
    %HTTPOptions = [{ssl,[{versions, ['tlsv1.2']}]}],
    HTTPOptions = [], 
    Options = [],
    httpc:request(Method, {URL, Header, ContentType, Body}, HTTPOptions, Options),
    ok.

2 个答案:

答案 0 :(得分:1)

最新的方式似乎是xmpp:get_type/1

Type = xmpp:get_type(Packet),

它返回一个原子,在本例中为normal

答案 1 :(得分:1)

实际上,在push_message函数中传递给fxml:get_tag_attr_s的第二个arg Packet失败了

{message,<<>>,normal,<<>>,
         {jid,<<"homer">>,<<"xxx.xxx.xxx.xxx">>,<<"conference">>,
              <<"homer">>,<<"xxx.xxx.xxx.xxx">>,<<"conference">>},
         {jid,<<"carl">>,<<"xxx.xxx.xxx.xxx">>,<<>>,<<"carl">>,
              <<"xxx.xxx.xxx.xxx">>,<<>>},
         [],
         [{text,<<>>,<<"sfsdfsdf">>}],
         undefined,[],#{}}

因为它不是xmlel

看起来像是记录&#34;消息&#34;在tools / xmpp_codec.hrl中定义 与&lt;&lt;&gt;&gt; id和type&#39; normal&#39;

xmpp_codec.hrl
    -record(message, {id :: binary(),
                      type = normal :: 'chat' | 'error' | 'groupchat' | 'headline' | 'normal',
                      lang :: binary(),
                      from :: any(),
                      to :: any(),
                      subject = [] :: [#text{}],
                      body = [] :: [#text{}],
                      thread :: binary(),
                      error :: #error{},
                      sub_els = [] :: [any()]}).

包含此文件并仅使用

Type =  Packet#message.type

或者,如果你期望二进制值

 Type =  erlang:atom_to_binary(Packet#message.type, utf8)