mirror of
https://github.com/yangjian102621/geekai.git
synced 2025-12-27 18:45:59 +08:00
Compare commits
10 Commits
feat-sms
...
feat-plugi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9806d5ff4c | ||
|
|
d1d13a72e4 | ||
|
|
00c520d066 | ||
|
|
797ff66474 | ||
|
|
9d51a478b9 | ||
|
|
1d4179df75 | ||
|
|
917b6012e8 | ||
|
|
da14632794 | ||
|
|
a868a8a8b7 | ||
|
|
5037df744f |
@@ -184,7 +184,7 @@ docker-compose up -d
|
|||||||
|
|
||||||
### 2. 修改配置文档
|
### 2. 修改配置文档
|
||||||
|
|
||||||
先拷贝项目中的 `api/go/config.sample.toml` 配置文档,修改代理地址和管理员密码:
|
先拷贝项目中的 `api/config.sample.toml` 配置文档,修改代理地址和管理员密码:
|
||||||
|
|
||||||
如何修改请参考[修改配置文档](#2-修改配置文档)
|
如何修改请参考[修改配置文档](#2-修改配置文档)
|
||||||
|
|
||||||
@@ -287,7 +287,7 @@ server {
|
|||||||
3. 运行后端程序:
|
3. 运行后端程序:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
cd api/go
|
cd api
|
||||||
# 1. 先下载依赖
|
# 1. 先下载依赖
|
||||||
go mod tidy
|
go mod tidy
|
||||||
# 2. 运行程序
|
# 2. 运行程序
|
||||||
@@ -340,7 +340,7 @@ npm run build
|
|||||||
你可以根据个人需求将项目打包成 windows/linux/darwin 平台项目。
|
你可以根据个人需求将项目打包成 windows/linux/darwin 平台项目。
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
cd api/go
|
cd api
|
||||||
# for all platforms
|
# for all platforms
|
||||||
make all
|
make all
|
||||||
# for linux only
|
# for linux only
|
||||||
|
|||||||
0
api/go/.gitignore → api/.gitignore
vendored
0
api/go/.gitignore → api/.gitignore
vendored
@@ -28,7 +28,7 @@ type AppServer struct {
|
|||||||
// 保存 Websocket 会话 UserId, 每个 UserId 只能连接一次
|
// 保存 Websocket 会话 UserId, 每个 UserId 只能连接一次
|
||||||
// 防止第三方直接连接 socket 调用 OpenAI API
|
// 防止第三方直接连接 socket 调用 OpenAI API
|
||||||
ChatSession *types.LMap[string, types.ChatSession] //map[sessionId]UserId
|
ChatSession *types.LMap[string, types.ChatSession] //map[sessionId]UserId
|
||||||
ChatClients *types.LMap[string, *types.WsClient] // Websocket 连接集合
|
ChatClients *types.LMap[string, *types.WsClient] // map[sessionId]Websocket 连接集合
|
||||||
ReqCancelFunc *types.LMap[string, context.CancelFunc] // HttpClient 请求取消 handle function
|
ReqCancelFunc *types.LMap[string, context.CancelFunc] // HttpClient 请求取消 handle function
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -12,6 +12,7 @@ type ApiRequest struct {
|
|||||||
type Message struct {
|
type Message struct {
|
||||||
Role string `json:"role"`
|
Role string `json:"role"`
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
|
FunctionCall FunctionCall `json:"function_call"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ApiResponse struct {
|
type ApiResponse struct {
|
||||||
18
api/core/types/function.go
Normal file
18
api/core/types/function.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
type FunctionCall struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Arguments string `json:"arguments"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Function struct {
|
||||||
|
Name string
|
||||||
|
Description string
|
||||||
|
Parameters []Parameter
|
||||||
|
}
|
||||||
|
|
||||||
|
type Parameter struct {
|
||||||
|
Type string
|
||||||
|
Required []string
|
||||||
|
Properties map[string]interface{}
|
||||||
|
}
|
||||||
@@ -33,8 +33,8 @@ require (
|
|||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
go.uber.org/dig v1.16.1 // indirect
|
go.uber.org/dig v1.16.1 // indirect
|
||||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
|
||||||
golang.org/x/net v0.7.0 // indirect
|
golang.org/x/net v0.9.0 // indirect
|
||||||
golang.org/x/text v0.7.0 // indirect
|
golang.org/x/text v0.9.0 // indirect
|
||||||
google.golang.org/protobuf v1.28.1 // indirect
|
google.golang.org/protobuf v1.28.1 // indirect
|
||||||
gopkg.in/ini.v1 v1.66.2 // indirect
|
gopkg.in/ini.v1 v1.66.2 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
@@ -59,6 +59,6 @@ require (
|
|||||||
go.uber.org/fx v1.19.3
|
go.uber.org/fx v1.19.3
|
||||||
go.uber.org/multierr v1.6.0 // indirect
|
go.uber.org/multierr v1.6.0 // indirect
|
||||||
golang.org/x/crypto v0.6.0
|
golang.org/x/crypto v0.6.0
|
||||||
golang.org/x/sys v0.5.0 // indirect
|
golang.org/x/sys v0.7.0 // indirect
|
||||||
gorm.io/gorm v1.25.1
|
gorm.io/gorm v1.25.1
|
||||||
)
|
)
|
||||||
@@ -140,16 +140,16 @@ golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUu
|
|||||||
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
|
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
|
||||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
|
||||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"chatplus/core"
|
"chatplus/core"
|
||||||
"chatplus/core/types"
|
"chatplus/core/types"
|
||||||
|
"chatplus/service/function"
|
||||||
"chatplus/store/model"
|
"chatplus/store/model"
|
||||||
"chatplus/store/vo"
|
"chatplus/store/vo"
|
||||||
"chatplus/utils"
|
"chatplus/utils"
|
||||||
@@ -30,10 +31,11 @@ const ErrorMsg = "抱歉,AI 助手开小差了,请稍后再试。"
|
|||||||
type ChatHandler struct {
|
type ChatHandler struct {
|
||||||
BaseHandler
|
BaseHandler
|
||||||
db *gorm.DB
|
db *gorm.DB
|
||||||
|
funcZaoBao *function.FuncZaoBao
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewChatHandler(app *core.AppServer, db *gorm.DB) *ChatHandler {
|
func NewChatHandler(app *core.AppServer, db *gorm.DB, zaoBao *function.FuncZaoBao) *ChatHandler {
|
||||||
handler := ChatHandler{db: db}
|
handler := ChatHandler{db: db, funcZaoBao: zaoBao}
|
||||||
handler.App = app
|
handler.App = app
|
||||||
return &handler
|
return &handler
|
||||||
}
|
}
|
||||||
@@ -192,11 +194,14 @@ func (h *ChatHandler) sendMessage(ctx context.Context, session types.ChatSession
|
|||||||
Content: prompt,
|
Content: prompt,
|
||||||
})
|
})
|
||||||
var apiKey string
|
var apiKey string
|
||||||
response, err := h.doRequest(ctx, userVo, &apiKey, req)
|
response, err := h.fakeRequest(ctx, userVo, &apiKey, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(err.Error(), "context canceled") {
|
if strings.Contains(err.Error(), "context canceled") {
|
||||||
logger.Info("用户取消了请求:", prompt)
|
logger.Info("用户取消了请求:", prompt)
|
||||||
return nil
|
return nil
|
||||||
|
} else if strings.Contains(err.Error(), "no available key") {
|
||||||
|
replyMessage(ws, "抱歉😔😔😔,系统已经没有可用的 API KEY🔑,您可以导入自己的 API KEY🔑 继续使用!🙏🙏🙏")
|
||||||
|
return nil
|
||||||
} else {
|
} else {
|
||||||
logger.Error(err)
|
logger.Error(err)
|
||||||
}
|
}
|
||||||
@@ -208,13 +213,16 @@ func (h *ChatHandler) sendMessage(ctx context.Context, session types.ChatSession
|
|||||||
defer response.Body.Close()
|
defer response.Body.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
contentType := response.Header.Get("Content-Type")
|
//contentType := response.Header.Get("Content-Type")
|
||||||
if strings.Contains(contentType, "text/event-stream") {
|
//if strings.Contains(contentType, "text/event-stream") || true {
|
||||||
|
if true {
|
||||||
replyCreatedAt := time.Now()
|
replyCreatedAt := time.Now()
|
||||||
// 循环读取 Chunk 消息
|
// 循环读取 Chunk 消息
|
||||||
var message = types.Message{}
|
var message = types.Message{}
|
||||||
var contents = make([]string, 0)
|
var contents = make([]string, 0)
|
||||||
var responseBody = types.ApiResponse{}
|
var functionCall = false
|
||||||
|
var functionName string
|
||||||
|
var arguments = make([]string, 0)
|
||||||
reader := bufio.NewReader(response.Body)
|
reader := bufio.NewReader(response.Body)
|
||||||
for {
|
for {
|
||||||
line, err := reader.ReadString('\n')
|
line, err := reader.ReadString('\n')
|
||||||
@@ -226,10 +234,11 @@ func (h *ChatHandler) sendMessage(ctx context.Context, session types.ChatSession
|
|||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if !strings.Contains(line, "data:") {
|
if !strings.Contains(line, "data:") || len(line) < 30 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var responseBody = types.ApiResponse{}
|
||||||
err = json.Unmarshal([]byte(line[6:]), &responseBody)
|
err = json.Unmarshal([]byte(line[6:]), &responseBody)
|
||||||
if err != nil || len(responseBody.Choices) == 0 { // 数据解析出错
|
if err != nil || len(responseBody.Choices) == 0 { // 数据解析出错
|
||||||
logger.Error(err, line)
|
logger.Error(err, line)
|
||||||
@@ -238,6 +247,24 @@ func (h *ChatHandler) sendMessage(ctx context.Context, session types.ChatSession
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun := responseBody.Choices[0].Delta.FunctionCall
|
||||||
|
if functionCall && fun.Name == "" {
|
||||||
|
arguments = append(arguments, fun.Arguments)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !utils.IsEmptyValue(fun) {
|
||||||
|
functionCall = true
|
||||||
|
functionName = fun.Name
|
||||||
|
replyChunkMessage(ws, types.WsMessage{Type: types.WsStart})
|
||||||
|
replyChunkMessage(ws, types.WsMessage{Type: types.WsMiddle, Content: fmt.Sprintf("正在调用函数 %s 作答 ...\n\n", functionName)})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if responseBody.Choices[0].FinishReason == "function_call" { // 函数调用完毕
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
// 初始化 role
|
// 初始化 role
|
||||||
if responseBody.Choices[0].Delta.Role != "" && message.Role == "" {
|
if responseBody.Choices[0].Delta.Role != "" && message.Role == "" {
|
||||||
message.Role = responseBody.Choices[0].Delta.Role
|
message.Role = responseBody.Choices[0].Delta.Role
|
||||||
@@ -255,6 +282,23 @@ func (h *ChatHandler) sendMessage(ctx context.Context, session types.ChatSession
|
|||||||
}
|
}
|
||||||
} // end for
|
} // end for
|
||||||
|
|
||||||
|
if functionCall { // 调用函数完成任务
|
||||||
|
// TODO 调用函数完成任务
|
||||||
|
data, err := h.funcZaoBao.Fetch()
|
||||||
|
if err != nil {
|
||||||
|
replyChunkMessage(ws, types.WsMessage{
|
||||||
|
Type: types.WsMiddle,
|
||||||
|
Content: "调用函数出错",
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
replyChunkMessage(ws, types.WsMessage{
|
||||||
|
Type: types.WsMiddle,
|
||||||
|
Content: data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
contents = append(contents, data)
|
||||||
|
}
|
||||||
|
|
||||||
// 消息发送成功
|
// 消息发送成功
|
||||||
if len(contents) > 0 {
|
if len(contents) > 0 {
|
||||||
// 更新用户的对话次数
|
// 更新用户的对话次数
|
||||||
@@ -269,8 +313,8 @@ func (h *ChatHandler) sendMessage(ctx context.Context, session types.ChatSession
|
|||||||
message.Content = strings.Join(contents, "")
|
message.Content = strings.Join(contents, "")
|
||||||
useMsg := types.Message{Role: "user", Content: prompt}
|
useMsg := types.Message{Role: "user", Content: prompt}
|
||||||
|
|
||||||
// 更新上下文消息
|
// 更新上下文消息,如果是调用函数则不需要更新上下文
|
||||||
if userVo.ChatConfig.EnableContext {
|
if userVo.ChatConfig.EnableContext && functionCall == false {
|
||||||
chatCtx = append(chatCtx, useMsg) // 提问消息
|
chatCtx = append(chatCtx, useMsg) // 提问消息
|
||||||
chatCtx = append(chatCtx, message) // 回复消息
|
chatCtx = append(chatCtx, message) // 回复消息
|
||||||
h.App.ChatContexts.Put(session.ChatId, chatCtx)
|
h.App.ChatContexts.Put(session.ChatId, chatCtx)
|
||||||
@@ -398,8 +442,7 @@ func (h *ChatHandler) doRequest(ctx context.Context, user vo.User, apiKey *strin
|
|||||||
if proxyURL == "" {
|
if proxyURL == "" {
|
||||||
client = &http.Client{}
|
client = &http.Client{}
|
||||||
} else { // 使用代理
|
} else { // 使用代理
|
||||||
uri := url.URL{}
|
proxy, _ := url.Parse(proxyURL)
|
||||||
proxy, _ := uri.Parse(proxyURL)
|
|
||||||
client = &http.Client{
|
client = &http.Client{
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
Proxy: http.ProxyURL(proxy),
|
Proxy: http.ProxyURL(proxy),
|
||||||
@@ -426,6 +469,13 @@ func (h *ChatHandler) doRequest(ctx context.Context, user vo.User, apiKey *strin
|
|||||||
return client.Do(request)
|
return client.Do(request)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *ChatHandler) fakeRequest(ctx context.Context, user vo.User, apiKey *string, req types.ApiRequest) (*http.Response, error) {
|
||||||
|
link := "https://img.r9it.com/chatgpt/response"
|
||||||
|
client := &http.Client{}
|
||||||
|
request, _ := http.NewRequest(http.MethodGet, link, nil)
|
||||||
|
return client.Do(request)
|
||||||
|
}
|
||||||
|
|
||||||
// 回复客户片段端消息
|
// 回复客户片段端消息
|
||||||
func replyChunkMessage(client types.Client, message types.WsMessage) {
|
func replyChunkMessage(client types.Client, message types.WsMessage) {
|
||||||
msg, err := json.Marshal(message)
|
msg, err := json.Marshal(message)
|
||||||
@@ -68,11 +68,18 @@ func (h *UserHandler) Register(c *gin.Context) {
|
|||||||
|
|
||||||
// check if the username is exists
|
// check if the username is exists
|
||||||
var item model.User
|
var item model.User
|
||||||
tx := h.db.Where("username = ?", data.Username).First(&item)
|
res := h.db.Where("username = ?", data.Username).First(&item)
|
||||||
if tx.RowsAffected > 0 {
|
if res.RowsAffected > 0 {
|
||||||
resp.ERROR(c, "用户名已存在")
|
resp.ERROR(c, "用户名已存在")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
res = h.db.Where("mobile = ?", data.Mobile).First(&item)
|
||||||
|
if res.RowsAffected > 0 {
|
||||||
|
resp.ERROR(c, "该手机号码以及被注册,请更换其他手机号")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// 默认订阅所有角色
|
// 默认订阅所有角色
|
||||||
var chatRoles []model.ChatRole
|
var chatRoles []model.ChatRole
|
||||||
h.db.Find(&chatRoles)
|
h.db.Find(&chatRoles)
|
||||||
@@ -110,7 +117,7 @@ func (h *UserHandler) Register(c *gin.Context) {
|
|||||||
} else {
|
} else {
|
||||||
user.Calls = config.UserInitCalls
|
user.Calls = config.UserInitCalls
|
||||||
}
|
}
|
||||||
res := h.db.Create(&user)
|
res = h.db.Create(&user)
|
||||||
if res.Error != nil {
|
if res.Error != nil {
|
||||||
resp.ERROR(c, "保存数据失败")
|
resp.ERROR(c, "保存数据失败")
|
||||||
logger.Error(res.Error)
|
logger.Error(res.Error)
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
# chatgpt-plus-java
|
|
||||||
|
|
||||||
chatgpt-plus 后端 API Java 语言实现,待开发。
|
|
||||||
|
|
||||||
|
|
||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"chatplus/handler/admin"
|
"chatplus/handler/admin"
|
||||||
logger2 "chatplus/logger"
|
logger2 "chatplus/logger"
|
||||||
"chatplus/service"
|
"chatplus/service"
|
||||||
|
"chatplus/service/function"
|
||||||
"chatplus/store"
|
"chatplus/store"
|
||||||
"context"
|
"context"
|
||||||
"embed"
|
"embed"
|
||||||
@@ -99,6 +100,12 @@ func main() {
|
|||||||
return xdb.NewWithBuffer(cBuff)
|
return xdb.NewWithBuffer(cBuff)
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
// 创建函数
|
||||||
|
fx.Provide(func() *function.FuncZaoBao {
|
||||||
|
token := os.Getenv("AL_API_TOKEN")
|
||||||
|
return function.NewZaoBao(token)
|
||||||
|
}),
|
||||||
|
|
||||||
// 创建控制器
|
// 创建控制器
|
||||||
fx.Provide(handler.NewChatRoleHandler),
|
fx.Provide(handler.NewChatRoleHandler),
|
||||||
fx.Provide(handler.NewUserHandler),
|
fx.Provide(handler.NewUserHandler),
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
# chatgpt-plus-php
|
|
||||||
|
|
||||||
chatgpt-plus 后端 API PHP 语言实现,待开发。
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
# chatgpt-plus-python
|
|
||||||
|
|
||||||
chatgpt-plus 后端 API Python 语言实现,待开发。
|
|
||||||
|
|
||||||
|
|
||||||
51
api/service/function/zao_bao.go
Normal file
51
api/service/function/zao_bao.go
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package function
|
||||||
|
|
||||||
|
import (
|
||||||
|
"chatplus/utils"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 每日早报函数实现
|
||||||
|
|
||||||
|
type FuncZaoBao struct {
|
||||||
|
apiURL string
|
||||||
|
token string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewZaoBao(token string) *FuncZaoBao {
|
||||||
|
return &FuncZaoBao{apiURL: "https://v2.alapi.cn/api/zaobao", token: token}
|
||||||
|
}
|
||||||
|
|
||||||
|
type resVo struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Msg string `json:"msg"`
|
||||||
|
Data struct {
|
||||||
|
Date string `json:"date"`
|
||||||
|
News []string `json:"news"`
|
||||||
|
WeiYu string `json:"weiyu"`
|
||||||
|
} `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FuncZaoBao) Fetch() (string, error) {
|
||||||
|
|
||||||
|
url := fmt.Sprintf("%s?format=json&token=%s", f.apiURL, f.token)
|
||||||
|
bytes, err := utils.HttpGet(url, "")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
var res resVo
|
||||||
|
err = utils.JsonDecode(string(bytes), &res)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.Code != 200 {
|
||||||
|
return "", fmt.Errorf("call api fail: %s", res.Msg)
|
||||||
|
}
|
||||||
|
builder := make([]string, 0)
|
||||||
|
builder = append(builder, fmt.Sprintf("**%s 早报:**", res.Data.Date))
|
||||||
|
builder = append(builder, res.Data.News...)
|
||||||
|
builder = append(builder, fmt.Sprintf("%s", res.Data.WeiYu))
|
||||||
|
return strings.Join(builder, "\n\n"), nil
|
||||||
|
}
|
||||||
@@ -1,23 +1,25 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"chatplus/core/types"
|
||||||
"chatplus/store/model"
|
"chatplus/store/model"
|
||||||
"chatplus/store/vo"
|
"chatplus/store/vo"
|
||||||
"chatplus/utils"
|
"chatplus/utils"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/lionsoul2014/ip2region/binding/golang/xdb"
|
||||||
|
"github.com/pkoukk/tiktoken-go"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/lionsoul2014/ip2region/binding/golang/xdb"
|
|
||||||
"github.com/pkoukk/tiktoken-go"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
testAesEncrypt()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Http client 取消操作
|
// Http client 取消操作
|
||||||
@@ -156,3 +158,45 @@ func testAesEncrypt() {
|
|||||||
}
|
}
|
||||||
fmt.Println("解密明文:", string(decrypt))
|
fmt.Println("解密明文:", string(decrypt))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func extractFunction() error {
|
||||||
|
open, err := os.Open("res/data.txt")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
reader := bufio.NewReader(open)
|
||||||
|
var contents = make([]string, 0)
|
||||||
|
var functionCall = false
|
||||||
|
var functionName string
|
||||||
|
for {
|
||||||
|
line, err := reader.ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if !strings.Contains(line, "data:") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var responseBody = types.ApiResponse{}
|
||||||
|
err = json.Unmarshal([]byte(line[6:]), &responseBody)
|
||||||
|
if err != nil || len(responseBody.Choices) == 0 { // 数据解析出错
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
function := responseBody.Choices[0].Delta.FunctionCall
|
||||||
|
if functionCall && function.Name == "" {
|
||||||
|
contents = append(contents, function.Arguments)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !utils.IsEmptyValue(function) {
|
||||||
|
functionCall = true
|
||||||
|
functionName = function.Name
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("函数名称: ", functionName)
|
||||||
|
fmt.Println(strings.Join(contents, ""))
|
||||||
|
return err
|
||||||
|
}
|
||||||
@@ -87,3 +87,25 @@ func Ip2Region(searcher *xdb.Searcher, ip string) string {
|
|||||||
}
|
}
|
||||||
return fmt.Sprintf("%s-%s-%s", arr[0], arr[2], arr[3])
|
return fmt.Sprintf("%s-%s-%s", arr[0], arr[2], arr[3])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsEmptyValue(obj interface{}) bool {
|
||||||
|
v := reflect.ValueOf(obj)
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Ptr, reflect.Interface:
|
||||||
|
return v.IsNil()
|
||||||
|
case reflect.Array, reflect.Slice, reflect.Map, reflect.String:
|
||||||
|
return v.Len() == 0
|
||||||
|
case reflect.Bool:
|
||||||
|
return !v.Bool()
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
return v.Int() == 0
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||||
|
return v.Uint() == 0
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
return v.Float() == 0
|
||||||
|
case reflect.Complex64, reflect.Complex128:
|
||||||
|
return v.Complex() == 0
|
||||||
|
default:
|
||||||
|
return reflect.DeepEqual(obj, reflect.Zero(reflect.TypeOf(obj)).Interface())
|
||||||
|
}
|
||||||
|
}
|
||||||
68
api/utils/http.go
Normal file
68
api/utils/http.go
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HttpGet(uri string, proxy string) ([]byte, error) {
|
||||||
|
var client *http.Client
|
||||||
|
if proxy == "" {
|
||||||
|
client = &http.Client{}
|
||||||
|
} else {
|
||||||
|
proxy, _ := url.Parse(proxy)
|
||||||
|
client = &http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
Proxy: http.ProxyURL(proxy),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest("GET", uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
return io.ReadAll(resp.Body)
|
||||||
|
}
|
||||||
|
|
||||||
|
func HttpPost(uri string, params map[string]interface{}, proxy string) ([]byte, error) {
|
||||||
|
data, err := json.Marshal(params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var client *http.Client
|
||||||
|
if proxy == "" {
|
||||||
|
client = &http.Client{}
|
||||||
|
} else {
|
||||||
|
proxy, _ := url.Parse(proxy)
|
||||||
|
client = &http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
Proxy: http.ProxyURL(proxy),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest("POST", uri, bytes.NewBuffer(data))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
return io.ReadAll(resp.Body)
|
||||||
|
}
|
||||||
@@ -25,6 +25,7 @@ func RandomNumber(bit int) int {
|
|||||||
min := intPow(10, bit-1)
|
min := intPow(10, bit-1)
|
||||||
max := intPow(10, bit) - 1
|
max := intPow(10, bit) - 1
|
||||||
|
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
return rand.Intn(max-min+1) + min
|
return rand.Intn(max-min+1) + min
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,7 +111,7 @@ onMounted(() => {
|
|||||||
httpGet('/api/user/profile').then(res => {
|
httpGet('/api/user/profile').then(res => {
|
||||||
form.value = res.data
|
form.value = res.data
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
ElMessage.error({message: '获取用户信息失败', appendTo: '#user-info'})
|
ElMessage.error("获取用户信息失败")
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -126,10 +126,10 @@ const afterRead = (file) => {
|
|||||||
// 执行上传操作
|
// 执行上传操作
|
||||||
httpPost('/api/upload', formData).then((res) => {
|
httpPost('/api/upload', formData).then((res) => {
|
||||||
form.value.avatar = res.data
|
form.value.avatar = res.data
|
||||||
ElMessage.success({message: '上传成功', appendTo: '#user-info', duration: 1000})
|
ElMessage.success('上传成功')
|
||||||
}).catch((e) => {
|
}).catch((e) => {
|
||||||
console.log(e.message)
|
console.log(e.message)
|
||||||
ElMessage.error({message: '上传失败', appendTo: '#user-info'})
|
ElMessage.error('上传失败')
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
error(err) {
|
error(err) {
|
||||||
@@ -144,16 +144,12 @@ const save = function () {
|
|||||||
httpPost('/api/user/profile/update', form.value).then(() => {
|
httpPost('/api/user/profile/update', form.value).then(() => {
|
||||||
ElMessage.success({
|
ElMessage.success({
|
||||||
message: '更新成功',
|
message: '更新成功',
|
||||||
appendTo: '#user-info',
|
|
||||||
onClose: () => emits('hide', false)
|
onClose: () => emits('hide', false)
|
||||||
})
|
})
|
||||||
// 更新用户数据
|
// 更新用户数据
|
||||||
emits('update-user', {nickname: form.value['nickname'], avatar: form.value['avatar']});
|
emits('update-user', {nickname: form.value['nickname'], avatar: form.value['avatar']});
|
||||||
}).catch(() => {
|
}).catch((e) => {
|
||||||
ElMessage.error({
|
ElMessage.error('更新失败:' + e.message)
|
||||||
message: '更新失败',
|
|
||||||
appendTo: document.getElementById('user-info')
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const close = function () {
|
const close = function () {
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ const sendMsg = () => {
|
|||||||
httpGet('/api/verify/token').then(res => {
|
httpGet('/api/verify/token').then(res => {
|
||||||
httpPost('/api/verify/sms', {token: res.data, mobile: props.mobile}).then(() => {
|
httpPost('/api/verify/sms', {token: res.data, mobile: props.mobile}).then(() => {
|
||||||
ElMessage.success('短信发送成功')
|
ElMessage.success('短信发送成功')
|
||||||
let time = 10
|
let time = 120
|
||||||
btnText.value = time
|
btnText.value = time
|
||||||
const handler = setInterval(() => {
|
const handler = setInterval(() => {
|
||||||
time = time - 1
|
time = time - 1
|
||||||
|
|||||||
@@ -283,7 +283,7 @@ const sendMessage = () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prompt.value.trim().length === 0 || canSend.value === false) {
|
if (prompt.value.trim().length === 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user