1 |
|
%%%------------------------------------------------------------------- |
2 |
|
%%% @copyright (C) 2018, Erlang-Solutions |
3 |
|
%%% @doc |
4 |
|
%%% |
5 |
|
%%% @end |
6 |
|
%%% Created : 30. Jan 2018 13:22 |
7 |
|
%%%------------------------------------------------------------------- |
8 |
|
-module(mod_inbox_utils). |
9 |
|
|
10 |
|
-include("mod_inbox.hrl"). |
11 |
|
-include("jlib.hrl"). |
12 |
|
|
13 |
|
-type inbox_fun() :: fun((mongooseim:host_type(), |
14 |
|
jid:jid(), |
15 |
|
jid:jid(), |
16 |
|
exml:element(), |
17 |
|
mongoose_acc:t()) -> mod_inbox:count_res()). |
18 |
|
|
19 |
|
%%%%%%%%%%%%%%%%%%% |
20 |
|
%% DB Operations shared by mod_inbox_one2one and mod_inbox_muclight |
21 |
|
-export([maybe_reset_unread_count/4, |
22 |
|
reset_unread_count_to_zero/3, |
23 |
|
maybe_write_to_inbox/6, |
24 |
|
write_to_sender_inbox/5, |
25 |
|
write_to_receiver_inbox/5, |
26 |
|
clear_inbox/3, |
27 |
|
get_reset_markers/1, |
28 |
|
if_chat_marker_get_id/2, |
29 |
|
has_chat_marker/1, |
30 |
|
fill_from_attr/2, |
31 |
|
wrapper_id/0, |
32 |
|
get_option_write_aff_changes/1, |
33 |
|
get_option_remove_on_kicked/1, |
34 |
|
extract_attr_jid/1, |
35 |
|
maybe_binary_to_positive_integer/1, |
36 |
|
maybe_muted_until/2, |
37 |
|
binary_to_bool/1, |
38 |
|
bool_to_binary/1, |
39 |
|
build_inbox_entry_key/2 |
40 |
|
]). |
41 |
|
|
42 |
|
-ignore_xref([ |
43 |
|
fill_from_attr/2, get_reset_markers/1, if_chat_marker_get_id/2 |
44 |
|
]). |
45 |
|
|
46 |
|
-spec maybe_reset_unread_count(HostType :: mongooseim:host_type(), |
47 |
|
User :: jid:jid(), |
48 |
|
Remote :: jid:jid(), |
49 |
|
Packet :: exml:element()) -> ok. |
50 |
|
maybe_reset_unread_count(HostType, User, Remote, Packet) -> |
51 |
168 |
ResetMarkers = get_reset_markers(HostType), |
52 |
168 |
case if_chat_marker_get_id(Packet, ResetMarkers) of |
53 |
|
undefined -> |
54 |
117 |
ok; |
55 |
|
Id -> |
56 |
51 |
reset_unread_count(HostType, User, Remote, Id) |
57 |
|
end. |
58 |
|
|
59 |
|
-spec reset_unread_count_to_zero(mongooseim:host_type(), jid:jid(), jid:jid()) -> ok. |
60 |
|
reset_unread_count_to_zero(HostType, From, Remote) -> |
61 |
3 |
InboxEntryKey = build_inbox_entry_key(From, Remote), |
62 |
3 |
ok = mod_inbox_backend:reset_unread(HostType, InboxEntryKey, undefined). |
63 |
|
|
64 |
|
-spec reset_unread_count(HostType ::mongooseim:host_type(), |
65 |
|
From :: jid:jid(), |
66 |
|
Remote :: jid:jid(), |
67 |
|
MsgId :: id()) -> ok. |
68 |
|
reset_unread_count(HostType, From, Remote, MsgId) -> |
69 |
51 |
InboxEntryKey = build_inbox_entry_key(From, Remote), |
70 |
51 |
ok = mod_inbox_backend:reset_unread(HostType, InboxEntryKey, MsgId). |
71 |
|
|
72 |
|
-spec write_to_sender_inbox(HostType :: mongooseim:host_type(), |
73 |
|
Sender :: jid:jid(), |
74 |
|
Receiver :: jid:jid(), |
75 |
|
Packet :: exml:element(), |
76 |
|
Acc :: mongoose_acc:t()) -> ok. |
77 |
|
write_to_sender_inbox(HostType, Sender, Receiver, Packet, Acc) -> |
78 |
113 |
MsgId = get_msg_id(Packet), |
79 |
113 |
Content = exml:to_binary(Packet), |
80 |
113 |
Timestamp = mongoose_acc:timestamp(Acc), |
81 |
|
%% no unread for a user because he writes new messages which assumes he read all previous messages. |
82 |
113 |
Count = 0, |
83 |
113 |
InboxEntryKey = build_inbox_entry_key(Sender, Receiver), |
84 |
113 |
mod_inbox_backend:set_inbox(HostType, InboxEntryKey, Content, Count, MsgId, Timestamp). |
85 |
|
|
86 |
|
-spec write_to_receiver_inbox(HostType :: mongooseim:host_type(), |
87 |
|
Sender :: jid:jid(), |
88 |
|
Receiver :: jid:jid(), |
89 |
|
Packet :: exml:element(), |
90 |
|
Acc :: mongoose_acc:t()) -> ok | {ok, integer()}. |
91 |
|
write_to_receiver_inbox(HostType, Sender, Receiver, Packet, Acc) -> |
92 |
180 |
MsgId = get_msg_id(Packet), |
93 |
180 |
Content = exml:to_binary(Packet), |
94 |
180 |
Timestamp = mongoose_acc:timestamp(Acc), |
95 |
180 |
InboxEntryKey = build_inbox_entry_key(Receiver, Sender), |
96 |
180 |
mod_inbox_backend:set_inbox_incr_unread(HostType, InboxEntryKey, |
97 |
|
Content, MsgId, Timestamp). |
98 |
|
|
99 |
|
-spec clear_inbox(HostType :: mongooseim:host_type(), |
100 |
|
User :: jid:user(), |
101 |
|
Server :: jid:server()) -> mod_inbox:write_res(). |
102 |
|
clear_inbox(HostType, User, Server) when is_binary(User) -> |
103 |
219 |
LUser = jid:nodeprep(User), |
104 |
219 |
LServer = jid:nameprep(Server), |
105 |
219 |
ok = mod_inbox_backend:clear_inbox(HostType, LUser, LServer). |
106 |
|
|
107 |
|
%%%%%%%%%%%%%%%%%%% |
108 |
|
%% Helpers |
109 |
|
|
110 |
|
-spec get_reset_markers(HostType :: mongooseim:host_type()) -> list(marker()). |
111 |
|
get_reset_markers(HostType) -> |
112 |
168 |
gen_mod:get_module_opt(HostType, mod_inbox, reset_markers, [<<"displayed">>]). |
113 |
|
|
114 |
|
-spec if_chat_marker_get_id(Packet :: exml:element(), |
115 |
|
Markers :: list(marker())) -> undefined | id(). |
116 |
|
if_chat_marker_get_id(Packet, Markers) when is_list(Markers) -> |
117 |
168 |
Ids = [if_chat_marker_get_id(Packet, M) || M <- Markers], |
118 |
168 |
Filtered = [El || El <- Ids, El /= undefined], |
119 |
168 |
case Filtered of |
120 |
|
[] -> |
121 |
117 |
undefined; |
122 |
|
[H | _] -> |
123 |
51 |
H |
124 |
|
end; |
125 |
|
if_chat_marker_get_id(Packet, Marker) -> |
126 |
168 |
case exml_query:paths(Packet, [{element, Marker}, {attr, <<"id">>}]) of |
127 |
|
[Id] -> |
128 |
51 |
Id; |
129 |
|
_ -> |
130 |
117 |
undefined |
131 |
|
end. |
132 |
|
|
133 |
|
|
134 |
|
-spec has_chat_marker(Packet :: exml:element()) -> boolean(). |
135 |
|
has_chat_marker(Packet) -> |
136 |
459 |
mongoose_chat_markers:has_chat_markers(Packet). |
137 |
|
|
138 |
|
-spec maybe_write_to_inbox(HostType, User, Remote, Packet, Acc, WriteF) -> |
139 |
|
mod_inbox:count_res() when |
140 |
|
HostType ::mongooseim:host_type(), |
141 |
|
User :: jid:jid(), |
142 |
|
Remote :: jid:jid(), |
143 |
|
Packet :: exml:element(), |
144 |
|
Acc :: mongoose_acc:t(), |
145 |
|
%% WriteF is write_to_receiver_inbox/5 or write_to_sender_inbox/5 |
146 |
|
WriteF :: inbox_fun(). |
147 |
|
maybe_write_to_inbox(HostType, User, Remote, Packet, Acc, WriteF) -> |
148 |
225 |
case has_chat_marker(Packet) of |
149 |
|
true -> |
150 |
34 |
ok; |
151 |
|
false -> |
152 |
191 |
Packet2 = fill_from_attr(Packet, User), |
153 |
191 |
WriteF(HostType, User, Remote, Packet2, Acc) |
154 |
|
end. |
155 |
|
|
156 |
|
-spec get_msg_id(Msg :: exml:element()) -> binary(). |
157 |
|
get_msg_id(#xmlel{name = <<"message">>} = Msg) -> |
158 |
293 |
exml_query:attr(Msg, <<"id">>, <<>>). |
159 |
|
|
160 |
|
-spec fill_from_attr(Msg :: exml:element(), From :: jid:jid()) -> exml:element(). |
161 |
|
fill_from_attr(Msg = #xmlel{attrs = Attrs}, From) -> |
162 |
191 |
case exml_query:attr(Msg, <<"from">>, undefined) of |
163 |
|
undefined -> |
164 |
169 |
FromBin = jid:to_binary(From), |
165 |
169 |
Msg#xmlel{attrs = [{<<"from">>, FromBin} | Attrs]}; |
166 |
|
_ -> |
167 |
22 |
Msg |
168 |
|
end. |
169 |
|
|
170 |
|
-spec wrapper_id() -> id(). |
171 |
|
wrapper_id() -> |
172 |
244 |
uuid:uuid_to_string(uuid:get_v4(), binary_standard). |
173 |
|
|
174 |
|
-spec get_option_write_aff_changes(HostType :: mongooseim:host_type()) -> boolean(). |
175 |
|
get_option_write_aff_changes(HostType) -> |
176 |
51 |
gen_mod:get_module_opt(HostType, mod_inbox, aff_changes, true). |
177 |
|
|
178 |
|
-spec get_option_remove_on_kicked(HostType :: mongooseim:host_type()) -> boolean(). |
179 |
|
get_option_remove_on_kicked(HostType) -> |
180 |
7 |
gen_mod:get_module_opt(HostType, mod_inbox, remove_on_kicked, true). |
181 |
|
|
182 |
|
extract_attr_jid(ResetStanza) -> |
183 |
46 |
case exml_query:attr(ResetStanza, <<"jid">>) of |
184 |
|
undefined -> |
185 |
3 |
{error, <<"jid-required">>}; |
186 |
|
Value -> |
187 |
43 |
case jid:from_binary(Value) of |
188 |
|
error -> |
189 |
2 |
{error, <<"invalid-jid">>}; |
190 |
41 |
JID -> JID |
191 |
|
end |
192 |
|
end. |
193 |
|
|
194 |
|
-spec maybe_binary_to_positive_integer(binary()) -> non_neg_integer() | {error, atom()}. |
195 |
|
maybe_binary_to_positive_integer(Bin) -> |
196 |
16 |
try erlang:binary_to_integer(Bin) of |
197 |
13 |
N when N >= 0 -> N; |
198 |
1 |
_ -> {error, non_positive_integer} |
199 |
2 |
catch error:badarg -> {error, 'NaN'} |
200 |
|
end. |
201 |
|
|
202 |
|
-spec maybe_muted_until(integer(), integer()) -> binary(). |
203 |
256 |
maybe_muted_until(0, _) -> <<"0">>; |
204 |
|
maybe_muted_until(MutedUntil, CurrentTS) -> |
205 |
18 |
case CurrentTS =< MutedUntil of |
206 |
17 |
true -> list_to_binary(calendar:system_time_to_rfc3339(MutedUntil, [{offset, "Z"}, {unit, microsecond}])); |
207 |
1 |
false -> <<"0">> |
208 |
|
end. |
209 |
|
|
210 |
|
-spec binary_to_bool(binary()) -> true | false | error. |
211 |
17 |
binary_to_bool(<<"true">>) -> true; |
212 |
260 |
binary_to_bool(<<"false">>) -> false; |
213 |
2 |
binary_to_bool(_) -> error. |
214 |
|
|
215 |
|
-spec bool_to_binary(boolean()) -> binary() | error. |
216 |
44 |
bool_to_binary(true) -> <<"true">>; |
217 |
260 |
bool_to_binary(false) -> <<"false">>; |
218 |
:-( |
bool_to_binary(_) -> error. |
219 |
|
|
220 |
|
build_inbox_entry_key(FromJid, ToJid) -> |
221 |
385 |
{LUser, LServer} = jid:to_lus(FromJid), |
222 |
385 |
ToBareJid = jid:to_binary(jid:to_lus(ToJid)), |
223 |
385 |
{LUser, LServer, ToBareJid}. |