@atproto/ozone 0.1.10 → 0.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/api/moderation/emitEvent.d.ts.map +1 -1
  3. package/dist/api/moderation/emitEvent.js +4 -0
  4. package/dist/api/moderation/emitEvent.js.map +1 -1
  5. package/dist/api/moderation/queryStatuses.d.ts.map +1 -1
  6. package/dist/api/moderation/queryStatuses.js +2 -1
  7. package/dist/api/moderation/queryStatuses.js.map +1 -1
  8. package/dist/api/util.d.ts +1 -1
  9. package/dist/api/util.d.ts.map +1 -1
  10. package/dist/api/util.js +2 -0
  11. package/dist/api/util.js.map +1 -1
  12. package/dist/db/migrations/20240408T192432676Z-mute-reporting.d.ts +4 -0
  13. package/dist/db/migrations/20240408T192432676Z-mute-reporting.d.ts.map +1 -0
  14. package/dist/db/migrations/20240408T192432676Z-mute-reporting.js +18 -0
  15. package/dist/db/migrations/20240408T192432676Z-mute-reporting.js.map +1 -0
  16. package/dist/db/migrations/index.d.ts +1 -0
  17. package/dist/db/migrations/index.d.ts.map +1 -1
  18. package/dist/db/migrations/index.js +2 -1
  19. package/dist/db/migrations/index.js.map +1 -1
  20. package/dist/db/schema/moderation_event.d.ts +1 -1
  21. package/dist/db/schema/moderation_event.d.ts.map +1 -1
  22. package/dist/db/schema/moderation_subject_status.d.ts +1 -0
  23. package/dist/db/schema/moderation_subject_status.d.ts.map +1 -1
  24. package/dist/lexicon/lexicons.d.ts +41 -0
  25. package/dist/lexicon/lexicons.d.ts.map +1 -1
  26. package/dist/lexicon/lexicons.js +50 -1
  27. package/dist/lexicon/lexicons.js.map +1 -1
  28. package/dist/lexicon/types/app/bsky/feed/defs.d.ts +1 -0
  29. package/dist/lexicon/types/app/bsky/feed/defs.d.ts.map +1 -1
  30. package/dist/lexicon/types/app/bsky/feed/defs.js.map +1 -1
  31. package/dist/lexicon/types/tools/ozone/moderation/defs.d.ts +22 -2
  32. package/dist/lexicon/types/tools/ozone/moderation/defs.d.ts.map +1 -1
  33. package/dist/lexicon/types/tools/ozone/moderation/defs.js +22 -2
  34. package/dist/lexicon/types/tools/ozone/moderation/defs.js.map +1 -1
  35. package/dist/lexicon/types/tools/ozone/moderation/emitEvent.d.ts +1 -1
  36. package/dist/lexicon/types/tools/ozone/moderation/emitEvent.d.ts.map +1 -1
  37. package/dist/lexicon/types/tools/ozone/moderation/queryStatuses.d.ts +2 -0
  38. package/dist/lexicon/types/tools/ozone/moderation/queryStatuses.d.ts.map +1 -1
  39. package/dist/mod-service/index.d.ts +6 -2
  40. package/dist/mod-service/index.d.ts.map +1 -1
  41. package/dist/mod-service/index.js +25 -1
  42. package/dist/mod-service/index.js.map +1 -1
  43. package/dist/mod-service/status.d.ts +21 -1
  44. package/dist/mod-service/status.d.ts.map +1 -1
  45. package/dist/mod-service/status.js +23 -0
  46. package/dist/mod-service/status.js.map +1 -1
  47. package/dist/mod-service/views.d.ts.map +1 -1
  48. package/dist/mod-service/views.js +3 -0
  49. package/dist/mod-service/views.js.map +1 -1
  50. package/package.json +3 -3
  51. package/src/api/moderation/emitEvent.ts +9 -0
  52. package/src/api/moderation/queryStatuses.ts +2 -0
  53. package/src/api/util.ts +2 -0
  54. package/src/db/migrations/20240408T192432676Z-mute-reporting.ts +15 -0
  55. package/src/db/migrations/index.ts +1 -0
  56. package/src/db/schema/moderation_event.ts +3 -0
  57. package/src/db/schema/moderation_subject_status.ts +1 -0
  58. package/src/lexicon/lexicons.ts +53 -1
  59. package/src/lexicon/types/app/bsky/feed/defs.ts +1 -0
  60. package/src/lexicon/types/tools/ozone/moderation/defs.ts +56 -0
  61. package/src/lexicon/types/tools/ozone/moderation/emitEvent.ts +3 -1
  62. package/src/lexicon/types/tools/ozone/moderation/queryStatuses.ts +2 -0
  63. package/src/mod-service/index.ts +33 -0
  64. package/src/mod-service/status.ts +26 -0
  65. package/src/mod-service/views.ts +3 -0
  66. package/tests/__snapshots__/moderation-events.test.ts.snap +7 -0
  67. package/tests/report-muting.test.ts +100 -0
