ejabberd icon indicating copy to clipboard operation
ejabberd copied to clipboard

Get Unread Message of user per chat

Open Carlososuna11 opened this issue 4 years ago • 2 comments

Hey! How can I get the number of unread messages by chat?

I am aware that ejabberd has an endpoint that returns the total number of unread messages but I also need how many messages per chat / user are to be able to show it on my front page.

Cheers

Carlososuna11 avatar Sep 24 '21 04:09 Carlososuna11

I think that is not possible. This patch implements a new command for that. The comand doesn't work when SQL or MAM is enabled because that would require even more code.

This patch is against ejabberd master git:
From fcecd293ea2ee6ae599f1c52bf825df6a2186183 Mon Sep 17 00:00:00 2001
From: Badlop <[email protected]>
Date: Tue, 28 Sep 2021 13:22:19 +0200
Subject: [PATCH] get_offline_count_contact, experimental dirty patch (#3685)

---
 src/mod_admin_extra.erl    | 10 ++++++++++
 src/mod_offline.erl        | 26 ++++++++++++++++++++++++++
 src/mod_offline_mnesia.erl | 26 +++++++++++++++++++++++++-
 3 files changed, 61 insertions(+), 1 deletion(-)

diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl
index 9834acf01..862800569 100644
--- a/src/mod_admin_extra.erl
+++ b/src/mod_admin_extra.erl
@@ -731,6 +731,16 @@ get_commands_spec() ->
 			result_example = 5,
 			result_desc = "Number",
 			result = {value, integer}},
+     #ejabberd_commands{name = get_offline_count_contact,
+			tags = [offline],
+			desc = "Get the number of unread offline messages with a contact",
+			policy = user,
+			module = mod_offline, function = count_offline_messages,
+			args = [{cuser, binary}, {cserver, binary}],
+			args_rename = [{server, host}],
+			result_example = 5,
+			result_desc = "Number",
+			result = {value, integer}},
      #ejabberd_commands{name = send_message, tags = [stanza],
 			desc = "Send a message to a local or remote bare of full JID",
 			longdesc = "When sending a groupchat message to a MUC room, "
diff --git a/src/mod_offline.erl b/src/mod_offline.erl
index 1d367eb72..6c33d72ea 100644
--- a/src/mod_offline.erl
+++ b/src/mod_offline.erl
@@ -55,6 +55,7 @@
 	 export/1,
 	 get_queue_length/2,
 	 count_offline_messages/2,
+	 count_offline_messages/4,
 	 get_offline_els/2,
 	 find_x_expire/2,
 	 c2s_handle_info/2,
@@ -104,6 +105,7 @@
 -callback read_all_messages(binary(), binary()) -> [#offline_msg{}].
 -callback remove_all_messages(binary(), binary()) -> {atomic, any()}.
 -callback count_messages(binary(), binary()) -> {ets_cache:tag(), non_neg_integer()}.
+-callback count_messages(binary(), binary(), binary(), binary()) -> {ets_cache:tag(), non_neg_integer()}.
 -callback use_cache(binary()) -> boolean().
 -callback cache_nodes(binary()) -> [node()].
 
@@ -1150,6 +1152,30 @@ count_offline_messages(User, Server) ->
 	    end
     end.
 
+%% Returns as integer the number of offline messages for a given user with contact
+-spec count_offline_messages(binary(), binary(), binary(), binary()) -> non_neg_integer().
+count_offline_messages(User, Server, ContactUser, ContactServer) ->
+    LUser = jid:nodeprep(User),
+    LServer = jid:nameprep(Server),
+    CUser = jid:nodeprep(ContactUser),
+    CServer = jid:nameprep(ContactServer),
+    Mod = gen_mod:db_mod(LServer, ?MODULE),
+    case use_mam_for_user(User, Server) of
+	true -> %% This is not updated !!!!
+	    ?ERROR_MSG("Not supported getting count_offline_messages when MAM enabled", []), -1;
+	_ ->
+	    case use_cache(Mod, LServer) of
+		true ->
+		    ets_cache:lookup(
+			?SPOOL_COUNTER_CACHE, {LUser, LServer, CUser, CServer},
+			fun() ->
+			    Mod:count_messages(LUser, LServer, CUser, CServer)
+			end);
+		false ->
+		    ets_cache:untag(Mod:count_messages(LUser, LServer, CUser, CServer))
+	    end
+    end.
+
 -spec store_message_in_db(module(), #offline_msg{}) -> ok | {error, any()}.
 store_message_in_db(Mod, #offline_msg{us = {User, Server}} = Msg) ->
     case Mod:store_message(Msg) of
diff --git a/src/mod_offline_mnesia.erl b/src/mod_offline_mnesia.erl
index a3c902fb3..32b42bc6b 100644
--- a/src/mod_offline_mnesia.erl
+++ b/src/mod_offline_mnesia.erl
@@ -29,7 +29,7 @@
 -export([init/2, store_message/1, pop_messages/2, remove_expired_messages/1,
 	 remove_old_messages/2, remove_user/2, read_message_headers/2,
 	 read_message/3, remove_message/3, read_all_messages/2,
-	 remove_all_messages/2, count_messages/2, import/1]).
+	 remove_all_messages/2, count_messages/2, count_messages/4, import/1]).
 -export([need_transform/1, transform/1]).
 
 -include_lib("xmpp/include/xmpp.hrl").
@@ -161,6 +161,17 @@ count_messages(LUser, LServer) ->
 		_ -> 0
 	    end}.
 
+count_messages(LUser, LServer, CUser, CServer) ->
+    US = {LUser, LServer},
+    Contact = {CUser, CServer},
+    F = fun () ->
+		count_mnesia_records(US, Contact)
+	end,
+    {cache, case mnesia:async_dirty(F) of
+		I when is_integer(I) -> I;
+		_ -> 0
+	    end}.
+
 import(#offline_msg{} = Msg) ->
     mnesia:dirty_write(Msg).
 
@@ -203,6 +214,19 @@ count_mnesia_records(US) ->
 	    0
     end.
 
+count_mnesia_records(US, {CU, CS} = _Contact) ->
+    MatchExpression = #offline_msg{us = US,
+                                   from = {jid, CU, CS, '_', '_', '_', '_'},
+                                  _ = '_'},
+    case mnesia:select(offline_msg, [{MatchExpression, [], [[]]}],
+		       ?BATCHSIZE, read) of
+	{Result, Cont} ->
+	    Count = length(Result),
+	    count_records_cont(Cont, Count);
+	'$end_of_table' ->
+	    0
+    end.
+
 count_records_cont(Cont, Count) ->
     case mnesia:select(Cont) of
 	{Result, Cont} ->
-- 
2.20.1

badlop avatar Sep 28 '21 11:09 badlop

There's an extension for this functionality, but it's not supported by ejabberd so far.

I've added the necessary code to the xmpp library a while ago, and I have a half-done patch for ejabberd. The customer who sponsored this work changed his plans and dropped the project. I have no need for this feature myself, but if someone else has, implementation cost would be cheaper given it could be based on my initial patch.

weiss avatar Sep 28 '21 14:09 weiss