新增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">
|
<div class="section">
|
||||||
<h2>聊天图片检测</h2>
|
<h2>聊天图片检测</h2>
|
||||||
<input type="file" id="imageInput">
|
<input type="file" id="imageInput">
|
||||||
|
<hr style="visibility: hidden">
|
||||||
<button onclick="checkChatImage()">检测图片</button>
|
<button onclick="checkChatImage()">检测图片</button>
|
||||||
<div id="imageResult" class="result"></div>
|
<div id="imageResult" class="result"></div>
|
||||||
</div>
|
</div>
|
||||||
|
12
web/main.js
12
web/main.js
@ -1,5 +1,5 @@
|
|||||||
// 创建 WebSocket 连接
|
// 创建 WebSocket 连接
|
||||||
const socket = new WebSocket('ws://localhost:8080'); // 后端WebSocket服务器的地址
|
const socket = new WebSocket('ws://localhost:9000/socket'); // 后端WebSocket服务器的地址
|
||||||
|
|
||||||
socket.onopen = () => {
|
socket.onopen = () => {
|
||||||
console.log('WebSocket 连接已建立');
|
console.log('WebSocket 连接已建立');
|
||||||
@ -40,8 +40,12 @@ function checkChatImage() {
|
|||||||
if (imageInput.files.length > 0) {
|
if (imageInput.files.length > 0) {
|
||||||
const file = imageInput.files[0];
|
const file = imageInput.files[0];
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = function(e) {
|
reader.readAsDataURL(file);
|
||||||
socket.send(JSON.stringify({ type: 'checkImage', imageData: e.target.result }));
|
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);
|
reader.readAsDataURL(file);
|
||||||
} else {
|
} else {
|
||||||
@ -74,7 +78,7 @@ function displayUrlResult(result) {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (result.isPhishing) {
|
if (result>=50) {
|
||||||
resultDiv.textContent = translations[language].warning;
|
resultDiv.textContent = translations[language].warning;
|
||||||
resultDiv.classList.remove('success');
|
resultDiv.classList.remove('success');
|
||||||
resultDiv.classList.add('error');
|
resultDiv.classList.add('error');
|
||||||
|
Loading…
Reference in New Issue
Block a user