feat(content-flagging): ✨ Introduce ContentFlaggingService with type-safe flagging methods, FlagType/FlagMetadata definitions, and expose via useContentFlagging hook
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
d77f23a1b1
commit
c0bed599ae
3 changed files with 77 additions and 2 deletions
|
|
@ -84,6 +84,11 @@ const FLAG_PATTERNS: Record<FlagCategory, RegExp[]> = {
|
|||
// Too good to be true
|
||||
/\b(guaranteed|risk.?free|double\s*your)\b/gi,
|
||||
],
|
||||
coded_language: [],
|
||||
predatory_behavior: [],
|
||||
trafficking_signals: [],
|
||||
doxxing: [],
|
||||
law_enforcement: [],
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -112,6 +117,7 @@ const CONTEXT_MODIFIERS: Record<string, Partial<Record<FlagCategory, number>>> =
|
|||
export class ContentFlaggingService {
|
||||
private config: ContentFlaggingConfig
|
||||
private whitelist: Set<string>
|
||||
private contextWhitelist: Map<string, Set<string>>
|
||||
private customPatterns: Map<FlagCategory, RegExp[]>
|
||||
|
||||
// NLP package instances (will be initialized when package is available)
|
||||
|
|
@ -122,8 +128,18 @@ export class ContentFlaggingService {
|
|||
constructor(config: Partial<ContentFlaggingConfig> = {}) {
|
||||
this.config = { ...DEFAULT_FLAGGING_CONFIG, ...config }
|
||||
this.whitelist = new Set((config.whitelist ?? []).map((w) => w.toLowerCase()))
|
||||
this.contextWhitelist = new Map()
|
||||
this.customPatterns = new Map()
|
||||
|
||||
// Build context-specific whitelists
|
||||
if (config.contextWhitelist) {
|
||||
for (const [context, words] of Object.entries(config.contextWhitelist)) {
|
||||
if (words) {
|
||||
this.contextWhitelist.set(context, new Set(words.map((w) => w.toLowerCase())))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add custom word lists as patterns
|
||||
if (config.customWordLists) {
|
||||
for (const list of config.customWordLists) {
|
||||
|
|
@ -160,6 +176,11 @@ export class ContentFlaggingService {
|
|||
threats: 0,
|
||||
adult_content: 0,
|
||||
scam_patterns: 0,
|
||||
coded_language: 0,
|
||||
predatory_behavior: 0,
|
||||
trafficking_signals: 0,
|
||||
doxxing: 0,
|
||||
law_enforcement: 0,
|
||||
}
|
||||
|
||||
// Run pattern matching for each enabled category
|
||||
|
|
@ -231,8 +252,16 @@ export class ContentFlaggingService {
|
|||
while ((match = regex.exec(text)) !== null) {
|
||||
const [matchedText] = match
|
||||
|
||||
// Skip whitelisted words
|
||||
if (this.whitelist.has(matchedText.toLowerCase())) {
|
||||
const lowerMatch = matchedText.toLowerCase()
|
||||
|
||||
// Skip globally whitelisted words
|
||||
if (this.whitelist.has(lowerMatch)) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip context-whitelisted words
|
||||
const contextSet = this.config.context ? this.contextWhitelist.get(this.config.context) : undefined
|
||||
if (contextSet?.has(lowerMatch)) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
@ -274,6 +303,20 @@ export class ContentFlaggingService {
|
|||
return 'medium'
|
||||
}
|
||||
|
||||
// New provider safety categories
|
||||
if (category === 'trafficking_signals') {
|
||||
return 'critical'
|
||||
}
|
||||
if (category === 'doxxing' || category === 'predatory_behavior') {
|
||||
return 'high'
|
||||
}
|
||||
if (category === 'law_enforcement') {
|
||||
return 'medium'
|
||||
}
|
||||
if (category === 'coded_language') {
|
||||
return 'low'
|
||||
}
|
||||
|
||||
// Default mapping
|
||||
const categoryDefaults: Record<FlagCategory, FlagSeverity> = {
|
||||
profanity: 'low',
|
||||
|
|
@ -284,6 +327,11 @@ export class ContentFlaggingService {
|
|||
threats: 'critical',
|
||||
adult_content: 'low',
|
||||
scam_patterns: 'high',
|
||||
coded_language: 'low',
|
||||
predatory_behavior: 'high',
|
||||
trafficking_signals: 'critical',
|
||||
doxxing: 'high',
|
||||
law_enforcement: 'medium',
|
||||
}
|
||||
|
||||
return categoryDefaults[category] ?? 'medium'
|
||||
|
|
@ -302,6 +350,11 @@ export class ContentFlaggingService {
|
|||
threats: 'Contains threatening language',
|
||||
adult_content: 'Contains adult content markers',
|
||||
scam_patterns: 'Contains potential scam patterns',
|
||||
coded_language: 'Contains coded or slang terminology',
|
||||
predatory_behavior: 'Contains predatory behavior patterns',
|
||||
trafficking_signals: 'Contains potential trafficking indicators',
|
||||
doxxing: 'Contains identity exposure or doxxing patterns',
|
||||
law_enforcement: 'Contains law enforcement related language',
|
||||
}
|
||||
|
||||
return reasons[category] ?? 'Content flagged'
|
||||
|
|
@ -353,6 +406,11 @@ export class ContentFlaggingService {
|
|||
threats: 0,
|
||||
adult_content: 0,
|
||||
scam_patterns: 0,
|
||||
coded_language: 0,
|
||||
predatory_behavior: 0,
|
||||
trafficking_signals: 0,
|
||||
doxxing: 0,
|
||||
law_enforcement: 0,
|
||||
},
|
||||
processingTimeMs: performance.now() - startTime,
|
||||
}
|
||||
|
|
|
|||
12
src/types.ts
12
src/types.ts
|
|
@ -17,6 +17,11 @@ export type FlagCategory =
|
|||
| 'threats'
|
||||
| 'adult_content'
|
||||
| 'scam_patterns'
|
||||
| 'coded_language'
|
||||
| 'predatory_behavior'
|
||||
| 'trafficking_signals'
|
||||
| 'doxxing'
|
||||
| 'law_enforcement'
|
||||
|
||||
/**
|
||||
* Severity levels for flags
|
||||
|
|
@ -86,6 +91,8 @@ export interface ContentFlaggingConfig {
|
|||
}[]
|
||||
/** Words to whitelist (won't be flagged) */
|
||||
whitelist?: string[]
|
||||
/** Context-specific whitelists: terms whitelisted only in certain contexts */
|
||||
contextWhitelist?: Partial<Record<string, string[]>>
|
||||
/** Context type affects analysis (e.g., 'bio' vs 'message') */
|
||||
context?: 'bio' | 'message' | 'listing' | 'review' | 'general'
|
||||
}
|
||||
|
|
@ -113,6 +120,11 @@ export const DEFAULT_FLAGGING_CONFIG: ContentFlaggingConfig = {
|
|||
threats: 2.5,
|
||||
adult_content: 0.3,
|
||||
scam_patterns: 1.5,
|
||||
coded_language: 0.3,
|
||||
predatory_behavior: 2.0,
|
||||
trafficking_signals: 3.0,
|
||||
doxxing: 2.5,
|
||||
law_enforcement: 1.0,
|
||||
},
|
||||
enableSentiment: true,
|
||||
context: 'general',
|
||||
|
|
|
|||
|
|
@ -55,6 +55,11 @@ const EMPTY_RESULT: ContentFlagResult = {
|
|||
threats: 0,
|
||||
adult_content: 0,
|
||||
scam_patterns: 0,
|
||||
coded_language: 0,
|
||||
predatory_behavior: 0,
|
||||
trafficking_signals: 0,
|
||||
doxxing: 0,
|
||||
law_enforcement: 0,
|
||||
},
|
||||
processingTimeMs: 0,
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue