main #101
56
app/other.go
Normal file
56
app/other.go
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"go-wechat/model/vo"
|
||||||
|
"go-wechat/service"
|
||||||
|
"go-wechat/utils"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SendAiSummary
|
||||||
|
// @description: 发送AI摘要
|
||||||
|
// @param ctx
|
||||||
|
func SendAiSummary(ctx *gin.Context) {
|
||||||
|
// 获取群Id
|
||||||
|
groupId := ctx.Query("id")
|
||||||
|
if groupId == "" {
|
||||||
|
ctx.String(http.StatusForbidden, "群Id不能为空")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取出群名称
|
||||||
|
groupInfo, err := service.GetFriendInfoById(groupId)
|
||||||
|
if err != nil {
|
||||||
|
ctx.String(http.StatusInternalServerError, "获取群信息失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取对话记录
|
||||||
|
var records []vo.TextMessageItem
|
||||||
|
if records, err = service.GetTextMessagesById(groupId); err != nil {
|
||||||
|
log.Printf("获取群[%s]对话记录失败, 错误信息: %v", groupId, err)
|
||||||
|
ctx.String(http.StatusInternalServerError, "获取群对话记录失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(records) < 10 {
|
||||||
|
ctx.String(http.StatusForbidden, "群对话记录不足10条,建议自己看")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 组装对话记录为字符串
|
||||||
|
var replyMsg string
|
||||||
|
replyMsg, err = utils.GetAiSummary(groupInfo.Nickname, records)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("群聊记录总结失败: %v", err.Error())
|
||||||
|
ctx.String(http.StatusInternalServerError, "群聊消息总结失败,错误信息: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
replyMsg = fmt.Sprintf("#昨日消息总结\n又是一天过去了,让我们一起来看看昨儿群友们都聊了什么有趣的话题吧~\n\n%s", replyMsg)
|
||||||
|
log.Printf("群[%s]对话记录总结成功,总结内容: %s", groupInfo.Nickname, replyMsg)
|
||||||
|
_ = utils.SendMessage(groupId, "", replyMsg, 0)
|
||||||
|
ctx.String(http.StatusOK, "操作完成")
|
||||||
|
}
|
9
main.go
9
main.go
@ -12,6 +12,8 @@ import (
|
|||||||
"html/template"
|
"html/template"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -22,6 +24,13 @@ func init() {
|
|||||||
initialization.Plugin() // 注册插件
|
initialization.Plugin() // 注册插件
|
||||||
tasks.InitTasks() // 初始化定时任务
|
tasks.InitTasks() // 初始化定时任务
|
||||||
mq.Init() // 初始化MQ
|
mq.Init() // 初始化MQ
|
||||||
|
|
||||||
|
if flag, _ := strconv.ParseBool(os.Getenv("DONT_SEND")); flag {
|
||||||
|
log.Printf("已设置环境变量DONT_SEND,不发送消息")
|
||||||
|
}
|
||||||
|
if flag, _ := strconv.ParseBool(os.Getenv("DONT_SAVE")); flag {
|
||||||
|
log.Printf("已设置环境变量DONT_SAVE,不保存消息")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -28,6 +28,7 @@ func Init(g *gin.Engine) {
|
|||||||
api.POST("/ai/model", app.ChangeUseAiModel) // 修改使用的AI模型
|
api.POST("/ai/model", app.ChangeUseAiModel) // 修改使用的AI模型
|
||||||
api.POST("/ai/free", app.ChangeAiFreeLimit) // 修改AI免费次数
|
api.POST("/ai/free", app.ChangeAiFreeLimit) // 修改AI免费次数
|
||||||
api.POST("/ai/assistant", app.ChangeUseAiAssistant) // 修改使用的AI助手
|
api.POST("/ai/assistant", app.ChangeUseAiAssistant) // 修改使用的AI助手
|
||||||
|
api.POST("/ai/summary", app.SendAiSummary) // 手动发送AI聊天总结
|
||||||
api.PUT("/welcome/status", app.ChangeEnableWelcomeStatus) // 修改是否开启迎新状态
|
api.PUT("/welcome/status", app.ChangeEnableWelcomeStatus) // 修改是否开启迎新状态
|
||||||
api.PUT("/command/status", app.ChangeEnableCommandStatus) // 修改是否开启指令状态
|
api.PUT("/command/status", app.ChangeEnableCommandStatus) // 修改是否开启指令状态
|
||||||
api.PUT("/news/status", app.ChangeEnableNewsStatus) // 修改是否开启早报状态
|
api.PUT("/news/status", app.ChangeEnableNewsStatus) // 修改是否开启早报状态
|
||||||
|
@ -1,15 +1,11 @@
|
|||||||
package summary
|
package summary
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/sashabaranov/go-openai"
|
|
||||||
"go-wechat/config"
|
|
||||||
"go-wechat/model/vo"
|
"go-wechat/model/vo"
|
||||||
"go-wechat/service"
|
"go-wechat/service"
|
||||||
"go-wechat/utils"
|
"go-wechat/utils"
|
||||||
"log"
|
"log"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -37,63 +33,21 @@ func AiSummary() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// 组装对话记录为字符串
|
// 组装对话记录为字符串
|
||||||
var content []string
|
var replyMsg string
|
||||||
for _, record := range records {
|
replyMsg, err = utils.GetAiSummary(group.Nickname, records)
|
||||||
content = append(content, fmt.Sprintf(`{"%s": "%s"}--end--`, record.Nickname, strings.ReplaceAll(record.Message, "\n", "。。")))
|
|
||||||
}
|
|
||||||
|
|
||||||
msgTmp := `请帮我总结一下一下的群聊内容的梗概,生成的梗概需要尽可能详细,需要带上一些聊天关键信息,并且带上群友名字。
|
|
||||||
注意,他们可能是多个话题,请仔细甄别。
|
|
||||||
每一行代表一个人的发言,每一行的的格式为: {"{nickname}": "{content}"}--end--
|
|
||||||
|
|
||||||
群名称: %s
|
|
||||||
聊天记录如下:
|
|
||||||
%s
|
|
||||||
`
|
|
||||||
|
|
||||||
msg := fmt.Sprintf(msgTmp, group.Nickname, strings.Join(content, "\n"))
|
|
||||||
|
|
||||||
// AI总结
|
|
||||||
messages := []openai.ChatCompletionMessage{
|
|
||||||
{
|
|
||||||
Role: openai.ChatMessageRoleUser,
|
|
||||||
Content: msg,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// 默认使用AI回复
|
|
||||||
conf := openai.DefaultConfig(config.Conf.Ai.ApiKey)
|
|
||||||
if config.Conf.Ai.BaseUrl != "" {
|
|
||||||
conf.BaseURL = fmt.Sprintf("%s/v1", config.Conf.Ai.BaseUrl)
|
|
||||||
}
|
|
||||||
ai := openai.NewClientWithConfig(conf)
|
|
||||||
var resp openai.ChatCompletionResponse
|
|
||||||
resp, err = ai.CreateChatCompletion(
|
|
||||||
context.Background(),
|
|
||||||
openai.ChatCompletionRequest{
|
|
||||||
Model: config.Conf.Ai.SummaryModel,
|
|
||||||
Messages: messages,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("群聊记录总结失败: %v", err.Error())
|
log.Printf("群聊记录总结失败: %v", err.Error())
|
||||||
utils.SendMessage(group.Wxid, "", "#昨日消息总结\n\n群聊消息总结失败,错误信息: "+err.Error(), 0)
|
_ = utils.SendMessage(group.Wxid, "", "#昨日消息总结\n\n群聊消息总结失败,错误信息: "+err.Error(), 0)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// 返回消息为空
|
replyMsg = fmt.Sprintf("#昨日消息总结\n又是一天过去了,让我们一起来看看昨儿群友们都聊了什么有趣的话题吧~\n\n%s", replyMsg)
|
||||||
if resp.Choices[0].Message.Content == "" {
|
|
||||||
utils.SendMessage(group.Wxid, "", "#昨日消息总结\n\n群聊消息总结失败,AI返回结果为空", 0)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
replyMsg := fmt.Sprintf("#昨日消息总结\n又是一天过去了,让我们一起来看看昨儿群友们都聊了什么有趣的话题吧~\n\n%s", resp.Choices[0].Message.Content)
|
|
||||||
//log.Printf("群[%s]对话记录总结成功,总结内容: %s", group.Wxid, replyMsg)
|
//log.Printf("群[%s]对话记录总结成功,总结内容: %s", group.Wxid, replyMsg)
|
||||||
utils.SendMessage(group.Wxid, "", replyMsg, 0)
|
_ = utils.SendMessage(group.Wxid, "", replyMsg, 0)
|
||||||
|
|
||||||
// 判断耗时是否达到15秒,不足就等待
|
// 判断耗时是否达到一分钟,不足就等待
|
||||||
if used := time.Now().Sub(start); used < 15*time.Second {
|
if used := time.Now().Sub(start); used < time.Minute {
|
||||||
time.Sleep(15*time.Second - used)
|
time.Sleep(time.Minute - used)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,8 @@ import (
|
|||||||
"go-wechat/common/current"
|
"go-wechat/common/current"
|
||||||
"go-wechat/config"
|
"go-wechat/config"
|
||||||
"log"
|
"log"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -17,6 +19,10 @@ import (
|
|||||||
// @param atId
|
// @param atId
|
||||||
// @param msg
|
// @param msg
|
||||||
func SendMessage(toId, atId, msg string, retryCount int) (err error) {
|
func SendMessage(toId, atId, msg string, retryCount int) (err error) {
|
||||||
|
if flag, _ := strconv.ParseBool(os.Getenv("DONT_SEND")); flag {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if retryCount > 5 {
|
if retryCount > 5 {
|
||||||
log.Printf("重试五次失败,停止发送")
|
log.Printf("重试五次失败,停止发送")
|
||||||
err = errors.New("重试五次失败,停止发送")
|
err = errors.New("重试五次失败,停止发送")
|
||||||
@ -62,6 +68,10 @@ func SendMessage(toId, atId, msg string, retryCount int) (err error) {
|
|||||||
// @param imgPath string 图片路径
|
// @param imgPath string 图片路径
|
||||||
// @param retryCount int 重试次数
|
// @param retryCount int 重试次数
|
||||||
func SendImage(toId, imgPath string, retryCount int) {
|
func SendImage(toId, imgPath string, retryCount int) {
|
||||||
|
if flag, _ := strconv.ParseBool(os.Getenv("DONT_SEND")); flag {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if retryCount > 5 {
|
if retryCount > 5 {
|
||||||
log.Printf("重试五次失败,停止发送")
|
log.Printf("重试五次失败,停止发送")
|
||||||
return
|
return
|
||||||
@ -94,6 +104,10 @@ func SendImage(toId, imgPath string, retryCount int) {
|
|||||||
// @param emotionHash string 表情包hash(md5值)
|
// @param emotionHash string 表情包hash(md5值)
|
||||||
// @param retryCount int 重试次数
|
// @param retryCount int 重试次数
|
||||||
func SendEmotion(toId, emotionHash string, retryCount int) {
|
func SendEmotion(toId, emotionHash string, retryCount int) {
|
||||||
|
if flag, _ := strconv.ParseBool(os.Getenv("DONT_SEND")); flag {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if retryCount > 5 {
|
if retryCount > 5 {
|
||||||
log.Printf("重试五次失败,停止发送")
|
log.Printf("重试五次失败,停止发送")
|
||||||
return
|
return
|
||||||
@ -200,6 +214,10 @@ func QuitChatroom(chatRoomId string, retryCount int) {
|
|||||||
// @param url string 链接
|
// @param url string 链接
|
||||||
// @param retryCount int 重试次数
|
// @param retryCount int 重试次数
|
||||||
func SendPublicMsg(wxId, title, digest, url string, retryCount int) {
|
func SendPublicMsg(wxId, title, digest, url string, retryCount int) {
|
||||||
|
if flag, _ := strconv.ParseBool(os.Getenv("DONT_SEND")); flag {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if retryCount > 5 {
|
if retryCount > 5 {
|
||||||
log.Printf("重试五次失败,停止发送")
|
log.Printf("重试五次失败,停止发送")
|
||||||
return
|
return
|
||||||
|
108
utils/summary.go
Normal file
108
utils/summary.go
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/sashabaranov/go-openai"
|
||||||
|
"go-wechat/config"
|
||||||
|
"go-wechat/model/vo"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetAiSummary
|
||||||
|
// @description: AI总结群聊记录
|
||||||
|
// @param groupName
|
||||||
|
// @param records
|
||||||
|
// @return replyMsg
|
||||||
|
// @return err
|
||||||
|
func GetAiSummary(groupName string, records []vo.TextMessageItem) (replyMsg string, err error) {
|
||||||
|
// 组装对话记录为字符串
|
||||||
|
var content []string
|
||||||
|
for _, record := range records {
|
||||||
|
content = append(content, fmt.Sprintf(`{"%s": "%s"}--end--`, record.Nickname, strings.ReplaceAll(record.Message, "\n", "。。")))
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt := `# Role: 数据分析师和信息整理专家
|
||||||
|
# Background: 用户需要从群聊记录中提取有价值的信息,包括技术话题讨论、工具分享、网站或资源链接。
|
||||||
|
# Profile: 你是一位专业的数据分析师,擅长从大量文本中提取关键信息,并能够高效地整理和总结内容。
|
||||||
|
# Skills: 数据分析、信息提取、内容总结、网络资源评估。
|
||||||
|
# Goals: 设计一个流程,帮助用户识别群聊中的技术话题、共享的工具和资源,并高效地总结每个话题的要点。
|
||||||
|
# Constrains: 该流程需要简洁明了,易于用户理解和操作,同时确保不遗漏任何重要信息。
|
||||||
|
# OutputFormat: 结构化的文本总结,包含关键讨论点、结论等。
|
||||||
|
# Workflow:
|
||||||
|
## 1. 快速浏览群聊记录
|
||||||
|
- 通过快速浏览聊天记录,初步识别出主要话题。
|
||||||
|
## 2. 识别和分类主要话题
|
||||||
|
- 识别群聊中的主要话题,分类讨论内容。
|
||||||
|
- 每个话题独立提取,避免混淆。
|
||||||
|
- 话题识别尽可能准确,确保不遗漏重要信息。
|
||||||
|
## 3. 编写总结概括
|
||||||
|
- 对每个话题进行总结,包含以下内容:
|
||||||
|
- 关键讨论点:主要的讨论内容和观点。
|
||||||
|
- 参与成员:参与讨论的成员的昵称。
|
||||||
|
- 结论:得出的结论或共识,内容要尽可能详细。
|
||||||
|
## 4. 组织信息
|
||||||
|
- 以逻辑清晰、易于理解的方式组织信息。
|
||||||
|
- 使用结构化文本,使总结内容易于阅读。
|
||||||
|
## 5. 生成最终总结
|
||||||
|
- 根据上述步骤,生成每个话题的详细总结。
|
||||||
|
- 确保每个总结完整且有条理。
|
||||||
|
- 返回数据格式为结构化文本,方便用户查阅。
|
||||||
|
## 6. 群聊记录消息格式
|
||||||
|
格式: {"{nickname}": "{content}"}--end--
|
||||||
|
# Examples:
|
||||||
|
- 话题:技术讨论 - AI在医疗领域的应用
|
||||||
|
- 关键讨论点:讨论了AI在医疗诊断中的应用前景和现有挑战。
|
||||||
|
- 成员:张三、李四、王五。
|
||||||
|
- 结论:AI技术有望提高医疗诊断的准确性和效率,但数据隐私和模型解释性仍需解决。
|
||||||
|
|
||||||
|
- 话题:美食讨论 - 杭州到底有没有美食
|
||||||
|
- 关键讨论点:讨论了杭州的美食有哪些、分布地区、评价如何。
|
||||||
|
- 成员:张三、李四、王五。
|
||||||
|
- 结论:杭州好像没有美食。
|
||||||
|
`
|
||||||
|
|
||||||
|
msg := fmt.Sprintf("群名称: %s\n聊天记录如下:\n%s", groupName, strings.Join(content, "\n"))
|
||||||
|
|
||||||
|
// AI总结
|
||||||
|
messages := []openai.ChatCompletionMessage{
|
||||||
|
{
|
||||||
|
Role: openai.ChatMessageRoleSystem,
|
||||||
|
Content: prompt,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Role: openai.ChatMessageRoleUser,
|
||||||
|
Content: msg,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// 默认使用AI回复
|
||||||
|
conf := openai.DefaultConfig(config.Conf.Ai.ApiKey)
|
||||||
|
if config.Conf.Ai.BaseUrl != "" {
|
||||||
|
conf.BaseURL = fmt.Sprintf("%s/v1", config.Conf.Ai.BaseUrl)
|
||||||
|
}
|
||||||
|
ai := openai.NewClientWithConfig(conf)
|
||||||
|
var resp openai.ChatCompletionResponse
|
||||||
|
resp, err = ai.CreateChatCompletion(
|
||||||
|
context.Background(),
|
||||||
|
openai.ChatCompletionRequest{
|
||||||
|
Model: config.Conf.Ai.SummaryModel,
|
||||||
|
Messages: messages,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("群聊记录总结失败: %v", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回消息为空
|
||||||
|
if resp.Choices[0].Message.Content == "" {
|
||||||
|
err = errors.New("AI返回结果为空")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
replyMsg = resp.Choices[0].Message.Content
|
||||||
|
return
|
||||||
|
}
|
@ -57,6 +57,9 @@
|
|||||||
|
|
||||||
<!-- 消息总结 -->
|
<!-- 消息总结 -->
|
||||||
{{define "summary"}}
|
{{define "summary"}}
|
||||||
|
{{ if eq .EnableSummary true }}
|
||||||
|
<button type="button" class="btn-link float-end text-blue-600" onclick="sendAiSummary({{.Wxid}})">手动发送总结</button>
|
||||||
|
{{ end }}
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="{{ if eq .EnableSummary true }}bg-green-600{{ else }}bg-gray-200{{ end }} relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-green-600 focus:ring-offset-2"
|
class="{{ if eq .EnableSummary true }}bg-green-600{{ else }}bg-gray-200{{ end }} relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-green-600 focus:ring-offset-2"
|
||||||
role="switch" aria-checked="false" onclick="changeSummaryEnableStatus({{.Wxid}})">
|
role="switch" aria-checked="false" onclick="changeSummaryEnableStatus({{.Wxid}})">
|
||||||
|
@ -56,6 +56,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{{ if eq .EnableAi true }}
|
{{ if eq .EnableAi true }}
|
||||||
<div class="float-end">
|
<div class="float-end">
|
||||||
|
<!-- 模型 -->
|
||||||
<div>
|
<div>
|
||||||
<label>
|
<label>
|
||||||
<select class="block w-full rounded-md border-0 py-1.5 pl-3 pr-10 text-gray-900 ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-green-600 sm:text-sm sm:leading-6" onchange="aiModelChange(event, {{.Wxid}})">
|
<select class="block w-full rounded-md border-0 py-1.5 pl-3 pr-10 text-gray-900 ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-green-600 sm:text-sm sm:leading-6" onchange="aiModelChange(event, {{.Wxid}})">
|
||||||
@ -68,6 +69,7 @@
|
|||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- AI角色 -->
|
||||||
<div class="float-end mt-1">
|
<div class="float-end mt-1">
|
||||||
<label>
|
<label>
|
||||||
<select
|
<select
|
||||||
|
@ -317,3 +317,30 @@ function changeAiFreeLimit(wxid, limitNumber) {
|
|||||||
window.location.reload();
|
window.location.reload();
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 发送AI群聊总结
|
||||||
|
function sendAiSummary(wxid) {
|
||||||
|
if (wxid === '') {
|
||||||
|
alert("wxid不能为空")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!confirm("确定发送AI群聊总结吗?")) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 请求接口
|
||||||
|
axios({
|
||||||
|
method: 'post',
|
||||||
|
url: '/api/ai/summary',
|
||||||
|
params: {
|
||||||
|
id: wxid,
|
||||||
|
}
|
||||||
|
}).then(function (response) {
|
||||||
|
console.log(`返回结果: ${JSON.stringify(response)}`);
|
||||||
|
alert(`${response.data}`)
|
||||||
|
}).catch(function (error) {
|
||||||
|
console.log(`错误信息: ${error}`);
|
||||||
|
alert(`${response.data}`)
|
||||||
|
}).finally(function () {
|
||||||
|
window.location.reload();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user