Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
15f2fcc4d1 |
@@ -17,6 +17,7 @@ import { Page, PageContent, PageHeader } from '../../components/page';
|
|||||||
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
||||||
import { SequenceCard } from '../../components/sequence-card';
|
import { SequenceCard } from '../../components/sequence-card';
|
||||||
import { SequenceCardStyle } from '../common-settings/styles.css';
|
import { SequenceCardStyle } from '../common-settings/styles.css';
|
||||||
|
import { dedupePolicyEntries, PolicyEntry, recommendationLabel } from './policyEntries';
|
||||||
|
|
||||||
// ── Policy event types ────────────────────────────────────────────────────────
|
// ── Policy event types ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@@ -38,13 +39,6 @@ type PolicyRuleContent = {
|
|||||||
recommendation?: string;
|
recommendation?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type PolicyEntry = {
|
|
||||||
entity: string;
|
|
||||||
reason: string;
|
|
||||||
recommendation: string;
|
|
||||||
stateKey: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type PolicyTab = 'users' | 'rooms' | 'servers';
|
type PolicyTab = 'users' | 'rooms' | 'servers';
|
||||||
|
|
||||||
// ── Helpers ───────────────────────────────────────────────────────────────────
|
// ── Helpers ───────────────────────────────────────────────────────────────────
|
||||||
@@ -53,13 +47,6 @@ function isGlob(entity: string): boolean {
|
|||||||
return entity.includes('*') || entity.includes('?');
|
return entity.includes('*') || entity.includes('?');
|
||||||
}
|
}
|
||||||
|
|
||||||
function recommendationLabel(rec: string): string {
|
|
||||||
// `m.ban` is the stable value; `org.matrix.mjolnir.ban` is the legacy
|
|
||||||
// (pre-stabilization) recommendation still emitted by older bots.
|
|
||||||
if (rec === 'm.ban' || rec === 'org.matrix.mjolnir.ban') return 'Ban';
|
|
||||||
return rec;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch all state events of a given type from a room's live forward state.
|
* Fetch all state events of a given type from a room's live forward state.
|
||||||
* Uses the raw matrix-js-sdk `getStateEvents` API which accepts any string type.
|
* Uses the raw matrix-js-sdk `getStateEvents` API which accepts any string type.
|
||||||
@@ -84,23 +71,6 @@ function extractPolicyEntries(events: MatrixEvent[]): PolicyEntry[] {
|
|||||||
.filter((entry) => entry.entity !== '');
|
.filter((entry) => entry.entity !== '');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Merge policy entries from the stable and legacy event types for a rule
|
|
||||||
* kind, de-duplicating by entity+recommendation so a room that emits both a
|
|
||||||
* stable and a legacy rule for the same target isn't double-listed.
|
|
||||||
*/
|
|
||||||
export function dedupePolicyEntries(entries: PolicyEntry[]): PolicyEntry[] {
|
|
||||||
const seen = new Set<string>();
|
|
||||||
const result: PolicyEntry[] = [];
|
|
||||||
entries.forEach((entry) => {
|
|
||||||
const key = `${entry.entity} ${entry.recommendation}`;
|
|
||||||
if (seen.has(key)) return;
|
|
||||||
seen.add(key);
|
|
||||||
result.push(entry);
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Entry row ─────────────────────────────────────────────────────────────────
|
// ── Entry row ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
function PolicyEntryRow({ entry }: { entry: PolicyEntry }) {
|
function PolicyEntryRow({ entry }: { entry: PolicyEntry }) {
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { test } from 'node:test';
|
||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import { dedupePolicyEntries, recommendationLabel } from './policyEntries';
|
||||||
|
|
||||||
|
const e = (entity: string, recommendation: string, stateKey = entity) => ({
|
||||||
|
entity,
|
||||||
|
reason: 'spam waves',
|
||||||
|
recommendation,
|
||||||
|
stateKey,
|
||||||
|
});
|
||||||
|
|
||||||
|
test('dedupePolicyEntries: a stable rule and its legacy twin show once (#321)', () => {
|
||||||
|
const out = dedupePolicyEntries([
|
||||||
|
e('@spammer:evil.example', 'm.ban'),
|
||||||
|
e('@spammer:evil.example', 'org.matrix.mjolnir.ban'),
|
||||||
|
e('@*:spam.example', 'm.ban'),
|
||||||
|
]);
|
||||||
|
assert.deepEqual(
|
||||||
|
out.map((x) => x.entity),
|
||||||
|
['@spammer:evil.example', '@*:spam.example'],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('dedupePolicyEntries: different recommendations stay separate', () => {
|
||||||
|
assert.equal(dedupePolicyEntries([e('@a:x', 'm.ban'), e('@a:x', 'org.example.mute')]).length, 2);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('recommendationLabel', () => {
|
||||||
|
assert.equal(recommendationLabel('m.ban'), 'Ban');
|
||||||
|
assert.equal(recommendationLabel('org.matrix.mjolnir.ban'), 'Ban');
|
||||||
|
assert.equal(recommendationLabel('org.example.mute'), 'org.example.mute');
|
||||||
|
});
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
export type PolicyEntry = {
|
||||||
|
entity: string;
|
||||||
|
reason: string;
|
||||||
|
recommendation: string;
|
||||||
|
stateKey: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `m.ban` is the stable value; `org.matrix.mjolnir.ban` is the legacy
|
||||||
|
* (pre-stabilization) recommendation still emitted by older bots.
|
||||||
|
*/
|
||||||
|
export const normalizeRecommendation = (rec: string): string =>
|
||||||
|
rec === 'org.matrix.mjolnir.ban' ? 'm.ban' : rec;
|
||||||
|
|
||||||
|
export function recommendationLabel(rec: string): string {
|
||||||
|
if (normalizeRecommendation(rec) === 'm.ban') return 'Ban';
|
||||||
|
return rec;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merge stable and legacy rule lists, one entry per entity + recommendation.
|
||||||
|
* [Gitea #321] Keyed on the normalised recommendation: a bot publishing both
|
||||||
|
* `m.ban` and its `org.matrix.mjolnir.ban` twin showed the rule twice.
|
||||||
|
*/
|
||||||
|
export function dedupePolicyEntries(entries: PolicyEntry[]): PolicyEntry[] {
|
||||||
|
const seen = new Set<string>();
|
||||||
|
const result: PolicyEntry[] = [];
|
||||||
|
entries.forEach((entry) => {
|
||||||
|
const key = `${entry.entity} ${normalizeRecommendation(entry.recommendation)}`;
|
||||||
|
if (seen.has(key)) return;
|
||||||
|
seen.add(key);
|
||||||
|
result.push(entry);
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user