@@ -57,6 +57,15 @@ const getSubjectStatusForModerationEvent = ({
57
57
  suspendUntil: null,
58
58
  lastReviewedAt: createdAt,
59
59
  }
60
+ case 'tools.ozone.moderation.defs#modEventUnmuteReporter':
61
+ return {
62
+ lastReviewedBy: createdBy,
63
+ muteReportingUntil: null,
64
+ // It's not likely to receive an unmute event that does not already have a status row
65
+ // but if it does happen, default to unnecessary
66
+ reviewState: defaultReviewState,
67
+ lastReviewedAt: createdAt,
68
+ }
60
69
  case 'tools.ozone.moderation.defs#modEventUnmute':
61
70
  return {
62
71
  lastReviewedBy: createdBy,
@@ -76,6 +85,18 @@ const getSubjectStatusForModerationEvent = ({
76
85
  ? new Date(Date.now() + durationInHours * HOUR).toISOString()
77
86
  : null,
78
87
  }
88
+ case 'tools.ozone.moderation.defs#modEventMuteReporter':
89
+ return {
90
+ lastReviewedBy: createdBy,
91
+ lastReviewedAt: createdAt,
92
+ // By default, mute for 24hrs
93
+ muteReportingUntil: new Date(
94
+ Date.now() + (durationInHours || 24) * HOUR,
95
+ ).toISOString(),
96
+ // It's not likely to receive a mute event on a subject that does not already have a status row
97
+ // but if it does happen, default to unnecessary
98
+ reviewState: defaultReviewState,
99
+ }
79
100
  case 'tools.ozone.moderation.defs#modEventMute':
80
101
  return {
81
102
  lastReviewedBy: createdBy,
@@ -140,6 +161,11 @@ export const adjustModerationSubjectStatus = async (
140
161
  .selectAll()
141
162
  .executeTakeFirst()
142
163
 
164
+ // If reporting is muted for this reporter, we don't want to update the subject status
165
+ if (meta?.isReporterMuted) {
166
+ return currentStatus || null
167
+ }
168
+
143
169
  const isAppealEvent =
144
170
  action === 'tools.ozone.moderation.defs#modEventReport' &&
145
171
  meta?.reportType === REASONAPPEAL
@@ -108,6 +108,7 @@ export class ModerationViews {
108
108
 
109
109
  if (
110
110
  [
111
+ 'tools.ozone.moderation.defs#modEventMuteReporter',
111
112
  'tools.ozone.moderation.defs#modEventTakedown',
112
113
  'tools.ozone.moderation.defs#modEventMute',
113
114
  ].includes(event.action)
@@ -157,6 +158,7 @@ export class ModerationViews {
157
158
  eventView.event = {
158
159
  ...eventView.event,
159
160
  reportType: event.meta?.reportType ?? undefined,
161
+ isReporterMuted: !!event.meta?.isReporterMuted,
160
162
  }
161
163
  }
162
164
 
@@ -500,6 +502,7 @@ export class ModerationViews {
500
502
  lastReportedAt: status.lastReportedAt ?? undefined,
501
503
  lastAppealedAt: status.lastAppealedAt ?? undefined,
502
504
  muteUntil: status.muteUntil ?? undefined,
505
+ muteReportingUntil: status.muteReportingUntil ?? undefined,
503
506
  suspendUntil: status.suspendUntil ?? undefined,
504
507
  takendown: status.takendown ?? undefined,
505
508
  appealed: status.appealed ?? undefined,
@@ -7,6 +7,7 @@ Object {
7
7
  "event": Object {
8
8
  "$type": "tools.ozone.moderation.defs#modEventReport",
9
9
  "comment": "X",
10
+ "isReporterMuted": false,
10
11
  "reportType": "com.atproto.moderation.defs#reasonMisleading",
11
12
  },
12
13
  "id": 1,
@@ -77,6 +78,7 @@ Array [
77
78
  "event": Object {
78
79
  "$type": "tools.ozone.moderation.defs#modEventReport",
79
80
  "comment": "X",
81
+ "isReporterMuted": false,
80
82
  "reportType": "com.atproto.moderation.defs#reasonSpam",
81
83
  },
82
84
  "id": 11,
@@ -90,6 +92,7 @@ Array [
90
92
  Object {
91
93
  "createdAt": "1970-01-01T00:00:00.000Z",
92
94
  "createdBy": "user(2)",
95
+ "creatorHandle": "mod-authority.test",
93
96
  "event": Object {
94
97
  "$type": "tools.ozone.moderation.defs#modEventTag",
95
98
  "add": Array [
@@ -113,6 +116,7 @@ Array [
113
116
  "event": Object {
114
117
  "$type": "tools.ozone.moderation.defs#modEventReport",
115
118
  "comment": "X",
119
+ "isReporterMuted": false,
116
120
  "reportType": "com.atproto.moderation.defs#reasonSpam",
117
121
  },
118
122
  "id": 5,
@@ -135,6 +139,7 @@ Array [
135
139
  "event": Object {
136
140
  "$type": "tools.ozone.moderation.defs#modEventReport",
137
141
  "comment": "X",
142
+ "isReporterMuted": false,
138
143
  "reportType": "com.atproto.moderation.defs#reasonSpam",
139
144
  },
140
145
  "id": 10,
@@ -149,6 +154,7 @@ Array [
149
154
  Object {
150
155
  "createdAt": "1970-01-01T00:00:00.000Z",
151
156
  "createdBy": "user(1)",
157
+ "creatorHandle": "mod-authority.test",
152
158
  "event": Object {
153
159
  "$type": "tools.ozone.moderation.defs#modEventTag",
154
160
  "add": Array [
@@ -172,6 +178,7 @@ Array [
172
178
  "event": Object {
173
179
  "$type": "tools.ozone.moderation.defs#modEventReport",
174
180
  "comment": "X",
181
+ "isReporterMuted": false,
175
182
  "reportType": "com.atproto.moderation.defs#reasonSpam",
176
183
  },
177
184
  "id": 3,
@@ -0,0 +1,100 @@
1
+ import {
2
+ TestNetwork,
3
+ SeedClient,
4
+ basicSeed,
5
+ ModeratorClient,
6
+ } from '@atproto/dev-env'
7
+ import {
8
+ ComAtprotoModerationDefs,
9
+ ToolsOzoneModerationDefs,
10
+ } from '@atproto/api'
11
+ import {
12
+ REVIEWNONE,
13
+ REVIEWOPEN,
14
+ } from '../src/lexicon/types/tools/ozone/moderation/defs'
15
+
16
+ describe('report-muting', () => {
17
+ let network: TestNetwork
18
+ let sc: SeedClient
19
+ let modClient: ModeratorClient
20
+
21
+ beforeAll(async () => {
22
+ network = await TestNetwork.create({
23
+ dbPostgresSchema: 'ozone_report_muting',
24
+ })
25
+ sc = network.getSeedClient()
26
+ modClient = network.ozone.getModClient()
27
+ await basicSeed(sc)
28
+ await network.processAll()
29
+ })
30
+
31
+ afterAll(async () => {
32
+ await network.close()
33
+ })
34
+
35
+ const assertSubjectStatus = async (
36
+ subject: string,
37
+ status?: string,
38
+ ): Promise<ToolsOzoneModerationDefs.SubjectStatusView | undefined> => {
39
+ const res = await modClient.queryStatuses({
40
+ subject,
41
+ })
42
+ expect(res.subjectStatuses[0]?.reviewState).toEqual(status)
43
+ return res.subjectStatuses[0]
44
+ }
45
+
46
+ it('does not change reviewState when muted reporter reports', async () => {
47
+ const bobsPostSubject = {
48
+ $type: 'com.atproto.repo.strongRef',
49
+ uri: sc.posts[sc.dids.bob][1].ref.uriStr,
50
+ cid: sc.posts[sc.dids.bob][1].ref.cidStr,
51
+ }
52
+ const carolsAccountSubject = {
53
+ $type: 'com.atproto.admin.defs#repoRef',
54
+ did: sc.dids.carol,
55
+ }
56
+
57
+ await modClient.emitEvent({
58
+ event: {
59
+ $type: 'tools.ozone.moderation.defs#modEventMuteReporter',
60
+ durationInHours: 24,
61
+ },
62
+ subject: carolsAccountSubject,
63
+ })
64
+ await sc.createReport({
65
+ reportedBy: sc.dids.carol,
66
+ reasonType: ComAtprotoModerationDefs.REASONMISLEADING,
67
+ reason: 'misleading',
68
+ subject: bobsPostSubject,
69
+ })
70
+
71
+ // Verify that a subject status was not created for bob's post since the reporter was muted
72
+ await assertSubjectStatus(bobsPostSubject.uri, undefined)
73
+ // Verify, however, that the event was logged
74
+ await modClient.queryEvents({
75
+ subject: bobsPostSubject.uri,
76
+ })
77
+
78
+ // Verify that reporting mute duration is stored for the reporter
79
+ const carolsStatus = await assertSubjectStatus(sc.dids.carol, REVIEWNONE)
80
+ expect(
81
+ new Date(`${carolsStatus?.muteReportingUntil}`).getTime(),
82
+ ).toBeGreaterThan(Date.now())
83
+
84
+ await modClient.emitEvent({
85
+ event: {
86
+ $type: 'tools.ozone.moderation.defs#modEventUnmuteReporter',
87
+ },
88
+ subject: carolsAccountSubject,
89
+ })
90
+ await sc.createReport({
91
+ reportedBy: sc.dids.carol,
92
+ reasonType: ComAtprotoModerationDefs.REASONMISLEADING,
93
+ reason: 'misleading',
94
+ subject: bobsPostSubject,
95
+ })
96
+
97
+ // Verify that a subject status was created for bob's post since the reporter was no longer muted
98
+ await assertSubjectStatus(bobsPostSubject.uri, REVIEWOPEN)
99
+ })
100
+ })