File size: 2,984 Bytes
41af422
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import fetch from 'node-fetch'

export interface TextAuditServiceOptions {
  apiKey: string
  apiSecret: string
  label: string
}

export interface TextAuditService {
  containsSensitiveWords(text: string): Promise<boolean>
}

/**
 * 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<boolean> {
    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,
}