Ejabberd中的自定义模块未调用

时间:2020-05-20 16:45:31

标签: erlang xmpp ejabberd ejabberd-module ejabberd-hooks

原始

我已经尝试了好几天,以将适用于先前版本的开源Ejabberd模块(mod_offline_http_post)适配到当前版本(20.04)。例如,某些部分现在很重要,例如mod_options和mod_depends都需要导出。它们不是模块源代码的一部分。我加了我仍然有一个错误。

.ejabberd-modules / sources / mod_offline_http_post / src / mod_offline_http_post.erl

%% name of module must match file name
%% Update: info@ph-f.nl
-module(mod_offline_http_post).
-author("dev@codepond.org").

-behaviour(gen_mod).

-export([start/2, stop/1, depends/2, mod_options/1, create_message/1, create_message/3]).

%% Required by ?INFO_MSG macros
-include("logger.hrl").

-include("scram.hrl").
-include("xmpp.hrl").

start(_Host, _Opt) ->
  ?INFO_MSG("mod_offline_http_post loading", []),
  inets:start(),
  ?INFO_MSG("HTTP client started", []),
  ejabberd_hooks:add(offline_message_hook, _Host, ?MODULE, create_message, 1).

stop (_Host) ->
  ?INFO_MSG("stopping mod_offline_http_post", []),
  ejabberd_hooks:delete(offline_message_hook, _Host, ?MODULE, create_message, 1).

depends(_Host, _Opts) ->
  [].

mod_options(_Host) ->
  [].

