From 23e353b18bc6094082ae3c32a4dfb507efa3dd60 Mon Sep 17 00:00:00 2001 From: RockYang Date: Sun, 28 Jan 2024 21:56:25 +0800 Subject: [PATCH] opt: enable use cdn url for mj-plus --- api/config.sample.toml | 22 +++--- api/core/types/config.go | 7 +- api/core/types/task.go | 2 +- api/handler/mj_handler.go | 26 +++++--- api/service/mj/client.go | 11 ++- api/service/mj/plus/client.go | 34 ++++++---- api/service/mj/plus/service.go | 65 +++++++++++++----- api/service/mj/pool.go | 70 +++++--------------- api/service/mj/service.go | 4 +- api/service/payment/payjs_service.go | 2 +- api/test/test.go | 7 +- deploy/conf/config.toml | 23 +++---- web/public/images/mic.gif | Bin 0 -> 38673 bytes web/src/assets/css/mobile/chat-session.styl | 16 ++++- web/src/main.js | 2 + web/src/views/ImageMj.vue | 5 +- web/src/views/mobile/ChatSession.vue | 55 ++++++++++----- 17 files changed, 193 insertions(+), 158 deletions(-) create mode 100644 web/public/images/mic.gif diff --git a/api/config.sample.toml b/api/config.sample.toml index 53fbaa80..27e24bf1 100644 --- a/api/config.sample.toml +++ b/api/config.sample.toml @@ -25,23 +25,16 @@ WeChatBot = false AppId = "" Token = "" -[SmsConfig] # 阿里云短信服务配置 - AccessKey = "" - AccessSecret = "" - Product = "Dysmsapi" - Domain = "dysmsapi.aliyuncs.com" - Sign = "" - CodeTempId = "" -[Sms] # Sms 配置,用于发送短信 +[SMS] # Sms 配置,用于发送短信 Active = "Ali" # 当前启用的短信服务,默认使用阿里云 - [Sms.SmsBao] + [SMS.Bao] Username = "" Password = "" Domain = "api.smsbao.com" Sign = "【极客学长】" CodeTemplate = "您的验证码是{code}。5分钟有效,若非本人操作,请忽略本短信。" - [Sms.Ali] + [SMS.Ali] AccessKey = "" AccessSecret = "" Product = "Dysmsapi" @@ -82,6 +75,7 @@ WeChatBot = false [[MjPlusConfigs]] Enabled = false ApiURL = "https://api.chatgpt-plus.net" # 目前暂时不支持更改 + CdnURL = "" # CND 加速的 URL,如果有的话就设置 ApiKey = "sk-xxx" NotifyURL = "https://ai.r9it.com/api/mj/notify" # 这里需要改成你的域名 @@ -113,9 +107,9 @@ WeChatBot = false [HuPiPayConfig] Enabled = false Name = "wechat" - AppId = "201906161477" - AppSecret = "7f403199d510fb2c6f0b9f2311800e7c" - PayURL = "https://api.xunhupay.com/payment/do.html" + AppId = "" + AppSecret = "" + ApiURL = "https://api.xunhupay.com" NotifyURL = "https://ai.r9it.com/api/payment/hupipay/notify" [SmtpConfig] # 注意,阿里云服务器禁用了25号端口,所以如果需要使用邮件功能,请别用阿里云服务器 @@ -130,5 +124,5 @@ WeChatBot = false Name = "wechat" # 请不要改动 AppId = "" # 商户 ID PrivateKey = "" # 秘钥 - ApiURL = "https://payjs.cn/api/native" + ApiURL = "https://payjs.cn" NotifyURL = "https://ai.r9it.com/api/payment/payjs/notify" # 异步回调地址,域名改成你自己的 \ No newline at end of file diff --git a/api/core/types/config.go b/api/core/types/config.go index f31ab96d..4fcb581d 100644 --- a/api/core/types/config.go +++ b/api/core/types/config.go @@ -19,7 +19,6 @@ type AppConfig struct { OSS OSSConfig // OSS config MjConfigs []MidJourneyConfig // mj AI draw service pool MjPlusConfigs []MidJourneyPlusConfig // MJ plus config - ImgCdnURL string // 图片反代加速地址 WeChatBot bool // 是否启用微信机器人 SdConfigs []StableDiffusionConfig // sd AI draw service pool @@ -51,6 +50,7 @@ type MidJourneyConfig struct { GuildId string // Server ID ChanelId string // Chanel ID UseCDN bool + ImgCdnURL string // 图片反代加速地址 DiscordAPI string DiscordGateway string } @@ -63,8 +63,9 @@ type StableDiffusionConfig struct { } type MidJourneyPlusConfig struct { - Enabled bool // 如果启用了 MidJourney Plus,将会自动禁用原生的MidJourney服务 - ApiURL string + Enabled bool // 如果启用了 MidJourney Plus,将会自动禁用原生的MidJourney服务 + ApiURL string // api 地址 + CdnURL string // CDN 加速地址 ApiKey string NotifyURL string // 任务进度更新回调地址 } diff --git a/api/core/types/task.go b/api/core/types/task.go index cb22c395..7e84aa65 100644 --- a/api/core/types/task.go +++ b/api/core/types/task.go @@ -17,7 +17,7 @@ const ( // MjTask MidJourney 任务 type MjTask struct { - Id int `json:"id"` + Id uint `json:"id"` TaskId string `json:"task_id"` ImgArr []string `json:"img_arr"` ChannelId string `json:"channel_id"` diff --git a/api/handler/mj_handler.go b/api/handler/mj_handler.go index b1c78756..11756b43 100644 --- a/api/handler/mj_handler.go +++ b/api/handler/mj_handler.go @@ -168,7 +168,7 @@ func (h *MidJourneyHandler) Image(c *gin.Context) { } h.pool.PushTask(types.MjTask{ - Id: int(job.Id), + Id: job.Id, TaskId: taskId, SessionId: data.SessionId, Type: types.TaskType(data.TaskType), @@ -178,7 +178,9 @@ func (h *MidJourneyHandler) Image(c *gin.Context) { }) client := h.pool.Clients.Get(uint(job.UserId)) - _ = client.Send([]byte("Task Updated")) + if client != nil { + _ = client.Send([]byte("Task Updated")) + } // update user's img calls h.db.Model(&model.User{}).Where("id = ?", job.UserId).UpdateColumn("img_calls", gorm.Expr("img_calls - ?", 1)) @@ -227,7 +229,7 @@ func (h *MidJourneyHandler) Upscale(c *gin.Context) { } h.pool.PushTask(types.MjTask{ - Id: int(job.Id), + Id: job.Id, SessionId: data.SessionId, Type: types.TaskUpscale, Prompt: data.Prompt, @@ -239,7 +241,9 @@ func (h *MidJourneyHandler) Upscale(c *gin.Context) { }) client := h.pool.Clients.Get(uint(job.UserId)) - _ = client.Send([]byte("Task Updated")) + if client != nil { + _ = client.Send([]byte("Task Updated")) + } resp.SUCCESS(c) } @@ -275,7 +279,7 @@ func (h *MidJourneyHandler) Variation(c *gin.Context) { } h.pool.PushTask(types.MjTask{ - Id: int(job.Id), + Id: job.Id, SessionId: data.SessionId, Type: types.TaskVariation, Prompt: data.Prompt, @@ -287,7 +291,9 @@ func (h *MidJourneyHandler) Variation(c *gin.Context) { }) client := h.pool.Clients.Get(uint(job.UserId)) - _ = client.Send([]byte("Task Updated")) + if client != nil { + _ = client.Send([]byte("Task Updated")) + } // update user's img calls h.db.Model(&model.User{}).Where("id = ?", job.UserId).UpdateColumn("img_calls", gorm.Expr("img_calls - ?", 1)) @@ -340,8 +346,8 @@ func (h *MidJourneyHandler) JobList(c *gin.Context) { if item.Progress < 100 && item.ImgURL == "" && item.OrgURL != "" { // 正在运行中任务使用代理访问图片 - if h.App.Config.ImgCdnURL != "" { - job.ImgURL = strings.ReplaceAll(job.OrgURL, "https://cdn.discordapp.com", h.App.Config.ImgCdnURL) + if job.UseProxy { + job.ImgURL = job.OrgURL } else { image, err := utils.DownloadImage(item.OrgURL, h.App.Config.ProxyURL) if err == nil { @@ -381,7 +387,9 @@ func (h *MidJourneyHandler) Remove(c *gin.Context) { } client := h.pool.Clients.Get(data.UserId) - _ = client.Send([]byte("Task Updated")) + if client != nil { + _ = client.Send([]byte("Task Updated")) + } resp.SUCCESS(c) } diff --git a/api/service/mj/client.go b/api/service/mj/client.go index bd557628..eada7586 100644 --- a/api/service/mj/client.go +++ b/api/service/mj/client.go @@ -12,13 +12,12 @@ import ( // MidJourney client type Client struct { - client *req.Client - Config types.MidJourneyConfig - imgCdnURL string - apiURL string + client *req.Client + Config types.MidJourneyConfig + apiURL string } -func NewClient(config types.MidJourneyConfig, proxy string, imgCdnURL string) *Client { +func NewClient(config types.MidJourneyConfig, proxy string) *Client { client := req.C().SetTimeout(10 * time.Second) var apiURL string // set proxy URL @@ -31,7 +30,7 @@ func NewClient(config types.MidJourneyConfig, proxy string, imgCdnURL string) *C } } - return &Client{client: client, Config: config, apiURL: apiURL, imgCdnURL: imgCdnURL} + return &Client{client: client, Config: config, apiURL: apiURL} } func (c *Client) Imagine(task types.MjTask) error { diff --git a/api/service/mj/plus/client.go b/api/service/mj/plus/client.go index b79afa24..b2035929 100644 --- a/api/service/mj/plus/client.go +++ b/api/service/mj/plus/client.go @@ -7,9 +7,10 @@ import ( "encoding/base64" "errors" "fmt" - "github.com/gin-gonic/gin" "io" + "github.com/gin-gonic/gin" + "github.com/imroc/req/v3" ) @@ -18,10 +19,17 @@ var logger = logger2.GetLogger() // Client MidJourney Plus Client type Client struct { Config types.MidJourneyPlusConfig + apiURL string } func NewClient(config types.MidJourneyPlusConfig) *Client { - return &Client{Config: config} + var apiURL string + if config.CdnURL != "" { + apiURL = config.CdnURL + } else { + apiURL = config.ApiURL + } + return &Client{Config: config, apiURL: apiURL} } type ImageReq struct { @@ -54,12 +62,12 @@ type ErrRes struct { } func (c *Client) Imagine(task types.MjTask) (ImageRes, error) { - apiURL := fmt.Sprintf("%s/mj-fast/mj/submit/imagine", c.Config.ApiURL) + apiURL := fmt.Sprintf("%s/mj-fast/mj/submit/imagine", c.apiURL) body := ImageReq{ BotType: "MID_JOURNEY", Prompt: task.Prompt, NotifyHook: c.Config.NotifyURL, - Base64Array: make([]string, 1), + Base64Array: make([]string, 0), } // 生成图片 Base64 编码 if len(task.ImgArr) > 0 { @@ -67,7 +75,7 @@ func (c *Client) Imagine(task types.MjTask) (ImageRes, error) { if err != nil { logger.Error("error with download image: ", err) } else { - body.Base64Array[0] = "data:image/png;base64," + base64.StdEncoding.EncodeToString(imageData) + body.Base64Array = append(body.Base64Array, "data:image/png;base64,"+base64.StdEncoding.EncodeToString(imageData)) } } @@ -80,12 +88,12 @@ func (c *Client) Imagine(task types.MjTask) (ImageRes, error) { SetErrorResult(&errRes). Post(apiURL) if err != nil { - errStr, _ := io.ReadAll(r.Body) - return ImageRes{}, fmt.Errorf("请求 API 出错:%v,%v", err, string(errStr)) + return ImageRes{}, fmt.Errorf("请求 API 出错:%v", err) } if r.IsErrorState() { - return ImageRes{}, fmt.Errorf("API 返回错误:%s", errRes.Error.Message) + errStr, _ := io.ReadAll(r.Body) + return ImageRes{}, fmt.Errorf("API 返回错误:%s,%v", errRes.Error.Message, string(errStr)) } return res, nil @@ -93,7 +101,7 @@ func (c *Client) Imagine(task types.MjTask) (ImageRes, error) { // Blend 融图 func (c *Client) Blend(task types.MjTask) (ImageRes, error) { - apiURL := fmt.Sprintf("%s/mj-fast/mj/submit/blend", c.Config.ApiURL) + apiURL := fmt.Sprintf("%s/mj-fast/mj/submit/blend", c.apiURL) body := ImageReq{ BotType: "MID_JOURNEY", Dimensions: "SQUARE", @@ -133,7 +141,7 @@ func (c *Client) Blend(task types.MjTask) (ImageRes, error) { // SwapFace 换脸 func (c *Client) SwapFace(task types.MjTask) (ImageRes, error) { - apiURL := fmt.Sprintf("%s/mj-fast/mj/insight-face/swap", c.Config.ApiURL) + apiURL := fmt.Sprintf("%s/mj-fast/mj/insight-face/swap", c.apiURL) // 生成图片 Base64 编码 if len(task.ImgArr) != 2 { return ImageRes{}, errors.New("参数错误,必须上传2张图片") @@ -189,7 +197,7 @@ func (c *Client) Upscale(task types.MjTask) (ImageRes, error) { "taskId": task.MessageId, "notifyHook": c.Config.NotifyURL, } - apiURL := fmt.Sprintf("%s/mj/submit/action", c.Config.ApiURL) + apiURL := fmt.Sprintf("%s/mj/submit/action", c.apiURL) var res ImageRes var errRes ErrRes r, err := req.C().R(). @@ -216,7 +224,7 @@ func (c *Client) Variation(task types.MjTask) (ImageRes, error) { "taskId": task.MessageId, "notifyHook": c.Config.NotifyURL, } - apiURL := fmt.Sprintf("%s/mj/submit/action", c.Config.ApiURL) + apiURL := fmt.Sprintf("%s/mj/submit/action", c.apiURL) var res ImageRes var errRes ErrRes r, err := req.C().R(). @@ -262,7 +270,7 @@ type QueryRes struct { } func (c *Client) QueryTask(taskId string) (QueryRes, error) { - apiURL := fmt.Sprintf("%s/mj/task/%s/fetch", c.Config.ApiURL, taskId) + apiURL := fmt.Sprintf("%s/mj/task/%s/fetch", c.apiURL, taskId) var res QueryRes r, err := req.C().R().SetHeader("Authorization", "Bearer "+c.Config.ApiKey). SetSuccessResult(&res). diff --git a/api/service/mj/plus/service.go b/api/service/mj/plus/service.go index ff761366..f653e851 100644 --- a/api/service/mj/plus/service.go +++ b/api/service/mj/plus/service.go @@ -86,10 +86,10 @@ func (s *Service) Run() { } if err != nil || (res.Code != 1 && res.Code != 22) { - errMsg := err.Error() + res.Description + errMsg := fmt.Sprintf("%v,%s", err, res.Description) logger.Error("绘画任务执行失败:", errMsg) // update the task progress - s.db.Model(&model.MidJourneyJob{Id: uint(task.Id)}).UpdateColumns(map[string]interface{}{ + s.db.Model(&model.MidJourneyJob{Id: task.Id}).UpdateColumns(map[string]interface{}{ "progress": -1, "err_msg": errMsg, }) @@ -105,10 +105,10 @@ func (s *Service) Run() { } logger.Infof("任务提交成功:%+v", res) // lock the task until the execute timeout - s.taskStartTimes[task.Id] = time.Now() + s.taskStartTimes[int(task.Id)] = time.Now() atomic.AddInt32(&s.HandledTaskNum, 1) // 更新任务 ID/频道 - s.db.Model(&model.MidJourneyJob{}).Where("id = ?", task.Id).UpdateColumns(map[string]interface{}{ + s.db.Debug().Model(&model.MidJourneyJob{Id: task.Id}).UpdateColumns(map[string]interface{}{ "task_id": res.Result, "channel_id": s.Name, }) @@ -152,26 +152,55 @@ type CBReq struct { } `json:"properties"` } -func (s *Service) Notify(data CBReq, job model.MidJourneyJob) error { +func (s *Service) Notify(job model.MidJourneyJob) error { + task, err := s.Client.QueryTask(job.TaskId) + if err != nil { + return err + } - job.Progress = utils.IntValue(strings.Replace(data.Progress, "%", "", 1), 0) - job.Prompt = data.Properties.FinalPrompt - if data.ImageUrl != "" { - job.OrgURL = data.ImageUrl + // 任务执行失败了 + if task.FailReason != "" { + s.db.Model(&model.MidJourneyJob{Id: job.Id}).UpdateColumns(map[string]interface{}{ + "progress": -1, + "err_msg": task.FailReason, + }) + return fmt.Errorf("task failed: %v", task.FailReason) + } + + if len(task.Buttons) > 0 { + job.Hash = GetImageHash(task.Buttons[0].CustomId) + } + oldProgress := job.Progress + job.Progress = utils.IntValue(strings.Replace(task.Progress, "%", "", 1), 0) + job.Prompt = task.PromptEn + if task.ImageUrl != "" { + if s.Client.Config.CdnURL != "" { + job.OrgURL = strings.Replace(task.ImageUrl, s.Client.Config.ApiURL, s.Client.Config.CdnURL, 1) + } else { + job.OrgURL = task.ImageUrl + } } job.UseProxy = true - job.MessageId = data.Id - logger.Debugf("JOB: %+v", job) - res := s.db.Updates(&job) - if res.Error != nil { - return fmt.Errorf("error with update job: %v", res.Error) + job.MessageId = task.Id + tx := s.db.Updates(&job) + if tx.Error != nil { + return fmt.Errorf("error with update database: %v", tx.Error) } - - if data.Status == "SUCCESS" { + if task.Status == "SUCCESS" { // release lock task atomic.AddInt32(&s.HandledTaskNum, -1) } - - s.notifyQueue.RPush(job.UserId) + // 通知前端更新任务进度 + if oldProgress != job.Progress { + s.notifyQueue.RPush(job.UserId) + } return nil } + +func GetImageHash(action string) string { + split := strings.Split(action, "::") + if len(split) > 5 { + return split[4] + } + return split[len(split)-1] +} diff --git a/api/service/mj/pool.go b/api/service/mj/pool.go index 9d2cc9d0..0e271bb3 100644 --- a/api/service/mj/pool.go +++ b/api/service/mj/pool.go @@ -6,11 +6,9 @@ import ( "chatplus/service/oss" "chatplus/store" "chatplus/store/model" - "chatplus/utils" "fmt" "github.com/go-redis/redis/v8" "strings" - "sync/atomic" "time" "gorm.io/gorm" @@ -35,9 +33,8 @@ func NewServicePool(db *gorm.DB, redisCli *redis.Client, manager *oss.UploaderMa if config.Enabled == false { continue } - if config.ApiURL != "https://gpt.bemore.lol" && config.ApiURL != "https://api.chat-plus.net" { - config.ApiURL = "https://api.chat-plus.net" - } + // rewrite api key + config.ApiURL = "https://api.chat-plus.net" client := plus.NewClient(config) name := fmt.Sprintf("mj-service-plus-%d", k) servicePlus := plus.NewService(name, taskQueue, notifyQueue, 10, 600, db, client) @@ -54,7 +51,7 @@ func NewServicePool(db *gorm.DB, redisCli *redis.Client, manager *oss.UploaderMa continue } // create mj client - client := NewClient(config, appConfig.ProxyURL, appConfig.ImgCdnURL) + client := NewClient(config, appConfig.ProxyURL) name := fmt.Sprintf("MjService-%d", k) // create mj service @@ -98,6 +95,9 @@ func (p *ServicePool) CheckTaskNotify() { continue } client := p.Clients.Get(userId) + if client == nil { + continue + } err = client.Send([]byte("Task Updated")) if err != nil { continue @@ -120,17 +120,17 @@ func (p *ServicePool) DownloadImages() { if v.OrgURL == "" { continue } + + logger.Infof("try to download image: %s", v.OrgURL) var imgURL string var err error if v.UseProxy { if servicePlus := p.getServicePlus(v.ChannelId); servicePlus != nil { task, _ := servicePlus.Client.QueryTask(v.TaskId) - if task.ImageUrl != "" { - imgURL, err = p.uploaderManager.GetUploadHandler().PutImg(task.ImageUrl, false) - } if len(task.Buttons) > 0 { - v.Hash = getImageHash(task.Buttons[0].CustomId) + v.Hash = plus.GetImageHash(task.Buttons[0].CustomId) } + imgURL, err = p.uploaderManager.GetUploadHandler().PutImg(v.OrgURL, false) } } else { imgURL, err = p.uploaderManager.GetUploadHandler().PutImg(v.OrgURL, true) @@ -138,12 +138,17 @@ func (p *ServicePool) DownloadImages() { if err != nil { logger.Error("error with download image: ", err) continue + } else { + logger.Info("download image %v successfully.", v.OrgURL) } v.ImgURL = imgURL p.db.Updates(&v) client := p.Clients.Get(uint(v.UserId)) + if client == nil { + continue + } err = client.Send([]byte("Task Updated")) if err != nil { continue @@ -179,7 +184,7 @@ func (p *ServicePool) Notify(data plus.CBReq) error { return nil } if servicePlus := p.getServicePlus(job.ChannelId); servicePlus != nil { - return servicePlus.Notify(data, job) + return servicePlus.Notify(job) } return nil @@ -211,40 +216,7 @@ func (p *ServicePool) SyncTaskProgress() { } if servicePlus := p.getServicePlus(v.ChannelId); servicePlus != nil { - task, err := servicePlus.Client.QueryTask(v.TaskId) - if err != nil { - continue - } - // 任务失败了 - if task.FailReason != "" { - p.db.Model(&model.MidJourneyJob{Id: v.Id}).UpdateColumns(map[string]interface{}{ - "progress": -1, - "err_msg": task.FailReason, - }) - continue - } - if len(task.Buttons) > 0 { - v.Hash = getImageHash(task.Buttons[0].CustomId) - } - oldProgress := v.Progress - v.Progress = utils.IntValue(strings.Replace(task.Progress, "%", "", 1), 0) - v.Prompt = task.PromptEn - if task.ImageUrl != "" { - v.OrgURL = task.ImageUrl - } - v.UseProxy = true - v.MessageId = task.Id - - p.db.Updates(&v) - - if task.Status == "SUCCESS" { - // release lock task - atomic.AddInt32(&servicePlus.HandledTaskNum, -1) - } - // 通知前端更新任务进度 - if oldProgress != v.Progress { - p.notifyQueue.RPush(v.UserId) - } + _ = servicePlus.Notify(v) } } @@ -263,11 +235,3 @@ func (p *ServicePool) getServicePlus(name string) *plus.Service { } return nil } - -func getImageHash(action string) string { - split := strings.Split(action, "::") - if len(split) > 5 { - return split[4] - } - return split[len(split)-1] -} diff --git a/api/service/mj/service.go b/api/service/mj/service.go index f0bfc47d..d354456c 100644 --- a/api/service/mj/service.go +++ b/api/service/mj/service.go @@ -97,7 +97,7 @@ func (s *Service) Run() { } // lock the task until the execute timeout - s.taskStartTimes[task.Id] = time.Now() + s.taskStartTimes[int(task.Id)] = time.Now() atomic.AddInt32(&s.handledTaskNum, 1) } @@ -152,7 +152,7 @@ func (s *Service) Notify(data CBReq) { job.OrgURL = data.Image.URL if s.client.Config.UseCDN { job.UseProxy = true - job.ImgURL = strings.ReplaceAll(data.Image.URL, "https://cdn.discordapp.com", s.client.imgCdnURL) + job.ImgURL = strings.ReplaceAll(data.Image.URL, "https://cdn.discordapp.com", s.client.Config.ImgCdnURL) } res = s.db.Updates(&job) diff --git a/api/service/payment/payjs_service.go b/api/service/payment/payjs_service.go index fb87a249..62d88854 100644 --- a/api/service/payment/payjs_service.go +++ b/api/service/payment/payjs_service.go @@ -56,7 +56,7 @@ func (js *PayJS) Pay(param JPayReq) JPayReps { } p.Add("mchid", js.config.AppId) - p.Add("Sign", js.sign(p)) + p.Add("sign", js.sign(p)) cli := http.Client{} apiURL := fmt.Sprintf("%s/api/native", js.config.ApiURL) diff --git a/api/test/test.go b/api/test/test.go index 008479b8..667f3f7b 100644 --- a/api/test/test.go +++ b/api/test/test.go @@ -2,11 +2,10 @@ package main import ( "fmt" - "strings" + "net/url" ) func main() { - str := "7151109597841850368 一个漂亮的中国女孩,手上拿着一桶爆米花,脸上带着迷人的微笑,电影效果" - index := strings.Index(str, " ") - fmt.Println(str[index+1:]) + u, err := url.Parse("https://api.chat-plus.net/mj/image/1706368258238514?aaa=bbb") + fmt.Println(u.Path, u.RawQuery, err) } diff --git a/deploy/conf/config.toml b/deploy/conf/config.toml index 2fe38d89..f848371d 100644 --- a/deploy/conf/config.toml +++ b/deploy/conf/config.toml @@ -26,23 +26,15 @@ WeChatBot = false AppId = "" Token = "" -[SmsConfig] # 阿里云短信服务配置 - AccessKey = "" - AccessSecret = "" - Product = "Dysmsapi" - Domain = "dysmsapi.aliyuncs.com" - Sign = "" - CodeTempId = "" - -[Sms] # Sms 配置,用于发送短信 +[SMS] # Sms 配置,用于发送短信 Active = "Ali" # 当前启用的短信服务,默认使用阿里云 - [Sms.SmsBao] + [SMS.Bao] Username = "" Password = "" Domain = "api.smsbao.com" Sign = "【极客学长】" CodeTemplate = "您的验证码是{code}。5分钟有效,若非本人操作,请忽略本短信。" - [Sms.Ali] + [SMS.Ali] AccessKey = "" AccessSecret = "" Product = "Dysmsapi" @@ -83,6 +75,7 @@ WeChatBot = false [[MjPlusConfigs]] Enabled = false ApiURL = "https://api.chatgpt-plus.net" # 目前暂时不支持更改 + CdnURL = "" # CND 加速的 URL,如果有的话就设置 ApiKey = "sk-xxx" NotifyURL = "https://ai.r9it.com/api/mj/notify" # 这里需要改成你的域名 @@ -114,9 +107,9 @@ WeChatBot = false [HuPiPayConfig] Enabled = false Name = "wechat" - AppId = "201906161477" - AppSecret = "7f403199d510fb2c6f0b9f2311800e7c" - PayURL = "https://api.xunhupay.com/payment/do.html" + AppId = "" + AppSecret = "" + ApiURL = "https://api.xunhupay.com" NotifyURL = "https://ai.r9it.com/api/payment/hupipay/notify" [SmtpConfig] # 注意,阿里云服务器禁用了25号端口,所以如果需要使用邮件功能,请别用阿里云服务器 @@ -131,5 +124,5 @@ WeChatBot = false Name = "wechat" # 请不要改动 AppId = "" # 商户 ID PrivateKey = "" # 秘钥 - ApiURL = "https://payjs.cn/api/native" + ApiURL = "https://payjs.cn" NotifyURL = "https://ai.r9it.com/api/payment/payjs/notify" # 异步回调地址,域名改成你自己的 \ No newline at end of file diff --git a/web/public/images/mic.gif b/web/public/images/mic.gif new file mode 100644 index 0000000000000000000000000000000000000000..d6e544dcb117bc9a2c26c523cae383eeae517dd9 GIT binary patch literal 38673 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 M#^M#GBsuf{0~z}HW&i*H literal 0 HcmV?d00001 diff --git a/web/src/assets/css/mobile/chat-session.styl b/web/src/assets/css/mobile/chat-session.styl index fa7bfb46..5da59cc3 100644 --- a/web/src/assets/css/mobile/chat-session.styl +++ b/web/src/assets/css/mobile/chat-session.styl @@ -27,7 +27,11 @@ } .button-voice { - padding 0 5px + padding 0 2px + + .el-icon { + font-size 24px + } height 30px } } @@ -52,6 +56,16 @@ } } +.van-overlay { + .mic-wrapper { + display flex + height 100vh + justify-content center + align-items center + flex-flow column + } +} + .van-theme-dark { .mobile-chat { .message-list-box { diff --git a/web/src/main.js b/web/src/main.js index 3119148a..4c455a38 100644 --- a/web/src/main.js +++ b/web/src/main.js @@ -19,6 +19,7 @@ import { List, NavBar, Notify, + Overlay, Picker, Popup, Search, @@ -65,6 +66,7 @@ app.use(Switch); app.use(Uploader); app.use(Tag); app.use(V3waterfall) +app.use(Overlay) app.use(router).use(ElementPlus).mount('#app') diff --git a/web/src/views/ImageMj.vue b/web/src/views/ImageMj.vue index 58e94c87..2dcb485c 100644 --- a/web/src/views/ImageMj.vue +++ b/web/src/views/ImageMj.vue @@ -716,10 +716,13 @@ const fetchFinishJobs = (userId) => { if (jobs[i].type === 'upscale' || jobs[i].type === 'swapFace') { jobs[i]['thumb_url'] = jobs[i]['img_url'] + '?imageView2/1/w/480/h/600/q/75' } else { - jobs[i]['can_opt'] = true jobs[i]['thumb_url'] = jobs[i]['img_url'] + '?imageView2/1/w/480/h/480/q/75' } } + + if (jobs[i].type === 'image' || jobs[i].type === 'variation') { + jobs[i]['can_opt'] = true + } } finishedJobs.value = jobs }).catch(e => { diff --git a/web/src/views/mobile/ChatSession.vue b/web/src/views/mobile/ChatSession.vue index d2e57551..63f731ce 100644 --- a/web/src/views/mobile/ChatSession.vue +++ b/web/src/views/mobile/ChatSession.vue @@ -64,7 +64,9 @@ > @@ -84,6 +86,19 @@ + + +
+
+ +
+ 说完了 +
+
@@ -101,6 +116,7 @@ import ChatReply from "@/components/mobile/ChatReply.vue"; import {getSessionId, getUserToken} from "@/store/session"; import {checkSession} from "@/action/session"; import Clipboard from "clipboard"; +import {Microphone} from "@element-plus/icons-vue"; const winHeight = ref(0) const navBarRef = ref(null) @@ -114,6 +130,7 @@ const modelValue = chatConfig.modelValue const title = chatConfig.title const chatId = chatConfig.chatId const loginUser = ref(null) +const showMic = ref(false) const url = location.protocol + '//' + location.host + '/mobile/chat/export?chat_id=' + chatId @@ -421,25 +438,29 @@ const shareChat = (option) => { } } +// eslint-disable-next-line no-undef +const recognition = new webkitSpeechRecognition() || SpeechRecognition(); +//recognition.lang = 'zh-CN' // 设置语音识别语言 +recognition.onresult = function (event) { + prompt.value = event.results[0][0].transcript +}; + +recognition.onerror = function (event) { + showNotify({type: 'danger', message: '语音识别错误:' + event.error}) +}; + +recognition.onend = function () { + console.log('语音识别结束'); +}; const inputVoice = () => { - const recognition = new webkitSpeechRecognition() || SpeechRecognition(); - // recognition.lang = 'zh-CN' // 设置语音识别语言 - - recognition.onresult = function (event) { - const result = event.results[0][0].transcript; - showToast('你说了: ' + result) - }; - - recognition.onerror = function (event) { - showNotify({type: 'danger', message: '语音识别错误:' + event.error}) - }; - - recognition.onend = function () { - console.log('语音识别结束'); - }; - + showMic.value = true recognition.start(); } + +const stopVoice = () => { + showMic.value = false + recognition.stop() +}