Compare commits

...

8 Commits

Author SHA1 Message Date
李寻欢
a11ba51246 Merge pull request '🆕 控制页面新增设置AI模型' (#21) from hotfix into main
Reviewed-on: https://gitee.ltd/lxh/go-wxhelper/pulls/21
2024-01-31 12:03:01 +08:00
李寻欢
ea4262adf0 🆕 控制页面新增设置AI模型 2024-01-31 12:02:33 +08:00
李寻欢
a9f6c9ff0d Merge pull request '🐛 Fix a bug.' (#20) from hotfix into main
Reviewed-on: https://gitee.ltd/lxh/go-wxhelper/pulls/20
2024-01-26 12:00:53 +08:00
李寻欢
244bff5714 🐛 Fix a bug. 2024-01-26 12:00:29 +08:00
李寻欢
b46271a111 Merge pull request '🐛 Fix a bug.' (#19) from hotfix into main
Reviewed-on: https://gitee.ltd/lxh/go-wxhelper/pulls/19
2024-01-25 16:33:59 +08:00
李寻欢
5d11cc7c8a 🐛 Fix a bug. 2024-01-25 16:33:45 +08:00
李寻欢
c6da056f98 Merge pull request '🎨 逻辑优化,那个网页,大大地提速' (#18) from hotfix into main
Reviewed-on: https://gitee.ltd/lxh/go-wxhelper/pulls/18
2024-01-25 16:27:51 +08:00
李寻欢
61e03a6a7b 🎨 逻辑优化,那个网页,大大地提速 2024-01-25 16:27:12 +08:00
13 changed files with 178 additions and 51 deletions

View File

@@ -16,6 +16,13 @@ type changeStatusParam struct {
UserId string `json:"userId"`
}
// changeUseAiModelParam
// @description: 修改使用的AI模型用的参数集
type changeUseAiModelParam struct {
WxId string `json:"wxid" binding:"required"` // 群Id或微信Id
Model string `json:"model" binding:"required"` // 模型代码
}
// ChangeEnableAiStatus
// @description: 修改是否开启AI
// @param ctx
@@ -39,6 +46,27 @@ func ChangeEnableAiStatus(ctx *gin.Context) {
ctx.String(http.StatusOK, "操作成功")
}
// ChangeUseAiModel
// @description: 修改使用的AI模型
// @param ctx
func ChangeUseAiModel(ctx *gin.Context) {
var p changeUseAiModelParam
if err := ctx.ShouldBind(&p); err != nil {
ctx.String(http.StatusBadRequest, "参数错误")
return
}
err := client.MySQL.Model(&entity.Friend{}).
Where("wxid = ?", p.WxId).
Update("`ai_model`", p.Model).Error
if err != nil {
log.Printf("修改【%s】的AI模型失败%s", p.WxId, err)
ctx.String(http.StatusInternalServerError, "操作失败: %s", err)
return
}
ctx.String(http.StatusOK, "操作成功")
}
// ChangeEnableGroupRankStatus
// @description: 修改是否开启水群排行榜
// @param ctx

View File

@@ -23,6 +23,7 @@ func Index(ctx *gin.Context) {
result["friends"] = friends
result["groups"] = groups
result["vnc"] = config.Conf.Wechat.VncUrl
result["aiModels"] = config.Conf.Ai.Models
// 渲染页面
ctx.HTML(http.StatusOK, "index.html", result)
}

View File

@@ -3,7 +3,7 @@ wechat:
# 微信HOOK接口地址
host: 10.0.0.73:19088
# 微信容器映射出来的vnc页面地址没有就不填
vncUrl: http://192.168.1.175:19087/vnc_lite.html
# vncUrl: http://192.168.1.175:19087/vnc_lite.html
# 是否在启动的时候自动设置hook服务的回调
autoSetCallback: false
# 回调IP如果是Docker运行本参数必填(填auto表示自动不适用于 docker 环境)如果Docker修改了映射格式为 ip:port
@@ -45,6 +45,19 @@ ai:
baseUrl: https://sxxx
# 人设
personality: 你的名字叫张三,你是一个百科机器人,你的爱好是看电影,你的性格是开朗的,你的专长是讲故事,你的梦想是当一名童话故事作家。你对政治没有一点儿兴趣,也不会讨论任何与政治相关的话题,你甚至可以拒绝回答这一类话题。
models:
- name: ChatGPT-4
model: gpt-4-0613
- name: 讯飞星火v3
model: SparkDesk3
- name: 讯飞星火随机
model: SparkDesk
- name: 月之暗面-8k
model: moonshot-v1-8k
- name: 月之暗面-32k
model: moonshot-v1-32k
- name: 月之暗面-128k
model: moonshot-v1-128k
# 资源配置
# map[k]v结构k 会变成全小写,所以这儿不能用大写字母

View File

@@ -3,9 +3,17 @@ package config
// ai
// @description: AI配置
type ai struct {
Enable bool `json:"enable" yaml:"enable"` // 是否启用AI
Model string `json:"model" yaml:"model"` // 模型
ApiKey string `json:"apiKey" yaml:"apiKey"` // API Key
BaseUrl string `json:"baseUrl" yaml:"baseUrl"` // API地址
Personality string `json:"personality" yaml:"personality"` // 人设
Enable bool `json:"enable" yaml:"enable"` // 是否启用AI
Model string `json:"model" yaml:"model"` // 模型
ApiKey string `json:"apiKey" yaml:"apiKey"` // API Key
BaseUrl string `json:"baseUrl" yaml:"baseUrl"` // API地址
Personality string `json:"personality" yaml:"personality"` // 人设
Models []aiModel `json:"models" yaml:"models"` // 模型列表
}
// aiModel
// @description: AI模型
type aiModel struct {
Name string `json:"name" yaml:"name"` // 模型名称
Model string `json:"model" yaml:"model"` // 模型代码
}

View File

@@ -7,16 +7,17 @@ import (
// Friend
// @description: 好友列表
type Friend struct {
Wxid string `json:"wxid"` // 微信原始Id
CustomAccount string `json:"customAccount"` // 微信号
Nickname string `json:"nickname"` // 昵称
Pinyin string `json:"pinyin"` // 昵称拼音大写首字母
PinyinAll string `json:"pinyinAll"` // 昵称全拼
EnableAi bool `json:"enableAI" gorm:"type:tinyint(1) default 0 not null"` // 是否使用AI
AiModel string `json:"aiModel"` // AI模型
EnableChatRank bool `json:"enableChatRank" gorm:"type:tinyint(1) default 0 not null"` // 是否使用聊天排行
EnableWelcome bool `json:"enableWelcome" gorm:"type:tinyint(1) default 0 not null"` // 是否启用迎新
IsOk bool `json:"isOk" gorm:"type:tinyint(1) default 0 not null"` // 是否正常
Wxid string `json:"wxid"` // 微信原始Id
CustomAccount string `json:"customAccount"` // 微信号
Nickname string `json:"nickname"` // 昵称
Pinyin string `json:"pinyin"` // 昵称拼音大写首字母
PinyinAll string `json:"pinyinAll"` // 昵称全拼
LastActive time.Time `json:"lastActive"` // 最后活跃时间
EnableAi bool `json:"enableAI" gorm:"type:tinyint(1) default 0 not null"` // 是否使用AI
AiModel string `json:"aiModel"` // AI模型
EnableChatRank bool `json:"enableChatRank" gorm:"type:tinyint(1) default 0 not null"` // 是否使用聊天排行
EnableWelcome bool `json:"enableWelcome" gorm:"type:tinyint(1) default 0 not null"` // 是否启用迎新
IsOk bool `json:"isOk" gorm:"type:tinyint(1) default 0 not null"` // 是否正常
}
func (Friend) TableName() string {
@@ -34,6 +35,7 @@ type GroupUser struct {
IsMember bool `json:"isMember" gorm:"type:tinyint(1) default 0 not null"` // 是否群成员
IsAdmin bool `json:"isAdmin" gorm:"type:tinyint(1) default 0 not null"` // 是否群主
JoinTime time.Time `json:"joinTime"` // 加入时间
LastActive time.Time `json:"lastActive"` // 最后活跃时间
LeaveTime *time.Time `json:"leaveTime"` // 离开时间
SkipChatRank bool `json:"skipChatRank" gorm:"type:tinyint(1) default 0 not null"` // 是否跳过聊天排行
}

View File

@@ -22,6 +22,7 @@ func Init(g *gin.Engine) {
// 接口
api := g.Group("/api")
api.PUT("/ai/status", app.ChangeEnableAiStatus) // 修改是否开启AI状态
api.POST("/ai/model", app.ChangeUseAiModel) // 修改使用的AI模型
api.PUT("/welcome/status", app.ChangeEnableWelcomeStatus) // 修改是否开启迎新状态
api.PUT("/command/status", app.ChangeEnableCommandStatus) // 修改是否开启指令状态
api.PUT("/grouprank/status", app.ChangeEnableGroupRankStatus) // 修改是否开启水群排行榜状态

View File

@@ -4,6 +4,7 @@ import (
"go-wechat/client"
"go-wechat/entity"
"go-wechat/vo"
"log"
"strings"
)
@@ -16,10 +17,11 @@ func GetAllFriend() (friends, groups []vo.FriendItem, err error) {
var records []vo.FriendItem
err = client.MySQL.
Table("t_friend AS tf").
Joins("LEFT JOIN t_message AS tm ON tf.wxid = tm.from_user").
Select("tf.*", "MAX(tm.create_at) AS last_active_time").
Group("tf.wxid").
Order("last_active_time DESC").
//Joins("LEFT JOIN t_message AS tm ON tf.wxid = tm.from_user").
//Select("tf.*", "MAX(tm.create_at) AS last_active").
Select("tf.*").
//Group("tf.wxid").
Order("tf.last_active DESC").
Find(&records).Error
if err != nil {
return
@@ -60,3 +62,27 @@ func CheckIsEnableCommand(userId string) (flag bool) {
client.MySQL.Model(&entity.Friend{}).Where("enable_command = 1").Where("wxid = ?", userId).Count(&coo)
return coo > 0
}
// updateLastActive
// @description: 更新最后活跃时间
// @param msg
func updateLastActive(msg entity.Message) {
var err error
// 如果是群,更新群成员最后活跃时间
if strings.HasSuffix(msg.FromUser, "@chatroom") {
err = client.MySQL.Model(&entity.GroupUser{}).
Where("group_id = ?", msg.FromUser).
Where("wxid = ?", msg.GroupUser).
Update("last_active", msg.CreateAt).Error
if err != nil {
log.Printf("更新群成员最后活跃时间失败, 错误信息: %v", err)
}
}
// 更新群或者好友活跃时间
err = client.MySQL.Model(&entity.Friend{}).
Where("wxid = ?", msg.FromUser).
Update("last_active", msg.CreateAt).Error
if err != nil {
log.Printf("更新群或者好友活跃时间失败, 错误信息: %v", err)
}
}

View File

@@ -13,10 +13,9 @@ import (
func GetGroupUsersByGroupId(groupId string) (records []vo.GroupUserItem, err error) {
err = client.MySQL.
Table("t_group_user AS tgu").
Joins("LEFT JOIN t_message AS tm ON tm.from_user = tgu.group_id AND tm.group_user = tgu.wxid").
//Select("tgu.wxid", "tgu.nickname", "tgu.head_image", "tgu.is_member", "tgu.leave_time",
// "tgu.skip_chat_rank", "MAX(tm.create_at) AS last_active_time").
Select("tgu.*", "MAX(tm.create_at) AS last_active_time").
//Joins("LEFT JOIN t_message AS tm ON tm.from_user = tgu.group_id AND tm.group_user = tgu.wxid").
//Select("tgu.*", "MAX(tm.create_at) AS last_active").
Select("tgu.*").
Where("tgu.group_id = ?", groupId).
Group("tgu.group_id, tgu.wxid").
Order("tgu.join_time DESC").

View File

@@ -32,4 +32,10 @@ func SaveMessage(msg entity.Message) {
log.Printf("消息入库失败, 错误信息: %v", err)
}
log.Printf("消息入库成功消息Id: %d", msg.MsgId)
// 更新最后活跃时间
// 只处理收到的消息
if msg.MsgId > 1 {
go updateLastActive(msg)
}
}

View File

@@ -66,6 +66,7 @@ func Sync() {
PinyinAll: friend.PinyinAll,
Wxid: friend.Wxid,
IsOk: true,
LastActive: time.Now().Local(),
}).Error
if err != nil {
log.Printf("新增好友失败: %s", err.Error())
@@ -149,14 +150,15 @@ func syncGroupUsers(tx *gorm.DB, gid string) {
if count == 0 {
// 新增
err = tx.Create(&entity.GroupUser{
GroupId: gid,
Account: cp.Account,
HeadImage: cp.HeadImage,
Nickname: cp.Nickname,
Wxid: cp.Wxid,
IsMember: true,
IsAdmin: wxid == baseResp.Data.Admin,
JoinTime: time.Now().Local(),
GroupId: gid,
Account: cp.Account,
HeadImage: cp.HeadImage,
Nickname: cp.Nickname,
Wxid: cp.Wxid,
IsMember: true,
IsAdmin: wxid == baseResp.Data.Admin,
JoinTime: time.Now().Local(),
LastActive: time.Now().Local(),
}).Error
if err != nil {
log.Printf("新增群成员失败: %s", err.Error())

View File

@@ -4,7 +4,6 @@
<meta charset="UTF-8">
<title>水群助手</title>
<!-- <link href="https://cdn.jsdelivr.net/npm/daisyui@4.4.14/dist/full.min.css" rel="stylesheet" type="text/css" />-->
<link href="assets/css/daisyui-4.4.14-full.min.css" rel="stylesheet" type="text/css" />
<link href="assets/css/index.css" rel="stylesheet" type="text/css" />
@@ -47,10 +46,10 @@
<td>{{ .CustomAccount }}</td>
<td>{{ .Nickname }}</td>
<td>
{{ if eq .LastActiveTime.IsNil true }}
{{ if eq .LastActive.IsNil true }}
无数据
{{ else }}
{{ .LastActiveTime }}
{{ .LastActive }}
{{ end }}
</td>
<td>
@@ -71,6 +70,16 @@
<div class="swap-on">✔️已启用</div>
<div class="swap-off">❌已禁用</div>
</label>
{{ if .EnableAi }}
<br />
<select class="select select-success select-xs w-1/2 max-w-xs" onchange="aiModelChange(event, {{.Wxid}})">
<option value="" {{ if eq .AiModel ""}}selected{{ end }}>默认(gpt-3.5-turbo-0613)</option>
{{$useModel := .AiModel}}
{{ range $.aiModels }}
<option value="{{.Model}}" {{ if eq $useModel .Model}}selected{{ end }}>{{.Name}}({{.Model}})</option>
{{ end }}
</select>
{{ end }}
</td>
<td>
<label class="swap swap-flip {{ checkSwap .EnableCommand }}">
@@ -110,10 +119,10 @@
<td>{{ .Wxid }}</td>
<td>{{ .Nickname }}</td>
<td>
{{ if eq .LastActiveTime.IsNil true }}
{{ if eq .LastActive.IsNil true }}
无数据
{{ else }}
{{ .LastActiveTime }}
{{ .LastActive }}
{{ end }}
</td>
<td>
@@ -135,6 +144,16 @@
<div class="swap-on">✔️已启用</div>
<div class="swap-off">❌已禁用</div>
</label>
{{ if .EnableAi }}
<br />
<select class="select select-success select-xs w-1/2 max-w-xs" onchange="aiModelChange(event, {{.Wxid}})">
<option value="" {{ if eq .AiModel ""}}selected{{ end }}>默认(gpt-3.5-turbo-0613)</option>
{{$useModel := .AiModel}}
{{ range $.aiModels }}
<option value="{{.Model}}" {{ if eq $useModel .Model}}selected{{ end }}>{{.Name}}({{.Model}})</option>
{{ end }}
</select>
{{ end }}
</td>
<td>
<!-- EnableChatRank -->

View File

@@ -113,7 +113,8 @@ function getGroupUsers(groupId, groupName) {
const groupUsers = response.data
// 循环渲染数据
groupUsers.forEach((groupUser, i) => {
const { wxid, nickname, isMember, isAdmin, joinTime, lastActiveTime, leaveTime, skipChatRank } = groupUser;
console.log(groupUser)
const { wxid, nickname, isMember, isAdmin, joinTime, lastActive, leaveTime, skipChatRank } = groupUser;
let row = tbody.insertRow(i);
// Insert data into cells
@@ -122,7 +123,7 @@ function getGroupUsers(groupId, groupName) {
row.insertCell(2).innerHTML = `<div class="badge badge-${isMember ? 'info' : 'error'} gap-2">${isMember ? '是' : '否'}</div>`;
row.insertCell(3).innerHTML = `<div class="badge badge-${isAdmin ? 'info' : 'error'} gap-2">${isAdmin ? '是' : '否'}</div>`;
row.insertCell(4).innerHTML = joinTime;
row.insertCell(5).innerHTML = lastActiveTime;
row.insertCell(5).innerHTML = lastActive;
row.insertCell(6).innerHTML = leaveTime;
row.insertCell(7).innerHTML = `<input type="checkbox" class="toggle toggle-error" ${skipChatRank ? 'checked' : ''} onclick="changeUserGroupRankSkipStatus('${groupId}', '${wxid}')" />`;
});
@@ -133,4 +134,25 @@ function getGroupUsers(groupId, groupName) {
// loading.style.display = "none"
groupNameTag.innerHTML = groupName
})
}
}
// AI模型变动
function aiModelChange(event, wxid) {
// 取出变动后的值
const modelStr = event.target.value;
console.log("AI模型变动: ", wxid, modelStr)
axios({
method: 'post',
url: '/api/ai/model',
data: {
wxid: wxid,
model: modelStr
}
}).then(function (response) {
console.log(`返回结果: ${JSON.stringify(response)}`);
alert(`${response.data}`)
}).catch(function (error) {
console.log(`错误信息: ${error}`);
alert("修改失败")
})
}

View File

@@ -12,26 +12,26 @@ type FriendItem struct {
Pinyin string // 昵称拼音大写首字母
PinyinAll string // 昵称全拼
Wxid string // 微信原始Id
LastActive types.DateTime // 最后活跃时间
EnableAi bool // 是否使用AI
AiModel string // AI模型
EnableChatRank bool // 是否使用聊天排行
EnableWelcome bool // 是否使用迎新
EnableCommand bool // 是否启用指令
IsOk bool // 是否还在通讯库(群聊是要还在群里也算)
LastActiveTime types.DateTime // 最后活跃时间
}
// GroupUserItem
// @description: 群成员列表数据
type GroupUserItem struct {
Wxid string `json:"wxid"` // 微信Id
Account string `json:"account"` // 账号
HeadImage string `json:"headImage"` // 头像
Nickname string `json:"nickname"` // 昵称
IsMember bool `json:"isMember" ` // 是否群成员
IsAdmin bool `json:"isAdmin"` // 是否群主
JoinTime types.DateTime `json:"joinTime"` // 加入时间
LastActiveTime types.DateTime `json:"lastActiveTime"` // 最后活跃时间
LeaveTime types.DateTime `json:"leaveTime"` // 离开时间
SkipChatRank bool `json:"skipChatRank" ` // 是否跳过聊天排行
Wxid string `json:"wxid"` // 微信Id
Account string `json:"account"` // 账号
HeadImage string `json:"headImage"` // 头像
Nickname string `json:"nickname"` // 昵称
IsMember bool `json:"isMember" ` // 是否群成员
IsAdmin bool `json:"isAdmin"` // 是否群主
JoinTime types.DateTime `json:"joinTime"` // 加入时间
LastActive types.DateTime `json:"lastActive"` // 最后活跃时间
LeaveTime types.DateTime `json:"leaveTime"` // 离开时间
SkipChatRank bool `json:"skipChatRank" ` // 是否跳过聊天排行
}