@meith/moderation 0.31.0 → 0.32.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meith/moderation",
3
- "version": "0.31.0",
3
+ "version": "0.32.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -19,7 +19,7 @@
19
19
  "access": "public"
20
20
  },
21
21
  "dependencies": {
22
- "@meith/core": "0.31.0",
23
- "@meith/i18n": "0.31.0"
22
+ "@meith/core": "0.32.0",
23
+ "@meith/i18n": "0.32.0"
24
24
  }
25
25
  }
package/src/index.ts CHANGED
@@ -41,11 +41,14 @@ export {
41
41
  export {
42
42
  inScope,
43
43
  type NewReport,
44
+ parseReportCategory,
44
45
  parseTargetKind,
45
46
  REASON_MAX,
46
47
  REASON_MIN,
48
+ REPORT_CATEGORIES,
47
49
  REPORT_TARGET_KINDS,
48
50
  REPORTS_PAGE_SIZE,
51
+ type ReportCategory,
49
52
  type ReportEvent,
50
53
  type ReportNotifierPort,
51
54
  type ReportPage,
package/src/modcp.ts CHANGED
@@ -160,6 +160,7 @@ export const MOD_LOG_LABEL_KEYS: Readonly<Record<string, string>> = {
160
160
  'post.edit': 'board.modlog.post.edit',
161
161
  'post.delete': 'board.modlog.post.delete',
162
162
  'post.restore': 'board.modlog.post.restore',
163
+ 'post.autohold': 'board.modlog.post.autohold',
163
164
  'report.resolve': 'board.modlog.report.resolve',
164
165
  'report.reject': 'board.modlog.report.reject',
165
166
  'warning.issue': 'board.modlog.warning.issue',
package/src/reports.ts CHANGED
@@ -4,6 +4,9 @@ import { msg } from '@meith/i18n'
4
4
  export const REPORT_TARGET_KINDS = ['post', 'thread', 'user', 'private_message'] as const
5
5
  export type ReportTargetKind = (typeof REPORT_TARGET_KINDS)[number]
6
6
 
7
+ export const REPORT_CATEGORIES = ['spam', 'off_topic', 'abuse', 'other'] as const
8
+ export type ReportCategory = (typeof REPORT_CATEGORIES)[number]
9
+
7
10
  export type ReportStatus = 'open' | 'resolved' | 'rejected'
8
11
 
9
12
  export const REASON_MIN = 3
@@ -21,8 +24,10 @@ export interface ReportTarget {
21
24
  export interface NewReport {
22
25
  readonly target: ReportTarget
23
26
  readonly reporterUserId: number
27
+ readonly category: ReportCategory
24
28
  readonly reason: string
25
29
  readonly at: Date
30
+ readonly flagThreshold: number
26
31
  }
27
32
 
28
33
  export interface ReportRow {
@@ -34,6 +39,7 @@ export interface ReportRow {
34
39
  readonly targetLabel: string
35
40
  readonly reporterUserId: number | null
36
41
  readonly reporterUsername: string | null
42
+ readonly category: ReportCategory
37
43
  readonly reason: string
38
44
  readonly status: ReportStatus
39
45
  readonly assignedToUserId: number | null
@@ -75,10 +81,11 @@ export interface ReportRepository {
75
81
  readonly limit: number
76
82
  readonly after?: string
77
83
  readonly offset?: number
84
+ readonly category?: ReportCategory
78
85
  },
79
86
  ): Promise<ReportPage>
80
87
 
81
- countOpen(scope: ReportScope): Promise<number>
88
+ countOpen(scope: ReportScope, category?: ReportCategory): Promise<number>
82
89
 
83
90
  find(id: number): Promise<{ report: ReportRow; events: readonly ReportEvent[] } | null>
84
91
 
@@ -129,9 +136,12 @@ export class ReportService {
129
136
  readonly targetId: number
130
137
  readonly reason: string
131
138
  readonly reporterUserId: number
139
+ readonly category?: ReportCategory
140
+ readonly flagThreshold?: number
132
141
  }): Promise<{ reportId: number; duplicate: boolean }> {
142
+ const category = input.category ?? 'other'
133
143
  const reason = input.reason.trim()
134
- if (reason.length < REASON_MIN) {
144
+ if ((category === 'other' || reason.length > 0) && reason.length < REASON_MIN) {
135
145
  throw new ValidationError(msg('error.moderation.say-what-wrong-with-briefly'))
136
146
  }
137
147
  if (reason.length > REASON_MAX) {
@@ -148,8 +158,10 @@ export class ReportService {
148
158
  const reportId = await this.reports.open({
149
159
  target,
150
160
  reporterUserId: input.reporterUserId,
161
+ category,
151
162
  reason,
152
163
  at: this.now(),
164
+ flagThreshold: Math.max(0, Math.trunc(input.flagThreshold ?? 0)),
153
165
  })
154
166
 
155
167
  return reportId === null ? { reportId: 0, duplicate: true } : { reportId, duplicate: false }
@@ -157,19 +169,24 @@ export class ReportService {
157
169
 
158
170
  async listOpen(
159
171
  scope: ReportScope,
160
- options: { readonly after?: string; readonly offset?: number } = {},
172
+ options: {
173
+ readonly after?: string
174
+ readonly offset?: number
175
+ readonly category?: ReportCategory
176
+ } = {},
161
177
  ): Promise<ReportPage> {
162
178
  if (scope.forumIds.length === 0 && !scope.global) return { rows: [] }
163
179
  return this.reports.listOpen(scope, {
164
180
  limit: REPORTS_PAGE_SIZE,
165
181
  ...(options.after === undefined ? {} : { after: options.after }),
166
182
  ...(options.offset === undefined ? {} : { offset: options.offset }),
183
+ ...(options.category === undefined ? {} : { category: options.category }),
167
184
  })
168
185
  }
169
186
 
170
- async countOpen(scope: ReportScope): Promise<number> {
187
+ async countOpen(scope: ReportScope, category?: ReportCategory): Promise<number> {
171
188
  if (scope.forumIds.length === 0 && !scope.global) return 0
172
- return this.reports.countOpen(scope)
189
+ return this.reports.countOpen(scope, category)
173
190
  }
174
191
 
175
192
  async open(
@@ -258,3 +275,7 @@ export function parseTargetKind(value: string | undefined): ReportTargetKind | n
258
275
  ? (value as ReportTargetKind)
259
276
  : null
260
277
  }
278
+
279
+ export function parseReportCategory(value: string | undefined): ReportCategory | null {
280
+ return REPORT_CATEGORIES.includes(value as ReportCategory) ? (value as ReportCategory) : null
281
+ }