我在linux机器上使用源代码中的ejabberd-17.03。
我以编程的方式从服务器上创建了一个临时聊天室,使用userA的jid,并向用户B发送直接邀请,他接受该邀请并加入聊天室。
我的用例是两个用户A和B在聊天室交换消息。如果没有用户在30秒内向其他用户发送任何消息,那么房间就向这两个用户发送随机选择的消息。
我这样做的原因如下:
start(_Host, _Opts) ->
ejabberd_hooks:add(user_send_packet, _Host, ?MODULE, myMessage, 95).
stop(_Host) ->
ejabberd_hooks:delete(user_send_packet, _Host, ?MODULE, myMessage,95).
depends(_Host, _Opts)->[{?MODULE,soft}].
mod_opt_type(_Option)->
ok.
myMessage({#message{from = From, to = To, body= Body} =Packet, C2SState}) ->
{UserA,UserB}=select_user(Packet),
PacketType=returnPacketType(Packet),
if
(PacketType==normal) ->
dosomething(),
{Timer_Result,Ref_or_Reason} = timer:apply_interval(30000, ?MODULE, func(), [Arguments]),
if
(Timer_Result == ok)->
ets:insert(ref_table, {Key, Ref_or_Reason});
(Timer_Result == error)->
io:format(" Could not delete user after timeout, Reason is ~p~n",[Ref_or_Reason])
end;
(PacketType==groupchat)->
do_something_else(),
{Timer_Result,Ref_or_Reason} = timer:apply_interval(30000, ?MODULE, func(), [Arguments]),
if
(Timer_Result == ok)->
replace_old_ref_with_new(Key, Ref_or_Reason, ref_table);
(Timer_Result == error)->
io:format(" Could not delete user after timeout, Reason is ~p~n",[Ref_or_Reason])
end
end
if
(somecondition()==true)->
delete_ref(Key, ref_table);
True->
do_nothing
end,
{Packet, C2SState}.现在,除了以下情况外,一切都很好:
1.创建聊天室,A和B之间开始消息交换,此时计时器也开始。
因此,我将ejabberd的日志记录级别更改为5,并且再次看到了给离线和联机用户的脱机消息没有得到delivered.even,尽管在ejabberd.yml中启用了mod_offline。
日志:
#message{
id = <<>>,type = error,lang = <<"en">>,
from =
{jid,<<"fWiTvj973AB”>>,<<“example.com">>,<<"Smack">>,<<"fwitvj973ab”>>,
<<"example.com">>,<<"Smack">>},
to =
{jid,<<"ac5a6b8c-66b8-4da7-8b1a-0f3ecb1e5gfd”>>,
<<"conference.example.com">>,<<"cXWmOrqEESd”>>,
<<"ac5a6b8c-66b8-4da7-8b1a-0f3ecb1e5gfd">>,
<<"conference.example.com">>,<<"cXWmOrqEESd">>},
subject = [#text{lang = <<>>,data = <<>>}],
body = [#text{lang = <<>>,data = <<"\"cXWmOrqEESd\"">>}],
thread = undefined,
sub_els =
[{xmlel,<<"q">>,[{<<"xmlns">>,<<"ns:custom”>>}],[]},
#stanza_error{
type = cancel,code = 503,by = <<>>,
reason = 'service-unavailable',
text =
#text{lang = <<"en">>,data = <<"User session terminated">>},
sub_els = []}],
meta = #{}}ejabberd.yml
###. ============
###' SHAPER RULES
shaper_rules:
## Maximum number of offline messages that users can have:
max_user_offline_messages:
- 5000: admin
- 100
###. =======
###' MODULES
##
## Modules enabled in all ejabberd virtual hosts.
##
modules:
mod_offline:
db_type: sql
access_max_user_messages: max_user_offline_messages
store_empty_body: unless_chat_state虽然我不需要这些离线消息能够完美地传递,但我倾向于认为它是否是停止我的计时器的原因(但我不明白为什么只有当创建房间的用户离线并返回时,它才会停止,为什么其他用户会这样做?)
为什么这个计时器会被停止,我如何使它定期运行?
发布于 2018-08-22 13:45:54
在模块的最底层提到了这一点
间隔计时器,即通过评估任何函数
apply_interval/4、send_interval/3和send_interval/2创建的计时器,都链接到计时器执行其任务的进程。
因此,timer:apply_interval将计时器服务器链接到启动定时器的进程,当调用进程退出时,定时器将被取消。
显然,定时器是从管理用户连接的进程中创建的,因此当用户断开连接时,定时器将自动取消。
您可以通过生成一个长期运行的进程来管理这个计时器来解决这个问题。
一个不相关的风格问题:在Erlang中,case通常比if更清晰。这段代码:
{Timer_Result,Ref_or_Reason} = timer:apply_interval(30000, ?MODULE, func(), [Arguments]),
if
(Timer_Result == ok)->
ets:insert(ref_table, {Key, Ref_or_Reason});
(Timer_Result == error)->
io:format(" Could not delete user after timeout, Reason is ~p~n",[Ref_or_Reason])
end;可以写成:
case timer:apply_interval(30000, ?MODULE, func(), [Arguments]) of
{ok, Ref} ->
ets:insert(ref_table, {Key, Ref});
{error, Reason} ->
io:format(" Could not delete user after timeout, Reason is ~p~n",[Reason])
end;发布于 2018-08-22 23:46:38
我建议阅读mod_muc_room.erl代码并使用muc的钩子。例如,当用户仅在聊天室(muc_filter_message钩子)中发送消息时,或者当用户在聊天室(muc_filter_presence钩子)中发送存在(连接、离开等)时,可以通知您。最好有一个处理定时器的进程(如mod_ping)。但是在大规模的情况下,您必须使用ejabberdC2s的c2s_handle_info和c2s_terminate挂钩来管理计时器。此外,我建议升级到Ejabberd 18.06或至少17.11因为这。
https://stackoverflow.com/questions/51943132
复制相似问题