Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement unbanning logic #2

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions policyeval/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ func (pe *PolicyEvaluator) HandleCommand(ctx context.Context, evt *event.Event)
zerolog.Ctx(ctx).Info().Str("command", cmd).Msg("Handling command")
switch cmd {
case "!join":
if len(args) == 0 {
pe.sendNotice(ctx, "Usage: `!join <room ID>...`")
return
}
for _, arg := range args {
_, err := pe.Bot.JoinRoom(ctx, arg, nil)
if err != nil {
Expand All @@ -39,6 +43,30 @@ func (pe *PolicyEvaluator) HandleCommand(ctx context.Context, evt *event.Event)
}
}
pe.sendSuccessReaction(ctx, evt.ID)
case "!leave":
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This accidentally made its way in from an unstaged commit from #1, needs removing

if len(args) == 0 {
pe.sendNotice(ctx, "Usage: `!leave <room ID>...`")
return
}
var target id.RoomID
if strings.HasPrefix(args[0], "#") {
rawTarget, err := pe.Bot.ResolveAlias(ctx, id.RoomAlias(args[0]))
if err != nil {
pe.sendNotice(ctx, "Failed to resolve alias %q: %v", args[0], err)
return
}
target = rawTarget.RoomID
} else {
target = id.RoomID(args[0])
}
for _, arg := range args {
_, err := pe.Bot.LeaveRoom(ctx, target, nil)
if err != nil {
pe.sendNotice(ctx, "Failed to leave room %q: %v", arg, err)
} else {
pe.sendNotice(ctx, "Left room %q", arg)
}
}
case "!redact":
if len(args) < 1 {
pe.sendNotice(ctx, "Usage: `!redact <user ID> [reason]`")
Expand Down Expand Up @@ -120,6 +148,89 @@ func (pe *PolicyEvaluator) HandleCommand(ctx context.Context, evt *event.Event)
Stringer("policy_event_id", resp.EventID).
Msg("Sent ban policy from command")
pe.sendSuccessReaction(ctx, evt.ID)
case "!remove-ban", "!remove-unban", "!remove-policy":
if len(args) < 2 {
pe.sendNotice(ctx, "Usage: `!remove-policy <list> <user ID | server name> <reason>`")
return
}
list := pe.FindListByShortcode(args[0])
if list == nil {
pe.sendNotice(ctx, `List %q not found`, args[0])
return
}
target := args[1]
var match policylist.Match
var entityType policylist.EntityType
if !strings.HasPrefix(target, "@") {
entityType = policylist.EntityTypeServer
match = pe.Store.MatchServer(pe.GetWatchedLists(), target)
} else {
entityType = policylist.EntityTypeUser
match = pe.Store.MatchUser(pe.GetWatchedLists(), id.UserID(target))
}
var existingStateKey string
if rec := match.Recommendations().BanOrUnban; rec != nil {
if rec.RoomID == list.RoomID {
existingStateKey = rec.StateKey
}
}
policy := &event.ModPolicyContent{}
resp, err := pe.SendPolicy(ctx, list.RoomID, entityType, existingStateKey, policy)
if err != nil {
pe.sendNotice(ctx, `Failed to remove policy: %v`, err)
return
}
zerolog.Ctx(ctx).Info().
Stringer("policy_list", list.RoomID).
Any("policy", policy).
Stringer("policy_event_id", resp.EventID).
Msg("Removed policy from command")
pe.sendSuccessReaction(ctx, evt.ID)
case "!add-unban":
if len(args) < 2 {
pe.sendNotice(ctx, "Usage: `!add-unban <list shortcode> <user ID | server name> <reason>`")
return
}
list := pe.FindListByShortcode(args[0])
if list == nil {
pe.sendNotice(ctx, `List %q not found`, args[0])
return
}
target := args[1]
var match policylist.Match
var entityType policylist.EntityType
if !strings.HasPrefix(target, "@") {
entityType = policylist.EntityTypeServer
match = pe.Store.MatchServer(pe.GetWatchedLists(), target)
} else {
entityType = policylist.EntityTypeUser
match = pe.Store.MatchUser(pe.GetWatchedLists(), id.UserID(target))
}
var existingStateKey string
if rec := match.Recommendations().BanOrUnban; rec != nil {
if rec.Recommendation == event.PolicyRecommendationUnban {
pe.sendNotice(ctx, "`%s` already has an unban recommendation: %s", target, rec.Reason)
return
} else if rec.RoomID == list.RoomID {
existingStateKey = rec.StateKey
}
}
policy := &event.ModPolicyContent{
Entity: target,
Reason: strings.Join(args[2:], " "),
Recommendation: event.PolicyRecommendationUnban,
}
resp, err := pe.SendPolicy(ctx, list.RoomID, entityType, existingStateKey, policy)
if err != nil {
pe.sendNotice(ctx, `Failed to send unban policy: %v`, err)
return
}
zerolog.Ctx(ctx).Info().
Stringer("policy_list", list.RoomID).
Any("policy", policy).
Stringer("policy_event_id", resp.EventID).
Msg("Sent unban policy from command")
pe.sendSuccessReaction(ctx, evt.ID)
case "!match":
start := time.Now()
match := pe.Store.MatchUser(nil, id.UserID(args[0]))
Expand Down
21 changes: 20 additions & 1 deletion policyeval/evaluate.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,5 +91,24 @@ func (pe *PolicyEvaluator) ReevaluateAffectedByLists(ctx context.Context, policy
}

func (pe *PolicyEvaluator) ReevaluateActions(ctx context.Context, actions []*database.TakenAction) {

for _, action := range actions {
if action.TargetUser == "" {
zerolog.Ctx(ctx).Warn().Any("action", action).Msg("Action has no target user")
continue
}
// unban users that were previously banned by this rule
if action.ActionType == database.TakenActionTypeBanOrUnban && action.Action == event.PolicyRecommendationBan {
// ensure that the user is actually banned in the room
if pe.Bot.StateStore.IsMembership(ctx, action.InRoomID, action.TargetUser, event.MembershipBan) {
// This is hacky
policy := &policylist.Policy{
RoomID: action.InRoomID,
ModPolicyContent: &event.ModPolicyContent{
Entity: action.RuleEntity,
},
}
pe.ApplyUnban(ctx, action.TargetUser, action.InRoomID, policy)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I need to check somewhere that auto_unban is enabled for the list that created the policy, this just blanket unbans at the moment

}
}
}
}
39 changes: 39 additions & 0 deletions policyeval/execute.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ func (pe *PolicyEvaluator) ApplyPolicy(ctx context.Context, userID id.UserID, po
// pe.sendNotice(ctx, "Database error in ApplyPolicy (GetAllByTargetUser): %v", err)
// return
//}
for _, room := range rooms {
pe.ApplyUnban(ctx, userID, room, recs.BanOrUnban)
}
}
}
}
Expand Down Expand Up @@ -108,6 +111,42 @@ func (pe *PolicyEvaluator) ApplyBan(ctx context.Context, userID id.UserID, roomI
}
}

