@atproto/ozone 0.1.79 → 0.1.81
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.
- package/CHANGELOG.md +15 -0
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +2 -0
- package/dist/api/index.js.map +1 -1
- package/dist/api/moderation/getReporterStats.d.ts +4 -0
- package/dist/api/moderation/getReporterStats.d.ts.map +1 -0
- package/dist/api/moderation/getReporterStats.js +17 -0
- package/dist/api/moderation/getReporterStats.js.map +1 -0
- package/dist/daemon/materialized-view-refresher.d.ts.map +1 -1
- package/dist/daemon/materialized-view-refresher.js +1 -0
- package/dist/daemon/materialized-view-refresher.js.map +1 -1
- package/dist/db/migrations/20250211T003647759Z-add-reporter-stats-index.d.ts +4 -0
- package/dist/db/migrations/20250211T003647759Z-add-reporter-stats-index.d.ts.map +1 -0
- package/dist/db/migrations/20250211T003647759Z-add-reporter-stats-index.js +38 -0
- package/dist/db/migrations/20250211T003647759Z-add-reporter-stats-index.js.map +1 -0
- package/dist/db/migrations/index.d.ts +1 -0
- package/dist/db/migrations/index.d.ts.map +1 -1
- package/dist/db/migrations/index.js +2 -1
- package/dist/db/migrations/index.js.map +1 -1
- package/dist/lexicon/index.d.ts +4 -0
- package/dist/lexicon/index.d.ts.map +1 -1
- package/dist/lexicon/index.js +8 -0
- package/dist/lexicon/index.js.map +1 -1
- package/dist/lexicon/lexicons.d.ts +300 -0
- package/dist/lexicon/lexicons.d.ts.map +1 -1
- package/dist/lexicon/lexicons.js +162 -0
- package/dist/lexicon/lexicons.js.map +1 -1
- package/dist/lexicon/types/com/atproto/sync/listReposByCollection.d.ts +46 -0
- package/dist/lexicon/types/com/atproto/sync/listReposByCollection.d.ts.map +1 -0
- package/dist/lexicon/types/com/atproto/sync/listReposByCollection.js +16 -0
- package/dist/lexicon/types/com/atproto/sync/listReposByCollection.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/moderation/defs.d.ts +22 -0
- package/dist/lexicon/types/tools/ozone/moderation/defs.d.ts.map +1 -1
- package/dist/lexicon/types/tools/ozone/moderation/defs.js +9 -0
- package/dist/lexicon/types/tools/ozone/moderation/defs.js.map +1 -1
- package/dist/lexicon/types/tools/ozone/moderation/getReporterStats.d.ts +36 -0
- package/dist/lexicon/types/tools/ozone/moderation/getReporterStats.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/moderation/getReporterStats.js +7 -0
- package/dist/lexicon/types/tools/ozone/moderation/getReporterStats.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/team/listMembers.d.ts +2 -0
- package/dist/lexicon/types/tools/ozone/team/listMembers.d.ts.map +1 -1
- package/dist/mod-service/index.d.ts +5 -1
- package/dist/mod-service/index.d.ts.map +1 -1
- package/dist/mod-service/index.js +94 -0
- package/dist/mod-service/index.js.map +1 -1
- package/dist/mod-service/types.d.ts +21 -0
- package/dist/mod-service/types.d.ts.map +1 -1
- package/dist/team/index.d.ts +3 -1
- package/dist/team/index.d.ts.map +1 -1
- package/dist/team/index.js +13 -1
- package/dist/team/index.js.map +1 -1
- package/package.json +3 -3
- package/src/api/index.ts +2 -0
- package/src/api/moderation/getReporterStats.ts +18 -0
- package/src/daemon/materialized-view-refresher.ts +1 -0
- package/src/db/migrations/20250211T003647759Z-add-reporter-stats-index.ts +38 -0
- package/src/db/migrations/index.ts +1 -0
- package/src/lexicon/index.ts +24 -0
- package/src/lexicon/lexicons.ts +171 -0
- package/src/lexicon/types/com/atproto/sync/listReposByCollection.ts +68 -0
- package/src/lexicon/types/tools/ozone/moderation/defs.ts +31 -0
- package/src/lexicon/types/tools/ozone/moderation/getReporterStats.ts +50 -0
- package/src/lexicon/types/tools/ozone/team/listMembers.ts +2 -0
- package/src/mod-service/index.ts +126 -0
- package/src/mod-service/types.ts +23 -0
- package/src/team/index.ts +20 -0
- package/tests/__snapshots__/team.test.ts.snap +0 -338
- package/tests/get-reporter-stats.test.ts +117 -0
- package/tests/team.test.ts +56 -26
- package/tsconfig.build.tsbuildinfo +1 -1
- package/tsconfig.tests.tsbuildinfo +1 -1
package/src/lexicon/lexicons.ts
CHANGED
|
@@ -3661,6 +3661,67 @@ export const schemaDict = {
|
|
|
3661
3661
|
},
|
|
3662
3662
|
},
|
|
3663
3663
|
},
|
|
3664
|
+
ComAtprotoSyncListReposByCollection: {
|
|
3665
|
+
lexicon: 1,
|
|
3666
|
+
id: 'com.atproto.sync.listReposByCollection',
|
|
3667
|
+
defs: {
|
|
3668
|
+
main: {
|
|
3669
|
+
type: 'query',
|
|
3670
|
+
description:
|
|
3671
|
+
'Enumerates all the DIDs which have records with the given collection NSID.',
|
|
3672
|
+
parameters: {
|
|
3673
|
+
type: 'params',
|
|
3674
|
+
required: ['collection'],
|
|
3675
|
+
properties: {
|
|
3676
|
+
collection: {
|
|
3677
|
+
type: 'string',
|
|
3678
|
+
format: 'nsid',
|
|
3679
|
+
},
|
|
3680
|
+
limit: {
|
|
3681
|
+
type: 'integer',
|
|
3682
|
+
description:
|
|
3683
|
+
'Maximum size of response set. Recommend setting a large maximum (1000+) when enumerating large DID lists.',
|
|
3684
|
+
minimum: 1,
|
|
3685
|
+
maximum: 2000,
|
|
3686
|
+
default: 500,
|
|
3687
|
+
},
|
|
3688
|
+
cursor: {
|
|
3689
|
+
type: 'string',
|
|
3690
|
+
},
|
|
3691
|
+
},
|
|
3692
|
+
},
|
|
3693
|
+
output: {
|
|
3694
|
+
encoding: 'application/json',
|
|
3695
|
+
schema: {
|
|
3696
|
+
type: 'object',
|
|
3697
|
+
required: ['repos'],
|
|
3698
|
+
properties: {
|
|
3699
|
+
cursor: {
|
|
3700
|
+
type: 'string',
|
|
3701
|
+
},
|
|
3702
|
+
repos: {
|
|
3703
|
+
type: 'array',
|
|
3704
|
+
items: {
|
|
3705
|
+
type: 'ref',
|
|
3706
|
+
ref: 'lex:com.atproto.sync.listReposByCollection#repo',
|
|
3707
|
+
},
|
|
3708
|
+
},
|
|
3709
|
+
},
|
|
3710
|
+
},
|
|
3711
|
+
},
|
|
3712
|
+
},
|
|
3713
|
+
repo: {
|
|
3714
|
+
type: 'object',
|
|
3715
|
+
required: ['did'],
|
|
3716
|
+
properties: {
|
|
3717
|
+
did: {
|
|
3718
|
+
type: 'string',
|
|
3719
|
+
format: 'did',
|
|
3720
|
+
},
|
|
3721
|
+
},
|
|
3722
|
+
},
|
|
3723
|
+
},
|
|
3724
|
+
},
|
|
3664
3725
|
ComAtprotoSyncNotifyOfUpdate: {
|
|
3665
3726
|
lexicon: 1,
|
|
3666
3727
|
id: 'com.atproto.sync.notifyOfUpdate',
|
|
@@ -12219,6 +12280,64 @@ export const schemaDict = {
|
|
|
12219
12280
|
},
|
|
12220
12281
|
},
|
|
12221
12282
|
},
|
|
12283
|
+
reporterStats: {
|
|
12284
|
+
type: 'object',
|
|
12285
|
+
required: [
|
|
12286
|
+
'did',
|
|
12287
|
+
'accountReportCount',
|
|
12288
|
+
'recordReportCount',
|
|
12289
|
+
'reportedAccountCount',
|
|
12290
|
+
'reportedRecordCount',
|
|
12291
|
+
'takendownAccountCount',
|
|
12292
|
+
'takendownRecordCount',
|
|
12293
|
+
'labeledAccountCount',
|
|
12294
|
+
'labeledRecordCount',
|
|
12295
|
+
],
|
|
12296
|
+
properties: {
|
|
12297
|
+
did: {
|
|
12298
|
+
type: 'string',
|
|
12299
|
+
format: 'did',
|
|
12300
|
+
},
|
|
12301
|
+
accountReportCount: {
|
|
12302
|
+
type: 'integer',
|
|
12303
|
+
description:
|
|
12304
|
+
'The total number of reports made by the user on accounts.',
|
|
12305
|
+
},
|
|
12306
|
+
recordReportCount: {
|
|
12307
|
+
type: 'integer',
|
|
12308
|
+
description:
|
|
12309
|
+
'The total number of reports made by the user on records.',
|
|
12310
|
+
},
|
|
12311
|
+
reportedAccountCount: {
|
|
12312
|
+
type: 'integer',
|
|
12313
|
+
description: 'The total number of accounts reported by the user.',
|
|
12314
|
+
},
|
|
12315
|
+
reportedRecordCount: {
|
|
12316
|
+
type: 'integer',
|
|
12317
|
+
description: 'The total number of records reported by the user.',
|
|
12318
|
+
},
|
|
12319
|
+
takendownAccountCount: {
|
|
12320
|
+
type: 'integer',
|
|
12321
|
+
description:
|
|
12322
|
+
"The total number of accounts taken down as a result of the user's reports.",
|
|
12323
|
+
},
|
|
12324
|
+
takendownRecordCount: {
|
|
12325
|
+
type: 'integer',
|
|
12326
|
+
description:
|
|
12327
|
+
"The total number of records taken down as a result of the user's reports.",
|
|
12328
|
+
},
|
|
12329
|
+
labeledAccountCount: {
|
|
12330
|
+
type: 'integer',
|
|
12331
|
+
description:
|
|
12332
|
+
"The total number of accounts labeled as a result of the user's reports.",
|
|
12333
|
+
},
|
|
12334
|
+
labeledRecordCount: {
|
|
12335
|
+
type: 'integer',
|
|
12336
|
+
description:
|
|
12337
|
+
"The total number of records labeled as a result of the user's reports.",
|
|
12338
|
+
},
|
|
12339
|
+
},
|
|
12340
|
+
},
|
|
12222
12341
|
},
|
|
12223
12342
|
},
|
|
12224
12343
|
ToolsOzoneModerationEmitEvent: {
|
|
@@ -12430,6 +12549,46 @@ export const schemaDict = {
|
|
|
12430
12549
|
},
|
|
12431
12550
|
},
|
|
12432
12551
|
},
|
|
12552
|
+
ToolsOzoneModerationGetReporterStats: {
|
|
12553
|
+
lexicon: 1,
|
|
12554
|
+
id: 'tools.ozone.moderation.getReporterStats',
|
|
12555
|
+
defs: {
|
|
12556
|
+
main: {
|
|
12557
|
+
type: 'query',
|
|
12558
|
+
description: 'Get reporter stats for a list of users.',
|
|
12559
|
+
parameters: {
|
|
12560
|
+
type: 'params',
|
|
12561
|
+
required: ['dids'],
|
|
12562
|
+
properties: {
|
|
12563
|
+
dids: {
|
|
12564
|
+
type: 'array',
|
|
12565
|
+
maxLength: 100,
|
|
12566
|
+
items: {
|
|
12567
|
+
type: 'string',
|
|
12568
|
+
format: 'did',
|
|
12569
|
+
},
|
|
12570
|
+
},
|
|
12571
|
+
},
|
|
12572
|
+
},
|
|
12573
|
+
output: {
|
|
12574
|
+
encoding: 'application/json',
|
|
12575
|
+
schema: {
|
|
12576
|
+
type: 'object',
|
|
12577
|
+
required: ['stats'],
|
|
12578
|
+
properties: {
|
|
12579
|
+
stats: {
|
|
12580
|
+
type: 'array',
|
|
12581
|
+
items: {
|
|
12582
|
+
type: 'ref',
|
|
12583
|
+
ref: 'lex:tools.ozone.moderation.defs#reporterStats',
|
|
12584
|
+
},
|
|
12585
|
+
},
|
|
12586
|
+
},
|
|
12587
|
+
},
|
|
12588
|
+
},
|
|
12589
|
+
},
|
|
12590
|
+
},
|
|
12591
|
+
},
|
|
12433
12592
|
ToolsOzoneModerationGetRepos: {
|
|
12434
12593
|
lexicon: 1,
|
|
12435
12594
|
id: 'tools.ozone.moderation.getRepos',
|
|
@@ -13794,6 +13953,15 @@ export const schemaDict = {
|
|
|
13794
13953
|
parameters: {
|
|
13795
13954
|
type: 'params',
|
|
13796
13955
|
properties: {
|
|
13956
|
+
disabled: {
|
|
13957
|
+
type: 'boolean',
|
|
13958
|
+
},
|
|
13959
|
+
roles: {
|
|
13960
|
+
type: 'array',
|
|
13961
|
+
items: {
|
|
13962
|
+
type: 'string',
|
|
13963
|
+
},
|
|
13964
|
+
},
|
|
13797
13965
|
limit: {
|
|
13798
13966
|
type: 'integer',
|
|
13799
13967
|
minimum: 1,
|
|
@@ -13993,6 +14161,7 @@ export const ids = {
|
|
|
13993
14161
|
ComAtprotoSyncGetRepoStatus: 'com.atproto.sync.getRepoStatus',
|
|
13994
14162
|
ComAtprotoSyncListBlobs: 'com.atproto.sync.listBlobs',
|
|
13995
14163
|
ComAtprotoSyncListRepos: 'com.atproto.sync.listRepos',
|
|
14164
|
+
ComAtprotoSyncListReposByCollection: 'com.atproto.sync.listReposByCollection',
|
|
13996
14165
|
ComAtprotoSyncNotifyOfUpdate: 'com.atproto.sync.notifyOfUpdate',
|
|
13997
14166
|
ComAtprotoSyncRequestCrawl: 'com.atproto.sync.requestCrawl',
|
|
13998
14167
|
ComAtprotoSyncSubscribeRepos: 'com.atproto.sync.subscribeRepos',
|
|
@@ -14133,6 +14302,8 @@ export const ids = {
|
|
|
14133
14302
|
ToolsOzoneModerationGetRecord: 'tools.ozone.moderation.getRecord',
|
|
14134
14303
|
ToolsOzoneModerationGetRecords: 'tools.ozone.moderation.getRecords',
|
|
14135
14304
|
ToolsOzoneModerationGetRepo: 'tools.ozone.moderation.getRepo',
|
|
14305
|
+
ToolsOzoneModerationGetReporterStats:
|
|
14306
|
+
'tools.ozone.moderation.getReporterStats',
|
|
14136
14307
|
ToolsOzoneModerationGetRepos: 'tools.ozone.moderation.getRepos',
|
|
14137
14308
|
ToolsOzoneModerationQueryEvents: 'tools.ozone.moderation.queryEvents',
|
|
14138
14309
|
ToolsOzoneModerationQueryStatuses: 'tools.ozone.moderation.queryStatuses',
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GENERATED CODE - DO NOT MODIFY
|
|
3
|
+
*/
|
|
4
|
+
import express from 'express'
|
|
5
|
+
import { ValidationResult, BlobRef } from '@atproto/lexicon'
|
|
6
|
+
import { CID } from 'multiformats/cid'
|
|
7
|
+
import { validate as _validate } from '../../../../lexicons'
|
|
8
|
+
import { $Typed, is$typed as _is$typed, OmitKey } from '../../../../util'
|
|
9
|
+
import { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server'
|
|
10
|
+
|
|
11
|
+
const is$typed = _is$typed,
|
|
12
|
+
validate = _validate
|
|
13
|
+
const id = 'com.atproto.sync.listReposByCollection'
|
|
14
|
+
|
|
15
|
+
export interface QueryParams {
|
|
16
|
+
collection: string
|
|
17
|
+
/** Maximum size of response set. Recommend setting a large maximum (1000+) when enumerating large DID lists. */
|
|
18
|
+
limit: number
|
|
19
|
+
cursor?: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type InputSchema = undefined
|
|
23
|
+
|
|
24
|
+
export interface OutputSchema {
|
|
25
|
+
cursor?: string
|
|
26
|
+
repos: Repo[]
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type HandlerInput = undefined
|
|
30
|
+
|
|
31
|
+
export interface HandlerSuccess {
|
|
32
|
+
encoding: 'application/json'
|
|
33
|
+
body: OutputSchema
|
|
34
|
+
headers?: { [key: string]: string }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface HandlerError {
|
|
38
|
+
status: number
|
|
39
|
+
message?: string
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export type HandlerOutput = HandlerError | HandlerSuccess | HandlerPipeThrough
|
|
43
|
+
export type HandlerReqCtx<HA extends HandlerAuth = never> = {
|
|
44
|
+
auth: HA
|
|
45
|
+
params: QueryParams
|
|
46
|
+
input: HandlerInput
|
|
47
|
+
req: express.Request
|
|
48
|
+
res: express.Response
|
|
49
|
+
resetRouteRateLimits: () => Promise<void>
|
|
50
|
+
}
|
|
51
|
+
export type Handler<HA extends HandlerAuth = never> = (
|
|
52
|
+
ctx: HandlerReqCtx<HA>,
|
|
53
|
+
) => Promise<HandlerOutput> | HandlerOutput
|
|
54
|
+
|
|
55
|
+
export interface Repo {
|
|
56
|
+
$type?: 'com.atproto.sync.listReposByCollection#repo'
|
|
57
|
+
did: string
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const hashRepo = 'repo'
|
|
61
|
+
|
|
62
|
+
export function isRepo<V>(v: V) {
|
|
63
|
+
return is$typed(v, id, hashRepo)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function validateRepo<V>(v: V) {
|
|
67
|
+
return validate<Repo & V>(v, id, hashRepo)
|
|
68
|
+
}
|
|
@@ -836,3 +836,34 @@ export function isRecordHosting<V>(v: V) {
|
|
|
836
836
|
export function validateRecordHosting<V>(v: V) {
|
|
837
837
|
return validate<RecordHosting & V>(v, id, hashRecordHosting)
|
|
838
838
|
}
|
|
839
|
+
|
|
840
|
+
export interface ReporterStats {
|
|
841
|
+
$type?: 'tools.ozone.moderation.defs#reporterStats'
|
|
842
|
+
did: string
|
|
843
|
+
/** The total number of reports made by the user on accounts. */
|
|
844
|
+
accountReportCount: number
|
|
845
|
+
/** The total number of reports made by the user on records. */
|
|
846
|
+
recordReportCount: number
|
|
847
|
+
/** The total number of accounts reported by the user. */
|
|
848
|
+
reportedAccountCount: number
|
|
849
|
+
/** The total number of records reported by the user. */
|
|
850
|
+
reportedRecordCount: number
|
|
851
|
+
/** The total number of accounts taken down as a result of the user's reports. */
|
|
852
|
+
takendownAccountCount: number
|
|
853
|
+
/** The total number of records taken down as a result of the user's reports. */
|
|
854
|
+
takendownRecordCount: number
|
|
855
|
+
/** The total number of accounts labeled as a result of the user's reports. */
|
|
856
|
+
labeledAccountCount: number
|
|
857
|
+
/** The total number of records labeled as a result of the user's reports. */
|
|
858
|
+
labeledRecordCount: number
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
const hashReporterStats = 'reporterStats'
|
|
862
|
+
|
|
863
|
+
export function isReporterStats<V>(v: V) {
|
|
864
|
+
return is$typed(v, id, hashReporterStats)
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
export function validateReporterStats<V>(v: V) {
|
|
868
|
+
return validate<ReporterStats & V>(v, id, hashReporterStats)
|
|
869
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GENERATED CODE - DO NOT MODIFY
|
|
3
|
+
*/
|
|
4
|
+
import express from 'express'
|
|
5
|
+
import { ValidationResult, BlobRef } from '@atproto/lexicon'
|
|
6
|
+
import { CID } from 'multiformats/cid'
|
|
7
|
+
import { validate as _validate } from '../../../../lexicons'
|
|
8
|
+
import { $Typed, is$typed as _is$typed, OmitKey } from '../../../../util'
|
|
9
|
+
import { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server'
|
|
10
|
+
import type * as ToolsOzoneModerationDefs from './defs.js'
|
|
11
|
+
|
|
12
|
+
const is$typed = _is$typed,
|
|
13
|
+
validate = _validate
|
|
14
|
+
const id = 'tools.ozone.moderation.getReporterStats'
|
|
15
|
+
|
|
16
|
+
export interface QueryParams {
|
|
17
|
+
dids: string[]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type InputSchema = undefined
|
|
21
|
+
|
|
22
|
+
export interface OutputSchema {
|
|
23
|
+
stats: ToolsOzoneModerationDefs.ReporterStats[]
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type HandlerInput = undefined
|
|
27
|
+
|
|
28
|
+
export interface HandlerSuccess {
|
|
29
|
+
encoding: 'application/json'
|
|
30
|
+
body: OutputSchema
|
|
31
|
+
headers?: { [key: string]: string }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface HandlerError {
|
|
35
|
+
status: number
|
|
36
|
+
message?: string
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type HandlerOutput = HandlerError | HandlerSuccess | HandlerPipeThrough
|
|
40
|
+
export type HandlerReqCtx<HA extends HandlerAuth = never> = {
|
|
41
|
+
auth: HA
|
|
42
|
+
params: QueryParams
|
|
43
|
+
input: HandlerInput
|
|
44
|
+
req: express.Request
|
|
45
|
+
res: express.Response
|
|
46
|
+
resetRouteRateLimits: () => Promise<void>
|
|
47
|
+
}
|
|
48
|
+
export type Handler<HA extends HandlerAuth = never> = (
|
|
49
|
+
ctx: HandlerReqCtx<HA>,
|
|
50
|
+
) => Promise<HandlerOutput> | HandlerOutput
|
package/src/mod-service/index.ts
CHANGED
|
@@ -58,6 +58,8 @@ import {
|
|
|
58
58
|
ModerationEventRow,
|
|
59
59
|
ModerationSubjectStatusRow,
|
|
60
60
|
ModerationSubjectStatusRowWithHandle,
|
|
61
|
+
ReporterStats,
|
|
62
|
+
ReporterStatsResult,
|
|
61
63
|
ReversibleModerationEvent,
|
|
62
64
|
} from './types'
|
|
63
65
|
import { formatLabel, formatLabelRow, signLabel } from './util'
|
|
@@ -1272,6 +1274,130 @@ export class ModerationService {
|
|
|
1272
1274
|
throw new InvalidRequestError('Email was accepted but not sent')
|
|
1273
1275
|
}
|
|
1274
1276
|
}
|
|
1277
|
+
|
|
1278
|
+
buildModerationQuery(
|
|
1279
|
+
subjectType: 'account' | 'record',
|
|
1280
|
+
createdByDids: string[],
|
|
1281
|
+
isActionQuery: boolean,
|
|
1282
|
+
): Promise<(Partial<ReporterStatsResult> & { did: string })[]> {
|
|
1283
|
+
const isAccount = subjectType === 'account'
|
|
1284
|
+
const actionTypes = [
|
|
1285
|
+
'tools.ozone.moderation.defs#modEventTakedown',
|
|
1286
|
+
'tools.ozone.moderation.defs#modEventLabel',
|
|
1287
|
+
] as const
|
|
1288
|
+
|
|
1289
|
+
const query = this.db.db
|
|
1290
|
+
.selectFrom('moderation_event as reports')
|
|
1291
|
+
.where(
|
|
1292
|
+
'reports.action',
|
|
1293
|
+
'=',
|
|
1294
|
+
'tools.ozone.moderation.defs#modEventReport',
|
|
1295
|
+
)
|
|
1296
|
+
.where('reports.subjectUri', isAccount ? 'is' : 'is not', null)
|
|
1297
|
+
.where('reports.createdBy', 'in', createdByDids)
|
|
1298
|
+
.select(['reports.createdBy as did'])
|
|
1299
|
+
|
|
1300
|
+
if (isActionQuery) {
|
|
1301
|
+
return query
|
|
1302
|
+
.leftJoin('moderation_event as actions', (join) =>
|
|
1303
|
+
join
|
|
1304
|
+
.onRef('actions.subjectDid', '=', 'reports.subjectDid')
|
|
1305
|
+
.on('actions.subjectUri', isAccount ? 'is' : 'is not', null)
|
|
1306
|
+
.onRef('actions.createdAt', '>', 'reports.createdAt')
|
|
1307
|
+
.on('actions.action', 'in', actionTypes),
|
|
1308
|
+
)
|
|
1309
|
+
.select([
|
|
1310
|
+
() =>
|
|
1311
|
+
sql<number>`COUNT(DISTINCT actions."subjectDid") FILTER (
|
|
1312
|
+
WHERE actions."action" = 'tools.ozone.moderation.defs#modEventTakedown'
|
|
1313
|
+
)`.as(`takendown${isAccount ? 'Account' : 'Record'}Count`),
|
|
1314
|
+
|
|
1315
|
+
() =>
|
|
1316
|
+
sql<number>`COUNT(DISTINCT actions."subjectDid") FILTER (
|
|
1317
|
+
WHERE actions."action" = 'tools.ozone.moderation.defs#modEventLabel'
|
|
1318
|
+
)`.as(`labeled${isAccount ? 'Account' : 'Record'}Count`),
|
|
1319
|
+
])
|
|
1320
|
+
.groupBy('reports.createdBy')
|
|
1321
|
+
.execute()
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
return query
|
|
1325
|
+
.select([
|
|
1326
|
+
(eb) =>
|
|
1327
|
+
eb.fn.count<number>('reports.id').as(`${subjectType}ReportCount`),
|
|
1328
|
+
(eb) =>
|
|
1329
|
+
eb.fn
|
|
1330
|
+
.count<number>(
|
|
1331
|
+
isAccount ? 'reports.subjectDid' : 'reports.subjectUri',
|
|
1332
|
+
)
|
|
1333
|
+
.distinct()
|
|
1334
|
+
.as(`reported${isAccount ? 'Account' : 'Record'}Count`),
|
|
1335
|
+
])
|
|
1336
|
+
.groupBy('reports.createdBy')
|
|
1337
|
+
.execute()
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
async getReporterStats(dids: string[]) {
|
|
1341
|
+
const [accountReports, recordReports, accountActions, recordActions] =
|
|
1342
|
+
await Promise.all([
|
|
1343
|
+
this.buildModerationQuery('account', dids, false),
|
|
1344
|
+
this.buildModerationQuery('record', dids, false),
|
|
1345
|
+
this.buildModerationQuery('account', dids, true),
|
|
1346
|
+
this.buildModerationQuery('record', dids, true),
|
|
1347
|
+
])
|
|
1348
|
+
|
|
1349
|
+
// Create a map to hold the aggregated stats for each `did`
|
|
1350
|
+
const statsMap = new Map<string, ReporterStats>()
|
|
1351
|
+
|
|
1352
|
+
// Helper function to ensure a `did` entry exists in the map
|
|
1353
|
+
const ensureDidEntry = (did: string) => {
|
|
1354
|
+
if (!statsMap.has(did)) {
|
|
1355
|
+
statsMap.set(did, {
|
|
1356
|
+
did,
|
|
1357
|
+
accountReportCount: 0,
|
|
1358
|
+
recordReportCount: 0,
|
|
1359
|
+
reportedAccountCount: 0,
|
|
1360
|
+
reportedRecordCount: 0,
|
|
1361
|
+
takendownAccountCount: 0,
|
|
1362
|
+
takendownRecordCount: 0,
|
|
1363
|
+
labeledAccountCount: 0,
|
|
1364
|
+
labeledRecordCount: 0,
|
|
1365
|
+
})
|
|
1366
|
+
}
|
|
1367
|
+
return statsMap.get(did)!
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
// Merge accountReports
|
|
1371
|
+
for (const report of accountReports) {
|
|
1372
|
+
const entry = ensureDidEntry(report.did)
|
|
1373
|
+
entry.accountReportCount = report.accountReportCount ?? 0
|
|
1374
|
+
entry.reportedAccountCount = report.reportedAccountCount ?? 0
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
// Merge recordReports
|
|
1378
|
+
for (const report of recordReports) {
|
|
1379
|
+
const entry = ensureDidEntry(report.did)
|
|
1380
|
+
entry.recordReportCount = report.recordReportCount ?? 0
|
|
1381
|
+
entry.reportedRecordCount = report.reportedRecordCount ?? 0
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
// Merge accountActions
|
|
1385
|
+
for (const action of accountActions) {
|
|
1386
|
+
const entry = ensureDidEntry(action.did)
|
|
1387
|
+
entry.takendownAccountCount = action.takendownAccountCount ?? 0
|
|
1388
|
+
entry.labeledAccountCount = action.labeledAccountCount ?? 0
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
// Merge recordActions
|
|
1392
|
+
for (const action of recordActions) {
|
|
1393
|
+
const entry = ensureDidEntry(action.did)
|
|
1394
|
+
entry.takendownRecordCount = action.takendownRecordCount ?? 0
|
|
1395
|
+
entry.labeledRecordCount = action.labeledRecordCount ?? 0
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
// Convert map values to an array and return
|
|
1399
|
+
return Array.from(statsMap.values())
|
|
1400
|
+
}
|
|
1275
1401
|
}
|
|
1276
1402
|
|
|
1277
1403
|
const parseTags = (tags?: string[]) =>
|
package/src/mod-service/types.ts
CHANGED
|
@@ -65,3 +65,26 @@ type RecordHostingView = {
|
|
|
65
65
|
export type ModerationSubjectHostingView =
|
|
66
66
|
| AccountHostingView
|
|
67
67
|
| RecordHostingView
|
|
68
|
+
|
|
69
|
+
export type ReporterStats = {
|
|
70
|
+
did: string
|
|
71
|
+
accountReportCount: number
|
|
72
|
+
recordReportCount: number
|
|
73
|
+
reportedAccountCount: number
|
|
74
|
+
reportedRecordCount: number
|
|
75
|
+
takendownAccountCount: number
|
|
76
|
+
takendownRecordCount: number
|
|
77
|
+
labeledAccountCount: number
|
|
78
|
+
labeledRecordCount: number
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export type ReporterStatsResult = {
|
|
82
|
+
accountReportCount?: number
|
|
83
|
+
recordReportCount?: number
|
|
84
|
+
reportedAccountCount?: number
|
|
85
|
+
reportedRecordCount?: number
|
|
86
|
+
takendownAccountCount?: number
|
|
87
|
+
takendownRecordCount?: number
|
|
88
|
+
labeledAccountCount?: number
|
|
89
|
+
labeledRecordCount?: number
|
|
90
|
+
}
|
package/src/team/index.ts
CHANGED
|
@@ -21,14 +21,34 @@ export class TeamService {
|
|
|
21
21
|
async list({
|
|
22
22
|
cursor,
|
|
23
23
|
limit = 25,
|
|
24
|
+
roles,
|
|
25
|
+
disabled,
|
|
24
26
|
}: {
|
|
25
27
|
cursor?: string
|
|
26
28
|
limit?: number
|
|
29
|
+
disabled?: boolean
|
|
30
|
+
roles?: string[]
|
|
27
31
|
}): Promise<{ members: Selectable<Member>[]; cursor?: string }> {
|
|
28
32
|
let builder = this.db.db.selectFrom('member').selectAll()
|
|
29
33
|
if (cursor) {
|
|
30
34
|
builder = builder.where('createdAt', '>', new Date(cursor))
|
|
31
35
|
}
|
|
36
|
+
if (roles !== undefined) {
|
|
37
|
+
const knownRoles = roles.filter(
|
|
38
|
+
(r) =>
|
|
39
|
+
r === 'tools.ozone.team.defs#roleAdmin' ||
|
|
40
|
+
r === 'tools.ozone.team.defs#roleModerator' ||
|
|
41
|
+
r === 'tools.ozone.team.defs#roleTriage',
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
// Optimization: no need to query to know that no values will be returned
|
|
45
|
+
if (!knownRoles.length) return { members: [] }
|
|
46
|
+
|
|
47
|
+
builder = builder.where('role', 'in', knownRoles)
|
|
48
|
+
}
|
|
49
|
+
if (disabled !== undefined) {
|
|
50
|
+
builder = builder.where('disabled', disabled ? 'is' : 'is not', true)
|
|
51
|
+
}
|
|
32
52
|
const members = await builder
|
|
33
53
|
.limit(limit)
|
|
34
54
|
.orderBy('createdAt', 'asc')
|