mirror of
https://github.com/processone/ejabberd.git
synced 2024-12-20 17:27:00 +01:00
Support MUC hats (XEP-0317, conversejs/prosody compatible)
This commit is contained in:
parent
5462a26a0a
commit
5d0e599f17
@ -65,6 +65,7 @@
|
||||
captcha_whitelist = (?SETS):empty() :: gb_sets:set(),
|
||||
mam = false :: boolean(),
|
||||
pubsub = <<"">> :: binary(),
|
||||
enable_hats = false :: boolean(),
|
||||
lang = ejabberd_option:language() :: binary()
|
||||
}).
|
||||
|
||||
@ -124,6 +125,7 @@
|
||||
history = #lqueue{} :: lqueue(),
|
||||
subject = [] :: [text()],
|
||||
subject_author = <<"">> :: binary(),
|
||||
hats_users = #{} :: #{ljid() => #{binary() => binary()}},
|
||||
just_created = erlang:system_time(microsecond) :: true | integer(),
|
||||
activity = treap:empty() :: treap:treap(),
|
||||
room_shaper = none :: ejabberd_shaper:shaper(),
|
||||
|
@ -76,6 +76,12 @@
|
||||
|
||||
-define(DEFAULT_MAX_USERS_PRESENCE,1000).
|
||||
|
||||
-define(MUC_HAT_ADD_CMD, <<"http://prosody.im/protocol/hats#add">>).
|
||||
-define(MUC_HAT_REMOVE_CMD, <<"http://prosody.im/protocol/hats#remove">>).
|
||||
-define(MUC_HAT_LIST_CMD, <<"p1:hats#list">>).
|
||||
-define(MAX_HATS_USERS, 100).
|
||||
-define(MAX_HATS_PER_USER, 10).
|
||||
|
||||
%-define(DBGFSM, true).
|
||||
|
||||
-ifdef(DBGFSM).
|
||||
@ -446,6 +452,8 @@ normal_state({route, <<"">>,
|
||||
process_iq_mucsub(From, IQ, StateData);
|
||||
#xcaptcha{} ->
|
||||
process_iq_captcha(From, IQ, StateData);
|
||||
#adhoc_command{} ->
|
||||
process_iq_adhoc(From, IQ, StateData);
|
||||
_ ->
|
||||
Txt = ?T("The feature requested is not "
|
||||
"supported by the conference"),
|
||||
@ -1405,6 +1413,12 @@ is_occupant_or_admin(JID, StateData) ->
|
||||
_ -> false
|
||||
end.
|
||||
|
||||
%% Check if the user is an admin or owner.
|
||||
-spec is_admin(jid(), state()) -> boolean().
|
||||
is_admin(JID, StateData) ->
|
||||
FAffiliation = get_affiliation(JID, StateData),
|
||||
FAffiliation == admin orelse FAffiliation == owner.
|
||||
|
||||
%% Decide the fate of the message and its sender
|
||||
%% Returns: continue_delivery | forget_message | {expulse_sender, Reason}
|
||||
-spec decide_fate_message(message(), jid(), state()) ->
|
||||
@ -1935,7 +1949,7 @@ filter_presence(Presence) ->
|
||||
XMLNS = xmpp:get_ns(El),
|
||||
case catch binary:part(XMLNS, 0, size(?NS_MUC)) of
|
||||
?NS_MUC -> false;
|
||||
_ -> true
|
||||
_ -> XMLNS /= ?NS_HATS
|
||||
end
|
||||
end, xmpp:get_els(Presence)),
|
||||
xmpp:set_els(Presence, Els).
|
||||
@ -2485,9 +2499,10 @@ send_new_presence(NJID, Reason, IsInitialPresence, StateData, OldStateData) ->
|
||||
Pres = if Presence == undefined -> #presence{};
|
||||
true -> Presence
|
||||
end,
|
||||
Packet = xmpp:set_subtag(
|
||||
Pres, #muc_user{items = [Item],
|
||||
status_codes = StatusCodes}),
|
||||
Packet = xmpp:set_subtag(
|
||||
add_presence_hats(NJID, Pres, StateData),
|
||||
#muc_user{items = [Item],
|
||||
status_codes = StatusCodes}),
|
||||
send_wrapped(jid:replace_resource(StateData#state.jid, Nick),
|
||||
Info#user.jid, Packet, Node1, StateData),
|
||||
Type = xmpp:get_type(Packet),
|
||||
@ -2536,7 +2551,9 @@ send_existing_presences1(ToJID, StateData) ->
|
||||
false -> Item0
|
||||
end,
|
||||
Packet = xmpp:set_subtag(
|
||||
Presence, #muc_user{items = [Item]}),
|
||||
add_presence_hats(
|
||||
FromJID, Presence, StateData),
|
||||
#muc_user{items = [Item]}),
|
||||
send_wrapped(jid:replace_resource(StateData#state.jid, FromNick),
|
||||
RealToJID, Packet, ?NS_MUCSUB_NODES_PRESENCE, StateData)
|
||||
end
|
||||
@ -3579,7 +3596,8 @@ get_config(Lang, StateData, From) ->
|
||||
{allow_voice_requests, Config#config.allow_voice_requests},
|
||||
{allow_subscription, Config#config.allow_subscription},
|
||||
{voice_request_min_interval, Config#config.voice_request_min_interval},
|
||||
{pubsub, Config#config.pubsub}]
|
||||
{pubsub, Config#config.pubsub},
|
||||
{enable_hats, Config#config.enable_hats}]
|
||||
++
|
||||
case ejabberd_captcha:is_feature_available() of
|
||||
true ->
|
||||
@ -3667,6 +3685,7 @@ set_config(Opts, Config, ServerHost, Lang) ->
|
||||
({maxusers, V}, C) -> C#config{max_users = V};
|
||||
({enablelogging, V}, C) -> C#config{logging = V};
|
||||
({pubsub, V}, C) -> C#config{pubsub = V};
|
||||
({enable_hats, V}, C) -> C#config{enable_hats = V};
|
||||
({lang, L}, C) -> C#config{lang = L};
|
||||
({captcha_whitelist, Js}, C) ->
|
||||
LJIDs = [jid:tolower(J) || J <- Js],
|
||||
@ -3897,6 +3916,9 @@ set_opts([{Opt, Val} | Opts], StateData) ->
|
||||
allow_subscription ->
|
||||
StateData#state{config =
|
||||
(StateData#state.config)#config{allow_subscription = Val}};
|
||||
enable_hats ->
|
||||
StateData#state{config =
|
||||
(StateData#state.config)#config{enable_hats = Val}};
|
||||
lang ->
|
||||
StateData#state{config =
|
||||
(StateData#state.config)#config{lang = Val}};
|
||||
@ -3927,6 +3949,11 @@ set_opts([{Opt, Val} | Opts], StateData) ->
|
||||
end,
|
||||
StateData#state{subject = Subj};
|
||||
subject_author -> StateData#state{subject_author = Val};
|
||||
hats_users ->
|
||||
Hats = maps:from_list(
|
||||
lists:map(fun({U, H}) -> {U, maps:from_list(H)} end,
|
||||
Val)),
|
||||
StateData#state{hats_users = Hats};
|
||||
_ -> StateData
|
||||
end,
|
||||
set_opts(Opts, NSD).
|
||||
@ -3983,6 +4010,7 @@ make_opts(StateData) ->
|
||||
?MAKE_CONFIG_OPT(#config.vcard),
|
||||
?MAKE_CONFIG_OPT(#config.vcard_xupdate),
|
||||
?MAKE_CONFIG_OPT(#config.pubsub),
|
||||
?MAKE_CONFIG_OPT(#config.enable_hats),
|
||||
?MAKE_CONFIG_OPT(#config.lang),
|
||||
{captcha_whitelist,
|
||||
(?SETS):to_list((StateData#state.config)#config.captcha_whitelist)},
|
||||
@ -3990,6 +4018,9 @@ make_opts(StateData) ->
|
||||
maps:to_list(StateData#state.affiliations)},
|
||||
{subject, StateData#state.subject},
|
||||
{subject_author, StateData#state.subject_author},
|
||||
{hats_users,
|
||||
lists:map(fun({U, H}) -> {U, maps:to_list(H)} end,
|
||||
maps:to_list(StateData#state.hats_users))},
|
||||
{hibernation_time, erlang:system_time(microsecond)},
|
||||
{subscribers, Subscribers}].
|
||||
|
||||
@ -4080,6 +4111,7 @@ maybe_forget_room(StateData) ->
|
||||
make_disco_info(_From, StateData) ->
|
||||
Config = StateData#state.config,
|
||||
Feats = [?NS_VCARD, ?NS_MUC, ?NS_DISCO_INFO, ?NS_DISCO_ITEMS,
|
||||
?NS_COMMANDS,
|
||||
?CONFIG_OPT_TO_FEATURE((Config#config.public),
|
||||
<<"muc_public">>, <<"muc_hidden">>),
|
||||
?CONFIG_OPT_TO_FEATURE((Config#config.persistent),
|
||||
@ -4119,6 +4151,77 @@ process_iq_disco_info(From, #iq{type = get, lang = Lang,
|
||||
DiscoInfo = make_disco_info(From, StateData),
|
||||
Extras = iq_disco_info_extras(Lang, StateData, false),
|
||||
{result, DiscoInfo#disco_info{xdata = [Extras]}};
|
||||
process_iq_disco_info(From, #iq{type = get, lang = Lang,
|
||||
sub_els = [#disco_info{node = ?NS_COMMANDS}]},
|
||||
StateData) ->
|
||||
case (StateData#state.config)#config.enable_hats andalso
|
||||
is_admin(From, StateData)
|
||||
of
|
||||
true ->
|
||||
{result,
|
||||
#disco_info{
|
||||
identities = [#identity{category = <<"automation">>,
|
||||
type = <<"command-list">>,
|
||||
name = translate:translate(
|
||||
Lang, ?T("Commands"))}]}};
|
||||
false ->
|
||||
Txt = ?T("Node not found"),
|
||||
{error, xmpp:err_item_not_found(Txt, Lang)}
|
||||
end;
|
||||
process_iq_disco_info(From, #iq{type = get, lang = Lang,
|
||||
sub_els = [#disco_info{node = ?MUC_HAT_ADD_CMD}]},
|
||||
StateData) ->
|
||||
case (StateData#state.config)#config.enable_hats andalso
|
||||
is_admin(From, StateData)
|
||||
of
|
||||
true ->
|
||||
{result,
|
||||
#disco_info{
|
||||
identities = [#identity{category = <<"automation">>,
|
||||
type = <<"command-node">>,
|
||||
name = translate:translate(
|
||||
Lang, ?T("Add a hat to a user"))}],
|
||||
features = [?NS_COMMANDS]}};
|
||||
false ->
|
||||
Txt = ?T("Node not found"),
|
||||
{error, xmpp:err_item_not_found(Txt, Lang)}
|
||||
end;
|
||||
process_iq_disco_info(From, #iq{type = get, lang = Lang,
|
||||
sub_els = [#disco_info{node = ?MUC_HAT_REMOVE_CMD}]},
|
||||
StateData) ->
|
||||
case (StateData#state.config)#config.enable_hats andalso
|
||||
is_admin(From, StateData)
|
||||
of
|
||||
true ->
|
||||
{result,
|
||||
#disco_info{
|
||||
identities = [#identity{category = <<"automation">>,
|
||||
type = <<"command-node">>,
|
||||
name = translate:translate(
|
||||
Lang, ?T("Remove a hat from a user"))}],
|
||||
features = [?NS_COMMANDS]}};
|
||||
false ->
|
||||
Txt = ?T("Node not found"),
|
||||
{error, xmpp:err_item_not_found(Txt, Lang)}
|
||||
end;
|
||||
process_iq_disco_info(From, #iq{type = get, lang = Lang,
|
||||
sub_els = [#disco_info{node = ?MUC_HAT_LIST_CMD}]},
|
||||
StateData) ->
|
||||
case (StateData#state.config)#config.enable_hats andalso
|
||||
is_admin(From, StateData)
|
||||
of
|
||||
true ->
|
||||
{result,
|
||||
#disco_info{
|
||||
identities = [#identity{category = <<"automation">>,
|
||||
type = <<"command-node">>,
|
||||
name = translate:translate(
|
||||
Lang, ?T("List users with hats"))}],
|
||||
features = [?NS_COMMANDS]}};
|
||||
false ->
|
||||
Txt = ?T("Node not found"),
|
||||
{error, xmpp:err_item_not_found(Txt, Lang)}
|
||||
end;
|
||||
process_iq_disco_info(From, #iq{type = get, lang = Lang,
|
||||
sub_els = [#disco_info{node = Node}]},
|
||||
StateData) ->
|
||||
@ -4199,6 +4302,46 @@ process_iq_disco_items(From, #iq{type = get, sub_els = [#disco_items{node = <<>>
|
||||
{result, #disco_items{}}
|
||||
end
|
||||
end;
|
||||
process_iq_disco_items(From, #iq{type = get, lang = Lang,
|
||||
sub_els = [#disco_items{node = ?NS_COMMANDS}]},
|
||||
StateData) ->
|
||||
case (StateData#state.config)#config.enable_hats andalso
|
||||
is_admin(From, StateData)
|
||||
of
|
||||
true ->
|
||||
{result,
|
||||
#disco_items{
|
||||
items = [#disco_item{jid = StateData#state.jid,
|
||||
node = ?MUC_HAT_ADD_CMD,
|
||||
name = translate:translate(
|
||||
Lang, ?T("Add a hat to a user"))},
|
||||
#disco_item{jid = StateData#state.jid,
|
||||
node = ?MUC_HAT_REMOVE_CMD,
|
||||
name = translate:translate(
|
||||
Lang, ?T("Remove a hat from a user"))},
|
||||
#disco_item{jid = StateData#state.jid,
|
||||
node = ?MUC_HAT_LIST_CMD,
|
||||
name = translate:translate(
|
||||
Lang, ?T("List users with hats"))}]}};
|
||||
false ->
|
||||
Txt = ?T("Node not found"),
|
||||
{error, xmpp:err_item_not_found(Txt, Lang)}
|
||||
end;
|
||||
process_iq_disco_items(From, #iq{type = get, lang = Lang,
|
||||
sub_els = [#disco_items{node = Node}]},
|
||||
StateData)
|
||||
when Node == ?MUC_HAT_ADD_CMD;
|
||||
Node == ?MUC_HAT_REMOVE_CMD;
|
||||
Node == ?MUC_HAT_LIST_CMD ->
|
||||
case (StateData#state.config)#config.enable_hats andalso
|
||||
is_admin(From, StateData)
|
||||
of
|
||||
true ->
|
||||
{result, #disco_items{}};
|
||||
false ->
|
||||
Txt = ?T("Node not found"),
|
||||
{error, xmpp:err_item_not_found(Txt, Lang)}
|
||||
end;
|
||||
process_iq_disco_items(_From, #iq{lang = Lang}, _StateData) ->
|
||||
Txt = ?T("Node not found"),
|
||||
{error, xmpp:err_item_not_found(Txt, Lang)}.
|
||||
@ -4441,6 +4584,271 @@ get_mucroom_disco_items(StateData) ->
|
||||
end, [], StateData#state.nicks),
|
||||
#disco_items{items = Items}.
|
||||
|
||||
-spec process_iq_adhoc(jid(), iq(), state()) ->
|
||||
{result, adhoc_command()} |
|
||||
{result, adhoc_command(), state()} |
|
||||
{error, stanza_error()}.
|
||||
process_iq_adhoc(_From, #iq{type = get}, _StateData) ->
|
||||
{error, xmpp:err_bad_request()};
|
||||
process_iq_adhoc(From, #iq{type = set, lang = Lang1,
|
||||
sub_els = [#adhoc_command{} = Request]},
|
||||
StateData) ->
|
||||
% Ad-Hoc Commands are used only for Hats here
|
||||
case (StateData#state.config)#config.enable_hats andalso
|
||||
is_admin(From, StateData)
|
||||
of
|
||||
true ->
|
||||
#adhoc_command{lang = Lang2, node = Node,
|
||||
action = Action, xdata = XData} = Request,
|
||||
Lang = case Lang2 of
|
||||
<<"">> -> Lang1;
|
||||
_ -> Lang2
|
||||
end,
|
||||
case {Node, Action} of
|
||||
{_, cancel} ->
|
||||
{result,
|
||||
xmpp_util:make_adhoc_response(
|
||||
Request,
|
||||
#adhoc_command{status = canceled, lang = Lang,
|
||||
node = Node})};
|
||||
{?MUC_HAT_ADD_CMD, execute} ->
|
||||
Form =
|
||||
#xdata{
|
||||
title = translate:translate(
|
||||
Lang, ?T("Add a hat to a user")),
|
||||
type = form,
|
||||
fields =
|
||||
[#xdata_field{
|
||||
type = 'jid-single',
|
||||
label = translate:translate(Lang, ?T("Jabber ID")),
|
||||
required = true,
|
||||
var = <<"jid">>},
|
||||
#xdata_field{
|
||||
type = 'text-single',
|
||||
label = translate:translate(Lang, ?T("Hat title")),
|
||||
var = <<"hat_title">>},
|
||||
#xdata_field{
|
||||
type = 'text-single',
|
||||
label = translate:translate(Lang, ?T("Hat URI")),
|
||||
required = true,
|
||||
var = <<"hat_uri">>}
|
||||
]},
|
||||
{result,
|
||||
xmpp_util:make_adhoc_response(
|
||||
Request,
|
||||
#adhoc_command{
|
||||
status = executing,
|
||||
xdata = Form})};
|
||||
{?MUC_HAT_ADD_CMD, complete} when XData /= undefined ->
|
||||
JID = try
|
||||
jid:decode(hd(xmpp_util:get_xdata_values(
|
||||
<<"jid">>, XData)))
|
||||
catch _:_ -> error
|
||||
end,
|
||||
URI = try
|
||||
hd(xmpp_util:get_xdata_values(
|
||||
<<"hat_uri">>, XData))
|
||||
catch _:_ -> error
|
||||
end,
|
||||
Title = case xmpp_util:get_xdata_values(
|
||||
<<"hat_title">>, XData) of
|
||||
[] -> <<"">>;
|
||||
[T] -> T
|
||||
end,
|
||||
if
|
||||
(JID /= error) and (URI /= error) ->
|
||||
case add_hat(JID, URI, Title, StateData) of
|
||||
{ok, NewStateData} ->
|
||||
store_room(NewStateData),
|
||||
send_update_presence(
|
||||
JID, NewStateData, StateData),
|
||||
{result,
|
||||
xmpp_util:make_adhoc_response(
|
||||
Request,
|
||||
#adhoc_command{status = completed}),
|
||||
NewStateData};
|
||||
{error, size_limit} ->
|
||||
Txt = ?T("Hats limit exceeded"),
|
||||
{error, xmpp:err_not_allowed(Txt, Lang)}
|
||||
end;
|
||||
true ->
|
||||
{error, xmpp:err_bad_request()}
|
||||
end;
|
||||
{?MUC_HAT_ADD_CMD, complete} ->
|
||||
{error, xmpp:err_bad_request()};
|
||||
{?MUC_HAT_ADD_CMD, _} ->
|
||||
Txt = ?T("Incorrect value of 'action' attribute"),
|
||||
{error, xmpp:err_bad_request(Txt, Lang)};
|
||||
{?MUC_HAT_REMOVE_CMD, execute} ->
|
||||
Form =
|
||||
#xdata{
|
||||
title = translate:translate(
|
||||
Lang, ?T("Remove a hat from a user")),
|
||||
type = form,
|
||||
fields =
|
||||
[#xdata_field{
|
||||
type = 'jid-single',
|
||||
label = translate:translate(Lang, ?T("Jabber ID")),
|
||||
required = true,
|
||||
var = <<"jid">>},
|
||||
#xdata_field{
|
||||
type = 'text-single',
|
||||
label = translate:translate(Lang, ?T("Hat URI")),
|
||||
required = true,
|
||||
var = <<"hat_uri">>}
|
||||
]},
|
||||
{result,
|
||||
xmpp_util:make_adhoc_response(
|
||||
Request,
|
||||
#adhoc_command{
|
||||
status = executing,
|
||||
xdata = Form})};
|
||||
{?MUC_HAT_REMOVE_CMD, complete} when XData /= undefined ->
|
||||
JID = try
|
||||
jid:decode(hd(xmpp_util:get_xdata_values(
|
||||
<<"jid">>, XData)))
|
||||
catch _:_ -> error
|
||||
end,
|
||||
URI = try
|
||||
hd(xmpp_util:get_xdata_values(
|
||||
<<"hat_uri">>, XData))
|
||||
catch _:_ -> error
|
||||
end,
|
||||
if
|
||||
(JID /= error) and (URI /= error) ->
|
||||
NewStateData = del_hat(JID, URI, StateData),
|
||||
store_room(NewStateData),
|
||||
send_update_presence(
|
||||
JID, NewStateData, StateData),
|
||||
{result,
|
||||
xmpp_util:make_adhoc_response(
|
||||
Request,
|
||||
#adhoc_command{status = completed}),
|
||||
NewStateData};
|
||||
true ->
|
||||
{error, xmpp:err_bad_request()}
|
||||
end;
|
||||
{?MUC_HAT_REMOVE_CMD, complete} ->
|
||||
{error, xmpp:err_bad_request()};
|
||||
{?MUC_HAT_REMOVE_CMD, _} ->
|
||||
Txt = ?T("Incorrect value of 'action' attribute"),
|
||||
{error, xmpp:err_bad_request(Txt, Lang)};
|
||||
{?MUC_HAT_LIST_CMD, execute} ->
|
||||
Hats = get_all_hats(StateData),
|
||||
Items =
|
||||
lists:map(
|
||||
fun({JID, URI, Title}) ->
|
||||
[#xdata_field{
|
||||
var = <<"jid">>,
|
||||
values = [jid:encode(JID)]},
|
||||
#xdata_field{
|
||||
var = <<"hat_title">>,
|
||||
values = [URI]},
|
||||
#xdata_field{
|
||||
var = <<"hat_uri">>,
|
||||
values = [Title]}]
|
||||
end, Hats),
|
||||
Form =
|
||||
#xdata{
|
||||
title = translate:translate(
|
||||
Lang, ?T("List of users with hats")),
|
||||
type = result,
|
||||
reported =
|
||||
[#xdata_field{
|
||||
label = translate:translate(Lang, ?T("Jabber ID")),
|
||||
var = <<"jid">>},
|
||||
#xdata_field{
|
||||
label = translate:translate(Lang, ?T("Hat title")),
|
||||
var = <<"hat_title">>},
|
||||
#xdata_field{
|
||||
label = translate:translate(Lang, ?T("Hat URI")),
|
||||
var = <<"hat_uri">>}],
|
||||
items = Items},
|
||||
{result,
|
||||
xmpp_util:make_adhoc_response(
|
||||
Request,
|
||||
#adhoc_command{
|
||||
status = completed,
|
||||
xdata = Form})};
|
||||
{?MUC_HAT_LIST_CMD, _} ->
|
||||
Txt = ?T("Incorrect value of 'action' attribute"),
|
||||
{error, xmpp:err_bad_request(Txt, Lang)};
|
||||
_ ->
|
||||
{error, xmpp:err_item_not_found()}
|
||||
end;
|
||||
_ ->
|
||||
{error, xmpp:err_forbidden()}
|
||||
end.
|
||||
|
||||
-spec add_hat(jid(), binary(), binary(), state()) ->
|
||||
{ok, state()} | {error, size_limit}.
|
||||
add_hat(JID, URI, Title, StateData) ->
|
||||
Hats = StateData#state.hats_users,
|
||||
LJID = jid:remove_resource(jid:tolower(JID)),
|
||||
UserHats = maps:get(LJID, Hats, #{}),
|
||||
UserHats2 = maps:put(URI, Title, UserHats),
|
||||
USize = maps:size(UserHats2),
|
||||
if
|
||||
USize =< ?MAX_HATS_PER_USER ->
|
||||
Hats2 = maps:put(LJID, UserHats2, Hats),
|
||||
Size = maps:size(Hats2),
|
||||
if
|
||||
Size =< ?MAX_HATS_USERS ->
|
||||
{ok, StateData#state{hats_users = Hats2}};
|
||||
true ->
|
||||
{error, size_limit}
|
||||
end;
|
||||
true ->
|
||||
{error, size_limit}
|
||||
end.
|
||||
|
||||
-spec del_hat(jid(), binary(), state()) -> state().
|
||||
del_hat(JID, URI, StateData) ->
|
||||
Hats = StateData#state.hats_users,
|
||||
LJID = jid:remove_resource(jid:tolower(JID)),
|
||||
UserHats = maps:get(LJID, Hats, #{}),
|
||||
UserHats2 = maps:remove(URI, UserHats),
|
||||
Hats2 =
|
||||
case maps:size(UserHats2) of
|
||||
0 ->
|
||||
maps:remove(LJID, Hats);
|
||||
_ ->
|
||||
maps:put(LJID, UserHats2, Hats)
|
||||
end,
|
||||
StateData#state{hats_users = Hats2}.
|
||||
|
||||
-spec get_all_hats(state()) -> list({jid(), binary(), binary()}).
|
||||
get_all_hats(StateData) ->
|
||||
lists:flatmap(
|
||||
fun({LJID, H}) ->
|
||||
JID = jid:make(LJID),
|
||||
lists:map(fun({URI, Title}) -> {JID, URI, Title} end,
|
||||
maps:to_list(H))
|
||||
end,
|
||||
maps:to_list(StateData#state.hats_users)).
|
||||
|
||||
-spec add_presence_hats(jid(), #presence{}, state()) -> #presence{}.
|
||||
add_presence_hats(JID, Pres, StateData) ->
|
||||
case (StateData#state.config)#config.enable_hats of
|
||||
true ->
|
||||
Hats = StateData#state.hats_users,
|
||||
LJID = jid:remove_resource(jid:tolower(JID)),
|
||||
UserHats = maps:get(LJID, Hats, #{}),
|
||||
case maps:size(UserHats) of
|
||||
0 -> Pres;
|
||||
_ ->
|
||||
Items =
|
||||
lists:map(fun({URI, Title}) ->
|
||||
#muc_hat{uri = URI, title = Title}
|
||||
end,
|
||||
maps:to_list(UserHats)),
|
||||
xmpp:set_subtag(Pres,
|
||||
#muc_hats{hats = Items})
|
||||
end;
|
||||
false ->
|
||||
Pres
|
||||
end.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
% Voice request support
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user