func (pe *PolicyEvaluator) ApplyUnban(ctx context.Context, userID id.UserID, roomID id.RoomID, policy *policylist.Policy) {
ta := &database.TakenAction{
TargetUser: userID,
InRoomID: roomID,
ActionType: database.TakenActionTypeBanOrUnban,
PolicyList: policy.RoomID,
RuleEntity: policy.Entity,
Action: policy.Recommendation,
TakenAt: time.Now(),
}
var err error
if !pe.DryRun {
_, err = pe.Bot.UnbanUser(ctx, roomID, &mautrix.ReqUnbanUser{
Reason: filterReason(policy.Reason),
UserID: userID,
})
}
if err != nil {
var respErr mautrix.HTTPError
if errors.As(err, &respErr) {
err = respErr
}
zerolog.Ctx(ctx).Err(err).Any("attempted_action", ta).Msg("Failed to unban user")
pe.sendNotice(ctx, "Failed to unban [%s](%s) in [%s](%s) for %s: %v", userID, userID.URI().MatrixToURL(), roomID, roomID.URI().MatrixToURL(), policy.Reason, err)
return
}
err = pe.DB.TakenAction.Put(ctx, ta)
if err != nil {
zerolog.Ctx(ctx).Err(err).Any("taken_action", ta).Msg("Failed to save taken action")
pe.sendNotice(ctx, "Unbanned [%s](%s) in [%s](%s) for %s, but failed to save to database: %v", userID, userID.URI().MatrixToURL(), roomID, roomID.URI().MatrixToURL(), policy.Reason, err)
} else {
zerolog.Ctx(ctx).Info().Any("taken_action", ta).Msg("Took action")
pe.sendNotice(ctx, "Unbanned [%s](%s) in [%s](%s) for %s", userID, userID.URI().MatrixToURL(), roomID, roomID.URI().MatrixToURL(), policy.Reason)
}
}

func pluralize(value int, unit string) string {
if value == 1 {
return "1 " + unit
Expand Down