未调用 Ejabberd 中的自定义模块
Custom module in Ejabberd is not called
原版
几天来我一直在尝试将适用于以前版本的开源 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
它现在似乎正在工作...当消息发送给离线用户时有一个日志,现在可以识别选项。一旦没有更多问题,我将 post 我所做的使其工作作为答案。这,对于其他人。
剩下的问题是我怀疑这个模块根本没有被调用。我谈到的日志不是来自模块,而是可能来自 Ejabberd 路由器。我讲它是因为过去没有这样的事情。那么,有一些积极的事情正在发生。为了更加确定,卸载模块时显示"Bad module"
这是新的 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,
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)
有效!!
太棒了。它现在正在工作。通知发送给离线用户。好吧,我的意思是发给离线用户的消息被转发到后端,后端又向用户发送 FCM 通知。
因为我花了 3 天时间解决这个问题,而且几乎没有关于当前版本 20.04 的信息,所以我将详细介绍遇到该问题的人的解决方案。
首先,除了 start/2 和 stop/1,如果您的自定义 module 支持选项,则导出这 3 个函数:depends/2(可能是一个函数表示依赖关系,即如果我没记错的话,你在那里列出了必需的 mod 规则)、mod_opt_type/1(每个选项都应该被验证)和 mod_options/2(你列出选项及其默认值的地方) .
其次,许多来自互联网的自定义 mod 规则是为不需要这些的以前版本制作的。然后,如果他们使用 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。您应该只保留前 3 个参数。
如果像我这样通过 RPM 包安装 Ejabberd,你可能在没有启用 LAGER 的情况下编译了服务器。好吧,或者像那个和这个这样的解释,至少对于自定义 mod 规则。我的意思是,您看到包中嵌入的 mod 规则的日志,而不是来自您的自定义 mod 规则的日志。要启用 LAGER 或您的信息(如果我没有使用正确的术语,请更正这些陈述),请在导出后立即执行此操作:
-ifndef(LAGER).
-define(LAGER, 1).
-endif.
尽管这段代码似乎只与调试过程有关,但当我在代码中没有自定义 module 时,根本没有调用它。有趣的奇怪。
我看到有些人在使用 module“mod_offline_http_post”时遇到问题,并出现类似找不到 ebin 目录的错误。问题是,一旦你在 .ejabberd-modules/sources 目录中创建了一个 git 克隆,module 就会在那里,但 Ejabberd 可能缺少对此的权限。之后就这样做:
chown -R ejabberd:ejabberd your_path_to_.ejabberd-modules/sources
之后,点击 ejabberdctl install mod_offline_http_post。
如果您看到类似 ArgumentX not used 的警告,请忽略它。它只是意味着它所说的。为了防止显示这个,只需在您的 erl 文件中未使用的 arg 前面加上下划线,即 _ArgumentX.
其他一些人甚至没有 .ejabberd-modules 目录。只需安装一个像 mod_cron 这样的随机贡献 module,就会创建此目录。
去 ejabberdctl 所在的地方,我的在 /opt/ejabberd-20.04/bin 并点击:
ejabberdctl module_install mod_cron
完成后,不要删除它,而是转到 /opt/ejabberd/.ejabberd-modules/sources 和 git 在那里克隆自定义 module。之后,通过执行 ejabberdctl module_uninstall mod_cron.
卸载 mod_cron
您的目录 .ejabberd-modules 可能在其他地方。在这些命令之后,您不需要重新启动 Ejabberd。事实上,module_install 编译、安装并启动 module。
好吧,由于您需要将 module 放入 ejabberd.yml,因此您必须重新启动 ejabberd,但一旦完成,如果某些事情不起作用,您最终会卸载自定义 module,重新安装后,无需重启Ejabberd。
我将 post 适用于 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", []).
生活更轻松。
原版
几天来我一直在尝试将适用于以前版本的开源 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
它现在似乎正在工作...当消息发送给离线用户时有一个日志,现在可以识别选项。一旦没有更多问题,我将 post 我所做的使其工作作为答案。这,对于其他人。
剩下的问题是我怀疑这个模块根本没有被调用。我谈到的日志不是来自模块,而是可能来自 Ejabberd 路由器。我讲它是因为过去没有这样的事情。那么,有一些积极的事情正在发生。为了更加确定,卸载模块时显示"Bad module"
这是新的 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,
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)
有效!!
太棒了。它现在正在工作。通知发送给离线用户。好吧,我的意思是发给离线用户的消息被转发到后端,后端又向用户发送 FCM 通知。
因为我花了 3 天时间解决这个问题,而且几乎没有关于当前版本 20.04 的信息,所以我将详细介绍遇到该问题的人的解决方案。
首先,除了 start/2 和 stop/1,如果您的自定义 module 支持选项,则导出这 3 个函数:depends/2(可能是一个函数表示依赖关系,即如果我没记错的话,你在那里列出了必需的 mod 规则)、mod_opt_type/1(每个选项都应该被验证)和 mod_options/2(你列出选项及其默认值的地方) .
其次,许多来自互联网的自定义 mod 规则是为不需要这些的以前版本制作的。然后,如果他们使用 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。您应该只保留前 3 个参数。
如果像我这样通过 RPM 包安装 Ejabberd,你可能在没有启用 LAGER 的情况下编译了服务器。好吧,或者像那个和这个这样的解释,至少对于自定义 mod 规则。我的意思是,您看到包中嵌入的 mod 规则的日志,而不是来自您的自定义 mod 规则的日志。要启用 LAGER 或您的信息(如果我没有使用正确的术语,请更正这些陈述),请在导出后立即执行此操作:
-ifndef(LAGER).
-define(LAGER, 1).
-endif.
尽管这段代码似乎只与调试过程有关,但当我在代码中没有自定义 module 时,根本没有调用它。有趣的奇怪。
我看到有些人在使用 module“mod_offline_http_post”时遇到问题,并出现类似找不到 ebin 目录的错误。问题是,一旦你在 .ejabberd-modules/sources 目录中创建了一个 git 克隆,module 就会在那里,但 Ejabberd 可能缺少对此的权限。之后就这样做: chown -R ejabberd:ejabberd your_path_to_.ejabberd-modules/sources 之后,点击 ejabberdctl install mod_offline_http_post。 如果您看到类似 ArgumentX not used 的警告,请忽略它。它只是意味着它所说的。为了防止显示这个,只需在您的 erl 文件中未使用的 arg 前面加上下划线,即 _ArgumentX.
其他一些人甚至没有 .ejabberd-modules 目录。只需安装一个像 mod_cron 这样的随机贡献 module,就会创建此目录。 去 ejabberdctl 所在的地方,我的在 /opt/ejabberd-20.04/bin 并点击: ejabberdctl module_install mod_cron 完成后,不要删除它,而是转到 /opt/ejabberd/.ejabberd-modules/sources 和 git 在那里克隆自定义 module。之后,通过执行 ejabberdctl module_uninstall mod_cron.
卸载 mod_cron您的目录 .ejabberd-modules 可能在其他地方。在这些命令之后,您不需要重新启动 Ejabberd。事实上,module_install 编译、安装并启动 module。
好吧,由于您需要将 module 放入 ejabberd.yml,因此您必须重新启动 ejabberd,但一旦完成,如果某些事情不起作用,您最终会卸载自定义 module,重新安装后,无需重启Ejabberd。
我将 post 适用于 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", []).
生活更轻松。