新增AI反诈助理页面和软件设置页面
This commit is contained in:
parent
72743e2809
commit
88b117c0c8
36
go/deepseek.go
Normal file
36
go/deepseek.go
Normal file
@ -0,0 +1,36 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/ssgo/httpclient"
|
||||
"github.com/ssgo/log"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Ans struct {
|
||||
Choices []struct {
|
||||
Message struct {
|
||||
Content string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ask(text string) string {
|
||||
log.DefaultLogger.Info("start ask", "text", text)
|
||||
ansR := httpclient.GetClient(time.Second*60).Post("https://api.ppinfra.com/v3/openai/chat/completions", map[string]any{
|
||||
"model": "deepseek/deepseek-r1/community",
|
||||
"messages": []map[string]any{
|
||||
{
|
||||
"role": "user",
|
||||
"content": text,
|
||||
},
|
||||
},
|
||||
"response_format": map[string]any{"type": "text"},
|
||||
}, "Content-Type", "application/json", "Authorization", "Bearer sk_siUuVF2utu9Mg0-LM5aM1KteXNQand33myY2iTq7Pcw")
|
||||
if err := ansR.Error; err != nil {
|
||||
log.DefaultLogger.Error("ask error", "err", err)
|
||||
}
|
||||
ans := Ans{}
|
||||
ansR.To(&ans)
|
||||
log.DefaultLogger.Info("ask stopped", "ans", ans)
|
||||
return ans.Choices[0].Message.Content
|
||||
}
|
35
go/go.mod
Normal file
35
go/go.mod
Normal file
@ -0,0 +1,35 @@
|
||||
module main
|
||||
|
||||
go 1.23.2
|
||||
|
||||
require (
|
||||
github.com/PuerkitoBio/goquery v1.10.2
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/sashabaranov/go-openai v1.38.0
|
||||
github.com/ssgo/httpclient v1.7.8
|
||||
github.com/ssgo/log v1.7.7
|
||||
github.com/ssgo/s v1.7.22
|
||||
github.com/ssgo/u v1.7.13
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/andybalholm/cascadia v1.3.3 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/gomodule/redigo v1.9.2 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 // indirect
|
||||
github.com/otiai10/gosseract/v2 v2.4.1 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||
github.com/shirou/gopsutil/v3 v3.24.5 // indirect
|
||||
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||
github.com/ssgo/config v1.7.9 // indirect
|
||||
github.com/ssgo/discover v1.7.9 // indirect
|
||||
github.com/ssgo/redis v1.7.7 // indirect
|
||||
github.com/ssgo/standard v1.7.7 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.14 // indirect
|
||||
github.com/tklauser/numcpus v0.9.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
golang.org/x/net v0.35.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
golang.org/x/text v0.22.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
70
go/image.go
Normal file
70
go/image.go
Normal file
@ -0,0 +1,70 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/otiai10/gosseract/v2"
|
||||
"github.com/ssgo/log"
|
||||
"github.com/ssgo/u"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func checkImg(imgStr string) error {
|
||||
text := ocrImage(u.UnBase64(strings.Split(imgStr, "base64,")[1]))
|
||||
log.DefaultLogger.Info("checkImg", "imgStrLen", len(imgStr), "imgLen", len(u.UnBase64(strings.Split(imgStr, "base64,")[1])), "text", text)
|
||||
score, err := parseRiskScore(ask(buildImgDetectionPrompt(text)))
|
||||
if err != nil {
|
||||
log.DefaultLogger.Error("parseImgResp error", "err", err)
|
||||
return err
|
||||
}
|
||||
err = conn.WriteJSON(map[string]any{
|
||||
"type": "imageResult",
|
||||
"result": map[string]any{
|
||||
"score": score,
|
||||
"isSuspicious": score >= 50,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
log.DefaultLogger.Error("sendImgScore Error", "err", err)
|
||||
return err
|
||||
}
|
||||
log.DefaultLogger.Info("sendImgScore")
|
||||
return nil
|
||||
}
|
||||
|
||||
func ocrImage(image []byte) string {
|
||||
client := gosseract.NewClient()
|
||||
defer client.Close()
|
||||
err := client.SetImageFromBytes(image)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
log.DefaultLogger.Error("ocr setimg error", "err", err)
|
||||
return ""
|
||||
}
|
||||
fmt.Println(u.Red("111"))
|
||||
text, err := client.Text()
|
||||
fmt.Println(text)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
log.DefaultLogger.Error("ocr error", "err", err)
|
||||
return ""
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
func buildImgDetectionPrompt(content string) string {
|
||||
return fmt.Sprintf(`你是一名反诈骗专家,专门分析聊天记录,判断其中是否涉及诈骗信息。请仔细阅读以下文字,并回答以下问题:
|
||||
|
||||
1. 这段内容是否包含诈骗信息?如果是,请给出具体的诈骗类型(如刷单、冒充客服、杀猪盘等)。
|
||||
2. 请分析这段内容是否具有诈骗话术,例如使用了“限时优惠”“保证赚钱”“不透露给别人”等常见诈骗用语。
|
||||
3. 诈骗信息通常会使用哪些伎俩欺骗受害者?请指出可能的欺诈手法。
|
||||
4. 请用简洁的语言(不超过100字)总结你的分析结果,并给出风险等级(低、中、高)。
|
||||
5. 如果有必要,请建议用户如何避免被骗。
|
||||
|
||||
请返回JSON格式:
|
||||
{
|
||||
"risk_score": 0-100,
|
||||
"risk_reason": "主要风险点"
|
||||
}
|
||||
|
||||
待分析的文本如下:%s`, content)
|
||||
}
|
47
go/main.go
Normal file
47
go/main.go
Normal file
@ -0,0 +1,47 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/ssgo/log"
|
||||
"github.com/ssgo/s"
|
||||
)
|
||||
|
||||
var conn *websocket.Conn
|
||||
|
||||
type Message struct {
|
||||
Type string
|
||||
Url string
|
||||
ImageData string
|
||||
Text string
|
||||
}
|
||||
|
||||
func main() {
|
||||
s.Config.Listen = "9000"
|
||||
s.Static("/", "../web/")
|
||||
s.RegisterSimpleWebsocket(0, "/socket", func(connect *websocket.Conn) {
|
||||
log.DefaultLogger.Info("Socket connected")
|
||||
conn = connect
|
||||
for {
|
||||
data := &Message{}
|
||||
err := conn.ReadJSON(data)
|
||||
if err != nil {
|
||||
log.DefaultLogger.Error("Read WS error", "err", err.Error())
|
||||
return
|
||||
}
|
||||
go onMessage(data)
|
||||
}
|
||||
}, "")
|
||||
s.Start()
|
||||
}
|
||||
|
||||
func onMessage(data *Message) {
|
||||
switch data.Type {
|
||||
case "checkPhishing":
|
||||
checkUrl(data.Url)
|
||||
case "checkImage":
|
||||
checkImg(data.ImageData)
|
||||
case "checkScript":
|
||||
checkText(data.Text)
|
||||
|
||||
}
|
||||
}
|
45
go/text.go
Normal file
45
go/text.go
Normal file
@ -0,0 +1,45 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/ssgo/log"
|
||||
)
|
||||
|
||||
func checkText(text string) error {
|
||||
log.DefaultLogger.Info("checkText", "text", text)
|
||||
score, err := parseRiskScore(ask(buildTextDetectionPrompt(text)))
|
||||
if err != nil {
|
||||
log.DefaultLogger.Error("parseTextResp error", "err", err)
|
||||
return err
|
||||
}
|
||||
err = conn.WriteJSON(map[string]any{
|
||||
"type": "textResult",
|
||||
"result": map[string]any{
|
||||
"score": score,
|
||||
"isSuspicious": score >= 50,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
log.DefaultLogger.Error("sendTextScore Error", "err", err)
|
||||
return err
|
||||
}
|
||||
log.DefaultLogger.Info("sendTextScore")
|
||||
return nil
|
||||
}
|
||||
func buildTextDetectionPrompt(content string) string {
|
||||
return fmt.Sprintf(`你是一名反诈骗专家,专门分析聊天记录,判断其中是否涉及诈骗信息。请仔细阅读以下文字,并回答以下问题:
|
||||
|
||||
1. 这段内容是否包含诈骗信息?如果是,请给出具体的诈骗类型(如刷单、冒充客服、杀猪盘等)。
|
||||
2. 请分析这段内容是否具有诈骗话术,例如使用了“限时优惠”“保证赚钱”“不透露给别人”等常见诈骗用语。
|
||||
3. 诈骗信息通常会使用哪些伎俩欺骗受害者?请指出可能的欺诈手法。
|
||||
4. 请用简洁的语言(不超过100字)总结你的分析结果,并给出风险等级(低、中、高)。
|
||||
5. 如果有必要,请建议用户如何避免被骗。
|
||||
|
||||
请返回JSON格式:
|
||||
{
|
||||
"risk_score": 0-100,
|
||||
"risk_reason": "主要风险点"
|
||||
}
|
||||
|
||||
待分析的文本如下:%s`, content)
|
||||
}
|
159
go/website.go
Normal file
159
go/website.go
Normal file
@ -0,0 +1,159 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"github.com/ssgo/log"
|
||||
"github.com/ssgo/u"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func checkUrl(url string) error {
|
||||
log.DefaultLogger.Info("UrlTest", "url", url)
|
||||
score := analyzeHandler(url)
|
||||
if score == -1 || score == -2 {
|
||||
log.DefaultLogger.Error("ckwebsiteErr")
|
||||
}
|
||||
log.DefaultLogger.Info("ckwebsite2", "score", score)
|
||||
err := conn.WriteJSON(map[string]any{
|
||||
"type": "urlResult",
|
||||
"result": score,
|
||||
})
|
||||
if err != nil {
|
||||
log.DefaultLogger.Error("sendURLScore Error", "err", err)
|
||||
return err
|
||||
}
|
||||
log.DefaultLogger.Info("sendURLScore")
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
MaxContentLength = 8000 // 根据模型上下文窗口限制
|
||||
RequestTimeout = 15 * time.Second
|
||||
)
|
||||
|
||||
type DeepseekConfig struct {
|
||||
Model string
|
||||
Temperature float32
|
||||
MaxTokens int
|
||||
}
|
||||
|
||||
var (
|
||||
aiConfig = DeepseekConfig{
|
||||
Model: "deepseek/deepseek-r1/community", // 根据API权限选择
|
||||
Temperature: 0.7,
|
||||
MaxTokens: 500,
|
||||
}
|
||||
)
|
||||
|
||||
// HTTP处理函数
|
||||
func analyzeHandler(targetURL string) int {
|
||||
|
||||
// 安全爬取网页内容
|
||||
content, err := safeCrawl(targetURL)
|
||||
if err != nil {
|
||||
return -2
|
||||
}
|
||||
|
||||
// 调用OpenAI分析
|
||||
score, err := analyzeContent(content)
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
log.DefaultLogger.Info("ckwebsite1", "score", score)
|
||||
return score
|
||||
}
|
||||
|
||||
func safeCrawl(targetURL string) (string, error) {
|
||||
client := &http.Client{
|
||||
Timeout: RequestTimeout,
|
||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||
if len(via) >= 5 {
|
||||
return fmt.Errorf("too many redirects (max 5)")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
fmt.Println(u.Red(111))
|
||||
resp, err := client.Get(targetURL)
|
||||
if err != nil {
|
||||
log.DefaultLogger.Error("222Err")
|
||||
fmt.Println(err)
|
||||
return "", fmt.Errorf("HTTP request failed: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
fmt.Println(u.Red(222))
|
||||
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 400 {
|
||||
log.DefaultLogger.Error("223Err")
|
||||
fmt.Println(err)
|
||||
return "", fmt.Errorf("invalid status code: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
doc, err := goquery.NewDocumentFromReader(resp.Body)
|
||||
if err != nil {
|
||||
log.DefaultLogger.Error("224Err")
|
||||
fmt.Println(err)
|
||||
return "", fmt.Errorf("HTML parsing failed: %v", err)
|
||||
}
|
||||
|
||||
doc.Find("script, style, noscript").Remove()
|
||||
text := doc.Find("body").Text()
|
||||
|
||||
// 预处理内容
|
||||
cleanText := strings.Join(strings.Fields(text), " ")
|
||||
if len(cleanText) > MaxContentLength {
|
||||
cleanText = cleanText[:MaxContentLength]
|
||||
}
|
||||
log.DefaultLogger.Info("getwebsite", "cleanText", cleanText)
|
||||
return cleanText, nil
|
||||
}
|
||||
|
||||
// AI分析处理
|
||||
func analyzeContent(content string) (int, error) {
|
||||
return parseRiskScore(ask(buildDetectionPrompt(content)))
|
||||
}
|
||||
|
||||
// 构造模板prompt
|
||||
func buildDetectionPrompt(content string) string {
|
||||
return fmt.Sprintf(`作为网络安全分析系统,请对以下网页内容进行风险评估。评估标准包括:
|
||||
1. 紧急金融操作请求(转账、验证账户等)
|
||||
2. 仿冒品牌或误导性信息
|
||||
3. 可疑链接/下载请求
|
||||
4. 语法及排版异常
|
||||
5. 非法个人信息收集
|
||||
|
||||
请返回JSON格式:
|
||||
{
|
||||
"risk_score": 0-100,
|
||||
"risk_reason": "主要风险点"
|
||||
}
|
||||
|
||||
网页内容:%s`, content)
|
||||
}
|
||||
|
||||
func parseRiskScore(response string) (int, error) {
|
||||
var result struct {
|
||||
RiskScore int `json:"risk_score"`
|
||||
}
|
||||
|
||||
start := strings.Index(response, "{")
|
||||
end := strings.LastIndex(response, "}")
|
||||
if start == -1 || end <= start {
|
||||
return 0, fmt.Errorf("invalid response format")
|
||||
}
|
||||
|
||||
err := json.Unmarshal([]byte(response[start:end+1]), &result)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to parse JSON: %v", err)
|
||||
}
|
||||
|
||||
if result.RiskScore < 0 || result.RiskScore > 100 {
|
||||
return 0, fmt.Errorf("invalid risk score range")
|
||||
}
|
||||
|
||||
return result.RiskScore, nil
|
||||
}
|
@ -22,6 +22,7 @@
|
||||
<div class="section">
|
||||
<h2>聊天图片检测</h2>
|
||||
<input type="file" id="imageInput">
|
||||
<hr style="visibility: hidden">
|
||||
<button onclick="checkChatImage()">检测图片</button>
|
||||
<div id="imageResult" class="result"></div>
|
||||
</div>
|
||||
|
12
web/main.js
12
web/main.js
@ -1,5 +1,5 @@
|
||||
// 创建 WebSocket 连接
|
||||
const socket = new WebSocket('ws://localhost:8080'); // 后端WebSocket服务器的地址
|
||||
const socket = new WebSocket('ws://localhost:9000/socket'); // 后端WebSocket服务器的地址
|
||||
|
||||
socket.onopen = () => {
|
||||
console.log('WebSocket 连接已建立');
|
||||
@ -40,8 +40,12 @@ function checkChatImage() {
|
||||
if (imageInput.files.length > 0) {
|
||||
const file = imageInput.files[0];
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
socket.send(JSON.stringify({ type: 'checkImage', imageData: e.target.result }));
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = (e) => {
|
||||
console.info(reader.result)
|
||||
console.info(reader.result.length)
|
||||
console.info(e.target.result.length)
|
||||
socket.send(JSON.stringify({ type: 'checkImage', imageData: reader.result }));
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
} else {
|
||||
@ -74,7 +78,7 @@ function displayUrlResult(result) {
|
||||
},
|
||||
};
|
||||
|
||||
if (result.isPhishing) {
|
||||
if (result>=50) {
|
||||
resultDiv.textContent = translations[language].warning;
|
||||
resultDiv.classList.remove('success');
|
||||
resultDiv.classList.add('error');
|
||||
|
Loading…
Reference in New Issue
Block a user