import fetch from 'node-fetch' export interface TextAuditServiceOptions { apiKey: string apiSecret: string label: string } export interface TextAuditService { containsSensitiveWords(text: string): Promise } /** * https://ai.baidu.com/ai-doc/ANTIPORN/Vk3h6xaga */ export class BaiduTextAuditService implements TextAuditService { private accessToken: string private expiredTime: number constructor(private options: TextAuditServiceOptions) { } async containsSensitiveWords(text: string): Promise { if (!await this.refreshAccessToken()) throw new Error('Access Token Error') const url = `https://aip.baidubce.com/rest/2.0/solution/v1/text_censor/v2/user_defined?access_token=${this.accessToken}` let headers: { 'Content-Type': 'application/x-www-form-urlencoded' 'Accept': 'application/json' } const response = await fetch(url, { headers, method: 'POST', body: `text=${encodeURIComponent(text)}` }) const data = await response.json() as { conclusionType: number; data: any; error_msg: string } if (data.error_msg) throw new Error(data.error_msg) // 审核结果类型,可取值1、2、3、4,分别代表1:合规,2:不合规,3:疑似,4:审核失败 if (data.conclusionType === 1) return false // https://ai.baidu.com/ai-doc/ANTIPORN/Nk3h6xbb2#%E7%BB%86%E5%88%86%E6%A0%87%E7%AD%BE%E5%AF%B9%E7%85%A7%E8%A1%A8 // 3 仅政治 const sensitive = data.data.filter(d => d.subType === 3).length > 0 if (sensitive || !this.options.label) return sensitive const str = JSON.stringify(data) for (const l of this.options.label.split(',')) { if (str.includes(l)) return true } return false } async refreshAccessToken() { if (!this.options.apiKey || !this.options.apiSecret) throw new Error('未配置 | Not configured.') try { if (this.accessToken && Math.floor(new Date().getTime() / 1000) <= this.expiredTime) return true const url = `https://aip.baidubce.com/oauth/2.0/token?client_id=${this.options.apiKey}&client_secret=${this.options.apiSecret}&grant_type=client_credentials` let headers: { 'Content-Type': 'application/json' 'Accept': 'application/json' } const response = await fetch(url, { headers }) const data = (await response.json()) as { access_token: string; expires_in: number } this.accessToken = data.access_token this.expiredTime = Math.floor(new Date().getTime() / 1000) + (+data.expires_in) return true } catch (error) { global.console.error(`百度审核${error}`) } return false } } export type TextAuditServiceProvider = 'baidu' // | 'ali' export type TextAuditServices = { [key in TextAuditServiceProvider]: new ( options: TextAuditServiceOptions, ) => TextAuditService; } export const textAuditServices: TextAuditServices = { baidu: BaiduTextAuditService, }