mirror of
https://github.com/processone/ejabberd
synced 2025-10-02 17:29:27 +02:00
Update implementation of XEP-0317 Hats to version 0.3.1 (#4380)
This commit is contained in:
parent
c3a24ffdf8
commit
2b7285e0b2
9 changed files with 511 additions and 302 deletions
|
@ -578,10 +578,10 @@
|
|||
<implements>
|
||||
<xmpp:SupportedXep>
|
||||
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0317.html"/>
|
||||
<xmpp:version>0.2.0</xmpp:version>
|
||||
<xmpp:version>0.3.1</xmpp:version>
|
||||
<xmpp:since>21.12</xmpp:since>
|
||||
<xmpp:status>complete</xmpp:status>
|
||||
<xmpp:note>mod_muc_room, 0.2.0 since 25.03</xmpp:note>
|
||||
<xmpp:note>mod_muc_room, 0.3.1 since 25.xx</xmpp:note>
|
||||
</xmpp:SupportedXep>
|
||||
</implements>
|
||||
<implements>
|
||||
|
|
|
@ -126,7 +126,8 @@
|
|||
history = #lqueue{} :: lqueue(),
|
||||
subject = [] :: [text()],
|
||||
subject_author = {<<"">>, #jid{}} :: {binary(), jid()},
|
||||
hats_users = #{} :: #{ljid() => #{binary() => binary()}},
|
||||
hats_defs = #{} :: #{binary() => {binary(), binary()}},
|
||||
hats_users = #{} :: #{ljid() => [binary()]},
|
||||
just_created = erlang:system_time(microsecond) :: true | integer(),
|
||||
activity = treap:empty() :: treap:treap(),
|
||||
room_shaper = none :: ejabberd_shaper:shaper(),
|
||||
|
|
2
mix.exs
2
mix.exs
|
@ -129,7 +129,7 @@ defmodule Ejabberd.MixProject do
|
|||
{:p1_utils, "~> 1.0"},
|
||||
{:pkix, "~> 1.0"},
|
||||
{:stringprep, ">= 1.0.26"},
|
||||
{:xmpp, ">= 1.11.1"},
|
||||
{:xmpp, git: "https://github.com/processone/xmpp", ref: "e9d901ea84fd3910ad32b715853397eb1155b41c", override: true},
|
||||
{:yconf, git: "https://github.com/processone/yconf", ref: "95692795a8a8d950ba560e5b07e6b80660557259", override: true}]
|
||||
++ cond_deps()
|
||||
end
|
||||
|
|
2
mix.lock
2
mix.lock
|
@ -34,6 +34,6 @@
|
|||
"stringprep": {:hex, :stringprep, "1.0.33", "22f42866b4f6f3c238ea2b9cb6241791184ddedbab55e94a025511f46325f3ca", [:rebar3], [{:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "96f8b30bc50887f605b33b46bca1d248c19a879319b8c482790e3b4da5da98c0"},
|
||||
"stun": {:hex, :stun, "1.2.21", "735855314ad22cb7816b88597d2f5ca22e24aa5e4d6010a0ef3affb33ceed6a5", [:rebar3], [{:fast_tls, "1.1.25", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "3d7fe8efb9d05b240a6aa9a6bf8b8b7bff2d802895d170443c588987dc1e12d9"},
|
||||
"unicode_util_compat": {:hex, :unicode_util_compat, "0.7.1", "a48703a25c170eedadca83b11e88985af08d35f37c6f664d6dcfb106a97782fc", [:rebar3], [], "hexpm", "b3a917854ce3ae233619744ad1e0102e05673136776fb2fa76234f3e03b23642"},
|
||||
"xmpp": {:hex, :xmpp, "1.11.1", "60181e7d3e8e48aa3b23b2792075cda37e2e507ec152490b866e61e5320cb1da", [:rebar3], [{:ezlib, "~> 1.0.12", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "~> 1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "~> 1.1.51", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "~> 1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "~> 1.0.29", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "a5c933df904ab3cec15425da334e410ce84ec3ae7b81efe069e5db368a7b3716"},
|
||||
"xmpp": {:git, "https://github.com/processone/xmpp", "e9d901ea84fd3910ad32b715853397eb1155b41c", [ref: "e9d901ea84fd3910ad32b715853397eb1155b41c"]},
|
||||
"yconf": {:git, "https://github.com/processone/yconf", "95692795a8a8d950ba560e5b07e6b80660557259", [ref: "95692795a8a8d950ba560e5b07e6b80660557259"]},
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@
|
|||
{stringprep, "~> 1.0.33", {git, "https://github.com/processone/stringprep", {tag, "1.0.33"}}},
|
||||
{if_var_true, stun,
|
||||
{stun, "~> 1.2.21", {git, "https://github.com/processone/stun", {tag, "1.2.21"}}}},
|
||||
{xmpp, "~> 1.11.1", {git, "https://github.com/processone/xmpp", {tag, "1.11.1"}}},
|
||||
{xmpp, ".*", {git, "https://github.com/processone/xmpp", "e9d901ea84fd3910ad32b715853397eb1155b41c"}},
|
||||
{yconf, ".*", {git, "https://github.com/processone/yconf", "95692795a8a8d950ba560e5b07e6b80660557259"}}
|
||||
]}.
|
||||
|
||||
|
|
11
rebar.lock
11
rebar.lock
|
@ -24,7 +24,10 @@
|
|||
{<<"stringprep">>,{pkg,<<"stringprep">>,<<"1.0.33">>},0},
|
||||
{<<"stun">>,{pkg,<<"stun">>,<<"1.2.21">>},0},
|
||||
{<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.1">>},1},
|
||||
{<<"xmpp">>,{pkg,<<"xmpp">>,<<"1.11.1">>},0},
|
||||
{<<"xmpp">>,
|
||||
{git,"https://github.com/processone/xmpp",
|
||||
{ref,"e9d901ea84fd3910ad32b715853397eb1155b41c"}},
|
||||
0},
|
||||
{<<"yconf">>,
|
||||
{git,"https://github.com/processone/yconf",
|
||||
{ref,"95692795a8a8d950ba560e5b07e6b80660557259"}},
|
||||
|
@ -55,8 +58,7 @@
|
|||
{<<"sqlite3">>, <<"E819DEFD280145C328457D7AF897D2E45E8E5270E18812EE30B607C99CDD21AF">>},
|
||||
{<<"stringprep">>, <<"22F42866B4F6F3C238EA2B9CB6241791184DDEDBAB55E94A025511F46325F3CA">>},
|
||||
{<<"stun">>, <<"735855314AD22CB7816B88597D2F5CA22E24AA5E4D6010A0EF3AFFB33CEED6A5">>},
|
||||
{<<"unicode_util_compat">>, <<"A48703A25C170EEDADCA83B11E88985AF08D35F37C6F664D6DCFB106A97782FC">>},
|
||||
{<<"xmpp">>, <<"60181E7D3E8E48AA3B23B2792075CDA37E2E507EC152490B866E61E5320CB1DA">>}]},
|
||||
{<<"unicode_util_compat">>, <<"A48703A25C170EEDADCA83B11E88985AF08D35F37C6F664D6DCFB106A97782FC">>}]},
|
||||
{pkg_hash_ext,[
|
||||
{<<"base64url">>, <<"F9B3ADD4731A02A9B0410398B475B33E7566A695365237A6BDEE1BB447719F5C">>},
|
||||
{<<"cache_tab">>, <<"4258009EB050B22AABE0C848E230BBA58401A6895C58C2FF74DFB635E3C35900">>},
|
||||
|
@ -82,6 +84,5 @@
|
|||
{<<"sqlite3">>, <<"3C0BA4E13322C2AD49DE4E2DDD28311366ADDE54BEAE8DBA9D9E3888F69D2857">>},
|
||||
{<<"stringprep">>, <<"96F8B30BC50887F605B33B46BCA1D248C19A879319B8C482790E3B4DA5DA98C0">>},
|
||||
{<<"stun">>, <<"3D7FE8EFB9D05B240A6AA9A6BF8B8B7BFF2D802895D170443C588987DC1E12D9">>},
|
||||
{<<"unicode_util_compat">>, <<"B3A917854CE3AE233619744AD1E0102E05673136776FB2FA76234F3E03B23642">>},
|
||||
{<<"xmpp">>, <<"A5C933DF904AB3CEC15425DA334E410CE84EC3AE7B81EFE069E5DB368A7B3716">>}]}
|
||||
{<<"unicode_util_compat">>, <<"B3A917854CE3AE233619744AD1E0102E05673136776FB2FA76234F3E03B23642">>}]}
|
||||
].
|
||||
|
|
|
@ -1766,7 +1766,7 @@ mod_doc() ->
|
|||
"The default value is an empty string.")}},
|
||||
{enable_hats,
|
||||
#{value => "true | false",
|
||||
note => "improved in 25.03",
|
||||
note => "improved in 25.xx",
|
||||
desc =>
|
||||
?T("Allow extended roles as defined in XEP-0317 Hats. "
|
||||
"Check the _`../../tutorials/muc-hats.md|MUC Hats`_ tutorial. "
|
||||
|
|
|
@ -429,11 +429,15 @@ need_transform({muc_room, {N, H}, _})
|
|||
?INFO_MSG("Mnesia table 'muc_room' will be converted to binary", []),
|
||||
true;
|
||||
need_transform({muc_room, {_N, _H}, Opts}) ->
|
||||
case lists:keymember(allow_private_messages, 1, Opts) of
|
||||
true ->
|
||||
case {lists:keymember(allow_private_messages, 1, Opts),
|
||||
lists:keymember(hats_defs, 1, Opts)} of
|
||||
{true, _} ->
|
||||
?INFO_MSG("Mnesia table 'muc_room' will be converted to allowpm", []),
|
||||
true;
|
||||
false ->
|
||||
{false, false} ->
|
||||
?INFO_MSG("Mnesia table 'muc_room' will be converted to Hats 0.3.0", []),
|
||||
true;
|
||||
{false, true} ->
|
||||
false
|
||||
end;
|
||||
|
||||
|
@ -459,7 +463,33 @@ transform(#muc_room{opts = Opts} = R) ->
|
|||
_ ->
|
||||
Opts
|
||||
end,
|
||||
R#muc_room{opts = Opts2};
|
||||
Opts4 =
|
||||
case lists:keyfind(hats_defs, 1, Opts2) of
|
||||
false ->
|
||||
{hats_users, HatsUsers} = lists:keyfind(hats_users, 1, Opts2),
|
||||
{HatsDefs, HatsUsers2} =
|
||||
lists:foldl(fun({Jid, UriTitleList}, {Defs, Assigns}) ->
|
||||
Defs2 =
|
||||
lists:foldl(fun({Uri, Title}, AccDef) ->
|
||||
maps:put(Uri, {Title, <<"">>}, AccDef)
|
||||
end,
|
||||
Defs,
|
||||
UriTitleList),
|
||||
Assigns2 =
|
||||
maps:put(Jid,
|
||||
[Uri || {Uri, _Title} <- UriTitleList],
|
||||
Assigns),
|
||||
{Defs2, Assigns2}
|
||||
end,
|
||||
{maps:new(), maps:new()},
|
||||
HatsUsers),
|
||||
Opts3 =
|
||||
lists:keyreplace(hats_users, 1, Opts2, {hats_users, maps:to_list(HatsUsers2)}),
|
||||
[{hats_defs, maps:to_list(HatsDefs)} | Opts3];
|
||||
{_, _} ->
|
||||
Opts2
|
||||
end,
|
||||
R#muc_room{opts = Opts4};
|
||||
transform(#muc_registered{us_host = {{U, S}, H}, nick = Nick} = R) ->
|
||||
R#muc_registered{us_host = {{iolist_to_binary(U), iolist_to_binary(S)},
|
||||
iolist_to_binary(H)},
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-protocol({xep, 317, '0.2.0', '21.12', "complete", "0.2.0 since 25.03"}).
|
||||
-protocol({xep, 317, '0.3.1', '21.12', "complete", "0.3.1 since 25.xx"}).
|
||||
-protocol({xep, 410, '1.1.0', '18.12', "complete", ""}).
|
||||
|
||||
-behaviour(p1_fsm).
|
||||
|
@ -79,9 +79,14 @@
|
|||
-define(MAX_USERS_DEFAULT_LIST,
|
||||
[5, 10, 20, 30, 50, 100, 200, 500, 1000, 2000, 5000]).
|
||||
|
||||
-define(MUC_HAT_ADD_CMD, <<"urn:xmpp:hats:commands:don">>).
|
||||
-define(MUC_HAT_REMOVE_CMD, <<"urn:xmpp:hats:commands:doff">>).
|
||||
-define(MUC_HAT_LIST_CMD, <<"urn:xmpp:hats:commands:dlist">>).
|
||||
-define(MUC_HAT_CREATE_CMD, <<"urn:xmpp:hats:commands:create">>).
|
||||
-define(MUC_HAT_DESTROY_CMD, <<"urn:xmpp:hats:commands:destroy">>).
|
||||
-define(MUC_HAT_LISTHATS_CMD, <<"urn:xmpp:hats:commands:list">>).
|
||||
|
||||
-define(MUC_HAT_ASSIGN_CMD, <<"urn:xmpp:hats:commands:assign">>).
|
||||
-define(MUC_HAT_UNASSIGN_CMD, <<"urn:xmpp:hats:commands:unassign">>).
|
||||
-define(MUC_HAT_LISTUSERS_CMD,<<"urn:xmpp:hats:commands:list-assigned">>).
|
||||
|
||||
-define(MAX_HATS_USERS, 100).
|
||||
-define(MAX_HATS_PER_USER, 10).
|
||||
-define(CLEAN_ROOM_TIMEOUT, 30000).
|
||||
|
@ -4258,11 +4263,10 @@ set_opts2([{Opt, Val} | Opts], StateData) ->
|
|||
StateData#state{subject_author = Val};
|
||||
subject_author when is_binary(Val) -> % ejabberd 23.04 or older
|
||||
StateData#state{subject_author = {Val, #jid{}}};
|
||||
hats_defs ->
|
||||
StateData#state{hats_defs = maps:from_list(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#state{hats_users = maps:from_list(Val)};
|
||||
hibernation_time -> StateData;
|
||||
Other ->
|
||||
?INFO_MSG("Unknown MUC room option, will be discarded: ~p", [Other]),
|
||||
|
@ -4343,9 +4347,8 @@ make_opts(StateData, Hibernation) ->
|
|||
{roles, maps:to_list(StateData#state.roles)},
|
||||
{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))},
|
||||
{hats_defs, maps:to_list(StateData#state.hats_defs)},
|
||||
{hats_users, maps:to_list(StateData#state.hats_users)},
|
||||
{hibernation_time, if Hibernation -> erlang:system_time(microsecond); true -> undefined end},
|
||||
{subscribers, Subscribers}].
|
||||
|
||||
|
@ -4461,6 +4464,10 @@ make_disco_info(From, StateData) ->
|
|||
true -> [?NS_MUCSUB];
|
||||
false -> []
|
||||
end
|
||||
++ case Config#config.enable_hats of
|
||||
true -> [?NS_HATS];
|
||||
false -> []
|
||||
end
|
||||
++ case gen_mod:is_loaded(StateData#state.server_host, mod_muc_occupantid) of
|
||||
true ->
|
||||
[?NS_OCCUPANT_ID];
|
||||
|
@ -4490,6 +4497,7 @@ 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) ->
|
||||
|
@ -4507,9 +4515,25 @@ process_iq_disco_info(From, #iq{type = get, lang = Lang,
|
|||
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) ->
|
||||
sub_els = [#disco_info{node = Node}]},
|
||||
StateData)
|
||||
when Node == ?MUC_HAT_CREATE_CMD;
|
||||
Node == ?MUC_HAT_DESTROY_CMD;
|
||||
Node == ?MUC_HAT_LISTHATS_CMD;
|
||||
Node == ?MUC_HAT_ASSIGN_CMD;
|
||||
Node == ?MUC_HAT_UNASSIGN_CMD;
|
||||
Node == ?MUC_HAT_LISTUSERS_CMD ->
|
||||
NodeName = case Node of
|
||||
?MUC_HAT_CREATE_CMD -> ?T("Create a Hat");
|
||||
?MUC_HAT_DESTROY_CMD -> ?T("Destroy a Hat");
|
||||
?MUC_HAT_LISTHATS_CMD -> ?T("List Hats");
|
||||
?MUC_HAT_ASSIGN_CMD -> ?T("Assign a Hat to a User");
|
||||
?MUC_HAT_UNASSIGN_CMD -> ?T("Remove a Hat from a User");
|
||||
?MUC_HAT_LISTUSERS_CMD -> ?T("List Users and their Hats")
|
||||
end,
|
||||
|
||||
case (StateData#state.config)#config.enable_hats andalso
|
||||
is_admin(From, StateData)
|
||||
of
|
||||
|
@ -4519,48 +4543,13 @@ process_iq_disco_info(From, #iq{type = get, lang = Lang,
|
|||
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"))}],
|
||||
Lang, NodeName)}],
|
||||
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) ->
|
||||
|
@ -4623,8 +4612,15 @@ iq_disco_info_extras(Lang, StateData, Static) ->
|
|||
StateData#state.server_host,
|
||||
Fs5,
|
||||
[StateData]),
|
||||
Fs7 = case (StateData#state.config)#config.enable_hats of
|
||||
true ->
|
||||
HatsHash = get_hats_hash(StateData),
|
||||
[{'hats#hash', [HatsHash]} | Fs6];
|
||||
false ->
|
||||
Fs6
|
||||
end,
|
||||
#xdata{type = result,
|
||||
fields = muc_roominfo:encode(Fs6, Lang)}.
|
||||
fields = muc_roominfo:encode(Fs7, Lang)}.
|
||||
|
||||
-spec process_iq_disco_items(jid(), iq(), state()) ->
|
||||
{error, stanza_error()} | {result, disco_items()}.
|
||||
|
@ -4657,15 +4653,27 @@ process_iq_disco_items(From, #iq{type = get, lang = Lang,
|
|||
{result,
|
||||
#disco_items{
|
||||
items = [#disco_item{jid = StateData#state.jid,
|
||||
node = ?MUC_HAT_ADD_CMD,
|
||||
node = ?MUC_HAT_CREATE_CMD,
|
||||
name = translate:translate(
|
||||
Lang, ?T("Add a hat to a user"))},
|
||||
Lang, ?T("Create a hat"))},
|
||||
#disco_item{jid = StateData#state.jid,
|
||||
node = ?MUC_HAT_REMOVE_CMD,
|
||||
node = ?MUC_HAT_DESTROY_CMD,
|
||||
name = translate:translate(
|
||||
Lang, ?T("Destroy a hat"))},
|
||||
#disco_item{jid = StateData#state.jid,
|
||||
node = ?MUC_HAT_LISTHATS_CMD,
|
||||
name = translate:translate(
|
||||
Lang, ?T("List hats"))},
|
||||
#disco_item{jid = StateData#state.jid,
|
||||
node = ?MUC_HAT_ASSIGN_CMD,
|
||||
name = translate:translate(
|
||||
Lang, ?T("Assign a hat to a user"))},
|
||||
#disco_item{jid = StateData#state.jid,
|
||||
node = ?MUC_HAT_UNASSIGN_CMD,
|
||||
name = translate:translate(
|
||||
Lang, ?T("Remove a hat from a user"))},
|
||||
#disco_item{jid = StateData#state.jid,
|
||||
node = ?MUC_HAT_LIST_CMD,
|
||||
node = ?MUC_HAT_LISTUSERS_CMD,
|
||||
name = translate:translate(
|
||||
Lang, ?T("List users with hats"))}]}};
|
||||
false ->
|
||||
|
@ -4675,9 +4683,12 @@ process_iq_disco_items(From, #iq{type = get, lang = Lang,
|
|||
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 ->
|
||||
when Node == ?MUC_HAT_CREATE_CMD;
|
||||
Node == ?MUC_HAT_DESTROY_CMD;
|
||||
Node == ?MUC_HAT_LISTHATS_CMD;
|
||||
Node == ?MUC_HAT_ASSIGN_CMD;
|
||||
Node == ?MUC_HAT_UNASSIGN_CMD;
|
||||
Node == ?MUC_HAT_LISTUSERS_CMD ->
|
||||
case (StateData#state.config)#config.enable_hats andalso
|
||||
is_admin(From, StateData)
|
||||
of
|
||||
|
@ -4944,270 +4955,436 @@ get_mucroom_disco_items(StateData) ->
|
|||
end, [], StateData#state.nicks),
|
||||
#disco_items{items = Items}.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
% Hats
|
||||
|
||||
%% @format-begin
|
||||
|
||||
-spec process_iq_adhoc(jid(), iq(), state()) ->
|
||||
{result, adhoc_command()} |
|
||||
{result, adhoc_command(), state()} |
|
||||
{error, stanza_error()}.
|
||||
{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) ->
|
||||
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
|
||||
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,
|
||||
#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">>}
|
||||
]},
|
||||
xmpp_util:make_adhoc_response(Request,
|
||||
#adhoc_command{status = canceled,
|
||||
lang = Lang,
|
||||
node = Node})};
|
||||
{Node, execute}
|
||||
when Node == ?MUC_HAT_CREATE_CMD;
|
||||
Node == ?MUC_HAT_DESTROY_CMD;
|
||||
Node == ?MUC_HAT_LISTHATS_CMD;
|
||||
Node == ?MUC_HAT_ASSIGN_CMD;
|
||||
Node == ?MUC_HAT_UNASSIGN_CMD;
|
||||
Node == ?MUC_HAT_LISTUSERS_CMD ->
|
||||
{Status, Form} = process_iq_adhoc_hats(Node, StateData, Lang),
|
||||
{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),
|
||||
xmpp_util:make_adhoc_response(Request,
|
||||
#adhoc_command{status = Status, xdata = Form})};
|
||||
{Node, complete}
|
||||
when XData /= undefined andalso Node == ?MUC_HAT_CREATE_CMD;
|
||||
Node == ?MUC_HAT_DESTROY_CMD;
|
||||
Node == ?MUC_HAT_ASSIGN_CMD;
|
||||
Node == ?MUC_HAT_UNASSIGN_CMD ->
|
||||
case process_iq_adhoc_hats_complete(Node, XData, StateData, Lang) of
|
||||
{ok, NewStateData} ->
|
||||
{result,
|
||||
xmpp_util:make_adhoc_response(
|
||||
Request,
|
||||
#adhoc_command{status = completed}),
|
||||
xmpp_util:make_adhoc_response(Request,
|
||||
#adhoc_command{status = completed}),
|
||||
NewStateData};
|
||||
true ->
|
||||
{error, XmlElement} ->
|
||||
{error, XmlElement};
|
||||
error ->
|
||||
{error, xmpp:err_bad_request()}
|
||||
end;
|
||||
{?MUC_HAT_REMOVE_CMD, complete} ->
|
||||
{Node, complete}
|
||||
when Node == ?MUC_HAT_CREATE_CMD;
|
||||
Node == ?MUC_HAT_DESTROY_CMD;
|
||||
Node == ?MUC_HAT_ASSIGN_CMD;
|
||||
Node == ?MUC_HAT_UNASSIGN_CMD ->
|
||||
{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, _} ->
|
||||
{Node, _}
|
||||
when Node == ?MUC_HAT_CREATE_CMD;
|
||||
Node == ?MUC_HAT_DESTROY_CMD;
|
||||
Node == ?MUC_HAT_LISTHATS_CMD;
|
||||
Node == ?MUC_HAT_ASSIGN_CMD;
|
||||
Node == ?MUC_HAT_UNASSIGN_CMD;
|
||||
Node == ?MUC_HAT_LISTUSERS_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()}
|
||||
_ ->
|
||||
{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.
|
||||
process_iq_adhoc_hats(?MUC_HAT_LISTHATS_CMD, StateData, Lang) ->
|
||||
Hats = get_defined_hats(StateData),
|
||||
Items =
|
||||
lists:map(fun({URI, Title, Hue}) ->
|
||||
[#xdata_field{var = <<"hats#uri">>, values = [URI]},
|
||||
#xdata_field{var = <<"hats#title">>, values = [Title]},
|
||||
#xdata_field{var = <<"hats#hue">>, values = [Hue]}]
|
||||
end,
|
||||
Hats),
|
||||
Form =
|
||||
#xdata{title = translate:translate(Lang, ?T("Hats List")),
|
||||
type = result,
|
||||
reported =
|
||||
[#xdata_field{label = translate:translate(Lang, ?T("Hat URI")),
|
||||
var = <<"hats#uri">>},
|
||||
#xdata_field{label = translate:translate(Lang, ?T("Hat Title")),
|
||||
var = <<"hats#title">>},
|
||||
#xdata_field{label = translate:translate(Lang, ?T("Hat Hue")),
|
||||
var = <<"hats#hue">>}],
|
||||
items = Items},
|
||||
{completed, Form};
|
||||
process_iq_adhoc_hats(?MUC_HAT_CREATE_CMD, _StateData, Lang) ->
|
||||
Form =
|
||||
#xdata{title = translate:translate(Lang, ?T("Create a hat")),
|
||||
type = form,
|
||||
fields =
|
||||
[#xdata_field{type = 'text-single',
|
||||
label = translate:translate(Lang, ?T("Hat URI")),
|
||||
required = true,
|
||||
var = <<"hats#uri">>},
|
||||
#xdata_field{type = 'text-single',
|
||||
label = translate:translate(Lang, ?T("Hat Title")),
|
||||
required = true,
|
||||
var = <<"hats#title">>},
|
||||
#xdata_field{type = 'text-single',
|
||||
label = translate:translate(Lang, ?T("Hat Hue")),
|
||||
var = <<"hats#hue">>}]},
|
||||
{executing, Form};
|
||||
process_iq_adhoc_hats(?MUC_HAT_DESTROY_CMD, _StateData, Lang) ->
|
||||
Form =
|
||||
#xdata{title = translate:translate(Lang, ?T("Destroy a hat")),
|
||||
type = form,
|
||||
fields =
|
||||
[#xdata_field{type = 'text-single',
|
||||
label = translate:translate(Lang, ?T("Hat URI")),
|
||||
required = true,
|
||||
var = <<"hat">>}]},
|
||||
{executing, Form};
|
||||
process_iq_adhoc_hats(?MUC_HAT_ASSIGN_CMD, StateData, Lang) ->
|
||||
Hats = get_defined_hats(StateData),
|
||||
Options =
|
||||
[#xdata_option{label = Title, value = Uri}
|
||||
|| {Uri, Title, _Hue} <- lists:keysort(2, Hats)],
|
||||
Form =
|
||||
#xdata{title = translate:translate(Lang, ?T("Assign a hat to a user")),
|
||||
type = form,
|
||||
fields =
|
||||
[#xdata_field{type = 'jid-single',
|
||||
label = translate:translate(Lang, ?T("Jabber ID")),
|
||||
required = true,
|
||||
var = <<"hats#jid">>},
|
||||
#xdata_field{type = 'list-single',
|
||||
label = translate:translate(Lang, ?T("The role")),
|
||||
var = <<"hat">>,
|
||||
options = Options}]},
|
||||
{executing, Form};
|
||||
process_iq_adhoc_hats(?MUC_HAT_UNASSIGN_CMD, StateData, Lang) ->
|
||||
Hats = get_defined_hats(StateData),
|
||||
Options =
|
||||
[#xdata_option{label = Title, value = Uri}
|
||||
|| {Uri, Title, _Hue} <- lists:keysort(2, Hats)],
|
||||
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 = <<"hats#jid">>},
|
||||
#xdata_field{type = 'list-single',
|
||||
label = translate:translate(Lang, ?T("The role")),
|
||||
var = <<"hat">>,
|
||||
options = Options}]},
|
||||
{executing, Form};
|
||||
process_iq_adhoc_hats(?MUC_HAT_LISTUSERS_CMD, StateData, Lang) ->
|
||||
Hats = get_assigned_hats(StateData),
|
||||
Items =
|
||||
lists:map(fun({JID, URI}) ->
|
||||
{URI, Title, Hue} = get_hat_details(URI, StateData),
|
||||
[#xdata_field{var = <<"hats#jid">>, values = [jid:encode(JID)]},
|
||||
#xdata_field{var = <<"hats#uri">>, values = [URI]},
|
||||
#xdata_field{var = <<"hats#title">>, values = [Title]},
|
||||
#xdata_field{var = <<"hats#hue">>, values = [Hue]}]
|
||||
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 = <<"hats#jid">>},
|
||||
#xdata_field{label = translate:translate(Lang, ?T("Hat URI")),
|
||||
var = <<"hats#uri">>},
|
||||
#xdata_field{label = translate:translate(Lang, ?T("Hat Title")),
|
||||
var = <<"hats#title">>},
|
||||
#xdata_field{label = translate:translate(Lang, ?T("Hat Hue")),
|
||||
var = <<"hats#hue">>}],
|
||||
items = Items},
|
||||
{completed, Form};
|
||||
process_iq_adhoc_hats(_, _, _) ->
|
||||
{executing, aaa}.
|
||||
|
||||
-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)
|
||||
process_iq_adhoc_hats_complete(?MUC_HAT_CREATE_CMD, XData, StateData, _Lang) ->
|
||||
URI = try
|
||||
hd(xmpp_util:get_xdata_values(<<"hats#uri">>, XData))
|
||||
catch
|
||||
_:_ ->
|
||||
error
|
||||
end,
|
||||
Title =
|
||||
case xmpp_util:get_xdata_values(<<"hats#title">>, XData) of
|
||||
[] ->
|
||||
<<"">>;
|
||||
[T] ->
|
||||
T
|
||||
end,
|
||||
StateData#state{hats_users = Hats2}.
|
||||
Hue = try
|
||||
hd(xmpp_util:get_xdata_values(<<"hats#hue">>, XData))
|
||||
catch
|
||||
_:_ ->
|
||||
error
|
||||
end,
|
||||
if (Title /= error) and (URI /= error) ->
|
||||
{ok, AffectedJids, NewStateData} = create_hat(URI, Title, Hue, StateData),
|
||||
store_room(NewStateData),
|
||||
broadcast_hats_change(NewStateData),
|
||||
[send_update_presence(AJid, NewStateData, StateData) || AJid <- AffectedJids],
|
||||
{ok, NewStateData};
|
||||
true ->
|
||||
error
|
||||
end;
|
||||
process_iq_adhoc_hats_complete(?MUC_HAT_DESTROY_CMD, XData, StateData, _Lang) ->
|
||||
URI = try
|
||||
hd(xmpp_util:get_xdata_values(<<"hat">>, XData))
|
||||
catch
|
||||
_:_ ->
|
||||
error
|
||||
end,
|
||||
if URI /= error ->
|
||||
{ok, AffectedJids, NewStateData} = destroy_hat(URI, StateData),
|
||||
store_room(NewStateData),
|
||||
broadcast_hats_change(NewStateData),
|
||||
[send_update_presence(AJid, NewStateData, StateData) || AJid <- AffectedJids],
|
||||
{ok, NewStateData};
|
||||
true ->
|
||||
error
|
||||
end;
|
||||
process_iq_adhoc_hats_complete(?MUC_HAT_ASSIGN_CMD, XData, StateData, Lang) ->
|
||||
JID = try
|
||||
jid:decode(hd(xmpp_util:get_xdata_values(<<"hats#jid">>, XData)))
|
||||
catch
|
||||
_:_ ->
|
||||
error
|
||||
end,
|
||||
URI = try
|
||||
hd(xmpp_util:get_xdata_values(<<"hat">>, XData))
|
||||
catch
|
||||
_:_ ->
|
||||
error
|
||||
end,
|
||||
if (JID /= error) and (URI /= error) ->
|
||||
case assign_hat(JID, URI, StateData) of
|
||||
{ok, NewStateData} ->
|
||||
store_room(NewStateData),
|
||||
send_update_presence(JID, NewStateData, StateData),
|
||||
{ok, NewStateData};
|
||||
{error, size_limit} ->
|
||||
Txt = ?T("Hats limit exceeded"),
|
||||
{error, xmpp:err_not_allowed(Txt, Lang)}
|
||||
end;
|
||||
true ->
|
||||
error
|
||||
end;
|
||||
process_iq_adhoc_hats_complete(?MUC_HAT_UNASSIGN_CMD, XData, StateData, _Lang) ->
|
||||
JID = try
|
||||
jid:decode(hd(xmpp_util:get_xdata_values(<<"hats#jid">>, XData)))
|
||||
catch
|
||||
_:_ ->
|
||||
error
|
||||
end,
|
||||
URI = try
|
||||
hd(xmpp_util:get_xdata_values(<<"hat">>, XData))
|
||||
catch
|
||||
_:_ ->
|
||||
error
|
||||
end,
|
||||
if (JID /= error) and (URI /= error) ->
|
||||
{ok, NewStateData} = unassign_hat(JID, URI, StateData),
|
||||
store_room(NewStateData),
|
||||
send_update_presence(JID, NewStateData, StateData),
|
||||
{ok, NewStateData};
|
||||
true ->
|
||||
error
|
||||
end.
|
||||
|
||||
-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)).
|
||||
%% TODO +++ clean
|
||||
create_hat(URI, Title, Hue, #state{hats_defs = Hats, hats_users = Users} = StateData) ->
|
||||
Hats2 = maps:put(URI, {Title, Hue}, Hats),
|
||||
|
||||
IsUpdate =
|
||||
case maps:find(URI, Hats) of
|
||||
{ok, {OldTitle, OldHue}} ->
|
||||
(OldTitle /= Title) or (OldHue /= Hue);
|
||||
error ->
|
||||
false
|
||||
end,
|
||||
|
||||
AffectedJids =
|
||||
case IsUpdate of
|
||||
true ->
|
||||
maps:fold(fun(Jid, AssignedHatsUris, ChangedAcc) ->
|
||||
case lists:member(URI, AssignedHatsUris) of
|
||||
false ->
|
||||
ChangedAcc;
|
||||
true ->
|
||||
[Jid | ChangedAcc]
|
||||
end
|
||||
end,
|
||||
[],
|
||||
Users);
|
||||
false ->
|
||||
[]
|
||||
end,
|
||||
{ok, AffectedJids, StateData#state{hats_defs = Hats2}}.
|
||||
|
||||
destroy_hat(URI, #state{hats_defs = Hats, hats_users = Users} = StateData) ->
|
||||
Hats2 = maps:remove(URI, Hats),
|
||||
{AffectedJids, Users2} =
|
||||
maps:fold(fun(Jid, AssignedHatsUris, {ChangedAcc, UsersAcc}) ->
|
||||
case AssignedHatsUris -- [URI] of
|
||||
[] ->
|
||||
{ChangedAcc, UsersAcc};
|
||||
AssignedHatsUris2 ->
|
||||
{[Jid | ChangedAcc], maps:put(Jid, AssignedHatsUris2, UsersAcc)}
|
||||
end
|
||||
end,
|
||||
{[], maps:new()},
|
||||
Users),
|
||||
{ok, AffectedJids, StateData#state{hats_defs = Hats2, hats_users = Users2}}.
|
||||
|
||||
broadcast_hats_change(StateData) ->
|
||||
Codes = [104],
|
||||
Message =
|
||||
#message{type = groupchat,
|
||||
id = p1_rand:get_string(),
|
||||
sub_els = [#muc_user{status_codes = Codes}]},
|
||||
send_wrapped_multiple(StateData#state.jid,
|
||||
get_users_and_subscribers_with_node(?NS_MUCSUB_NODES_CONFIG, StateData),
|
||||
Message,
|
||||
?NS_MUCSUB_NODES_CONFIG,
|
||||
StateData).
|
||||
|
||||
-spec assign_hat(jid(), binary(), state()) -> {ok, state()} | {error, size_limit}.
|
||||
assign_hat(JID, URI, StateData) ->
|
||||
Hats = StateData#state.hats_users,
|
||||
LJID =
|
||||
jid:remove_resource(
|
||||
jid:tolower(JID)),
|
||||
UserHats = maps:get(LJID, Hats, []),
|
||||
UserHats2 = lists:umerge([URI], UserHats),
|
||||
USize = length(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 unassign_hat(jid(), binary(), state()) -> {ok, state()} | {error, size_limit}.
|
||||
unassign_hat(JID, URI, StateData) ->
|
||||
Hats = StateData#state.hats_users,
|
||||
LJID =
|
||||
jid:remove_resource(
|
||||
jid:tolower(JID)),
|
||||
UserHats = maps:get(LJID, Hats, []),
|
||||
UserHats2 = lists:delete(URI, UserHats),
|
||||
Hats2 = maps:put(LJID, UserHats2, Hats),
|
||||
{ok, StateData#state{hats_users = Hats2}}.
|
||||
|
||||
-spec get_defined_hats(state()) -> [{binary(), binary(), binary()}].
|
||||
get_defined_hats(StateData) ->
|
||||
lists:map(fun({Uri, {Title, Hue}}) -> {Uri, Title, Hue} end,
|
||||
maps:to_list(StateData#state.hats_defs)).
|
||||
|
||||
-spec get_assigned_hats(state()) -> [{jid(), binary()}].
|
||||
get_assigned_hats(StateData) ->
|
||||
lists:flatmap(fun({LJID, H}) ->
|
||||
JID = jid:make(LJID),
|
||||
lists:map(fun(URI) -> {JID, URI} end, H)
|
||||
end,
|
||||
maps:to_list(StateData#state.hats_users)).
|
||||
|
||||
get_hats_hash(StateData) ->
|
||||
str:sha(
|
||||
misc:term_to_base64(get_assigned_hats(StateData))).
|
||||
|
||||
get_hat_details(Uri, StateData) ->
|
||||
lists:keyfind(Uri, 1, get_defined_hats(StateData)).
|
||||
|
||||
-spec add_presence_hats(jid(), #presence{}, state()) -> #presence{}.
|
||||
add_presence_hats(JID, Pres, StateData) ->
|
||||
case (StateData#state.config)#config.enable_hats of
|
||||
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;
|
||||
LJID =
|
||||
jid:remove_resource(
|
||||
jid:tolower(JID)),
|
||||
UserHats = maps:get(LJID, Hats, []),
|
||||
case length(UserHats) of
|
||||
0 ->
|
||||
Pres;
|
||||
_ ->
|
||||
Items =
|
||||
lists:map(fun({URI, Title}) ->
|
||||
#muc_hat{uri = URI, title = Title}
|
||||
lists:map(fun(URI) ->
|
||||
{URI, Title, Hue} = get_hat_details(URI, StateData),
|
||||
#muc_hat{uri = URI,
|
||||
title = Title,
|
||||
hue = Hue}
|
||||
end,
|
||||
maps:to_list(UserHats)),
|
||||
xmpp:set_subtag(Pres,
|
||||
#muc_hats{hats = Items})
|
||||
UserHats),
|
||||
xmpp:set_subtag(Pres, #muc_hats{hats = Items})
|
||||
end;
|
||||
false ->
|
||||
Pres
|
||||
end.
|
||||
%% @format-end
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
-spec process_iq_moderate(jid(), iq(), binary(), binary() | undefined, state()) ->
|
||||
{result, undefined, state()} |
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue