Добавить автомодерацию: политики, порог жалоб, settings/hits API и тесты.
Refs EventHub/EventHubBack#37 Refs EventHub/EventHubBack#38 Refs EventHub/EventHubBack#39 Refs EventHub/EventHubBack#40
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
-module(core_automod_hit).
|
||||
-include("records.hrl").
|
||||
|
||||
-export([create/5, get_by_id/1, list/0, list/1, resolve/3]).
|
||||
|
||||
-spec create(EntityType :: calendar | event | review,
|
||||
EntityId :: binary(),
|
||||
Trigger :: keyword | report_threshold,
|
||||
MatchedWords :: [binary()],
|
||||
ActionTaken :: atom()) -> {ok, #automod_hit{}}.
|
||||
create(EntityType, EntityId, Trigger, MatchedWords, ActionTaken) ->
|
||||
case lists:member(automod_hit, mnesia:system_info(tables)) of
|
||||
false ->
|
||||
{ok, #automod_hit{
|
||||
id = <<"transient">>,
|
||||
entity_type = EntityType,
|
||||
entity_id = EntityId,
|
||||
trigger = Trigger,
|
||||
matched_words = MatchedWords,
|
||||
action_taken = ActionTaken,
|
||||
status = open,
|
||||
created_at = calendar:universal_time(),
|
||||
resolved_at = undefined,
|
||||
resolved_by = <<>>
|
||||
}};
|
||||
true ->
|
||||
Id = infra_utils:generate_id(16),
|
||||
Hit = #automod_hit{
|
||||
id = Id,
|
||||
entity_type = EntityType,
|
||||
entity_id = EntityId,
|
||||
trigger = Trigger,
|
||||
matched_words = MatchedWords,
|
||||
action_taken = ActionTaken,
|
||||
status = open,
|
||||
created_at = calendar:universal_time(),
|
||||
resolved_at = undefined,
|
||||
resolved_by = <<>>
|
||||
},
|
||||
mnesia:dirty_write(Hit),
|
||||
{ok, Hit}
|
||||
end.
|
||||
|
||||
-spec get_by_id(binary()) -> {ok, #automod_hit{}} | {error, not_found}.
|
||||
get_by_id(Id) ->
|
||||
case mnesia:dirty_read(automod_hit, Id) of
|
||||
[Hit] -> {ok, Hit};
|
||||
[] -> {error, not_found}
|
||||
end.
|
||||
|
||||
-spec list() -> [#automod_hit{}].
|
||||
list() ->
|
||||
mnesia:dirty_match_object(#automod_hit{_ = '_'}).
|
||||
|
||||
-spec list([{atom(), term()}]) -> [#automod_hit{}].
|
||||
list(Filters) ->
|
||||
lists:filter(fun(H) -> match_filters(H, Filters) end, list()).
|
||||
|
||||
-spec resolve(binary(), approved | rejected, binary()) ->
|
||||
{ok, #automod_hit{}} | {error, not_found | already_resolved}.
|
||||
resolve(Id, Status, AdminId) when Status =:= approved; Status =:= rejected ->
|
||||
case get_by_id(Id) of
|
||||
{ok, #automod_hit{status = open} = Hit} ->
|
||||
Updated = Hit#automod_hit{
|
||||
status = Status,
|
||||
resolved_at = calendar:universal_time(),
|
||||
resolved_by = AdminId
|
||||
},
|
||||
mnesia:dirty_write(Updated),
|
||||
{ok, Updated};
|
||||
{ok, _} ->
|
||||
{error, already_resolved};
|
||||
Error ->
|
||||
Error
|
||||
end.
|
||||
|
||||
match_filters(H, Filters) ->
|
||||
lists:all(fun
|
||||
({status, S}) -> H#automod_hit.status =:= S;
|
||||
({trigger, T}) -> H#automod_hit.trigger =:= T;
|
||||
({entity_type, T}) -> H#automod_hit.entity_type =:= T;
|
||||
(_) -> true
|
||||
end, Filters).
|
||||
@@ -0,0 +1,83 @@
|
||||
-module(core_automod_settings).
|
||||
-include("records.hrl").
|
||||
|
||||
-export([get/0, update/2, ensure_defaults/0]).
|
||||
|
||||
-define(ID, <<"default">>).
|
||||
-define(DEFAULT_ACTION, flag).
|
||||
-define(DEFAULT_MATCH, word_boundary).
|
||||
-define(DEFAULT_THRESHOLD, 3).
|
||||
|
||||
-spec get() -> #automod_settings{}.
|
||||
get() ->
|
||||
case catch mnesia:dirty_read(automod_settings, ?ID) of
|
||||
[S] -> S;
|
||||
[] ->
|
||||
case catch ensure_defaults() of
|
||||
ok ->
|
||||
case catch mnesia:dirty_read(automod_settings, ?ID) of
|
||||
[S2] -> S2;
|
||||
_ -> default_record(<<>>)
|
||||
end;
|
||||
_ ->
|
||||
default_record(<<>>)
|
||||
end;
|
||||
{'EXIT', _} ->
|
||||
default_record(<<>>);
|
||||
_ ->
|
||||
default_record(<<>>)
|
||||
end.
|
||||
|
||||
-spec ensure_defaults() -> ok.
|
||||
ensure_defaults() ->
|
||||
case lists:member(automod_settings, mnesia:system_info(tables)) of
|
||||
false ->
|
||||
ok;
|
||||
true ->
|
||||
case mnesia:dirty_read(automod_settings, ?ID) of
|
||||
[_] -> ok;
|
||||
[] ->
|
||||
mnesia:dirty_write(default_record(<<>>)),
|
||||
ok
|
||||
end
|
||||
end.
|
||||
|
||||
-spec update(map() | [{atom(), term()}], binary()) ->
|
||||
{ok, #automod_settings{}} | {error, term()}.
|
||||
update(Updates, AdminId) when is_map(Updates) ->
|
||||
update(maps:to_list(Updates), AdminId);
|
||||
update(Updates, AdminId) when is_list(Updates) ->
|
||||
Current = get(),
|
||||
case apply_updates(Current, Updates) of
|
||||
{error, _} = Err -> Err;
|
||||
{ok, Next0} ->
|
||||
Next = Next0#automod_settings{
|
||||
updated_at = calendar:universal_time(),
|
||||
updated_by = AdminId
|
||||
},
|
||||
mnesia:dirty_write(Next),
|
||||
{ok, Next}
|
||||
end.
|
||||
|
||||
default_record(UpdatedBy) ->
|
||||
#automod_settings{
|
||||
id = ?ID,
|
||||
keyword_action = ?DEFAULT_ACTION,
|
||||
match_mode = ?DEFAULT_MATCH,
|
||||
report_threshold = ?DEFAULT_THRESHOLD,
|
||||
updated_at = calendar:universal_time(),
|
||||
updated_by = UpdatedBy
|
||||
}.
|
||||
|
||||
apply_updates(S, []) -> {ok, S};
|
||||
apply_updates(S, [{keyword_action, A} | Rest])
|
||||
when A =:= reject; A =:= flag; A =:= censor ->
|
||||
apply_updates(S#automod_settings{keyword_action = A}, Rest);
|
||||
apply_updates(S, [{match_mode, M} | Rest])
|
||||
when M =:= word_boundary; M =:= substring ->
|
||||
apply_updates(S#automod_settings{match_mode = M}, Rest);
|
||||
apply_updates(S, [{report_threshold, N} | Rest])
|
||||
when is_integer(N), N >= 1 ->
|
||||
apply_updates(S#automod_settings{report_threshold = N}, Rest);
|
||||
apply_updates(_S, [{Field, _} | _]) ->
|
||||
{error, {invalid_field, Field}}.
|
||||
Reference in New Issue
Block a user