create_message({Action, Packet} = Acc) when (Packet#message.type == chat) and (Packet#message.body /= []) ->
        [{text, _, Body}] = Packet#message.body,
        post_offline_message(Packet#message.from, Packet#message.to, Body, Packet#message.id),
  Acc;

create_message(Acc) ->
  Acc.

create_message(_From, _To, Packet) when (Packet#message.type == chat) and (Packet#message.body /= []) ->
  Body = fxml:get_path_s(Packet, [{elem, list_to_binary("body")}, cdata]),
  MessageId = fxml:get_tag_attr_s(list_to_binary("id"), Packet),
  post_offline_message(_From, _To, Body, MessageId),
  ok.

post_offline_message(From, To, Body, MessageId) ->
  ?INFO_MSG("Posting From ~p To ~p Body ~p ID ~p~n",[From, To, Body, MessageId]),
  Token = gen_mod:get_module_opt(To#jid.lserver, ?MODULE, auth_token, fun(S) -> iolist_to_binary(S) end, list_to_binary("")),
  PostUrl = gen_mod:get_module_opt(To#jid.lserver, ?MODULE, post_url, fun(S) -> iolist_to_binary(S) end, list_to_binary("")),
  ToUser = To#jid.luser,
  FromUser = From#jid.luser,
  Vhost = To#jid.lserver,
  case gen_mod:get_module_opt(To#jid.lserver, ?MODULE, confidential, false) of
    true -> Data = string:join(["to=", binary_to_list(ToUser), "&from=", binary_to_list(FromUser), "&vhost=", binary_to_list(Vhost), "&messageId=", binary_to_list(MessageId)], "");
    false -> Data = string:join(["to=", binary_to_list(ToUser), "&from=", binary_to_list(FromUser), "&vhost=", binary_to_list(Vhost), "&body=", binary_to_list(Body), "&messageId=", binary_to_list(MessageId)], "")
  end,
  Request = {binary_to_list(PostUrl), [{"Authorization", binary_to_list(Token)}], "application/x-www-form-urlencoded", Data},
  httpc:request(post, Request,[],[]),
  ?INFO_MSG("post request sent", []).

.ejabberd-modules / sources / mod_offline_http_post / conf / mod_offline_http_post.yml

modules:
  mod_offline_http_post:
    auth_token: "secret"
    post_url: "http://SERVER_IP_ADDRESS/end_of_url"
    confidential: false
    
### Local Variables:
### mode: yaml
### End:
### vim: set filetype=yaml tabstop=8

如有需要,我可以提供更多详细信息。

在此先感谢您的帮助。

编辑1

看来它现在正在工作...将消息发送给脱机用户并且现在可以识别选项时,会出现一条日志。一旦没有其他问题,我将发布为使它起作用而进行的操作。这,对于其他人。

剩下的问题是我完全怀疑该模块是否被调用。我谈论的日志不是来自模块,而是可能来自Ejabberd路由器。我之所以谈论它,是因为过去没有这样的事情。那么,发生了一些积极的事情。为了更加确定,在卸载模块时会显示“错误的模块”。

这是新的错误代码:

%% name of module must match file name
%% Update: info@ph-f.nl
-module(mod_offline_http_post).
-author("dev@codepond.org").

-behaviour(gen_mod).

-export([start/2,
        stop/1,
        depends/2,
        mod_options/1,
        mod_opt_type/1,
        create_message/1,
        create_message/3]).

%% Required by ?INFO_MSG macros
-include("logger.hrl").

-include("scram.hrl").
-include("xmpp.hrl").

start(_Host, _Opt) ->
  ?INFO_MSG("mod_offline_http_post loading", []),
  inets:start(),
  ?INFO_MSG("HTTP client started", []),
  ejabberd_hooks:add(offline_message_hook, _Host, ?MODULE, create_message, 1).

stop (_Host) ->
  ?INFO_MSG("stopping mod_offline_http_post", []),
  ejabberd_hooks:delete(offline_message_hook, _Host, ?MODULE, create_message, 1).

depends(_Host, _Opts) ->
  [].

mod_options(_Host) ->
  [{auth_token, <<"secret">>},
  {post_url, <<"http://example.com/test">>},
  {confidential, false}].

mod_opt_type(auth_token) ->
  fun iolist_to_binary/1;
mod_opt_type(post_url) ->
  fun iolist_to_binary/1;
mod_opt_type(confidential) ->
  fun (B) when is_boolean(B) -> B end.

create_message({Action, Packet} = Acc) when (Packet#message.type == chat) and (Packet#message.body /= []) ->
	[{text, _, Body}] = Packet#message.body,
	post_offline_message(Packet#message.from, Packet#message.to, Body, Packet#message.id),
  Acc;

create_message(Acc) ->
  Acc.

create_message(_From, _To, Packet) when (Packet#message.type == chat) and (Packet#message.body /= []) ->
  Body = fxml:get_path_s(Packet, [{elem, list_to_binary("body")}, cdata]),
  MessageId = fxml:get_tag_attr_s(list_to_binary("id"), Packet),
  post_offline_message(_From, _To, Body, MessageId),
  ok.

post_offline_message(From, To, Body, MessageId) ->
  ?DEBUG("Posting From ~p To ~p Body ~p ID ~p~n",[From, To, Body, MessageId]),
  Token = gen_mod:get_module_opt(To#jid.lserver, ?MODULE, auth_token, fun(S) -> iolist_to_binary(S) end, list_to_binary("")),
  PostUrl = gen_mod:get_module_opt(To#jid.lserver, ?MODULE, post_url, fun(S) -> iolist_to_binary(S) end, list_to_binary("")),
  ToUser = To#jid.luser,
  FromUser = From#jid.luser,
  Vhost = To#jid.lserver,
  case gen_mod:get_module_opt(To#jid.lserver, ?MODULE, confidential, false) of
    true -> Data = string:join(["to=", binary_to_list(ToUser), "&from=", binary_to_list(FromUser), "&vhost=", binary_to_list(Vhost), "&messageId=", binary_to_list(MessageId)], "");
    false -> Data = string:join(["to=", binary_to_list(ToUser), "&from=", binary_to_list(FromUser), "&vhost=", binary_to_list(Vhost), "&body=", binary_to_list(Body), "&messageId=", binary_to_list(MessageId)], "")
  end,
  Request = {binary_to_list(PostUrl), [{"Authorization", binary_to_list(Token)}], "application/x-www-form-urlencoded", Data},
  httpc:request(post, Request,[],[]),
  ?DEBUG("post request sent", []).

编辑2

这是一个非常有希望的案例。该模块被调用。只是它崩溃了,这是另一个非常有趣的情况。这是错误日志:

2020-05-21 11:53:35.897 [error] <0.508.0>@ejabberd_hooks:safe_apply:240 Hook offline_message_hook crashed when running mod_offline_http_post:create_message/1:
** exception error: undefined function gen_mod:get_module_opt/5
   in function  mod_offline_http_post:post_offline_message/4 (/opt/ejabberd/.ejabberd-modules/sources/mod_offline_http_post/src/mod_offline_http_post.erl, line 63)
   in call from mod_offline_http_post:create_message/1 (/opt/ejabberd/.ejabberd-modules/sources/mod_offline_http_post/src/mod_offline_http_post.erl, line 49)
   in call from ejabberd_hooks:safe_apply/4 (src/ejabberd_hooks.erl, line 236)
   in call from ejabberd_hooks:run_fold1/4 (src/ejabberd_hooks.erl, line 217)
   in call from ejabberd_sm:route/1 (src/ejabberd_sm.erl, line 146)
   in call from ejabberd_router:do_route/1 (src/ejabberd_router.erl, line 399)
   in call from ejabberd_router:route/1 (src/ejabberd_router.erl, line 92)

1 个答案:

答案 0 :(得分:2)

工作正常!

很棒。现在正在工作。通知发送给离线用户。好吧,我的意思是将发给离线用户的消息转发到后端,然后后端将FCM通知发送给用户。

由于我花了3天的时间,并且几乎没有关于该问题的当前版本20.04的信息,所以我将详细介绍针对遇到此问题的解决方案。

最重要的是,如果您的自定义模块支持以下选项,则除了start / 2和stop / 1之外,还导出这3个函数:depends / 2(可能是一个表示依赖项的函数,即如果我不列出依赖项,则在其中列出所需的模块错误),mod_opt_type / 1(应验证每个选项)和mod_options / 2(在其中列出选项及其默认值)。

第二,Internet上的许多自定义模块都是为不需要这些模块的以前版本制作的。然后,如果他们使用gen_mod:get_module_opt,则添加验证和默认值。您最终拥有gen_mod:get_module_opt / 4或gen_mod:get_module_opt / 5。现在,由于您已经在mod_options / 2和mod_opt_type / 1中获得了每个选项的验证和默认值,因此mod:get_module_opt变为mod:get_module_opt / 3。您应该只保留前三个参数。

如果像我这样通过RPM软件包安装Ejabberd,则可能在未启用LAGER的情况下编译了服务器。好吧,或者至少像这样的解释,至少对于自定义模块。我的意思是,您看到的是包中嵌入的模块的日志,但看不到自定义模块中的模块的日志。要启用LAGER或您的信息(如果我使用的术语不正确,请更正这些声明),请在导出后立即执行以下操作:

-ifndef(LAGER).
-define(LAGER, 1).
-endif.

尽管这段代码似乎只与调试过程有关,但是当我的代码中没有自定义模块时,根本不会调用它。有趣的是。

我看到有些人对该模块“ mod_offline_http_post”遇到问题,并出现错误,例如找不到ebin目录。关键是,一旦您在.ejabberd-modules / sources目录中创建了一个git克隆,该模块就会存在,但是Ejabberd可能对此没有权限。之后执行以下操作: chown -R ejabberd:ejabberd your_path_to_.ejabberd-modules / sources 之后,点击ejabberdctl install mod_offline_http_post。 如果看到未使用ArgumentX之类的警告,请忽略此警告。这只是说的意思。为防止显示此错误,只需在erl文件中未使用的arg前面加一个下划线,即_ArgumentX。

其他一些人甚至没有.ejabberd-modules目录。大家好,只需安装一个随机的contrib模块(如mod_cron),就会创建此目录。 转到ejabberdctl所在的位置,我的位置在/opt/ejabberd-20.04/bin中并命中: ejabberdctl module_install mod_cron 完成后,不要删除它,而是转到/opt/ejabberd/.ejabberd-modules/sources并在其中git clone自定义模块。之后,通过执行ejabberdctl module_uninstall mod_cron来卸载mod_cron。

您的目录.ejabberd-modules可能位于其他位置。执行这些命令后,无需重新启动Ejabberd。实际上,module_install会编译,安装和启动模块。

好吧,由于您需要将模块放入ejabberd.yml中,因此必须重新启动ejabberd,但是一旦完成,如果某些事情不起作用,您最终需要卸载自定义模块,一旦重新安装,则不需要重新启动Ejabberd。

我将发布适用于Ejabberd 19.02的原始代码以及适用于20.04的已编辑代码

对于19.02

%% name of module must match file name
%% Update: info@ph-f.nl
-module(mod_offline_http_post).
-author("dev@codepond.org").

-behaviour(gen_mod).

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

-include("scram.hrl").
-include("xmpp.hrl").
-include("logger.hrl").

start(_Host, _Opt) ->
  ?INFO_MSG("mod_offline_http_post loading", []),
  inets:start(),
  ?INFO_MSG("HTTP client started", []),
  ejabberd_hooks:add(offline_message_hook, _Host, ?MODULE, create_message, 1).

stop (_Host) ->
  ?INFO_MSG("stopping mod_offline_http_post", []),
  ejabberd_hooks:delete(offline_message_hook, _Host, ?MODULE, create_message, 1).

create_message({Action, Packet} = Acc) when (Packet#message.type == chat) and (Packet#message.body /= []) ->
	[{text, _, Body}] = Packet#message.body,
	post_offline_message(Packet#message.from, Packet#message.to, Body, Packet#message.id),
  Acc;

create_message(Acc) ->
  Acc.

create_message(_From, _To, Packet) when (Packet#message.type == chat) and (Packet#message.body /= []) ->
  Body = fxml:get_path_s(Packet, [{elem, list_to_binary("body")}, cdata]),
  MessageId = fxml:get_tag_attr_s(list_to_binary("id"), Packet),
  post_offline_message(_From, _To, Body, MessageId),
  ok.

post_offline_message(From, To, Body, MessageId) ->
  ?INFO_MSG("Posting From ~p To ~p Body ~p ID ~p~n",[From, To, Body, MessageId]),
  Token = gen_mod:get_module_opt(To#jid.lserver, ?MODULE, auth_token, fun(S) -> iolist_to_binary(S) end, list_to_binary("")),
  PostUrl = gen_mod:get_module_opt(To#jid.lserver, ?MODULE, post_url, fun(S) -> iolist_to_binary(S) end, list_to_binary("")),
  ToUser = To#jid.luser,
  FromUser = From#jid.luser,
  Vhost = To#jid.lserver,
  case gen_mod:get_module_opt(To#jid.lserver, ?MODULE, confidential, false) of
    true -> Data = string:join(["to=", binary_to_list(ToUser), "&from=", binary_to_list(FromUser), "&vhost=", binary_to_list(Vhost), "&messageId=", binary_to_list(MessageId)], "");
    false -> Data = string:join(["to=", binary_to_list(ToUser), "&from=", binary_to_list(FromUser), "&vhost=", binary_to_list(Vhost), "&body=", binary_to_list(Body), "&messageId=", binary_to_list(MessageId)], "")
  end,
  Request = {binary_to_list(PostUrl), [{"Authorization", binary_to_list(Token)}], "application/x-www-form-urlencoded", Data},
  httpc:request(post, Request,[],[]),
  ?INFO_MSG("post request sent", []).

对于20.04

%% name of module must match file name
%% Update: pape.diack@live.fr
-module(mod_offline_http_post).
-author("dev@codepond.org").

-behaviour(gen_mod).

-export([start/2,
        stop/1,
        depends/2,
        mod_options/1,
        mod_opt_type/1,
        create_message/1,
        create_message/3]).

-ifndef(LAGER).
-define(LAGER, 1).
-endif.

-include("logger.hrl").
-include("xmpp.hrl").

start(_Host, _Opt) ->
  ?INFO_MSG("mod_offline_http_post loading", []),
  inets:start(),
  ?INFO_MSG("HTTP client started", []),
  ejabberd_hooks:add(offline_message_hook, _Host, ?MODULE, create_message, 50).

stop (_Host) ->
  ?INFO_MSG("stopping mod_offline_http_post", []),
  ejabberd_hooks:delete(offline_message_hook, _Host, ?MODULE, create_message, 50).

depends(_Host, _Opts) ->
  [].

mod_options(_Host) ->
  [{auth_token, <<"secret">>},
  {post_url, <<"http://example.com/test">>},
  {confidential, false}].

mod_opt_type(auth_token) ->
  fun iolist_to_binary/1;
mod_opt_type(post_url) ->
  fun iolist_to_binary/1;
mod_opt_type(confidential) ->
  fun (B) when is_boolean(B) -> B end.

create_message({Action, Packet} = Acc) when (Packet#message.type == chat) and (Packet#message.body /= []) ->
	[{text, _, Body}] = Packet#message.body,
	post_offline_message(Packet#message.from, Packet#message.to, Body, Packet#message.id),
  Acc;

create_message(Acc) ->
  Acc.

create_message(_From, _To, Packet) when (Packet#message.type == chat) and (Packet#message.body /= []) ->
  Body = fxml:get_path_s(Packet, [{elem, list_to_binary("body")}, cdata]),
  MessageId = fxml:get_tag_attr_s(list_to_binary("id"), Packet),
  post_offline_message(_From, _To, Body, MessageId),
  ok.

post_offline_message(From, To, Body, MessageId) ->
  ?INFO_MSG("Posting From ~p To ~p Body ~p ID ~p~n",[From, To, Body, MessageId]),
  Token = gen_mod:get_module_opt(To#jid.lserver, ?MODULE, auth_token),
  PostUrl = gen_mod:get_module_opt(To#jid.lserver, ?MODULE, post_url),
  ToUser = To#jid.luser,
  FromUser = From#jid.luser,
  Vhost = To#jid.lserver,
  case gen_mod:get_module_opt(To#jid.lserver, ?MODULE, confidential) of
    true -> Data = string:join(["to=", binary_to_list(ToUser), "&from=", binary_to_list(FromUser), "&vhost=", binary_to_list(Vhost), "&messageId=", binary_to_list(MessageId)], "");
    false -> Data = string:join(["to=", binary_to_list(ToUser), "&from=", binary_to_list(FromUser), "&vhost=", binary_to_list(Vhost), "&body=", binary_to_list(Body), "&messageId=", binary_to_list(MessageId)], "")
  end,
  Request = {binary_to_list(PostUrl), [{"Authorization", binary_to_list(Token)}, {"Logged-Out", "logged-out"}], "application/x-www-form-urlencoded", Data},
  httpc:request(post, Request,[],[]),
  ?INFO_MSG("post request sent", []).

过上轻松的生活。