Ejabberd自定义模块优化:消息确认与离线推送逻辑问题
解决Ejabberd自定义模块中消息确认触发问题
你的问题核心在于当前仅在离线消息钩子中处理确认逻辑,而在线消息不会触发该钩子,导致只有接收方离线时才会返回确认。下面是单模块内的解决方案,同时保留离线推送功能:
1. 调整钩子注册
新版Ejabberd中,user_send_message钩子会在用户发送消息时触发(无论接收方在线与否),我们需要注册这个钩子来处理全局的消息确认,同时保留offline_message_hook处理离线推送:
start(_Host, _Opts) -> % 注册用户发送消息钩子,优先级50(合理范围,避免干扰其他模块) ejabberd_hooks:add(user_send_message, _Host, ?MODULE, handle_user_send_message, 50), % 保留原离线推送钩子 ejabberd_hooks:add(offline_message_hook, _Host, ?MODULE, handle_offline_push, 50), ok.
2. 实现全局消息确认逻辑
在handle_user_send_message函数中,统一处理所有聊天消息的确认返回,确保无论接收方状态如何,发送方都能收到服务器确认:
handle_user_send_message(From, To, Packet) -> % 仅处理聊天类型消息,过滤其他类型(如组聊、系统通知) case xml:get_tag_attr_s(<<"type">>, Packet) of <<"chat">> -> % 构造符合Java客户端预期的确认Stanza ConfirmStanza = build_confirm_packet(From, To, OriginalPacket), % 发送确认消息给原发送方(注意路由方向:服务器 -> 发送方) ejabberd_router:route(To#jid.lserver, From, ConfirmStanza); _ -> ok end, % 必须返回原始消息,保证正常消息流程不受影响 {From, To, Packet}. % 构造客户端要求的独特确认结构,根据实际需求调整元素 build_confirm_packet(From, To, OriginalPacket) -> % 关联原消息ID,方便客户端匹配 OriginalMsgId = xml:get_tag_attr_s(<<"id">>, OriginalPacket), ConfirmMsgId = <<OriginalMsgId/binary, "-server-ack">>, xml:element(<<"message">>, [{<<"from">>, To#jid.lserver}, {<<"to">>, jid:to_binary(From)}, {<<"type">>, <<"chat">>}, {<<"id">>, ConfirmMsgId}], [xml:element(<<"body">>, [], [xml:cdata(<<"Message received by server">>)]), % 这里添加Java客户端需要的自定义元素,比如<server-ack status="received" /> xml:element(<<"server-ack">>, [{<<"status">>, <<"received">>}], [])] ).
3. 保留离线推送逻辑
原有的离线推送功能正常,直接保留handle_offline_push函数即可:
handle_offline_push(From, To, Packet) -> case xml:get_tag_attr_s(<<"type">>, Packet) of <<"chat">> -> send_app_server_push(From, To, Packet); _ -> ok end. send_app_server_push(From, To, Packet) -> % 这里实现向应用服务器发送推送的逻辑,比如HTTP请求 PushContent = #{ from_user => jid:to_binary(From), to_user => jid:to_binary(To), message_body => xml:get_tag_cdata(xml:get_subtag(Packet, <<"body">>)) }, % 调用HTTP客户端发送(需确保ejabberd开启httpc支持) httpc:request(post, {"https://your-app-server/push-endpoint", [], "application/json", jsx:encode(PushContent)}, [], []), ok.
关键说明
- 为什么用
user_send_message:这个钩子覆盖所有用户主动发送的消息场景,无论接收方在线/离线,完美解决你之前仅离线触发的问题。 - 钩子优先级:设置为50是常规选择,既保证我们的逻辑能执行,又不会抢占核心模块的处理顺序。
- 消息流程:处理完确认后必须返回原始消息,否则会阻断正常的消息投递。
内容的提问来源于stack exchange,提问作者Benny Abramovici
相关产品推荐
相关产品推荐

