@joliegg/moderation 0.6.0 → 0.9.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/dist/actions.d.ts +28 -0
- package/dist/actions.js +48 -0
- package/dist/audit/emitter.d.ts +26 -0
- package/dist/audit/emitter.js +63 -0
- package/dist/audit/events.d.ts +75 -0
- package/dist/audit/events.js +2 -0
- package/dist/audit/index.d.ts +2 -0
- package/dist/audit/index.js +18 -0
- package/dist/client.d.ts +22 -0
- package/dist/client.js +107 -0
- package/dist/index.d.ts +3 -41
- package/dist/index.js +20 -213
- package/dist/providers/aws.d.ts +11 -0
- package/dist/providers/aws.js +58 -0
- package/dist/providers/google.d.ts +21 -0
- package/dist/providers/google.js +61 -0
- package/dist/providers/openai.d.ts +15 -0
- package/dist/providers/openai.js +54 -0
- package/dist/providers/webrisk.d.ts +9 -0
- package/dist/providers/webrisk.js +33 -0
- package/dist/raid/age.d.ts +6 -0
- package/dist/raid/age.js +19 -0
- package/dist/raid/detector.d.ts +56 -0
- package/dist/raid/detector.js +90 -0
- package/dist/raid/index.d.ts +2 -0
- package/dist/raid/index.js +18 -0
- package/dist/rubrics/defaults.d.ts +19 -0
- package/dist/rubrics/defaults.js +32 -0
- package/dist/rubrics/index.d.ts +3 -0
- package/dist/rubrics/index.js +19 -0
- package/dist/rubrics/rubric.d.ts +21 -0
- package/dist/rubrics/rubric.js +57 -0
- package/dist/rubrics/types.d.ts +27 -0
- package/dist/rubrics/types.js +2 -0
- package/dist/spam/cache.d.ts +99 -0
- package/dist/spam/cache.js +210 -0
- package/dist/spam/index.d.ts +1 -0
- package/dist/spam/index.js +17 -0
- package/dist/text/index.d.ts +2 -0
- package/dist/text/index.js +18 -0
- package/dist/text/mentions.d.ts +31 -0
- package/dist/text/mentions.js +55 -0
- package/dist/text/normalize.d.ts +15 -0
- package/dist/text/normalize.js +45 -0
- package/dist/types/config.d.ts +13 -0
- package/dist/types/config.js +2 -0
- package/dist/types/index.d.ts +3 -10
- package/dist/types/index.js +15 -0
- package/package.json +66 -13
- package/src/actions.ts +50 -0
- package/src/audit/emitter.ts +77 -0
- package/src/audit/events.ts +89 -0
- package/src/audit/index.ts +2 -0
- package/src/client.ts +137 -0
- package/src/index.ts +3 -277
- package/src/providers/aws.ts +58 -0
- package/src/providers/google.ts +63 -0
- package/src/providers/openai.ts +64 -0
- package/src/providers/webrisk.ts +30 -0
- package/src/raid/age.ts +19 -0
- package/src/raid/detector.ts +133 -0
- package/src/raid/index.ts +2 -0
- package/src/rubrics/defaults.ts +32 -0
- package/src/rubrics/index.ts +3 -0
- package/src/rubrics/rubric.ts +62 -0
- package/src/rubrics/types.ts +30 -0
- package/src/spam/cache.ts +342 -0
- package/src/spam/index.ts +1 -0
- package/src/text/index.ts +2 -0
- package/src/text/mentions.ts +91 -0
- package/src/text/normalize.ts +43 -0
- package/src/types/config.ts +14 -0
- package/src/types/index.ts +5 -11
- /package/dist/{url-blacklist.json → data/url-blacklist.json} +0 -0
- /package/dist/{url-shorteners.json → data/url-shorteners.json} +0 -0
- /package/src/{url-blacklist.json → data/url-blacklist.json} +0 -0
- /package/src/{url-shorteners.json → data/url-shorteners.json} +0 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Severity } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Moderation actions taxonomy. These should be stable so they can be persisted
|
|
4
|
+
* as a canonical list of actions.
|
|
5
|
+
*/
|
|
6
|
+
export declare const ACTION_TYPES: {
|
|
7
|
+
readonly TIMEOUT: "timeout";
|
|
8
|
+
readonly BAN: "ban";
|
|
9
|
+
readonly KICK: "kick";
|
|
10
|
+
readonly WARN: "warn";
|
|
11
|
+
readonly DELETE: "delete";
|
|
12
|
+
readonly RESTRICT: "restrict";
|
|
13
|
+
readonly APPEAL_APPROVE: "appeal_approve";
|
|
14
|
+
readonly APPEAL_DENY: "appeal_deny";
|
|
15
|
+
readonly MENTION_SPAM: "mention_spam";
|
|
16
|
+
readonly NEW_USER_RESTRICT: "new_user_restrict";
|
|
17
|
+
readonly SPAM_DETECTED: "spam_detected";
|
|
18
|
+
readonly ESCALATION: "escalation";
|
|
19
|
+
readonly RAID_DETECTED: "raid_detected";
|
|
20
|
+
readonly RAID_TIMEOUT: "raid_timeout";
|
|
21
|
+
readonly RAID_JOIN: "raid_join";
|
|
22
|
+
readonly PERMISSION_BLOCK: "permission_block";
|
|
23
|
+
readonly UNTIMEOUT: "untimeout";
|
|
24
|
+
readonly UNBAN: "unban";
|
|
25
|
+
};
|
|
26
|
+
export type ActionType = typeof ACTION_TYPES[keyof typeof ACTION_TYPES];
|
|
27
|
+
/** Default severity per action type. */
|
|
28
|
+
export declare const SEVERITY_BY_ACTION: Record<ActionType, Severity>;
|
package/dist/actions.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SEVERITY_BY_ACTION = exports.ACTION_TYPES = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Moderation actions taxonomy. These should be stable so they can be persisted
|
|
6
|
+
* as a canonical list of actions.
|
|
7
|
+
*/
|
|
8
|
+
exports.ACTION_TYPES = {
|
|
9
|
+
TIMEOUT: 'timeout',
|
|
10
|
+
BAN: 'ban',
|
|
11
|
+
KICK: 'kick',
|
|
12
|
+
WARN: 'warn',
|
|
13
|
+
DELETE: 'delete',
|
|
14
|
+
RESTRICT: 'restrict',
|
|
15
|
+
APPEAL_APPROVE: 'appeal_approve',
|
|
16
|
+
APPEAL_DENY: 'appeal_deny',
|
|
17
|
+
MENTION_SPAM: 'mention_spam',
|
|
18
|
+
NEW_USER_RESTRICT: 'new_user_restrict',
|
|
19
|
+
SPAM_DETECTED: 'spam_detected',
|
|
20
|
+
ESCALATION: 'escalation',
|
|
21
|
+
RAID_DETECTED: 'raid_detected',
|
|
22
|
+
RAID_TIMEOUT: 'raid_timeout',
|
|
23
|
+
RAID_JOIN: 'raid_join',
|
|
24
|
+
PERMISSION_BLOCK: 'permission_block',
|
|
25
|
+
UNTIMEOUT: 'untimeout',
|
|
26
|
+
UNBAN: 'unban',
|
|
27
|
+
};
|
|
28
|
+
/** Default severity per action type. */
|
|
29
|
+
exports.SEVERITY_BY_ACTION = {
|
|
30
|
+
timeout: 'medium',
|
|
31
|
+
ban: 'critical',
|
|
32
|
+
kick: 'high',
|
|
33
|
+
warn: 'low',
|
|
34
|
+
delete: 'low',
|
|
35
|
+
restrict: 'medium',
|
|
36
|
+
appeal_approve: 'low',
|
|
37
|
+
appeal_deny: 'low',
|
|
38
|
+
mention_spam: 'medium',
|
|
39
|
+
new_user_restrict: 'low',
|
|
40
|
+
spam_detected: 'medium',
|
|
41
|
+
escalation: 'high',
|
|
42
|
+
raid_detected: 'critical',
|
|
43
|
+
raid_timeout: 'medium',
|
|
44
|
+
raid_join: 'low',
|
|
45
|
+
permission_block: 'low',
|
|
46
|
+
untimeout: 'low',
|
|
47
|
+
unban: 'low',
|
|
48
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { AuditEvent, AuditEventKind, AuditEventMap } from './events';
|
|
2
|
+
/**
|
|
3
|
+
* Subscriber handler type for a specific audit event kind. Sync or
|
|
4
|
+
* async; the emitter fire-and-forgets async handlers so slow
|
|
5
|
+
* persistence sinks don't block the enforcement path.
|
|
6
|
+
*/
|
|
7
|
+
export type AuditHandler<K extends AuditEventKind> = (event: AuditEventMap[K]) => void | Promise<void>;
|
|
8
|
+
/**
|
|
9
|
+
* Typed event emitter for moderation audit events.
|
|
10
|
+
*
|
|
11
|
+
* Subscribers register for specific event kinds with full type safety.
|
|
12
|
+
* Handlers that throw are isolated — a broken subscriber never blocks
|
|
13
|
+
* other subscribers or the emitter itself.
|
|
14
|
+
*
|
|
15
|
+
* Intentionally NOT `extends EventEmitter` from `node:events`: the
|
|
16
|
+
* typing story for Node's built-in emitter is painful, and the feature
|
|
17
|
+
* set we need (on / off / emit) is small enough to implement directly.
|
|
18
|
+
*/
|
|
19
|
+
export declare class AuditTrailEmitter {
|
|
20
|
+
private handlers;
|
|
21
|
+
on<K extends AuditEventKind>(kind: K, handler: AuditHandler<K>): this;
|
|
22
|
+
off<K extends AuditEventKind>(kind: K, handler: AuditHandler<K>): this;
|
|
23
|
+
emit(event: AuditEvent): void;
|
|
24
|
+
listenerCount(kind: AuditEventKind): number;
|
|
25
|
+
removeAllListeners(kind?: AuditEventKind): this;
|
|
26
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AuditTrailEmitter = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Typed event emitter for moderation audit events.
|
|
6
|
+
*
|
|
7
|
+
* Subscribers register for specific event kinds with full type safety.
|
|
8
|
+
* Handlers that throw are isolated — a broken subscriber never blocks
|
|
9
|
+
* other subscribers or the emitter itself.
|
|
10
|
+
*
|
|
11
|
+
* Intentionally NOT `extends EventEmitter` from `node:events`: the
|
|
12
|
+
* typing story for Node's built-in emitter is painful, and the feature
|
|
13
|
+
* set we need (on / off / emit) is small enough to implement directly.
|
|
14
|
+
*/
|
|
15
|
+
class AuditTrailEmitter {
|
|
16
|
+
handlers = new Map();
|
|
17
|
+
on(kind, handler) {
|
|
18
|
+
if (!this.handlers.has(kind)) {
|
|
19
|
+
this.handlers.set(kind, new Set());
|
|
20
|
+
}
|
|
21
|
+
this.handlers.get(kind).add(handler);
|
|
22
|
+
return this;
|
|
23
|
+
}
|
|
24
|
+
off(kind, handler) {
|
|
25
|
+
this.handlers.get(kind)?.delete(handler);
|
|
26
|
+
return this;
|
|
27
|
+
}
|
|
28
|
+
emit(event) {
|
|
29
|
+
const handlers = this.handlers.get(event.kind);
|
|
30
|
+
if (!handlers) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
for (const handler of handlers) {
|
|
34
|
+
try {
|
|
35
|
+
// Fire-and-forget. Async handlers are not awaited so the
|
|
36
|
+
// emitter's caller isn't blocked on slow persistence sinks.
|
|
37
|
+
// Subscribers own their own error handling for async work.
|
|
38
|
+
const result = handler(event);
|
|
39
|
+
if (result instanceof Promise) {
|
|
40
|
+
result.catch(err => {
|
|
41
|
+
console.error(`[Jolie::Moderation::AuditTrail] async handler error for ${event.kind}:`, err);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
console.error(`[Jolie::Moderation::AuditTrail] handler error for ${event.kind}:`, err);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
listenerCount(kind) {
|
|
51
|
+
return this.handlers.get(kind)?.size ?? 0;
|
|
52
|
+
}
|
|
53
|
+
removeAllListeners(kind) {
|
|
54
|
+
if (kind) {
|
|
55
|
+
this.handlers.delete(kind);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
this.handlers.clear();
|
|
59
|
+
}
|
|
60
|
+
return this;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
exports.AuditTrailEmitter = AuditTrailEmitter;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { ActionType } from '../actions';
|
|
2
|
+
import type { SpamMessageRef, SpamReason } from '../spam/cache';
|
|
3
|
+
export type Platform = 'discord' | 'twitch';
|
|
4
|
+
/** Base fields shared by every audit event. */
|
|
5
|
+
interface AuditEventBase {
|
|
6
|
+
guildId: string;
|
|
7
|
+
platform: Platform;
|
|
8
|
+
timestamp: Date;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* A moderator (human or system) performed an enforcement action.
|
|
12
|
+
* This is the primary audit log entry.
|
|
13
|
+
*/
|
|
14
|
+
export interface ActionEvent extends AuditEventBase {
|
|
15
|
+
kind: 'action';
|
|
16
|
+
actionType: ActionType;
|
|
17
|
+
targetId: string;
|
|
18
|
+
targetName: string;
|
|
19
|
+
moderatorId: string;
|
|
20
|
+
moderatorName: string;
|
|
21
|
+
reason: string;
|
|
22
|
+
details?: string;
|
|
23
|
+
duration?: number;
|
|
24
|
+
channelId?: string;
|
|
25
|
+
messageId?: string;
|
|
26
|
+
evidence?: string[];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Automatic spam detection fired. Independent of whether enforcement
|
|
30
|
+
* succeeded — the detection itself is the audit event.
|
|
31
|
+
*/
|
|
32
|
+
export interface SpamDetectedEvent extends AuditEventBase {
|
|
33
|
+
kind: 'spam_detected';
|
|
34
|
+
userId: string;
|
|
35
|
+
reason: SpamReason;
|
|
36
|
+
details?: string;
|
|
37
|
+
priorMessageIds: SpamMessageRef[];
|
|
38
|
+
channelId?: string;
|
|
39
|
+
}
|
|
40
|
+
/** Raid mode detection crossed the join-count threshold. */
|
|
41
|
+
export interface RaidDetectedEvent extends AuditEventBase {
|
|
42
|
+
kind: 'raid_detected';
|
|
43
|
+
joinCount: number;
|
|
44
|
+
windowSeconds: number;
|
|
45
|
+
autoTriggered: boolean;
|
|
46
|
+
}
|
|
47
|
+
/** A message was blocked by the per-user permissions check. */
|
|
48
|
+
export interface PermissionBlockEvent extends AuditEventBase {
|
|
49
|
+
kind: 'permission_block';
|
|
50
|
+
userId: string;
|
|
51
|
+
violations: string[];
|
|
52
|
+
channelId?: string;
|
|
53
|
+
messageId?: string;
|
|
54
|
+
}
|
|
55
|
+
/** An appeal was reviewed (approved or denied). */
|
|
56
|
+
export interface AppealReviewedEvent extends AuditEventBase {
|
|
57
|
+
kind: 'appeal_reviewed';
|
|
58
|
+
appealId: string;
|
|
59
|
+
userId: string;
|
|
60
|
+
reviewerId: string;
|
|
61
|
+
approved: boolean;
|
|
62
|
+
note?: string;
|
|
63
|
+
}
|
|
64
|
+
/** Union of every audit event shape. Add new kinds here. */
|
|
65
|
+
export type AuditEvent = ActionEvent | SpamDetectedEvent | RaidDetectedEvent | PermissionBlockEvent | AppealReviewedEvent;
|
|
66
|
+
/** Map from event kind → concrete event type. Used by the emitter. */
|
|
67
|
+
export interface AuditEventMap {
|
|
68
|
+
action: ActionEvent;
|
|
69
|
+
spam_detected: SpamDetectedEvent;
|
|
70
|
+
raid_detected: RaidDetectedEvent;
|
|
71
|
+
permission_block: PermissionBlockEvent;
|
|
72
|
+
appeal_reviewed: AppealReviewedEvent;
|
|
73
|
+
}
|
|
74
|
+
export type AuditEventKind = keyof AuditEventMap;
|
|
75
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./events"), exports);
|
|
18
|
+
__exportStar(require("./emitter"), exports);
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ModerationConfiguration, ModerationResult } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Composes text / image / link / audio moderation across the configured
|
|
4
|
+
* providers.
|
|
5
|
+
*/
|
|
6
|
+
export declare class ModerationClient {
|
|
7
|
+
private googleLanguage?;
|
|
8
|
+
private googleSpeech?;
|
|
9
|
+
private aws?;
|
|
10
|
+
private webRisk?;
|
|
11
|
+
private openai?;
|
|
12
|
+
private banList;
|
|
13
|
+
private urlBlackList;
|
|
14
|
+
constructor(configuration: ModerationConfiguration);
|
|
15
|
+
moderateText(text: string, minimumConfidence?: number, options?: {
|
|
16
|
+
provider?: 'google' | 'openai' | 'all';
|
|
17
|
+
}): Promise<ModerationResult>;
|
|
18
|
+
moderateImage(url: string, minimumConfidence?: number): Promise<ModerationResult>;
|
|
19
|
+
moderateLink(url: string, allowShorteners?: boolean): Promise<ModerationResult>;
|
|
20
|
+
moderateAudio(url: string, language?: string, minimumConfidence?: number): Promise<ModerationResult>;
|
|
21
|
+
}
|
|
22
|
+
export default ModerationClient;
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ModerationClient = void 0;
|
|
7
|
+
const client_rekognition_1 = require("@aws-sdk/client-rekognition");
|
|
8
|
+
const language_1 = require("@google-cloud/language");
|
|
9
|
+
const speech_1 = require("@google-cloud/speech");
|
|
10
|
+
const google_1 = require("./providers/google");
|
|
11
|
+
const aws_1 = require("./providers/aws");
|
|
12
|
+
const webrisk_1 = require("./providers/webrisk");
|
|
13
|
+
const openai_1 = require("./providers/openai");
|
|
14
|
+
const url_blacklist_json_1 = __importDefault(require("./data/url-blacklist.json"));
|
|
15
|
+
const url_shorteners_json_1 = __importDefault(require("./data/url-shorteners.json"));
|
|
16
|
+
/**
|
|
17
|
+
* Composes text / image / link / audio moderation across the configured
|
|
18
|
+
* providers.
|
|
19
|
+
*/
|
|
20
|
+
class ModerationClient {
|
|
21
|
+
googleLanguage;
|
|
22
|
+
googleSpeech;
|
|
23
|
+
aws;
|
|
24
|
+
webRisk;
|
|
25
|
+
openai;
|
|
26
|
+
banList = [];
|
|
27
|
+
urlBlackList = [];
|
|
28
|
+
constructor(configuration) {
|
|
29
|
+
if (configuration.aws) {
|
|
30
|
+
this.aws = new aws_1.RekognitionProvider(new client_rekognition_1.Rekognition(configuration.aws));
|
|
31
|
+
}
|
|
32
|
+
if (typeof configuration.google?.keyFile === 'string') {
|
|
33
|
+
this.googleLanguage = new google_1.GoogleLanguageProvider(new language_1.LanguageServiceClient({ keyFile: configuration.google.keyFile }));
|
|
34
|
+
this.googleSpeech = new google_1.GoogleSpeechProvider(new speech_1.SpeechClient({ keyFile: configuration.google.keyFile }));
|
|
35
|
+
}
|
|
36
|
+
if (typeof configuration.google?.apiKey === 'string') {
|
|
37
|
+
this.webRisk = new webrisk_1.WebRiskProvider(configuration.google.apiKey);
|
|
38
|
+
}
|
|
39
|
+
if (typeof configuration.openai?.apiKey === 'string') {
|
|
40
|
+
this.openai = new openai_1.OpenAIModerationProvider(configuration.openai.apiKey);
|
|
41
|
+
}
|
|
42
|
+
if (Array.isArray(configuration.banList)) {
|
|
43
|
+
this.banList = configuration.banList;
|
|
44
|
+
}
|
|
45
|
+
if (Array.isArray(configuration.urlBlackList)) {
|
|
46
|
+
this.urlBlackList = configuration.urlBlackList;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async moderateText(text, minimumConfidence = 50, options = {}) {
|
|
50
|
+
const categories = [];
|
|
51
|
+
const normalized = text.toLowerCase();
|
|
52
|
+
const matches = this.banList.filter(w => normalized.includes(w));
|
|
53
|
+
if (matches.length > 0) {
|
|
54
|
+
const words = normalized.split(' ');
|
|
55
|
+
categories.push({ category: 'BAN_LIST', confidence: (matches.length / words.length) * 100 });
|
|
56
|
+
}
|
|
57
|
+
const provider = options.provider ?? 'google';
|
|
58
|
+
if ((provider === 'google' || provider === 'all') && this.googleLanguage) {
|
|
59
|
+
const google = await this.googleLanguage.moderateText(text, minimumConfidence);
|
|
60
|
+
categories.push(...google);
|
|
61
|
+
}
|
|
62
|
+
if ((provider === 'openai' || provider === 'all') && this.openai) {
|
|
63
|
+
const openai = await this.openai.moderateText(text, minimumConfidence);
|
|
64
|
+
categories.push(...openai);
|
|
65
|
+
}
|
|
66
|
+
return { source: text, moderation: categories };
|
|
67
|
+
}
|
|
68
|
+
async moderateImage(url, minimumConfidence = 50) {
|
|
69
|
+
if (!this.aws) {
|
|
70
|
+
return { source: url, moderation: [] };
|
|
71
|
+
}
|
|
72
|
+
const moderation = await this.aws.moderateImage(url, minimumConfidence);
|
|
73
|
+
return { source: url, moderation };
|
|
74
|
+
}
|
|
75
|
+
async moderateLink(url, allowShorteners = false) {
|
|
76
|
+
try {
|
|
77
|
+
const domain = new URL(url).hostname;
|
|
78
|
+
if (this.urlBlackList.some(u => url.includes(u))) {
|
|
79
|
+
return { source: url, moderation: [{ category: 'CUSTOM_BLACK_LIST', confidence: 100 }] };
|
|
80
|
+
}
|
|
81
|
+
if (url_blacklist_json_1.default.some(u => u === domain)) {
|
|
82
|
+
return { source: url, moderation: [{ category: 'BLACK_LIST', confidence: 100 }] };
|
|
83
|
+
}
|
|
84
|
+
if (!allowShorteners && url_shorteners_json_1.default.some(u => u === domain)) {
|
|
85
|
+
return { source: url, moderation: [{ category: 'URL_SHORTENER', confidence: 100 }] };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return { source: url, moderation: [] };
|
|
90
|
+
}
|
|
91
|
+
if (!this.webRisk) {
|
|
92
|
+
return { source: url, moderation: [] };
|
|
93
|
+
}
|
|
94
|
+
const moderation = await this.webRisk.checkLink(url);
|
|
95
|
+
return { source: url, moderation };
|
|
96
|
+
}
|
|
97
|
+
async moderateAudio(url, language = 'en-US', minimumConfidence = 50) {
|
|
98
|
+
if (!this.googleSpeech) {
|
|
99
|
+
return { source: url, moderation: [] };
|
|
100
|
+
}
|
|
101
|
+
const buffer = await (0, google_1.fetchAudio)(url);
|
|
102
|
+
const transcription = await this.googleSpeech.transcribe(buffer, language);
|
|
103
|
+
return this.moderateText(transcription, minimumConfidence);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
exports.ModerationClient = ModerationClient;
|
|
107
|
+
exports.default = ModerationClient;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,41 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* @class ModerationClient
|
|
6
|
-
*/
|
|
7
|
-
declare class ModerationClient {
|
|
8
|
-
private rekognitionClient?;
|
|
9
|
-
private googleLanguageClient?;
|
|
10
|
-
private googleSpeechClient?;
|
|
11
|
-
private googleAPIKey?;
|
|
12
|
-
private banList?;
|
|
13
|
-
private urlBlackList?;
|
|
14
|
-
/**
|
|
15
|
-
*
|
|
16
|
-
* @param {ModerationConfiguration} configuration
|
|
17
|
-
*/
|
|
18
|
-
constructor(configuration: ModerationConfiguration);
|
|
19
|
-
/**
|
|
20
|
-
* Returns a list of moderation categories detected on a text
|
|
21
|
-
*
|
|
22
|
-
* @param {string} text The text to moderate
|
|
23
|
-
* @param {number} [minimumConfidence = 50] The minimum confidence required for a category to be considered
|
|
24
|
-
*
|
|
25
|
-
* @returns {Promise<ModerationResult>} The list of results that were detected with the minimum confidence specified
|
|
26
|
-
*/
|
|
27
|
-
moderateText(text: string, minimumConfidence?: number): Promise<ModerationResult>;
|
|
28
|
-
/**
|
|
29
|
-
* Returns a list of moderation categories detected on an image
|
|
30
|
-
*
|
|
31
|
-
* @param {string} url
|
|
32
|
-
* @param {number} [minimumConfidence = 50] The minimum confidence required for a category to be considered
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* @returns {Promise<ModerationResult[]>} The list of results that were detected with the minimum confidence specified
|
|
36
|
-
*/
|
|
37
|
-
moderateImage(url: string, minimumConfidence?: number): Promise<ModerationResult>;
|
|
38
|
-
moderateLink(url: string, allowShorteners?: boolean): Promise<ModerationResult>;
|
|
39
|
-
moderateAudio(url: string, language?: string, minimumConfidence?: number): Promise<ModerationResult>;
|
|
40
|
-
}
|
|
41
|
-
export default ModerationClient;
|
|
1
|
+
export { ModerationClient, default } from './client';
|
|
2
|
+
export * from './types';
|
|
3
|
+
export * from './actions';
|