diff --git a/applogic/app.go b/applogic/app.go
index 8cf56ad..6885999 100644
--- a/applogic/app.go
+++ b/applogic/app.go
@@ -204,6 +204,32 @@ func (app *App) EnsureCustomTableExist() error {
return nil
}
+func (app *App) EnsureCustomTableExistSP() error {
+ createTableSQL := `
+ CREATE TABLE IF NOT EXISTS custom_table (
+ user_id TEXT PRIMARY KEY,
+ promptstr TEXT NOT NULL,
+ promptstr_stat INTEGER,
+ str1 TEXT,
+ str2 TEXT,
+ str3 TEXT,
+ str4 TEXT,
+ str5 TEXT,
+ str6 TEXT,
+ str7 TEXT,
+ str8 TEXT,
+ str9 TEXT,
+ str10 TEXT
+ );`
+
+ _, err := app.DB.Exec(createTableSQL)
+ if err != nil {
+ return fmt.Errorf("error creating custom_table: %w", err)
+ }
+
+ return nil
+}
+
func (app *App) EnsureUserContextTableExists() error {
createTableSQL := `
CREATE TABLE IF NOT EXISTS user_context (
@@ -237,6 +263,39 @@ func (app *App) EnsureUserContextTableExists() error {
return nil
}
+func (app *App) EnsureUserContextTableExistsSP() error {
+ createTableSQL := `
+ CREATE TABLE IF NOT EXISTS user_context (
+ user_id TEXT PRIMARY KEY,
+ conversation_id TEXT NOT NULL,
+ parent_message_id TEXT
+ );`
+
+ _, err := app.DB.Exec(createTableSQL)
+ if err != nil {
+ return fmt.Errorf("error creating user_context table: %w", err)
+ }
+
+ // 为 conversation_id 创建索引
+ createConvIDIndexSQL := `CREATE INDEX IF NOT EXISTS idx_user_context_conversation_id ON user_context(conversation_id);`
+
+ _, err = app.DB.Exec(createConvIDIndexSQL)
+ if err != nil {
+ return fmt.Errorf("error creating index on user_context(conversation_id): %w", err)
+ }
+
+ // 为 parent_message_id 创建索引
+ // 只有当您需要根据 parent_message_id 进行查询时才添加此索引
+ createParentMsgIDIndexSQL := `CREATE INDEX IF NOT EXISTS idx_user_context_parent_message_id ON user_context(parent_message_id);`
+
+ _, err = app.DB.Exec(createParentMsgIDIndexSQL)
+ if err != nil {
+ return fmt.Errorf("error creating index on user_context(parent_message_id): %w", err)
+ }
+
+ return nil
+}
+
func (app *App) handleUserContext(userID int64) (string, string, error) {
var conversationID, parentMessageID string
@@ -265,6 +324,34 @@ func (app *App) handleUserContext(userID int64) (string, string, error) {
return conversationID, parentMessageID, nil
}
+func (app *App) handleUserContextSP(userID string) (string, string, error) {
+ var conversationID, parentMessageID string
+
+ // 检查用户上下文是否存在
+ query := `SELECT conversation_id, parent_message_id FROM user_context WHERE user_id = ?`
+ err := app.DB.QueryRow(query, userID).Scan(&conversationID, &parentMessageID)
+ if err != nil {
+ if err == sql.ErrNoRows {
+ // 用户上下文不存在,创建新的
+ conversationID = utils.GenerateUUID() // 假设generateUUID()是一个生成UUID的函数
+ parentMessageID = ""
+
+ // 插入新的用户上下文
+ insertQuery := `INSERT INTO user_context (user_id, conversation_id, parent_message_id) VALUES (?, ?, ?)`
+ _, err = app.DB.Exec(insertQuery, userID, conversationID, parentMessageID)
+ if err != nil {
+ return "", "", err
+ }
+ } else {
+ // 查询过程中出现了其他错误
+ return "", "", err
+ }
+ }
+
+ // 返回conversationID和parentMessageID
+ return conversationID, parentMessageID, nil
+}
+
func (app *App) migrateUserToNewContext(userID int64) error {
// 生成新的conversationID
newConversationID := utils.GenerateUUID() // 假设generateUUID()是一个生成UUID的函数
@@ -279,6 +366,20 @@ func (app *App) migrateUserToNewContext(userID int64) error {
return nil
}
+func (app *App) migrateUserToNewContextSP(userID string) error {
+ // 生成新的conversationID
+ newConversationID := utils.GenerateUUID() // 假设GenerateUUID()是一个生成UUID的函数
+
+ // 更新用户上下文
+ updateQuery := `UPDATE user_context SET conversation_id = ?, parent_message_id = '' WHERE user_id = ?`
+ _, err := app.DB.Exec(updateQuery, newConversationID, userID)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
func (app *App) updateUserContext(userID int64, parentMessageID string) error {
updateQuery := `UPDATE user_context SET parent_message_id = ? WHERE user_id = ?`
_, err := app.DB.Exec(updateQuery, parentMessageID, userID)
@@ -288,6 +389,15 @@ func (app *App) updateUserContext(userID int64, parentMessageID string) error {
return nil
}
+func (app *App) updateUserContextSP(userID string, parentMessageID string) error {
+ updateQuery := `UPDATE user_context SET parent_message_id = ? WHERE user_id = ?`
+ _, err := app.DB.Exec(updateQuery, parentMessageID, userID)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
func (app *App) updateUserContextPro(userID int64, conversationID, parentMessageID string) error {
updateQuery := `
UPDATE user_context
@@ -300,6 +410,18 @@ func (app *App) updateUserContextPro(userID int64, conversationID, parentMessage
return nil
}
+func (app *App) updateUserContextProSP(userID string, conversationID, parentMessageID string) error {
+ updateQuery := `
+ UPDATE user_context
+ SET conversation_id = ?, parent_message_id = ?
+ WHERE user_id = ?;`
+ _, err := app.DB.Exec(updateQuery, conversationID, parentMessageID, userID)
+ if err != nil {
+ return fmt.Errorf("error updating user context: %w", err)
+ }
+ return nil
+}
+
func (app *App) getHistory(conversationID, parentMessageID string) ([]structs.Message, error) {
// 如果不开启上下文
if config.GetNoContext() {
@@ -385,6 +507,20 @@ func (app *App) AddUserMemory(userID int64, conversationID, parentMessageID, con
return app.ensureMemoryLimit(userID)
}
+func (app *App) AddUserMemorySP(userID string, conversationID, parentMessageID, conversationTitle string) error {
+ // 插入新的记忆
+ insertMemorySQL := `
+ INSERT INTO user_memories (user_id, conversation_id, parent_message_id, conversation_title)
+ VALUES (?, ?, ?, ?);`
+ _, err := app.DB.Exec(insertMemorySQL, userID, conversationID, parentMessageID, conversationTitle)
+ if err != nil {
+ return fmt.Errorf("error inserting new memory: %w", err)
+ }
+
+ // 检查并保持记忆数量不超过10条
+ return app.ensureMemoryLimitSP(userID)
+}
+
func (app *App) updateConversationTitle(userID int64, conversationID, parentMessageID, newTitle string) error {
// 定义SQL更新语句
updateQuery := `
@@ -401,6 +537,22 @@ func (app *App) updateConversationTitle(userID int64, conversationID, parentMess
return nil
}
+func (app *App) updateConversationTitleSP(userID string, conversationID, parentMessageID, newTitle string) error {
+ // 定义SQL更新语句
+ updateQuery := `
+ UPDATE user_memories
+ SET conversation_title = ?
+ WHERE user_id = ? AND conversation_id = ? AND parent_message_id = ?;`
+
+ // 执行SQL更新操作
+ _, err := app.DB.Exec(updateQuery, newTitle, userID, conversationID, parentMessageID)
+ if err != nil {
+ return fmt.Errorf("error updating conversation title: %w", err)
+ }
+
+ return nil
+}
+
func (app *App) ensureMemoryLimit(userID int64) error {
// 查询当前记忆总数
countQuerySQL := `SELECT COUNT(*) FROM user_memories WHERE user_id = ?;`
@@ -430,6 +582,35 @@ func (app *App) ensureMemoryLimit(userID int64) error {
return nil
}
+func (app *App) ensureMemoryLimitSP(userID string) error {
+ // 查询当前记忆总数
+ countQuerySQL := `SELECT COUNT(*) FROM user_memories WHERE user_id = ?;`
+ var count int
+ row := app.DB.QueryRow(countQuerySQL, userID)
+ err := row.Scan(&count)
+ if err != nil {
+ return fmt.Errorf("error counting memories: %w", err)
+ }
+
+ // 如果记忆超过5条,则删除最旧的记忆
+ if count > 5 {
+ deleteOldestMemorySQL := `
+ DELETE FROM user_memories
+ WHERE memory_id IN (
+ SELECT memory_id FROM user_memories
+ WHERE user_id = ?
+ ORDER BY memory_id ASC
+ LIMIT ?
+ );`
+ _, err := app.DB.Exec(deleteOldestMemorySQL, userID, count-5)
+ if err != nil {
+ return fmt.Errorf("error deleting old memories: %w", err)
+ }
+ }
+
+ return nil
+}
+
func (app *App) GetUserMemories(userID int64) ([]structs.Memory, error) {
// 定义查询SQL,获取所有相关的记忆
querySQL := `
@@ -458,3 +639,32 @@ func (app *App) GetUserMemories(userID int64) ([]structs.Memory, error) {
return memories, nil
}
+
+func (app *App) GetUserMemoriesSP(userID string) ([]structs.Memory, error) {
+ // 定义查询SQL,获取所有相关的记忆
+ querySQL := `
+ SELECT conversation_id, parent_message_id, conversation_title
+ FROM user_memories
+ WHERE user_id = ?;
+ `
+ rows, err := app.DB.Query(querySQL, userID)
+ if err != nil {
+ return nil, fmt.Errorf("error querying user memories: %w", err)
+ }
+ defer rows.Close() // 确保关闭rows以释放数据库资源
+
+ var memories []structs.Memory
+ for rows.Next() {
+ var m structs.Memory
+ if err := rows.Scan(&m.ConversationID, &m.ParentMessageID, &m.ConversationTitle); err != nil {
+ return nil, fmt.Errorf("error scanning memory: %w", err)
+ }
+ memories = append(memories, m)
+ }
+
+ if err := rows.Err(); err != nil {
+ return nil, fmt.Errorf("error during rows iteration: %w", err)
+ }
+
+ return memories, nil
+}
diff --git a/applogic/gensokyo_sp.go b/applogic/gensokyo_sp.go
new file mode 100644
index 0000000..e7184a4
--- /dev/null
+++ b/applogic/gensokyo_sp.go
@@ -0,0 +1,1016 @@
+package applogic
+
+import (
+ "bufio"
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "io"
+ "math/rand"
+ "net/http"
+ "net/url"
+ "strconv"
+ "strings"
+ "sync"
+
+ "github.com/hoshinonyaruko/gensokyo-llm/acnode"
+ "github.com/hoshinonyaruko/gensokyo-llm/config"
+ "github.com/hoshinonyaruko/gensokyo-llm/fmtf"
+ "github.com/hoshinonyaruko/gensokyo-llm/prompt"
+ "github.com/hoshinonyaruko/gensokyo-llm/promptkb"
+ "github.com/hoshinonyaruko/gensokyo-llm/structs"
+ "github.com/hoshinonyaruko/gensokyo-llm/utils"
+)
+
+// UserInfoSP 结构体用于储存用户信息
+type UserInfoSP struct {
+ UserID string
+ GroupID string
+ RealMessageType string
+ MessageType string
+}
+
+// globalMapSP 用于存储conversationID与UserInfoSP的映射
+var globalMapSP sync.Map
+
+func (app *App) GensokyoHandlerSP(w http.ResponseWriter, r *http.Request) {
+ // 只处理POST请求
+ if r.Method != http.MethodPost {
+ http.Error(w, "Only POST method is allowed", http.StatusMethodNotAllowed)
+ return
+ }
+
+ // 获取访问者的IP地址
+ ip := r.RemoteAddr // 注意:这可能包含端口号
+ ip = strings.Split(ip, ":")[0] // 去除端口号,仅保留IP地址
+
+ // 获取IP白名单
+ whiteList := config.IPWhiteList()
+
+ // 检查IP是否在白名单中
+ if !utils.Contains(whiteList, ip) {
+ // 尝试获取URL中的access_token
+ accessToken := r.URL.Query().Get("access_token")
+ if accessToken == "" || accessToken != config.GetAccessKey() {
+ http.Error(w, "Access denied", http.StatusForbidden) // 使用403 Forbidden作为更合适的HTTP状态码
+ return
+ }
+ }
+
+ // 读取请求体
+ body, err := io.ReadAll(r.Body)
+ if err != nil {
+ http.Error(w, "Error reading request body", http.StatusInternalServerError)
+ return
+ }
+ defer r.Body.Close()
+
+ // 解析请求体到OnebotGroupMessage结构体
+ var message structs.OnebotGroupMessageS
+
+ err = json.Unmarshal(body, &message)
+ if err != nil {
+ fmtf.Printf("Error parsing request body: %+v\n", string(body))
+ http.Error(w, "Error parsing request body", http.StatusInternalServerError)
+ return
+ }
+
+ // 打印日志信息,包括prompt参数
+ fmtf.Printf("收到onebotv11信息: %+v\n", string(body))
+
+ // 打印消息和其他相关信息
+ fmtf.Printf("Received message: %v\n", message.Message)
+ fmtf.Printf("Full message details: %+v\n", message)
+
+ // 进行array转换
+ // 检查并解析消息类型
+ if _, ok := message.Message.(string); !ok {
+ // 如果不是字符串,处理消息以转换为字符串,强制转换
+ message.Message = ParseMessageContent(message.Message)
+ }
+
+ var promptstr string
+ // 读取URL参数 "prompt"
+ promptstr = r.URL.Query().Get("prompt")
+ if promptstr != "" {
+ // 使用 prompt 变量进行后续处理
+ fmtf.Printf("收到prompt参数: %s\n", promptstr)
+ }
+
+ var lockPrompt bool
+ // 读取URL参数 "lock_prompt"
+ lockPromptValue := r.URL.Query().Get("lock_prompt")
+ if lockPromptValue != "" {
+ // 转换lockPromptValue从字符串到bool
+ lockPrompt, err = strconv.ParseBool(lockPromptValue)
+ if err != nil {
+ // 如果转换失败,可能是因为参数不存在或参数不是有效的布尔字符串 ("true", "false")
+ fmtf.Printf("错误:无法解析lock_prompt参数: %s\n", err)
+ } else {
+ // 使用 lockPrompt 变量进行后续处理
+ if lockPrompt {
+ fmtf.Println("lock_prompt已激活")
+ }
+ }
+ }
+
+ var nomemory bool
+ // 读取URL参数 "lock_prompt"
+ nomemoryValue := r.URL.Query().Get("no_memory")
+ if nomemoryValue != "" {
+ // 转换lockPromptValue从字符串到bool
+ nomemory, err = strconv.ParseBool(nomemoryValue)
+ if err != nil {
+ // 如果转换失败,可能是因为参数不存在或参数不是有效的布尔字符串 ("true", "false")
+ fmtf.Printf("错误:无法解析no_memory参数: %s\n", err)
+ } else {
+ // 使用 lockPrompt 变量进行后续处理
+ if nomemory {
+ fmtf.Println("no_memory已激活")
+ }
+ }
+ }
+
+ // 判断是否是群聊,然后检查触发词
+ if message.RealMessageType != "group_private" && message.MessageType != "private" {
+ // 去除含2个[[]]的内容
+ checkstr := utils.RemoveBracketsContent(message.RawMessage)
+ if !checkMessageForHints(checkstr, message.SelfID, promptstr) {
+ // 获取概率值
+ chance := config.GetGroupHintChance(promptstr)
+
+ // 生成0-100之间的随机数
+ randomValue := rand.Intn(100)
+
+ // 比较随机值与配置中的概率
+ if randomValue >= chance {
+ w.WriteHeader(http.StatusOK)
+ w.Write([]byte("Group message not hint words."))
+ return
+ } else {
+ // 记录日志,表明概率检查通过
+ fmt.Printf("Probability check passed: %d%% chance, random value: %d\n", chance, randomValue)
+ }
+ } else {
+ fmt.Printf("checkMessageForHints check passed")
+ }
+ }
+
+ // 直接从ob11事件获取selfid
+ selfid := strconv.FormatInt(message.SelfID, 10)
+
+ // 读取URL参数 "api"
+ api := r.URL.Query().Get("api")
+ if api != "" {
+ // 使用 prompt 变量进行后续处理
+ fmtf.Printf("收到api参数: %s\n", api)
+ }
+
+ // 从URL查询参数中获取skip_lang_check
+ skipLangCheckStr := r.URL.Query().Get("skip_lang_check")
+
+ // 默认skipLangCheck为false
+ skipLangCheck := false
+
+ if skipLangCheckStr != "" {
+ // 尝试将获取的字符串转换为布尔值
+ var err error
+ skipLangCheck, err = strconv.ParseBool(skipLangCheckStr)
+ if err != nil {
+ // 如果转换出错,向客户端返回错误消息
+ fmt.Fprintf(w, "Invalid skip_lang_check value: %s", skipLangCheckStr)
+ return
+ }
+ fmt.Printf("收到 skip_lang_check 参数: %v\n", skipLangCheck)
+ }
+
+ // 判断message.Message的类型
+ switch msg := message.Message.(type) {
+ case string:
+ // message.Message是一个string
+ fmtf.Printf("userid:[%v]Received string message: %s\n", message.UserID, msg)
+
+ //是否过滤群信息
+ if !config.GetGroupmessage() {
+ fmtf.Printf("你设置了不响应群信息:%v", message)
+ return
+ }
+
+ // 从GetRestoreCommand获取重置指令的列表
+ restoreCommands := config.GetRestoreCommand()
+
+ checkResetCommand := msg
+ if config.GetIgnoreExtraTips() {
+ checkResetCommand = utils.RemoveBracketsContent(checkResetCommand)
+ }
+
+ // 检查checkResetCommand是否在restoreCommands列表中
+ isResetCommand := false
+ for _, command := range restoreCommands {
+ if checkResetCommand == command {
+ isResetCommand = true
+ break
+ }
+ }
+
+ if utils.BlacklistInterceptSP(message, selfid, promptstr) {
+ fmtf.Printf("userid:[%v]groupid:[%v]这位用户或群在黑名单中,被拦截", message.UserID, message.GroupID)
+ return
+ }
+
+ //处理重置指令
+ if isResetCommand {
+ fmtf.Println("处理重置操作")
+ if config.GetGroupContext() == 2 && message.MessageType != "private" {
+ app.migrateUserToNewContextSP(message.GroupID)
+ } else {
+ app.migrateUserToNewContextSP(message.UserID)
+ }
+ RestoreResponse := config.GetRandomRestoreResponses()
+ if message.RealMessageType == "group_private" || message.MessageType == "private" {
+ if !config.GetUsePrivateSSE() {
+ utils.SendPrivateMessageSP(message.UserID, RestoreResponse, selfid, promptstr)
+ } else {
+ utils.SendSSEPrivateRestoreMessageSP(message.UserID, RestoreResponse, promptstr, selfid)
+ }
+ } else {
+ utils.SendGroupMessageSP(message.GroupID, message.UserID, RestoreResponse, selfid, promptstr)
+ }
+ // 处理故事情节的重置
+ if config.GetGroupContext() == 2 && message.MessageType != "private" {
+ app.deleteCustomRecordSP(message.GroupID)
+ } else {
+ app.deleteCustomRecordSP(message.UserID)
+ }
+ return
+ }
+
+ withdrawCommand := config.GetWithdrawCommand()
+
+ // 检查checkResetCommand是否在WithdrawCommand列表中
+ iswithdrawCommand := false
+ for _, command := range withdrawCommand {
+ if checkResetCommand == command {
+ iswithdrawCommand = true
+ break
+ }
+ }
+
+ // 处理撤回信息
+ if iswithdrawCommand {
+ handleWithdrawMessageSP(message)
+ return
+ }
+
+ // newmsg 是一个用于缓存和安全判断的临时量
+ newmsg := message.Message.(string)
+ // 去除注入的提示词
+ if config.GetIgnoreExtraTips() {
+ newmsg = utils.RemoveBracketsContent(newmsg)
+ }
+
+ var (
+ vector []float64
+ lastSelectedVectorID int // 用于存储最后选取的相似文本的ID
+ )
+
+ // 进行字数拦截
+ if config.GetQuestionMaxLenth() != 0 {
+ if utils.LengthInterceptSP(newmsg, message, selfid, promptstr) {
+ fmtf.Printf("字数过长,可在questionMaxLenth配置项修改,Q: %v", newmsg)
+ // 发送响应
+ w.WriteHeader(http.StatusOK)
+ w.Write([]byte("question too long"))
+ return
+ }
+ }
+
+ // 进行语言判断拦截 skipLangCheck为false时
+ if len(config.GetAllowedLanguages()) > 0 && !skipLangCheck {
+ if utils.LanguageInterceptSP(newmsg, message, selfid, promptstr) {
+ fmtf.Printf("不安全!不支持的语言,可在config.yml设置允许的语言,allowedLanguages配置项,Q: %v", newmsg)
+ // 发送响应
+ w.WriteHeader(http.StatusOK)
+ w.Write([]byte("language not support"))
+ return
+ }
+ }
+
+ // 如果使用向量缓存 或者使用 向量安全词
+ if config.GetUseCache(promptstr) == 2 || config.GetVectorSensitiveFilter() {
+ if config.GetPrintHanming() {
+ fmtf.Printf("计算向量的文本: %v", newmsg)
+ }
+ // 计算文本向量
+ vector, err = app.CalculateTextEmbedding(newmsg)
+ if err != nil {
+ fmtf.Printf("Error calculating text embedding: %v", err)
+ // 发送响应
+ w.WriteHeader(http.StatusOK)
+ w.Write([]byte("Error calculating text embedding"))
+ return
+ }
+ }
+
+ // 缓存省钱部分
+ if config.GetUseCache(promptstr) == 2 {
+ //fmtf.Printf("计算向量: %v", vector)
+ cacheThreshold := config.GetCacheThreshold()
+ // 搜索相似文本和对应的ID
+ similarTexts, ids, err := app.searchForSingleVector(vector, cacheThreshold)
+ if err != nil {
+ fmtf.Printf("Error searching for similar texts: %v", err)
+ // 发送响应
+ w.WriteHeader(http.StatusOK)
+ w.Write([]byte("Error searching for similar texts"))
+ return
+ }
+
+ if len(similarTexts) > 0 {
+ // 总是获取最相似的文本的ID,不管是否最终使用
+ lastSelectedVectorID = ids[0]
+
+ chance := rand.Intn(100)
+ // 检查是否满足设定的概率
+ if chance < config.GetCacheChance() {
+ // 使用最相似的文本的答案
+ fmtf.Printf("读取表:%v\n", similarTexts[0])
+ responseText, err := app.GetRandomAnswer(similarTexts[0])
+ if err == nil {
+ fmtf.Printf("缓存命中,Q:%v,A:%v\n", newmsg, responseText)
+ //加入上下文
+ if app.AddSingleContextSP(message, responseText) {
+ fmtf.Printf("缓存加入上下文成功")
+ }
+ // 发送响应消息
+ if message.RealMessageType == "group_private" || message.MessageType == "private" {
+ if !config.GetUsePrivateSSE() {
+ utils.SendPrivateMessageSP(message.UserID, responseText, selfid, promptstr)
+ } else {
+ utils.SendSSEPrivateMessageSP(message.UserID, responseText, promptstr, selfid)
+ }
+ } else {
+ utils.SendGroupMessageSP(message.GroupID, message.UserID, responseText, selfid, promptstr)
+ }
+ // 发送响应
+ w.WriteHeader(http.StatusOK)
+ w.Write([]byte("Request received and use cache"))
+ return // 成功使用缓存答案,提前退出
+ } else {
+ fmtf.Printf("Error getting random answer: %v", err)
+
+ }
+ } else {
+ fmtf.Printf("缓存命中,但没有符合概率,继续执行后续代码\n")
+ // 注意:这里不需要再生成 lastSelectedVectorID,因为上面已经生成
+ }
+ } else {
+ // 没有找到相似文本,存储新的文本及其向量
+ newVectorID, err := app.insertVectorData(newmsg, vector)
+ if err != nil {
+ fmtf.Printf("Error inserting new vector data: %v", err)
+ // 发送响应
+ w.WriteHeader(http.StatusOK)
+ w.Write([]byte("Error inserting new vector data"))
+ return
+ }
+ lastSelectedVectorID = int(newVectorID) // 存储新插入向量的ID
+ fmtf.Printf("没找到缓存,准备储存了lastSelectedVectorID: %v\n", lastSelectedVectorID)
+ }
+
+ // 这里继续执行您的逻辑,比如生成新的答案等
+ // 注意:根据实际情况调整后续逻辑
+ }
+
+ //提示词安全部分
+ if config.GetAntiPromptAttackPath() != "" {
+ if checkResponseThreshold(newmsg) {
+ fmtf.Printf("提示词不安全,过滤:%v", message)
+ saveresponse := config.GetRandomSaveResponse()
+ if saveresponse != "" {
+ if message.RealMessageType == "group_private" || message.MessageType == "private" {
+ if !config.GetUsePrivateSSE() {
+ utils.SendPrivateMessageSP(message.UserID, saveresponse, selfid, promptstr)
+ } else {
+ utils.SendSSEPrivateSafeMessageSP(message.UserID, saveresponse, promptstr, selfid)
+ }
+ } else {
+ utils.SendGroupMessageSP(message.GroupID, message.UserID, saveresponse, selfid, promptstr)
+ }
+ }
+ // 发送响应
+ w.WriteHeader(http.StatusOK)
+ w.Write([]byte("Request received and not safe"))
+ return
+ }
+ }
+
+ var conversationID, parentMessageID string
+ // 请求conversation api 增加当前群/用户上下文
+ if config.GetGroupContext() == 2 && message.MessageType != "private" {
+ conversationID, parentMessageID, err = app.handleUserContextSP(message.GroupID)
+ } else {
+ conversationID, parentMessageID, err = app.handleUserContextSP(message.UserID)
+ }
+
+ // 使用map映射conversationID和uid gid的关系
+ StoreUserInfoSP(conversationID, message.UserID, message.GroupID, message.RealMessageType, message.MessageType)
+
+ // 保存记忆
+ memoryCommand := config.GetMemoryCommand()
+
+ // 检查checkResetCommand是否在memoryCommand列表中
+ ismemoryCommand := false
+ for _, command := range memoryCommand {
+ if checkResetCommand == command {
+ ismemoryCommand = true
+ break
+ }
+ }
+
+ // 处理保存记忆
+ if ismemoryCommand {
+ app.handleSaveMemorySP(message, conversationID, parentMessageID, promptstr) // 适配群
+ return
+ }
+
+ // 记忆列表
+ memoryLoadCommand := config.GetMemoryLoadCommand()
+
+ // 检查checkResetCommand是否在memoryLoadCommand列表中或以其为前缀
+ ismemoryLoadCommand := false
+ isPrefixedMemoryLoadCommand := false // 新增变量用于检测前缀匹配
+ for _, command := range memoryLoadCommand {
+ if checkResetCommand == command {
+ ismemoryLoadCommand = true
+ break
+ }
+ if strings.HasPrefix(checkResetCommand, command) { // 检查前缀
+ isPrefixedMemoryLoadCommand = true
+ }
+ }
+
+ // 处理记忆列表
+ if ismemoryLoadCommand {
+ app.handleMemoryListSP(message, promptstr) // 适配群
+ return
+ }
+
+ // 新增处理载入记忆的逻辑
+ if isPrefixedMemoryLoadCommand {
+ app.handleLoadMemorySP(message, checkResetCommand, promptstr) // 适配群
+ return
+ }
+
+ // 新对话
+ newConversationCommand := config.GetNewConversationCommand()
+
+ // 检查checkResetCommand是否在newConversationCommand列表中
+ isnewConversationCommand := false
+ for _, command := range newConversationCommand {
+ if checkResetCommand == command {
+ isnewConversationCommand = true
+ break
+ }
+ }
+
+ // 处理新对话
+ if isnewConversationCommand {
+ app.handleNewConversationSP(message, conversationID, parentMessageID, promptstr) // 适配群
+ return
+ }
+
+ //每句话清空上一句话的messageBuilder
+ ClearMessage(conversationID)
+ fmtf.Printf("conversationID: %s,parentMessageID%s\n", conversationID, parentMessageID)
+ if err != nil {
+ fmtf.Printf("Error handling user context: %v\n", err)
+ // 发送响应
+ w.WriteHeader(http.StatusOK)
+ w.Write([]byte("Error handling user context"))
+ return
+ }
+
+ // 请求模型使用原文请求,并应用安全策略
+ requestmsg := message.Message.(string)
+
+ if config.GetPrintHanming() {
+ fmtf.Printf("消息进入替换前:%v", requestmsg)
+ }
+
+ // 繁体转换简体 安全策略
+ requestmsg, err = utils.ConvertTraditionalToSimplified(requestmsg)
+ if err != nil {
+ fmtf.Printf("繁体转换简体失败:%v", err)
+ }
+
+ // 替换in替换词规则
+ if config.GetSensitiveMode() {
+ requestmsg = acnode.CheckWordIN(requestmsg)
+ }
+
+ // 按提示词区分的细化替换 这里主要不是为了安全和敏感词,而是细化效果,也就没有使用acnode提高效率
+ requestmsg = utils.ReplaceTextIn(requestmsg, promptstr)
+
+ if config.GetGroupContext() == 2 && message.MessageType != "private" {
+ fmtf.Printf("实际请求conversation端点内容:[%v]%v\n", message.GroupID, requestmsg)
+ } else {
+ fmtf.Printf("实际请求conversation端点内容:[%v]%v\n", message.UserID, requestmsg)
+ }
+
+ // 本次指令不受到记忆的影响,例外.用于玩法类,不需要上下文的场景.
+ if nomemory {
+ parentMessageID = ""
+ }
+
+ requestBody, err := json.Marshal(map[string]interface{}{
+ "message": requestmsg,
+ "conversationId": conversationID,
+ "parentMessageId": parentMessageID,
+ "user_id": message.UserID,
+ })
+
+ if err != nil {
+ fmtf.Printf("Error marshalling request: %v\n", err)
+ return
+ }
+
+ // 构建URL并发送请求到conversation接口
+ port := config.GetPort()
+ portStr := fmt.Sprintf(":%d", port)
+
+ // 初始化URL,根据api参数动态调整路径
+ basePath := "/conversation"
+
+ //MARK:能定义每个yml自己要调用的conversation端点
+ newPath := config.GetConversationPath(promptstr)
+ // 允许覆盖请求不同的conversation
+ if newPath != "/conversation" && newPath != "" {
+ fmtf.Printf("覆盖api参数: %s\n", newPath)
+ basePath = newPath // 动态替换conversation部分为ConversationPath,这个配置是包含了/的
+ }
+
+ if api != "" {
+ fmtf.Printf("收到api参数: %s\n", api)
+ basePath = "/" + api // 动态替换conversation部分为api参数值
+ }
+
+ var baseURL string
+
+ if config.GetLotus(promptstr) == "" {
+ baseURL = "http://127.0.0.1" + portStr + basePath
+ } else {
+ baseURL = config.GetLotus(promptstr) + basePath
+ }
+
+ // 在加入prompt之前 判断promptstr.yml是否存在
+ if !prompt.CheckPromptExistence(promptstr) {
+ fmtf.Printf("该请求内容所对应yml文件不存在:[%v]:[%v]\n", requestmsg, promptstr)
+ promptstr = ""
+ }
+
+ // 使用net/url包来构建和编码URL
+ urlParams := url.Values{}
+ if promptstr != "" {
+ urlParams.Add("prompt", promptstr)
+ }
+
+ // 元器和glm会根据userid参数来自动封禁用户
+ if config.GetApiType() == 5 || basePath == "/conversation_glm" || config.GetApiType() == 6 || basePath == "/conversation_yq" {
+ urlParams.Add("userid", message.UserID)
+ }
+
+ // 将查询参数编码后附加到基本URL上
+ fullURL := baseURL
+ if len(urlParams) > 0 {
+ fullURL += "?" + urlParams.Encode()
+ }
+
+ fmtf.Printf("Generated URL:%v\n", fullURL)
+
+ resp, err := http.Post(fullURL, "application/json", bytes.NewBuffer(requestBody))
+ if err != nil {
+ fmtf.Printf("Error sending request to conversation interface: %v\n", err)
+ return
+ }
+
+ defer resp.Body.Close()
+
+ var lastMessageID string
+ var response string
+
+ if config.GetuseSse(promptstr) == 2 {
+ // 处理SSE流式响应
+ reader := bufio.NewReader(resp.Body)
+ for {
+ line, err := reader.ReadBytes('\n')
+ if err != nil {
+ if err == io.EOF {
+ break // 流结束
+ }
+ fmtf.Printf("Error reading SSE response: %v\n", err)
+ return
+ }
+
+ // 忽略空行
+ if string(line) == "\n" {
+ continue
+ }
+
+ // 处理接收到的数据
+ if !config.GetHideExtraLogs() {
+ fmtf.Printf("Received SSE data: %s", string(line))
+ }
+
+ // 去除"data: "前缀后进行JSON解析
+ jsonData := strings.TrimPrefix(string(line), "data: ")
+ var responseData map[string]interface{}
+ if err := json.Unmarshal([]byte(jsonData), &responseData); err == nil {
+ //接收到最后一条信息
+ if id, ok := responseData["messageId"].(string); ok {
+
+ conversationid := responseData["conversationId"].(string)
+ // 从conversation对应的sync map取出对应的用户和群号,避免高并发内容发送错乱
+ userinfo, _ := GetUserInfoSP(conversationid)
+
+ lastMessageID = id // 更新lastMessageID
+ // 检查是否有未发送的消息部分
+ key := utils.GetKeySP(userinfo.GroupID, userinfo.UserID)
+ accumulatedMessageInterface, exists := groupUserMessages.Load(key)
+ var accumulatedMessage string
+ if exists {
+ accumulatedMessage = accumulatedMessageInterface.(string)
+ }
+
+ // 提取response字段
+ if response, ok = responseData["response"].(string); ok {
+
+ // 如果accumulatedMessage是response的子串,则提取新的部分并发送
+ if exists && strings.HasPrefix(response, accumulatedMessage) {
+ newPart := response[len(accumulatedMessage):]
+ if newPart != "" {
+ fmtf.Printf("A完整信息: %s,已发送信息:%s 新部分:%s\n", response, accumulatedMessage, newPart)
+ // 判断消息类型,如果是私人消息或私有群消息,发送私人消息;否则,根据配置决定是否发送群消息
+ if userinfo.RealMessageType == "group_private" || userinfo.MessageType == "private" {
+ if !config.GetUsePrivateSSE() {
+ utils.SendPrivateMessageSP(userinfo.UserID, newPart, selfid, promptstr)
+ } else {
+ //判断是否最后一条
+ state := 11 //继续
+ messageSSE := structs.InterfaceBody{
+ Content: newPart,
+ State: state,
+ }
+ utils.SendPrivateMessageSSESP(userinfo.UserID, messageSSE, promptstr, selfid)
+ }
+ } else {
+ // 这里发送的是newPart api最后补充的部分
+ if !config.GetMdPromptKeyboardAtGroup() {
+
+ utils.SendGroupMessageSP(userinfo.GroupID, userinfo.UserID, newPart, selfid, promptstr)
+
+ } else {
+ go utils.SendGroupMessageMdPromptKeyboardSP(userinfo.GroupID, userinfo.UserID, newPart, selfid, newmsg, response, promptstr)
+
+ }
+ }
+ } else {
+ // 流的最后一次是完整结束的
+ fmtf.Printf("A完整信息: %s(sse完整结束)\n", response)
+ }
+
+ } else if response != "" {
+ // 如果accumulatedMessage不存在或不是子串,print
+ fmtf.Printf("B完整信息: %s,已发送信息:%s", response, accumulatedMessage)
+ if accumulatedMessage == "" {
+ // 判断消息类型,如果是私人消息或私有群消息,发送私人消息;否则,根据配置决定是否发送群消息
+ if userinfo.RealMessageType == "group_private" || userinfo.MessageType == "private" {
+ if !config.GetUsePrivateSSE() {
+ utils.SendPrivateMessageSP(userinfo.UserID, response, selfid, promptstr)
+ } else {
+ //判断是否最后一条
+ state := 11 //下一个是11 由末尾补充负责
+ messageSSE := structs.InterfaceBody{
+ Content: response,
+ State: state,
+ }
+ utils.SendPrivateMessageSSESP(userinfo.UserID, messageSSE, promptstr, selfid)
+ }
+ } else {
+ if !config.GetMdPromptKeyboardAtGroup() {
+ utils.SendGroupMessageSP(userinfo.GroupID, userinfo.UserID, response, selfid, promptstr)
+
+ } else {
+
+ go utils.SendGroupMessageMdPromptKeyboardSP(userinfo.GroupID, userinfo.UserID, response, selfid, newmsg, response, promptstr)
+
+ }
+
+ }
+ }
+ }
+
+ // 缓存省钱部分 这里默认不被覆盖,如果主配置开了缓存,始终缓存.
+ if config.GetUseCache() == 2 {
+ if response != "" {
+ fmtf.Printf("缓存了Q:%v,A:%v,向量ID:%v", newmsg, response, lastSelectedVectorID)
+ app.InsertQAEntry(newmsg, response, lastSelectedVectorID)
+ } else {
+ fmtf.Printf("缓存Q:%v时遇到问题,A为空,检查api是否存在问题", newmsg)
+ }
+ }
+
+ // 清空key的值
+ groupUserMessages.Store(key, "")
+ }
+ } else {
+ //发送信息
+ if !config.GetHideExtraLogs() {
+ fmtf.Printf("收到流数据,切割并发送信息: %s", string(line))
+ }
+ splitAndSendMessagesSP(string(line), newmsg, selfid, promptstr)
+ }
+ }
+ }
+
+ // 在SSE流结束后更新用户上下文 在这里调用gensokyo流式接口的最后一步 插推荐气泡
+ if lastMessageID != "" {
+ fmtf.Printf("lastMessageID: %s\n", lastMessageID)
+ if config.GetGroupContext() == 2 && message.MessageType != "private" {
+ err := app.updateUserContextSP(message.GroupID, lastMessageID)
+ if err != nil {
+ fmtf.Printf("Error updating user context: %v\n", err)
+ }
+ } else {
+ err := app.updateUserContextSP(message.UserID, lastMessageID)
+ if err != nil {
+ fmtf.Printf("Error updating user context: %v\n", err)
+ }
+ }
+
+ if message.RealMessageType == "group_private" || message.MessageType == "private" {
+ if config.GetUsePrivateSSE() {
+
+ // 发气泡和按钮
+ var promptkeyboard []string
+ if !config.GetUseAIPromptkeyboard() {
+ promptkeyboard = config.GetPromptkeyboard()
+ } else {
+ fmtf.Printf("ai生成气泡:%v", "Q"+newmsg+"A"+response)
+ promptkeyboard = promptkb.GetPromptKeyboardAI("Q"+newmsg+"A"+response, promptstr)
+ }
+
+ // 使用acnode.CheckWordOUT()过滤promptkeyboard中的每个字符串
+ for i, item := range promptkeyboard {
+ promptkeyboard[i] = acnode.CheckWordOUT(item)
+ }
+
+ // 添加第四个气泡
+ if config.GetNo4Promptkeyboard() {
+ // 合并所有命令到一个数组
+ var allCommands []string
+
+ // 获取并添加RestoreResponses
+ RestoreResponses := config.GetRestoreCommand()
+ allCommands = append(allCommands, RestoreResponses...)
+
+ // 获取并添加memoryLoadCommand
+ memoryLoadCommand := config.GetMemoryLoadCommand()
+ allCommands = append(allCommands, memoryLoadCommand...)
+
+ // 获取并添加memoryCommand
+ memoryCommand := config.GetMemoryCommand()
+ allCommands = append(allCommands, memoryCommand...)
+
+ // 获取并添加newConversationCommand
+ newConversationCommand := config.GetNewConversationCommand()
+ allCommands = append(allCommands, newConversationCommand...)
+
+ // 检查合并后的命令数组长度
+ if len(allCommands) > 0 {
+ // 随机选择一个命令
+ selectedCommand := allCommands[rand.Intn(len(allCommands))]
+
+ // 在promptkeyboard的末尾添加选中的命令
+ if len(promptkeyboard) > 0 {
+ promptkeyboard = append(promptkeyboard, selectedCommand)
+ } else {
+ // 如果promptkeyboard为空,我们也应当初始化它,并添加选中的命令
+ promptkeyboard = []string{selectedCommand}
+ }
+ }
+ }
+
+ //最后一条了
+ messageSSE := structs.InterfaceBody{
+ Content: " ",
+ State: 20,
+ PromptKeyboard: promptkeyboard,
+ }
+ utils.SendPrivateMessageSSESP(message.UserID, messageSSE, promptstr, selfid)
+ ResetIndex(newmsg)
+ }
+ }
+ }
+ } else {
+ // 处理常规响应
+ responseBody, err := io.ReadAll(resp.Body)
+ if err != nil {
+ fmtf.Printf("Error reading response body: %v\n", err)
+ return
+ }
+ fmtf.Printf("Response from conversation interface: %s\n", string(responseBody))
+
+ // 使用map解析响应数据以获取response字段和messageId
+ var responseData map[string]interface{}
+ if err := json.Unmarshal(responseBody, &responseData); err != nil {
+ fmtf.Printf("Error unmarshalling response data: %v\n", err)
+ return
+ }
+ var ok bool
+ // 使用提取的response内容发送消息
+ if response, ok = responseData["response"].(string); ok && response != "" {
+ // 判断消息类型,如果是私人消息或私有群消息,发送私人消息;否则,根据配置决定是否发送群消息
+ if message.RealMessageType == "group_private" || message.MessageType == "private" {
+ utils.SendPrivateMessageSP(message.UserID, response, selfid, promptstr)
+ } else {
+ utils.SendGroupMessageSP(message.GroupID, message.UserID, response, selfid, promptstr)
+ }
+ }
+
+ // 更新用户上下文
+ if messageId, ok := responseData["messageId"].(string); ok {
+ if config.GetGroupContext() == 2 && message.MessageType != "private" {
+ err := app.updateUserContextSP(message.GroupID, messageId)
+ if err != nil {
+ fmtf.Printf("Error updating user context: %v\n", err)
+ }
+ } else {
+ err := app.updateUserContextSP(message.UserID, messageId)
+ if err != nil {
+ fmtf.Printf("Error updating user context: %v\n", err)
+ }
+ }
+
+ }
+ }
+
+ // OUT规则不仅对实际发送api生效,也对http结果生效
+ if config.GetSensitiveModeType() == 1 {
+ response = acnode.CheckWordOUT(response)
+ }
+
+ // 发送响应
+ w.WriteHeader(http.StatusOK)
+ w.Write([]byte("Request received and processed Q:" + newmsg + " A:" + response))
+
+ if response == "" {
+ return
+ }
+
+ case map[string]interface{}:
+ // message.Message是一个map[string]interface{}
+ // 理论上不应该执行到这里,因为我们已确保它是字符串
+ fmtf.Println("Received map message, handling not implemented yet")
+ // 处理map类型消息的逻辑(TODO)
+
+ default:
+ // message.Message是一个未知类型
+ // 理论上不应该执行到这里,因为我们已确保它是字符串
+ fmtf.Printf("Received message of unexpected type: %T\n", msg)
+ return
+ }
+
+}
+
+// StoreUserInfoSP 用于存储用户信息到全局 map
+func StoreUserInfoSP(conversationID string, userID string, groupID string, realMessageType string, messageType string) {
+ userInfo := UserInfoSP{
+ UserID: userID,
+ GroupID: groupID,
+ RealMessageType: realMessageType,
+ MessageType: messageType,
+ }
+ globalMapSP.Store(conversationID, userInfo)
+}
+
+// GetUserInfoSP 根据conversationID获取用户信息
+func GetUserInfoSP(conversationID string) (UserInfoSP, bool) {
+ value, ok := globalMapSP.Load(conversationID)
+ if ok {
+ return value.(UserInfoSP), true
+ }
+ return UserInfoSP{}, false
+}
+
+// 处理撤回信息的函数
+func handleWithdrawMessageSP(message structs.OnebotGroupMessageS) {
+ fmtf.Println("处理撤回操作")
+ var id string
+
+ // 根据消息类型决定使用哪个ID
+ switch message.RealMessageType {
+ case "group_private", "guild_private":
+ id = message.UserID
+ case "group", "guild":
+ id = message.GroupID
+ case "interaction":
+ id = message.GroupID
+ default:
+ fmt.Println("Unsupported message type for withdrawal:", message.RealMessageType)
+ return
+ }
+
+ selfidstr := strconv.FormatInt(message.SelfID, 10)
+ // 调用DeleteLatestMessage函数
+ err := utils.DeleteLatestMessageSP(message.RealMessageType, id, message.UserID, selfidstr)
+ if err != nil {
+ fmt.Println("Error deleting latest message:", err)
+ return
+ }
+}
+
+func splitAndSendMessagesSP(line string, newmesssage string, selfid string, promptstr string) {
+ // 提取JSON部分
+ dataPrefix := "data: "
+ jsonStr := strings.TrimPrefix(line, dataPrefix)
+
+ // 解析JSON数据
+ var sseData struct {
+ Response string `json:"response"`
+ ConversationId string `json:"conversationId"`
+ }
+ err := json.Unmarshal([]byte(jsonStr), &sseData)
+ if err != nil {
+ fmtf.Printf("Error unmarshalling SSE data: %v\n", err)
+ return
+ }
+
+ if sseData.Response != "\n\n" {
+ // 处理提取出的信息
+ processMessageSP(sseData.Response, sseData.ConversationId, newmesssage, selfid, promptstr)
+ } else {
+ fmtf.Printf("忽略llm末尾的换行符")
+ }
+}
+
+func processMessageSP(response string, conversationid string, newmesssage string, selfid string, promptstr string) {
+ // 从conversation对应的sync map取出对应的用户和群号,避免高并发内容发送错乱
+ userinfo, _ := GetUserInfoSP(conversationid)
+ key := utils.GetKeySP(userinfo.GroupID, userinfo.UserID)
+
+ // 定义中文全角和英文标点符号
+ punctuations := []rune{'。', '!', '?', ',', ',', '.', '!', '?', '~'}
+
+ for _, char := range response {
+ AppendRune(conversationid, char)
+ if utils.ContainsRuneSP(punctuations, char, userinfo.GroupID, userinfo.UserID, promptstr) {
+ // 达到标点符号,发送累积的整个消息
+ if GetMessageLength(conversationid) > 0 {
+ accumulatedMessage, _ := GetCurrentMessage(conversationid)
+ // 锁定
+ processMessageMu.Lock()
+ // 从sync.map读取当前的value
+ valueInterface, _ := groupUserMessages.Load(key)
+ value, _ := valueInterface.(string)
+ // 添加当前messageBuilder中的新内容
+ value += accumulatedMessage
+ // 储存新的内容到sync.map
+ groupUserMessages.Store(key, value)
+ processMessageMu.Unlock() // 完成更新后时解锁
+
+ // 判断消息类型,如果是私人消息或私有群消息,发送私人消息;否则,根据配置决定是否发送群消息
+ if userinfo.RealMessageType == "group_private" || userinfo.MessageType == "private" {
+ if !config.GetUsePrivateSSE() {
+ utils.SendPrivateMessageSP(userinfo.UserID, accumulatedMessage, selfid, promptstr)
+ } else {
+ if IncrementIndex(newmesssage) == 1 {
+ //第一条信息
+ //取出当前信息作为按钮回调
+ //CallbackData := GetStringById(lastMessageID)
+ uerid := userinfo.UserID
+ messageSSE := structs.InterfaceBody{
+ Content: accumulatedMessage,
+ State: 1,
+ ActionButton: 10,
+ CallbackData: uerid,
+ }
+ utils.SendPrivateMessageSSESP(userinfo.UserID, messageSSE, promptstr, selfid)
+ } else {
+ //SSE的前半部分
+ messageSSE := structs.InterfaceBody{
+ Content: accumulatedMessage,
+ State: 1,
+ }
+ utils.SendPrivateMessageSSESP(userinfo.UserID, messageSSE, promptstr, selfid)
+ }
+ }
+ } else {
+ utils.SendGroupMessageSP(userinfo.GroupID, userinfo.UserID, accumulatedMessage, selfid, promptstr)
+ }
+
+ ClearMessage(conversationid)
+ }
+ }
+ }
+}
diff --git a/applogic/memory.go b/applogic/memory.go
index 29d696e..f162f59 100644
--- a/applogic/memory.go
+++ b/applogic/memory.go
@@ -129,6 +129,53 @@ func (app *App) handleSaveMemory(msg structs.OnebotGroupMessage, ConversationID
app.sendMemoryResponseWithkeyBoard(msg, saveMemoryResponse, keyboard, promptstr)
}
+// 保存记忆
+func (app *App) handleSaveMemorySP(msg structs.OnebotGroupMessageS, ConversationID string, ParentMessageID string, promptstr string) {
+ conversationTitle := "2024-5-19/18:26" // 默认标题,根据实际需求可能需要调整为动态生成的时间戳
+
+ userid := msg.UserID
+ if config.GetGroupContext() == 2 && msg.MessageType != "private" {
+ userid = msg.GroupID
+ }
+
+ // 添加用户记忆
+ err := app.AddUserMemorySP(userid, ConversationID, ParentMessageID, conversationTitle)
+ if err != nil {
+ log.Printf("Error saving memory: %s", err)
+ return
+ }
+
+ // 启动Go routine进行历史信息处理和标题更新
+ go func() {
+ userHistory, err := app.getHistory(ConversationID, ParentMessageID)
+ if err != nil {
+ log.Printf("Error retrieving history: %s", err)
+ return
+ }
+
+ // 处理历史信息为特定格式的字符串
+ memoryTitle := formatHistory(userHistory)
+ newTitle := GetMemoryTitle(memoryTitle) // 获取最终的记忆标题
+
+ // 更新记忆标题
+ err = app.updateConversationTitleSP(userid, ConversationID, ParentMessageID, newTitle)
+ if err != nil {
+ log.Printf("Error updating conversation title: %s", err)
+ }
+ }()
+
+ var keyboard []string // 准备一个空的键盘数组
+ // 获取记忆载入命令
+ memoryLoadCommands := config.GetMemoryLoadCommand()
+ if len(memoryLoadCommands) > 0 {
+ keyboard = append(keyboard, memoryLoadCommands[0]) // 添加第一个命令到键盘数组
+ }
+
+ // 发送保存成功的响应
+ saveMemoryResponse := "记忆保存成功!"
+ app.sendMemoryResponseWithkeyBoardSP(msg, saveMemoryResponse, keyboard, promptstr)
+}
+
// 获取记忆列表
func (app *App) handleMemoryList(msg structs.OnebotGroupMessage, promptstr string) {
@@ -188,6 +235,65 @@ func (app *App) handleMemoryList(msg structs.OnebotGroupMessage, promptstr strin
app.sendMemoryResponseByline(msg, responseBuilder.String(), keyboard, promptstr)
}
+// 获取记忆列表
+func (app *App) handleMemoryListSP(msg structs.OnebotGroupMessageS, promptstr string) {
+
+ userid := msg.UserID
+ if config.GetGroupContext() == 2 && msg.MessageType != "private" {
+ userid = msg.GroupID
+ }
+
+ memories, err := app.GetUserMemoriesSP(userid)
+ if err != nil {
+ log.Printf("Error retrieving memories: %s", err)
+ return
+ }
+
+ // 组合格式化的文本
+ var responseBuilder strings.Builder
+ responseBuilder.WriteString("当前记忆列表:\n")
+
+ // 准备键盘数组,最多包含4个标题
+ var keyboard []string
+ var loadMemoryCommand string
+ // 获取载入记忆指令
+ memoryLoadCommands := config.GetMemoryLoadCommand()
+ if len(memoryLoadCommands) > 0 {
+ loadMemoryCommand = memoryLoadCommands[0]
+ } else {
+ loadMemoryCommand = "未设置载入指令"
+ }
+
+ for _, memory := range memories {
+ // 使用acnode.CheckWordOUT()过滤
+ memory.ConversationTitle = acnode.CheckWordOUT(memory.ConversationTitle)
+
+ if config.GetMemoryListMD() == 0 {
+ responseBuilder.WriteString(memory.ConversationTitle + "\n")
+ }
+ keyboard = append(keyboard, loadMemoryCommand+" "+memory.ConversationTitle) // 添加新的标题
+ }
+
+ var exampleTitle string
+ if len(memories) > 0 {
+ exampleTitle = string([]rune(memories[0].ConversationTitle)[:3])
+ }
+
+ if config.GetMemoryListMD() == 0 {
+ responseBuilder.WriteString(fmt.Sprintf("提示:发送 %s 任意标题开头的前n字即可载入记忆\n如:%s %s", loadMemoryCommand, loadMemoryCommand, exampleTitle))
+ } else {
+ if len(keyboard) == 0 {
+ responseBuilder.WriteString("目前还没有对话记忆...点按记忆按钮来保存新的记忆吧")
+ } else {
+ responseBuilder.WriteString("点击蓝色文字载入记忆")
+ }
+
+ }
+
+ // 发送组合后的信息,包括键盘数组
+ app.sendMemoryResponseBylineSP(msg, responseBuilder.String(), keyboard, promptstr)
+}
+
// 载入记忆
func (app *App) handleLoadMemory(msg structs.OnebotGroupMessage, checkResetCommand string, promptstr string) {
@@ -245,6 +351,63 @@ func (app *App) handleLoadMemory(msg structs.OnebotGroupMessage, checkResetComma
app.sendMemoryResponse(msg, responseMessage, promptstr)
}
+// 载入记忆
+func (app *App) handleLoadMemorySP(msg structs.OnebotGroupMessageS, checkResetCommand string, promptstr string) {
+
+ userid := msg.UserID
+ if config.GetGroupContext() == 2 && msg.MessageType != "private" {
+ userid = msg.GroupID
+ }
+
+ // 从配置获取载入记忆指令
+ memoryLoadCommands := config.GetMemoryLoadCommand()
+
+ // 移除所有载入记忆指令部分
+ for _, command := range memoryLoadCommands {
+ checkResetCommand = strings.Replace(checkResetCommand, command, "", -1)
+ }
+
+ // 移除空格得到匹配词
+ matchTerm := strings.TrimSpace(checkResetCommand)
+
+ // 判断处理过的字符串是否以"+"开头 移除+号
+ matchTerm = strings.TrimPrefix(matchTerm, "+")
+
+ // 获取用户记忆
+ memories, err := app.GetUserMemoriesSP(userid)
+ if err != nil {
+ log.Printf("Error retrieving memories: %s", err)
+ app.sendMemoryResponseSP(msg, "获取记忆失败", promptstr)
+ return
+ }
+
+ // 查找匹配的记忆
+ var matchedMemory *structs.Memory
+ for _, memory := range memories {
+ if strings.HasPrefix(memory.ConversationTitle, matchTerm) {
+ matchedMemory = &memory
+ break
+ }
+ }
+
+ if matchedMemory == nil {
+ app.sendMemoryResponseSP(msg, "未找到匹配的记忆", promptstr)
+ return
+ }
+
+ // 载入记忆
+ err = app.updateUserContextProSP(userid, matchedMemory.ConversationID, matchedMemory.ParentMessageID)
+ if err != nil {
+ log.Printf("Error adding memory: %s", err)
+ app.sendMemoryResponseSP(msg, "载入记忆失败", promptstr)
+ return
+ }
+
+ // 组合回复信息
+ responseMessage := fmt.Sprintf("成功载入了标题为 '%s' 的记忆", matchedMemory.ConversationTitle)
+ app.sendMemoryResponseSP(msg, responseMessage, promptstr)
+}
+
func (app *App) sendMemoryResponseWithkeyBoard(msg structs.OnebotGroupMessage, response string, keyboard []string, promptstr string) {
strSelfID := strconv.FormatInt(msg.SelfID, 10)
if msg.RealMessageType == "group_private" || msg.MessageType == "private" {
@@ -258,6 +421,19 @@ func (app *App) sendMemoryResponseWithkeyBoard(msg structs.OnebotGroupMessage, r
}
}
+func (app *App) sendMemoryResponseWithkeyBoardSP(msg structs.OnebotGroupMessageS, response string, keyboard []string, promptstr string) {
+ strSelfID := strconv.FormatInt(msg.SelfID, 10)
+ if msg.RealMessageType == "group_private" || msg.MessageType == "private" {
+ if !config.GetUsePrivateSSE() {
+ utils.SendPrivateMessageSP(msg.UserID, response, strSelfID, promptstr)
+ } else {
+ utils.SendSSEPrivateMessageWithKeyboardSP(msg.UserID, response, keyboard, promptstr, strSelfID)
+ }
+ } else {
+ utils.SendGroupMessageSP(msg.GroupID, msg.UserID, response, strSelfID, promptstr)
+ }
+}
+
func (app *App) sendMemoryResponse(msg structs.OnebotGroupMessage, response string, promptstr string) {
strSelfID := strconv.FormatInt(msg.SelfID, 10)
if msg.RealMessageType == "group_private" || msg.MessageType == "private" {
@@ -271,6 +447,19 @@ func (app *App) sendMemoryResponse(msg structs.OnebotGroupMessage, response stri
}
}
+func (app *App) sendMemoryResponseSP(msg structs.OnebotGroupMessageS, response string, promptstr string) {
+ strSelfID := strconv.FormatInt(msg.SelfID, 10)
+ if msg.RealMessageType == "group_private" || msg.MessageType == "private" {
+ if !config.GetUsePrivateSSE() {
+ utils.SendPrivateMessageSP(msg.UserID, response, strSelfID, promptstr)
+ } else {
+ utils.SendSSEPrivateMessageSP(msg.UserID, response, promptstr, strSelfID)
+ }
+ } else {
+ utils.SendGroupMessageSP(msg.GroupID, msg.UserID, response, strSelfID, promptstr)
+ }
+}
+
func (app *App) sendMemoryResponseByline(msg structs.OnebotGroupMessage, response string, keyboard []string, promptstr string) {
strSelfID := strconv.FormatInt(msg.SelfID, 10)
if msg.RealMessageType == "group_private" || msg.MessageType == "private" {
@@ -296,6 +485,31 @@ func (app *App) sendMemoryResponseByline(msg structs.OnebotGroupMessage, respons
}
}
+func (app *App) sendMemoryResponseBylineSP(msg structs.OnebotGroupMessageS, response string, keyboard []string, promptstr string) {
+ strSelfID := strconv.FormatInt(msg.SelfID, 10)
+ if msg.RealMessageType == "group_private" || msg.MessageType == "private" {
+ if !config.GetUsePrivateSSE() {
+ utils.SendPrivateMessageSP(msg.UserID, response, strSelfID, promptstr)
+ } else {
+ // 更新键盘数组,确保最多只有三个元素
+ if len(keyboard) >= 3 {
+ keyboard = keyboard[:3]
+ }
+ utils.SendSSEPrivateMessageByLineSP(msg.UserID, response, keyboard, promptstr, strSelfID)
+ }
+ } else {
+ if config.GetMemoryListMD() == 0 {
+ utils.SendGroupMessageSP(msg.GroupID, msg.UserID, response, strSelfID, promptstr)
+ } else {
+ // 更新键盘数组,确保最多只有五个元素
+ if len(keyboard) >= 5 {
+ keyboard = keyboard[:5]
+ }
+ utils.SendGroupMessageMdPromptKeyboardV2SP(msg.GroupID, msg.UserID, response, strSelfID, promptstr, keyboard)
+ }
+ }
+}
+
func formatHistory(history []structs.Message) string {
var result string
for _, message := range history {
@@ -354,3 +568,50 @@ func (app *App) handleNewConversation(msg structs.OnebotGroupMessage, conversati
app.sendMemoryResponse(msg, saveMemoryResponse, promotstr)
}
}
+
+func (app *App) handleNewConversationSP(msg structs.OnebotGroupMessageS, conversationID string, parentMessageID string, promotstr string) {
+ // 使用预定义的时间戳作为会话标题
+ conversationTitle := "2024-5-19/18:26" // 实际应用中应使用动态生成的时间戳
+ userid := msg.UserID
+
+ if config.GetGroupContext() == 2 && msg.MessageType != "private" {
+ userid = msg.GroupID
+ }
+
+ // 添加用户记忆
+ err := app.AddUserMemorySP(userid, conversationID, parentMessageID, conversationTitle)
+ if err != nil {
+ log.Printf("Error saving memory: %s", err)
+ return
+ }
+
+ // 启动Go routine进行历史信息处理和标题更新
+ go func() {
+ userHistory, err := app.getHistory(conversationID, parentMessageID)
+ if err != nil {
+ log.Printf("Error retrieving history: %s", err)
+ return
+ }
+
+ // 处理历史信息为特定格式的字符串
+ memoryTitle := formatHistory(userHistory)
+ newTitle := GetMemoryTitle(memoryTitle) // 获取最终的记忆标题
+
+ // 更新记忆标题
+ err = app.updateConversationTitleSP(userid, conversationID, parentMessageID, newTitle)
+ if err != nil {
+ log.Printf("Error updating conversation title: %s", err)
+ }
+ }()
+
+ // 迁移用户到新的上下文
+ app.migrateUserToNewContextSP(userid)
+
+ // 获取并使用配置中指定的加载记忆指令
+ loadCommand := config.GetMemoryLoadCommand()
+ if len(loadCommand) > 0 {
+ loadMemoryCommand := loadCommand[0] // 使用数组中的第一个指令
+ saveMemoryResponse := fmt.Sprintf("旧的对话已经保存,可发送 %s 来查看,可以开始新的对话了!", loadMemoryCommand)
+ app.sendMemoryResponseSP(msg, saveMemoryResponse, promotstr)
+ }
+}
diff --git a/applogic/promptstr.go b/applogic/promptstr.go
index 70cad43..0343253 100644
--- a/applogic/promptstr.go
+++ b/applogic/promptstr.go
@@ -106,6 +106,17 @@ func (app *App) deleteCustomRecord(userID int64) error {
return nil
}
+func (app *App) deleteCustomRecordSP(userID string) error {
+ deleteSQL := `DELETE FROM custom_table WHERE user_id = ?;`
+
+ _, err := app.DB.Exec(deleteSQL, userID)
+ if err != nil {
+ return fmt.Errorf("error deleting record from custom_table: %w", err)
+ }
+
+ return nil
+}
+
// Helper function to get index from field name
func fieldIndex(field string) int {
if strings.HasPrefix(field, "str") && len(field) > 3 {
diff --git a/applogic/singlecontext.go b/applogic/singlecontext.go
index 7064aed..0bc3bb4 100644
--- a/applogic/singlecontext.go
+++ b/applogic/singlecontext.go
@@ -48,3 +48,45 @@ func (app *App) AddSingleContext(message structs.OnebotGroupMessage, responseTex
return true
}
+
+// 直接根据缓存来储存上下文
+// 其实向量缓存是一个单轮的QA缓存,因为这个项目很初步,很显然无法应对上下文场景的缓存
+// 通过这种方式,将每次缓存的内容也加入上下文,可能会有一个初步的效果提升.
+func (app *App) AddSingleContextSP(message structs.OnebotGroupMessageS, responseText string) bool {
+ // 请求conversation api 增加当前用户上下文
+ conversationID, parentMessageID, err := app.handleUserContextSP(message.UserID)
+ if err != nil {
+ fmtf.Printf("error in AddSingleContext app.handleUserContex :%v", err)
+ return false
+ }
+
+ // 构造用户消息并添加到上下文
+ userMessage := structs.Message{
+ ConversationID: conversationID,
+ ParentMessageID: parentMessageID,
+ Text: message.Message.(string),
+ Role: "user",
+ CreatedAt: time.Now().Format(time.RFC3339),
+ }
+ userMessageID, err := app.addMessage(userMessage)
+ if err != nil {
+ fmtf.Printf("error in AddSingleContext app.addMessage(userMessage) :%v", err)
+ return false
+ }
+
+ // 构造助理消息并添加到上下文
+ assistantMessage := structs.Message{
+ ConversationID: conversationID,
+ ParentMessageID: userMessageID,
+ Text: responseText,
+ Role: "assistant",
+ CreatedAt: time.Now().Format(time.RFC3339),
+ }
+ _, err = app.addMessage(assistantMessage)
+ if err != nil {
+ fmtf.Printf("error in AddSingleContext app.addMessage(assistantMessage) :%v", err)
+ return false
+ }
+
+ return true
+}
diff --git a/config/config.go b/config/config.go
index 6fcc4d9..fbe7fb6 100644
--- a/config/config.go
+++ b/config/config.go
@@ -3518,3 +3518,13 @@ func getMdPromptKeyboardAtGroupCmdsInternal(options ...string) []string {
return envContents
}
+
+// 获取Stringob11
+func GetStringob11() bool {
+ mu.Lock()
+ defer mu.Unlock()
+ if instance != nil {
+ return instance.Settings.Stringob11
+ }
+ return false
+}
diff --git a/main.go b/main.go
index 53aff04..0154cfd 100644
--- a/main.go
+++ b/main.go
@@ -95,10 +95,19 @@ func main() {
if err != nil {
log.Fatalf("Failed to ensure database tables exist: %v", err)
}
- // 确保user_context表存在
- err = app.EnsureUserContextTableExists()
- if err != nil {
- log.Fatalf("Failed to ensure user_context table exists: %v", err)
+
+ if !config.GetStringob11() {
+ // 确保user_context表存在
+ err = app.EnsureUserContextTableExists()
+ if err != nil {
+ log.Fatalf("Failed to ensure user_context table exists: %v", err)
+ }
+ } else {
+ // 确保user_context表存在
+ err = app.EnsureUserContextTableExistsSP()
+ if err != nil {
+ log.Fatalf("Failed to ensure user_context table exists: %v", err)
+ }
}
// 确保向量表存在
@@ -119,10 +128,18 @@ func main() {
log.Fatalf("Failed to ensure SensitiveWordsTable table exists: %v", err)
}
- // 故事模式存档
- err = app.EnsureCustomTableExist()
- if err != nil {
- log.Fatalf("Failed to ensure CustomTableExist table exists: %v", err)
+ if !config.GetStringob11() {
+ // 故事模式存档
+ err = app.EnsureCustomTableExist()
+ if err != nil {
+ log.Fatalf("Failed to ensure CustomTableExist table exists: %v", err)
+ }
+ } else {
+ // 故事模式存档
+ err = app.EnsureCustomTableExistSP()
+ if err != nil {
+ log.Fatalf("Failed to ensure CustomTableExist table exists: %v", err)
+ }
}
// 用户多个记忆表
@@ -253,7 +270,12 @@ func main() {
}
// 设置路由
- http.HandleFunc("/gensokyo", app.GensokyoHandler)
+ if !config.GetStringob11() {
+ http.HandleFunc("/gensokyo", app.GensokyoHandler)
+ } else {
+ http.HandleFunc("/gensokyo", app.GensokyoHandlerSP)
+ }
+
var wspath string
if conf.Settings.WSPath == "nil" {
wspath = "/"
diff --git a/structs/struct.go b/structs/struct.go
index 56ef671..301b2a0 100644
--- a/structs/struct.go
+++ b/structs/struct.go
@@ -52,6 +52,29 @@ type OnebotGroupMessage struct {
IsBindedUserId bool `json:"is_binded_user_id,omitempty"` //当前用户号号是否是binded后的
}
+type OnebotGroupMessageS struct {
+ RawMessage string `json:"raw_message"`
+ MessageID string `json:"message_id"`
+ GroupID string `json:"group_id"` // Can be either string or int depending on p.Settings.CompleteFields
+ MessageType string `json:"message_type"`
+ PostType string `json:"post_type"`
+ SelfID int64 `json:"self_id"` // Can be either string or int
+ Sender Sender `json:"sender"`
+ SubType string `json:"sub_type"`
+ Time int64 `json:"time"`
+ Avatar string `json:"avatar,omitempty"`
+ Echo string `json:"echo,omitempty"`
+ Message interface{} `json:"message"` // For array format
+ MessageSeq int `json:"message_seq"`
+ Font int `json:"font"`
+ UserID string `json:"user_id"`
+ RealMessageType string `json:"real_message_type,omitempty"` //当前信息的真实类型 group group_private guild guild_private
+ RealUserID string `json:"real_user_id,omitempty"` //当前真实uid
+ RealGroupID string `json:"real_group_id,omitempty"` //当前真实gid
+ IsBindedGroupId bool `json:"is_binded_group_id,omitempty"` //当前群号是否是binded后的
+ IsBindedUserId bool `json:"is_binded_user_id,omitempty"` //当前用户号号是否是binded后的
+}
+
type Sender struct {
Nickname string `json:"nickname"`
TinyID string `json:"tiny_id"`
@@ -291,6 +314,8 @@ type Settings struct {
SpecialNameToQ []ReplacementNamePair `yaml:"specialNameToQ"`
NoEmoji int `yaml:"noEmoji"` // 0 false 1 false 2 true
+ Stringob11 bool `yaml:"stringob11"`
+
HunyuanType int `yaml:"hunyuanType"`
MaxTokensHunyuan int `yaml:"maxTokensHunyuan"`
HunyuanStreamModeration bool `yaml:"hunyuanStreamModeration"`
diff --git a/template/config_template.go b/template/config_template.go
index f2976cc..bc65e88 100644
--- a/template/config_template.go
+++ b/template/config_template.go
@@ -15,6 +15,7 @@ settings:
lotus : "" #当填写另一个gensokyo-llm的http地址时,将请求另一个的conversation端点,实现多个llm不需要多次配置,简化配置,单独使用请忽略留空.例:http://192.168.0.1:12345(包含http头和端口)
pathToken : "" #gensokyo正向http-api的access_token(是onebotv11标准的)
apiType : 0 #0=混元 1=文心(文心平台包含了N种模型...) 2=gpt 3=rwkv 4=通义千问 5=智谱AI 6=腾讯元器
+ stringob11 : false #兼容string模式ob11
oneApi : false #内置了一个简化版的oneApi
oneApiPort : 50052 #内置简化版oneApi所监听的地址 :50052/v1
diff --git a/utils/blacklist.go b/utils/blacklist.go
index bd06a09..df39dbb 100644
--- a/utils/blacklist.go
+++ b/utils/blacklist.go
@@ -136,3 +136,48 @@ func BlacklistIntercept(message structs.OnebotGroupMessage, selfid string, promp
return false // 用户ID不在黑名单中,不拦截
}
+
+// BlacklistIntercept 检查用户ID是否在黑名单中,如果在,则发送预设消息
+func BlacklistInterceptSP(message structs.OnebotGroupMessageS, selfid string, promptstr string) bool {
+ // 检查群ID是否在黑名单中
+ if IsInBlacklist(message.GroupID) {
+ // 获取黑名单响应消息
+ responseMessage := config.GetBlacklistResponseMessages()
+
+ // 根据消息类型发送响应
+ if message.RealMessageType == "group_private" || message.MessageType == "private" {
+ if !config.GetUsePrivateSSE() {
+ SendPrivateMessageSP(message.UserID, responseMessage, selfid, promptstr)
+ } else {
+ SendSSEPrivateMessageSP(message.UserID, responseMessage, promptstr, selfid)
+ }
+ } else {
+ SendGroupMessageSP(message.GroupID, message.UserID, responseMessage, selfid, promptstr)
+ }
+
+ fmt.Printf("groupid:[%v]这个群在黑名单中,被拦截\n", message.GroupID)
+ return true // 拦截
+ }
+
+ // 检查用户ID是否在黑名单中
+ if IsInBlacklist(message.UserID) {
+ // 获取黑名单响应消息
+ responseMessage := config.GetBlacklistResponseMessages()
+
+ // 根据消息类型发送响应
+ if message.RealMessageType == "group_private" || message.MessageType == "private" {
+ if !config.GetUsePrivateSSE() {
+ SendPrivateMessageSP(message.UserID, responseMessage, selfid, promptstr)
+ } else {
+ SendSSEPrivateMessageSP(message.UserID, responseMessage, promptstr, selfid)
+ }
+ } else {
+ SendGroupMessageSP(message.GroupID, message.UserID, responseMessage, selfid, promptstr)
+ }
+
+ fmt.Printf("userid:[%v]这位用户在黑名单中,被拦截\n", message.UserID)
+ return true // 拦截
+ }
+
+ return false // 用户ID不在黑名单中,不拦截
+}
diff --git a/utils/utils.go b/utils/utils.go
index 17582f7..0dd96a2 100644
--- a/utils/utils.go
+++ b/utils/utils.go
@@ -35,16 +35,33 @@ type ResponseData struct {
} `json:"data"`
}
+// ResponseData 是用于解析HTTP响应的结构体
+type ResponseDataSP struct {
+ Data struct {
+ MessageID string `json:"message_id"`
+ } `json:"data"`
+}
+
// MessageIDInfo 代表消息ID及其到期时间
type MessageIDInfo struct {
MessageID int64 // 消息ID
Expires time.Time // 到期时间
}
+// MessageIDInfo 代表消息ID及其到期时间
+type MessageIDInfoSP struct {
+ MessageID string // 消息ID
+ Expires time.Time // 到期时间
+}
+
// UserIDMessageIDs 存储每个用户ID对应的消息ID数组及其有效期
var UserIDMessageIDs = make(map[int64][]MessageIDInfo)
var muUserIDMessageIDs sync.RWMutex // 用于UserIDMessageIDs的读写锁
+// UserIDMessageIDs 存储每个用户ID对应的消息ID数组及其有效期
+var UserIDMessageIDsSP = make(map[string][]MessageIDInfoSP)
+var muUserIDMessageIDsSP sync.RWMutex // 用于UserIDMessageIDs的读写锁
+
var (
baseURLMap = make(map[string]string)
baseURLMapMu sync.Mutex
@@ -148,6 +165,11 @@ func GetKey(groupid int64, userid int64) string {
return fmt.Sprintf("%d.%d", groupid, userid)
}
+// 获取复合键
+func GetKeySP(groupid string, userid string) string {
+ return fmt.Sprintf("%s.%s", groupid, userid)
+}
+
// 随机的分布发送
func ContainsRune(slice []rune, value rune, groupid int64, userid int64, promptstr string) bool {
var probability int
@@ -172,6 +194,30 @@ func ContainsRune(slice []rune, value rune, groupid int64, userid int64, prompts
return false
}
+// 随机的分布发送
+func ContainsRuneSP(slice []rune, value rune, groupid string, userid string, promptstr string) bool {
+ var probability int
+ if groupid == userid {
+ // 获取私聊百分比
+ probability = config.GetSplitByPuntuations(promptstr)
+ } else {
+ // 获取群聊百分比
+ probability = config.GetSplitByPuntuationsGroup(promptstr)
+ }
+
+ for _, item := range slice {
+ if item == value {
+ // 将概率转换为0到1之间的浮点数
+ probabilityPercentage := float64(probability) / 100.0
+ // 生成一个0到1之间的随机浮点数
+ randomValue := rand.Float64()
+ // 如果随机数小于或等于概率,则返回true
+ return randomValue <= probabilityPercentage
+ }
+ }
+ return false
+}
+
// 取出ai回答
func ExtractEventDetails(eventData map[string]interface{}) (string, structs.UsageInfo) {
var responseTextBuilder strings.Builder
@@ -318,6 +364,116 @@ func SendGroupMessage(groupID int64, userID int64, message string, selfid string
return nil
}
+func SendGroupMessageSP(groupID string, userID string, message string, selfid string, promptstr string) error {
+ //TODO: 用userid作为了echo,在ws收到回调信息的时候,加入到全局撤回数组,AddMessageID,实现撤回
+ if server.IsSelfIDExists(selfid) {
+ // 创建消息结构体
+ msg := map[string]interface{}{
+ "action": "send_group_msg",
+ "params": map[string]interface{}{
+ "group_id": groupID,
+ "user_id": userID,
+ "message": message,
+ },
+ "echo": userID,
+ }
+
+ // 发送消息
+ return server.SendMessageBySelfID(selfid, msg)
+ }
+ var baseURL string
+ if len(config.GetHttpPaths()) > 0 {
+ baseURL, _ = GetBaseURLByUserID(selfid)
+ } else {
+ // 获取基础URL
+ baseURL = config.GetHttpPath() // 假设config.getHttpPath()返回基础URL
+ }
+
+ // 构建完整的URL
+ baseURL = baseURL + "/send_group_msg"
+
+ // 获取PathToken并检查其是否为空
+ pathToken := config.GetPathToken()
+ // 使用net/url包构建URL
+ u, err := url.Parse(baseURL)
+ if err != nil {
+ panic("URL parsing failed: " + err.Error())
+ }
+
+ // 添加access_token参数
+ query := u.Query()
+ if pathToken != "" {
+ query.Set("access_token", pathToken)
+ }
+ u.RawQuery = query.Encode()
+
+ if config.GetSensitiveModeType() == 1 {
+ message = acnode.CheckWordOUT(message)
+ }
+
+ // 是否不显示Emoji
+ if config.GetNoEmoji(promptstr) == 2 {
+ message = RemoveEmojis(message)
+ }
+
+ //精细化替换 每个yml配置文件都可以具有一个非全局的文本替换规则
+ message = ReplaceTextOut(message, promptstr)
+
+ // 去除末尾的换行符 不去除会导致不好看
+ message = removeTrailingCRLFs(message)
+
+ // 繁体转换简体 安全策略 防止用户诱导ai发繁体绕过替换规则
+ message, err = ConvertTraditionalToSimplified(message)
+ if err != nil {
+ fmtf.Printf("繁体转换简体失败:%v", err)
+ }
+
+ // 构造请求体
+ requestBody, err := json.Marshal(map[string]interface{}{
+ "group_id": groupID,
+ "user_id": userID,
+ "message": message,
+ })
+ fmtf.Printf("发群信息请求:%v", string(requestBody))
+ fmtf.Printf("实际发送信息:%v", message)
+ if err != nil {
+ return fmtf.Errorf("failed to marshal request body: %w", err)
+ }
+
+ // 发送POST请求
+ resp, err := http.Post(u.String(), "application/json", bytes.NewBuffer(requestBody))
+ if err != nil {
+ return fmtf.Errorf("failed to send POST request: %w", err)
+ }
+ defer resp.Body.Close()
+
+ // 检查响应状态
+ if resp.StatusCode != http.StatusOK {
+ return fmtf.Errorf("received non-OK response status: %s", resp.Status)
+ }
+
+ // 读取响应体
+ bodyBytes, err := io.ReadAll(resp.Body)
+ if err != nil {
+ return fmt.Errorf("failed to read response body: %w", err)
+ }
+
+ // 解析响应体以获取message_id
+ var responseData ResponseDataSP
+ if err := json.Unmarshal(bodyBytes, &responseData); err != nil {
+ return fmt.Errorf("failed to unmarshal response data: %w", err)
+ }
+ messageID := responseData.Data.MessageID
+
+ // 添加messageID到全局变量
+ AddMessageIDSP(userID, messageID)
+
+ // 输出响应体,这一步是可选的
+ fmt.Println("Response Body:", string(bodyBytes))
+
+ return nil
+}
+
func SendGroupMessageMdPromptKeyboard(groupID int64, userID int64, message string, selfid string, newmsg string, response string, promptstr string) error {
//TODO: 用userid作为了echo,在ws收到回调信息的时候,加入到全局撤回数组,AddMessageID,实现反向ws连接时候的撤回
if server.IsSelfIDExists(selfid) {
@@ -578,7 +734,7 @@ func SendGroupMessageMdPromptKeyboard(groupID int64, userID int64, message strin
return nil
}
-func SendGroupMessageMdPromptKeyboardV2(groupID int64, userID int64, message string, selfid string, promptstr string, promptkeyboard []string) error {
+func SendGroupMessageMdPromptKeyboardSP(groupID string, userID string, message string, selfid string, newmsg string, response string, promptstr string) error {
//TODO: 用userid作为了echo,在ws收到回调信息的时候,加入到全局撤回数组,AddMessageID,实现反向ws连接时候的撤回
if server.IsSelfIDExists(selfid) {
// 创建消息结构体
@@ -641,6 +797,26 @@ func SendGroupMessageMdPromptKeyboardV2(groupID int64, userID int64, message str
if err != nil {
fmtf.Printf("繁体转换简体失败:%v", err)
}
+
+ // 首先获取当前的keyboard
+ var promptkeyboard []string
+ if !config.GetUseAIPromptkeyboard() {
+ promptkeyboard = config.GetPromptkeyboard()
+ } else {
+ mdCMDs := config.GetMdPromptKeyboardAtGroupCmds(promptstr)
+ if len(mdCMDs) > 0 {
+ promptkeyboard = mdCMDs
+ } else {
+ fmtf.Printf("ai生成气泡:%v", "Q"+newmsg+"A"+response)
+ promptkeyboard = promptkb.GetPromptKeyboardAI("Q"+newmsg+"A"+response, promptstr)
+ }
+ }
+
+ // 使用acnode.CheckWordOUT()过滤promptkeyboard中的每个字符串
+ for i, item := range promptkeyboard {
+ promptkeyboard[i] = acnode.CheckWordOUT(item)
+ }
+
var mdContent string
// 这里把message构造成一个cq,md码
if config.GetMemoryListMD() == 2 {
@@ -668,36 +844,68 @@ func SendGroupMessageMdPromptKeyboardV2(groupID int64, userID int64, message str
}
fmt.Println(mdContent)
- var promptKeyboardMd structs.PromptKeyboardMarkdown
- if config.GetMemoryListMD() == 1 {
- // 构建Buttons
- buttons := []structs.Button{}
- // 添加promptkeyboard的按钮,每个按钮一行
- for i, label := range promptkeyboard {
- buttons = append(buttons, structs.Button{
- ID: fmt.Sprintf("%d", i+1),
- RenderData: structs.RenderData{
- Label: label,
- VisitedLabel: "已载入",
- Style: 1,
+
+ // 构建Buttons
+ buttons := []structs.Button{}
+ // 添加promptkeyboard的按钮,每个按钮一行
+ for i, label := range promptkeyboard {
+ buttons = append(buttons, structs.Button{
+ ID: fmt.Sprintf("%d", i+1),
+ RenderData: structs.RenderData{
+ Label: label,
+ VisitedLabel: label,
+ Style: 1,
+ },
+ Action: structs.Action{
+ Type: 2,
+ Permission: structs.Permission{
+ Type: 2,
+ SpecifyRoleIDs: []string{"1", "2", "3"},
},
- Action: structs.Action{
- Type: 2,
- Permission: structs.Permission{
- Type: 2,
- SpecifyRoleIDs: []string{"1", "2", "3"},
- },
- Data: label,
- UnsupportTips: "请升级新版手机QQ",
- Enter: true,
- Reply: true,
+ Data: label,
+ UnsupportTips: "请升级新版手机QQ",
+ Enter: true,
+ Reply: true,
+ },
+ })
+ }
+
+ // 添加"重置", "撤回", "重发"按钮,它们在一个单独的行
+ rowWithThreeButtons := []structs.Button{}
+ labels := []string{"重置", "忽略", "记忆", "载入"}
+
+ for i, label := range labels {
+ actionType := 1
+ if label == "载入" {
+ actionType = 2 // 设置特定的 ActionType
+ }
+
+ button := structs.Button{
+ ID: fmt.Sprintf("%d", i+4), // 确保ID不重复
+ RenderData: structs.RenderData{
+ Label: label,
+ VisitedLabel: label,
+ Style: 1,
+ },
+ Action: structs.Action{
+ Type: actionType, // 使用条件变量设置的 actionType
+ Permission: structs.Permission{
+ Type: 2,
+ SpecifyRoleIDs: []string{"1", "2", "3"},
},
- })
+ Data: label,
+ UnsupportTips: "请升级新版手机QQ",
+ },
}
- // 构建完整的PromptKeyboardMarkdown对象
- var rows []structs.Row // 初始化一个空切片来存放行
+ rowWithThreeButtons = append(rowWithThreeButtons, button)
+ }
+
+ // 构建完整的PromptKeyboardMarkdown对象
+ var rows []structs.Row // 初始化一个空切片来存放行
+ // GetMemoryListMD==1 将buttons添加到rows
+ if config.GetMemoryListMD() == 1 {
// 遍历所有按钮,并每个按钮创建一行
for _, button := range buttons {
row := structs.Row{
@@ -705,15 +913,223 @@ func SendGroupMessageMdPromptKeyboardV2(groupID int64, userID int64, message str
}
rows = append(rows, row) // 将新行添加到行切片中
}
+ }
- // 构建 PromptKeyboardMarkdown 结构体
- promptKeyboardMd = structs.PromptKeyboardMarkdown{
- Markdown: structs.Markdown{
- Content: mdContent,
- },
- Keyboard: structs.Keyboard{
- Content: structs.KeyboardContent{
- Rows: rows, // 使用动态创建的行数组
+ // 添加特定的 rowWithThreeButtons 至 rows 数组的末尾
+ row := structs.Row{
+ Buttons: rowWithThreeButtons, // 将当前三个按钮放入
+ }
+ rows = append(rows, row)
+
+ // 构建 PromptKeyboardMarkdown 结构体
+ promptKeyboardMd := structs.PromptKeyboardMarkdown{
+ Markdown: structs.Markdown{
+ Content: mdContent,
+ },
+ Keyboard: structs.Keyboard{
+ Content: structs.KeyboardContent{
+ Rows: rows, // 使用动态创建的行数组
+ },
+ },
+ Content: "keyboard",
+ MsgID: "123",
+ Timestamp: fmt.Sprintf("%d", time.Now().Unix()),
+ MsgType: 2,
+ }
+
+ // 序列化成JSON
+ mdContentBytes, err := json.Marshal(promptKeyboardMd)
+ if err != nil {
+ fmt.Printf("Error marshaling to JSON: %v", err)
+ return nil
+ }
+
+ // 编码成Base64
+ encoded := base64.StdEncoding.EncodeToString(mdContentBytes)
+ segmentContent := "[CQ:markdown,data=base64://" + encoded + "]"
+
+ // 构造请求体
+ requestBody, err := json.Marshal(map[string]interface{}{
+ "group_id": groupID,
+ "user_id": userID,
+ "message": segmentContent,
+ })
+ fmtf.Printf("发群信息请求:%v", string(requestBody))
+ fmtf.Printf("实际发送信息:%v", message)
+ if err != nil {
+ return fmtf.Errorf("failed to marshal request body: %w", err)
+ }
+
+ // 发送POST请求
+ resp, err := http.Post(u.String(), "application/json", bytes.NewBuffer(requestBody))
+ if err != nil {
+ return fmtf.Errorf("failed to send POST request: %w", err)
+ }
+ defer resp.Body.Close()
+
+ // 检查响应状态
+ if resp.StatusCode != http.StatusOK {
+ return fmtf.Errorf("received non-OK response status: %s", resp.Status)
+ }
+
+ // 读取响应体
+ bodyBytes, err := io.ReadAll(resp.Body)
+ if err != nil {
+ return fmt.Errorf("failed to read response body: %w", err)
+ }
+
+ // 解析响应体以获取message_id
+ var responseData ResponseDataSP
+ if err := json.Unmarshal(bodyBytes, &responseData); err != nil {
+ return fmt.Errorf("failed to unmarshal response data: %w", err)
+ }
+ messageID := responseData.Data.MessageID
+
+ // 添加messageID到全局变量
+ AddMessageIDSP(userID, messageID)
+
+ // 输出响应体,这一步是可选的
+ fmt.Println("Response Body:", string(bodyBytes))
+
+ return nil
+}
+
+func SendGroupMessageMdPromptKeyboardV2(groupID int64, userID int64, message string, selfid string, promptstr string, promptkeyboard []string) error {
+ //TODO: 用userid作为了echo,在ws收到回调信息的时候,加入到全局撤回数组,AddMessageID,实现反向ws连接时候的撤回
+ if server.IsSelfIDExists(selfid) {
+ // 创建消息结构体
+ msg := map[string]interface{}{
+ "action": "send_group_msg",
+ "params": map[string]interface{}{
+ "group_id": groupID,
+ "user_id": userID,
+ "message": message,
+ },
+ "echo": userID,
+ }
+
+ // 发送消息
+ return server.SendMessageBySelfID(selfid, msg)
+ }
+ var baseURL string
+ if len(config.GetHttpPaths()) > 0 {
+ baseURL, _ = GetBaseURLByUserID(selfid)
+ } else {
+ // 获取基础URL
+ baseURL = config.GetHttpPath() // 假设config.getHttpPath()返回基础URL
+ }
+
+ // 构建完整的URL
+ baseURL = baseURL + "/send_group_msg"
+
+ // 获取PathToken并检查其是否为空
+ pathToken := config.GetPathToken()
+ // 使用net/url包构建URL
+ u, err := url.Parse(baseURL)
+ if err != nil {
+ panic("URL parsing failed: " + err.Error())
+ }
+
+ // 添加access_token参数
+ query := u.Query()
+ if pathToken != "" {
+ query.Set("access_token", pathToken)
+ }
+ u.RawQuery = query.Encode()
+
+ if config.GetSensitiveModeType() == 1 {
+ message = acnode.CheckWordOUT(message)
+ }
+
+ // 是否不显示Emoji
+ if config.GetNoEmoji(promptstr) == 2 {
+ message = RemoveEmojis(message)
+ }
+
+ //精细化替换 每个yml配置文件都可以具有一个非全局的文本替换规则
+ message = ReplaceTextOut(message, promptstr)
+
+ // 去除末尾的换行符 不去除会导致不好看
+ message = removeTrailingCRLFs(message)
+
+ // 繁体转换简体 安全策略 防止用户诱导ai发繁体绕过替换规则
+ message, err = ConvertTraditionalToSimplified(message)
+ if err != nil {
+ fmtf.Printf("繁体转换简体失败:%v", err)
+ }
+ var mdContent string
+ // 这里把message构造成一个cq,md码
+ if config.GetMemoryListMD() == 2 {
+ //构建Markdown内容,对promptkeyboard的内容进行URL编码
+ var sb strings.Builder
+ // 添加初始消息
+ sb.WriteString(message)
+ sb.WriteString("\r")
+ lastIndex := len(promptkeyboard) - 1 // 获取最后一个索引
+ // 遍历promptkeyboard数组,为每个元素生成一个标签
+ for i, cmd := range promptkeyboard {
+ // 对每个命令进行URL编码
+ encodedCmd := url.QueryEscape(cmd)
+ // 构建并添加qqbot-cmd-input标签
+ if i == lastIndex {
+ // 如果是最后一个元素,则不添加 \r
+ sb.WriteString(fmt.Sprintf("", encodedCmd, encodedCmd))
+ } else {
+ sb.WriteString(fmt.Sprintf("\r", encodedCmd, encodedCmd))
+ }
+ }
+ mdContent = sb.String()
+ } else {
+ mdContent = message
+ }
+
+ fmt.Println(mdContent)
+ var promptKeyboardMd structs.PromptKeyboardMarkdown
+ if config.GetMemoryListMD() == 1 {
+ // 构建Buttons
+ buttons := []structs.Button{}
+ // 添加promptkeyboard的按钮,每个按钮一行
+ for i, label := range promptkeyboard {
+ buttons = append(buttons, structs.Button{
+ ID: fmt.Sprintf("%d", i+1),
+ RenderData: structs.RenderData{
+ Label: label,
+ VisitedLabel: "已载入",
+ Style: 1,
+ },
+ Action: structs.Action{
+ Type: 2,
+ Permission: structs.Permission{
+ Type: 2,
+ SpecifyRoleIDs: []string{"1", "2", "3"},
+ },
+ Data: label,
+ UnsupportTips: "请升级新版手机QQ",
+ Enter: true,
+ Reply: true,
+ },
+ })
+ }
+
+ // 构建完整的PromptKeyboardMarkdown对象
+ var rows []structs.Row // 初始化一个空切片来存放行
+
+ // 遍历所有按钮,并每个按钮创建一行
+ for _, button := range buttons {
+ row := structs.Row{
+ Buttons: []structs.Button{button}, // 将当前按钮加入到新行中
+ }
+ rows = append(rows, row) // 将新行添加到行切片中
+ }
+
+ // 构建 PromptKeyboardMarkdown 结构体
+ promptKeyboardMd = structs.PromptKeyboardMarkdown{
+ Markdown: structs.Markdown{
+ Content: mdContent,
+ },
+ Keyboard: structs.Keyboard{
+ Content: structs.KeyboardContent{
+ Rows: rows, // 使用动态创建的行数组
},
},
Content: "keyboard",
@@ -791,7 +1207,434 @@ func SendGroupMessageMdPromptKeyboardV2(groupID int64, userID int64, message str
return nil
}
-func SendPrivateMessage(UserID int64, message string, selfid string, promptstr string) error {
+func SendGroupMessageMdPromptKeyboardV2SP(groupID string, userID string, message string, selfid string, promptstr string, promptkeyboard []string) error {
+ //TODO: 用userid作为了echo,在ws收到回调信息的时候,加入到全局撤回数组,AddMessageID,实现反向ws连接时候的撤回
+ if server.IsSelfIDExists(selfid) {
+ // 创建消息结构体
+ msg := map[string]interface{}{
+ "action": "send_group_msg",
+ "params": map[string]interface{}{
+ "group_id": groupID,
+ "user_id": userID,
+ "message": message,
+ },
+ "echo": userID,
+ }
+
+ // 发送消息
+ return server.SendMessageBySelfID(selfid, msg)
+ }
+ var baseURL string
+ if len(config.GetHttpPaths()) > 0 {
+ baseURL, _ = GetBaseURLByUserID(selfid)
+ } else {
+ // 获取基础URL
+ baseURL = config.GetHttpPath() // 假设config.getHttpPath()返回基础URL
+ }
+
+ // 构建完整的URL
+ baseURL = baseURL + "/send_group_msg"
+
+ // 获取PathToken并检查其是否为空
+ pathToken := config.GetPathToken()
+ // 使用net/url包构建URL
+ u, err := url.Parse(baseURL)
+ if err != nil {
+ panic("URL parsing failed: " + err.Error())
+ }
+
+ // 添加access_token参数
+ query := u.Query()
+ if pathToken != "" {
+ query.Set("access_token", pathToken)
+ }
+ u.RawQuery = query.Encode()
+
+ if config.GetSensitiveModeType() == 1 {
+ message = acnode.CheckWordOUT(message)
+ }
+
+ // 是否不显示Emoji
+ if config.GetNoEmoji(promptstr) == 2 {
+ message = RemoveEmojis(message)
+ }
+
+ //精细化替换 每个yml配置文件都可以具有一个非全局的文本替换规则
+ message = ReplaceTextOut(message, promptstr)
+
+ // 去除末尾的换行符 不去除会导致不好看
+ message = removeTrailingCRLFs(message)
+
+ // 繁体转换简体 安全策略 防止用户诱导ai发繁体绕过替换规则
+ message, err = ConvertTraditionalToSimplified(message)
+ if err != nil {
+ fmtf.Printf("繁体转换简体失败:%v", err)
+ }
+ var mdContent string
+ // 这里把message构造成一个cq,md码
+ if config.GetMemoryListMD() == 2 {
+ //构建Markdown内容,对promptkeyboard的内容进行URL编码
+ var sb strings.Builder
+ // 添加初始消息
+ sb.WriteString(message)
+ sb.WriteString("\r")
+ lastIndex := len(promptkeyboard) - 1 // 获取最后一个索引
+ // 遍历promptkeyboard数组,为每个元素生成一个标签
+ for i, cmd := range promptkeyboard {
+ // 对每个命令进行URL编码
+ encodedCmd := url.QueryEscape(cmd)
+ // 构建并添加qqbot-cmd-input标签
+ if i == lastIndex {
+ // 如果是最后一个元素,则不添加 \r
+ sb.WriteString(fmt.Sprintf("", encodedCmd, encodedCmd))
+ } else {
+ sb.WriteString(fmt.Sprintf("\r", encodedCmd, encodedCmd))
+ }
+ }
+ mdContent = sb.String()
+ } else {
+ mdContent = message
+ }
+
+ fmt.Println(mdContent)
+ var promptKeyboardMd structs.PromptKeyboardMarkdown
+ if config.GetMemoryListMD() == 1 {
+ // 构建Buttons
+ buttons := []structs.Button{}
+ // 添加promptkeyboard的按钮,每个按钮一行
+ for i, label := range promptkeyboard {
+ buttons = append(buttons, structs.Button{
+ ID: fmt.Sprintf("%d", i+1),
+ RenderData: structs.RenderData{
+ Label: label,
+ VisitedLabel: "已载入",
+ Style: 1,
+ },
+ Action: structs.Action{
+ Type: 2,
+ Permission: structs.Permission{
+ Type: 2,
+ SpecifyRoleIDs: []string{"1", "2", "3"},
+ },
+ Data: label,
+ UnsupportTips: "请升级新版手机QQ",
+ Enter: true,
+ Reply: true,
+ },
+ })
+ }
+
+ // 构建完整的PromptKeyboardMarkdown对象
+ var rows []structs.Row // 初始化一个空切片来存放行
+
+ // 遍历所有按钮,并每个按钮创建一行
+ for _, button := range buttons {
+ row := structs.Row{
+ Buttons: []structs.Button{button}, // 将当前按钮加入到新行中
+ }
+ rows = append(rows, row) // 将新行添加到行切片中
+ }
+
+ // 构建 PromptKeyboardMarkdown 结构体
+ promptKeyboardMd = structs.PromptKeyboardMarkdown{
+ Markdown: structs.Markdown{
+ Content: mdContent,
+ },
+ Keyboard: structs.Keyboard{
+ Content: structs.KeyboardContent{
+ Rows: rows, // 使用动态创建的行数组
+ },
+ },
+ Content: "keyboard",
+ MsgID: "123",
+ Timestamp: fmt.Sprintf("%d", time.Now().Unix()),
+ MsgType: 2,
+ }
+ } else {
+ // 构建 PromptKeyboardMarkdown 结构体
+ promptKeyboardMd = structs.PromptKeyboardMarkdown{
+ Markdown: structs.Markdown{
+ Content: mdContent,
+ },
+ Content: "keyboard",
+ MsgID: "123",
+ Timestamp: fmt.Sprintf("%d", time.Now().Unix()),
+ MsgType: 2,
+ }
+ }
+
+ // 序列化成JSON
+ mdContentBytes, err := json.Marshal(promptKeyboardMd)
+ if err != nil {
+ fmt.Printf("Error marshaling to JSON: %v", err)
+ return nil
+ }
+
+ // 编码成Base64
+ encoded := base64.StdEncoding.EncodeToString(mdContentBytes)
+ segmentContent := "[CQ:markdown,data=base64://" + encoded + "]"
+
+ // 构造请求体
+ requestBody, err := json.Marshal(map[string]interface{}{
+ "group_id": groupID,
+ "user_id": userID,
+ "message": segmentContent,
+ })
+ fmtf.Printf("发群信息请求:%v", string(requestBody))
+ fmtf.Printf("实际发送信息:%v", message)
+ if err != nil {
+ return fmtf.Errorf("failed to marshal request body: %w", err)
+ }
+
+ // 发送POST请求
+ resp, err := http.Post(u.String(), "application/json", bytes.NewBuffer(requestBody))
+ if err != nil {
+ return fmtf.Errorf("failed to send POST request: %w", err)
+ }
+ defer resp.Body.Close()
+
+ // 检查响应状态
+ if resp.StatusCode != http.StatusOK {
+ return fmtf.Errorf("received non-OK response status: %s", resp.Status)
+ }
+
+ // 读取响应体
+ bodyBytes, err := io.ReadAll(resp.Body)
+ if err != nil {
+ return fmt.Errorf("failed to read response body: %w", err)
+ }
+
+ // 解析响应体以获取message_id
+ var responseData ResponseDataSP
+ if err := json.Unmarshal(bodyBytes, &responseData); err != nil {
+ return fmt.Errorf("failed to unmarshal response data: %w", err)
+ }
+ messageID := responseData.Data.MessageID
+
+ // 添加messageID到全局变量
+ AddMessageIDSP(userID, messageID)
+
+ // 输出响应体,这一步是可选的
+ fmt.Println("Response Body:", string(bodyBytes))
+
+ return nil
+}
+
+func SendPrivateMessage(UserID int64, message string, selfid string, promptstr string) error {
+ if server.IsSelfIDExists(selfid) {
+ // 创建消息结构体
+ msg := map[string]interface{}{
+ "action": "send_private_msg",
+ "params": map[string]interface{}{
+ "user_id": UserID,
+ "message": message,
+ },
+ "echo": UserID,
+ }
+
+ // 发送消息
+ return server.SendMessageBySelfID(selfid, msg)
+ }
+ var baseURL string
+ if len(config.GetHttpPaths()) > 0 {
+ baseURL, _ = GetBaseURLByUserID(selfid)
+ } else {
+ // 获取基础URL
+ baseURL = config.GetHttpPath() // 假设config.getHttpPath()返回基础URL
+ }
+
+ // 构建完整的URL
+ baseURL = baseURL + "/send_private_msg"
+
+ // 获取PathToken并检查其是否为空
+ pathToken := config.GetPathToken()
+ // 使用net/url包构建URL
+ u, err := url.Parse(baseURL)
+ if err != nil {
+ panic("URL parsing failed: " + err.Error())
+ }
+
+ // 添加access_token参数
+ query := u.Query()
+ if pathToken != "" {
+ query.Set("access_token", pathToken)
+ }
+ u.RawQuery = query.Encode()
+
+ if config.GetSensitiveModeType() == 1 {
+ message = acnode.CheckWordOUT(message)
+ }
+
+ // 是否不显示Emoji
+ if config.GetNoEmoji(promptstr) == 2 {
+ message = RemoveEmojis(message)
+ }
+
+ //精细化替换 每个yml配置文件都可以具有一个非全局的文本替换规则
+ message = ReplaceTextOut(message, promptstr)
+
+ // 去除末尾的换行符 不去除会导致不好看
+ message = removeTrailingCRLFs(message)
+
+ // 繁体转换简体 安全策略 防止用户诱导ai发繁体绕过替换规则
+ message, err = ConvertTraditionalToSimplified(message)
+ if err != nil {
+ fmtf.Printf("繁体转换简体失败:%v", err)
+ }
+
+ // 构造请求体
+ requestBody, err := json.Marshal(map[string]interface{}{
+ "user_id": UserID,
+ "message": message,
+ })
+
+ if err != nil {
+ return fmtf.Errorf("failed to marshal request body: %w", err)
+ }
+ fmtf.Printf("实际发送信息:%v", message)
+
+ // 发送POST请求
+ resp, err := http.Post(u.String(), "application/json", bytes.NewBuffer(requestBody))
+ if err != nil {
+ return fmtf.Errorf("failed to send POST request: %w", err)
+ }
+ defer resp.Body.Close()
+
+ // 检查响应状态
+ if resp.StatusCode != http.StatusOK {
+ return fmtf.Errorf("received non-OK response status: %s", resp.Status)
+ }
+
+ // 读取响应体
+ bodyBytes, err := io.ReadAll(resp.Body)
+ if err != nil {
+ return fmt.Errorf("failed to read response body: %w", err)
+ }
+
+ // 解析响应体以获取message_id
+ var responseData ResponseData
+ if err := json.Unmarshal(bodyBytes, &responseData); err != nil {
+ return fmt.Errorf("failed to unmarshal response data: %w", err)
+ }
+ messageID := responseData.Data.MessageID
+
+ // 添加messageID到全局变量
+ AddMessageID(UserID, messageID)
+
+ // 输出响应体,这一步是可选的
+ fmt.Println("Response Body:", string(bodyBytes))
+
+ return nil
+}
+
+func SendPrivateMessageSP(UserID string, message string, selfid string, promptstr string) error {
+ if server.IsSelfIDExists(selfid) {
+ // 创建消息结构体
+ msg := map[string]interface{}{
+ "action": "send_private_msg",
+ "params": map[string]interface{}{
+ "user_id": UserID,
+ "message": message,
+ },
+ "echo": UserID,
+ }
+
+ // 发送消息
+ return server.SendMessageBySelfID(selfid, msg)
+ }
+ var baseURL string
+ if len(config.GetHttpPaths()) > 0 {
+ baseURL, _ = GetBaseURLByUserID(selfid)
+ } else {
+ // 获取基础URL
+ baseURL = config.GetHttpPath() // 假设config.getHttpPath()返回基础URL
+ }
+
+ // 构建完整的URL
+ baseURL = baseURL + "/send_private_msg"
+
+ // 获取PathToken并检查其是否为空
+ pathToken := config.GetPathToken()
+ // 使用net/url包构建URL
+ u, err := url.Parse(baseURL)
+ if err != nil {
+ panic("URL parsing failed: " + err.Error())
+ }
+
+ // 添加access_token参数
+ query := u.Query()
+ if pathToken != "" {
+ query.Set("access_token", pathToken)
+ }
+ u.RawQuery = query.Encode()
+
+ if config.GetSensitiveModeType() == 1 {
+ message = acnode.CheckWordOUT(message)
+ }
+
+ // 是否不显示Emoji
+ if config.GetNoEmoji(promptstr) == 2 {
+ message = RemoveEmojis(message)
+ }
+
+ //精细化替换 每个yml配置文件都可以具有一个非全局的文本替换规则
+ message = ReplaceTextOut(message, promptstr)
+
+ // 去除末尾的换行符 不去除会导致不好看
+ message = removeTrailingCRLFs(message)
+
+ // 繁体转换简体 安全策略 防止用户诱导ai发繁体绕过替换规则
+ message, err = ConvertTraditionalToSimplified(message)
+ if err != nil {
+ fmtf.Printf("繁体转换简体失败:%v", err)
+ }
+
+ // 构造请求体
+ requestBody, err := json.Marshal(map[string]interface{}{
+ "user_id": UserID,
+ "message": message,
+ })
+
+ if err != nil {
+ return fmtf.Errorf("failed to marshal request body: %w", err)
+ }
+ fmtf.Printf("实际发送信息:%v", message)
+
+ // 发送POST请求
+ resp, err := http.Post(u.String(), "application/json", bytes.NewBuffer(requestBody))
+ if err != nil {
+ return fmtf.Errorf("failed to send POST request: %w", err)
+ }
+ defer resp.Body.Close()
+
+ // 检查响应状态
+ if resp.StatusCode != http.StatusOK {
+ return fmtf.Errorf("received non-OK response status: %s", resp.Status)
+ }
+
+ // 读取响应体
+ bodyBytes, err := io.ReadAll(resp.Body)
+ if err != nil {
+ return fmt.Errorf("failed to read response body: %w", err)
+ }
+
+ // 解析响应体以获取message_id
+ var responseData ResponseDataSP
+ if err := json.Unmarshal(bodyBytes, &responseData); err != nil {
+ return fmt.Errorf("failed to unmarshal response data: %w", err)
+ }
+ messageID := responseData.Data.MessageID
+
+ // 添加messageID到全局变量
+ AddMessageIDSP(UserID, messageID)
+
+ // 输出响应体,这一步是可选的
+ fmt.Println("Response Body:", string(bodyBytes))
+
+ return nil
+}
+
+func SendPrivateMessageRaw(UserID int64, message string, selfid string) error {
if server.IsSelfIDExists(selfid) {
// 创建消息结构体
msg := map[string]interface{}{
@@ -832,27 +1675,6 @@ func SendPrivateMessage(UserID int64, message string, selfid string, promptstr s
}
u.RawQuery = query.Encode()
- if config.GetSensitiveModeType() == 1 {
- message = acnode.CheckWordOUT(message)
- }
-
- // 是否不显示Emoji
- if config.GetNoEmoji(promptstr) == 2 {
- message = RemoveEmojis(message)
- }
-
- //精细化替换 每个yml配置文件都可以具有一个非全局的文本替换规则
- message = ReplaceTextOut(message, promptstr)
-
- // 去除末尾的换行符 不去除会导致不好看
- message = removeTrailingCRLFs(message)
-
- // 繁体转换简体 安全策略 防止用户诱导ai发繁体绕过替换规则
- message, err = ConvertTraditionalToSimplified(message)
- if err != nil {
- fmtf.Printf("繁体转换简体失败:%v", err)
- }
-
// 构造请求体
requestBody, err := json.Marshal(map[string]interface{}{
"user_id": UserID,
@@ -862,7 +1684,6 @@ func SendPrivateMessage(UserID int64, message string, selfid string, promptstr s
if err != nil {
return fmtf.Errorf("failed to marshal request body: %w", err)
}
- fmtf.Printf("实际发送信息:%v", message)
// 发送POST请求
resp, err := http.Post(u.String(), "application/json", bytes.NewBuffer(requestBody))
@@ -898,21 +1719,7 @@ func SendPrivateMessage(UserID int64, message string, selfid string, promptstr s
return nil
}
-func SendPrivateMessageRaw(UserID int64, message string, selfid string) error {
- if server.IsSelfIDExists(selfid) {
- // 创建消息结构体
- msg := map[string]interface{}{
- "action": "send_private_msg",
- "params": map[string]interface{}{
- "user_id": UserID,
- "message": message,
- },
- "echo": UserID,
- }
-
- // 发送消息
- return server.SendMessageBySelfID(selfid, msg)
- }
+func SendPrivateMessageSSE(UserID int64, message structs.InterfaceBody, promptstr string, selfid string) error {
var baseURL string
if len(config.GetHttpPaths()) > 0 {
baseURL, _ = GetBaseURLByUserID(selfid)
@@ -922,7 +1729,7 @@ func SendPrivateMessageRaw(UserID int64, message string, selfid string) error {
}
// 构建完整的URL
- baseURL = baseURL + "/send_private_msg"
+ baseURL = baseURL + "/send_private_msg_sse"
// 获取PathToken并检查其是否为空
pathToken := config.GetPathToken()
@@ -939,12 +1746,51 @@ func SendPrivateMessageRaw(UserID int64, message string, selfid string) error {
}
u.RawQuery = query.Encode()
- // 构造请求体
+ // 调试用的
+ if config.GetPrintHanming() {
+ fmtf.Printf("流式信息替换前:%v", message.Content)
+ }
+
+ // 检查是否需要启用敏感词过滤
+ if config.GetSensitiveModeType() == 1 && message.Content != "" {
+ message.Content = acnode.CheckWordOUT(message.Content)
+ }
+
+ // 是否不显示Emoji
+ if config.GetNoEmoji(promptstr) == 2 {
+ message.Content = RemoveEmojis(message.Content)
+ }
+
+ //精细化替换 每个yml配置文件都可以具有一个非全局的文本替换规则
+ message.Content = ReplaceTextOut(message.Content, promptstr)
+
+ // 调试用的
+ if config.GetPrintHanming() {
+ fmtf.Printf("流式信息替换后:%v", message.Content)
+ }
+
+ // 去除末尾的换行符 不去除会导致sse接口始终等待
+ message.Content = removeTrailingCRLFs(message.Content)
+
+ // 繁体转换简体 安全策略 防止用户诱导ai发繁体绕过替换规则
+ message.Content, err = ConvertTraditionalToSimplified(message.Content)
+ if err != nil {
+ fmtf.Printf("繁体转换简体失败:%v", err)
+ }
+
+ if message.Content == "" {
+ message.Content = " "
+ fmtf.Printf("过滤空SendPrivateMessageSSE,可能是llm api只发了换行符.")
+ return nil
+ }
+
+ fmtf.Printf("实际发送信息:%v", message.Content)
+
+ // 构造请求体,包括InterfaceBody
requestBody, err := json.Marshal(map[string]interface{}{
"user_id": UserID,
"message": message,
})
-
if err != nil {
return fmtf.Errorf("failed to marshal request body: %w", err)
}
@@ -983,7 +1829,7 @@ func SendPrivateMessageRaw(UserID int64, message string, selfid string) error {
return nil
}
-func SendPrivateMessageSSE(UserID int64, message structs.InterfaceBody, promptstr string, selfid string) error {
+func SendPrivateMessageSSESP(UserID string, message structs.InterfaceBody, promptstr string, selfid string) error {
var baseURL string
if len(config.GetHttpPaths()) > 0 {
baseURL, _ = GetBaseURLByUserID(selfid)
@@ -1059,6 +1905,16 @@ func SendPrivateMessageSSE(UserID int64, message structs.InterfaceBody, promptst
return fmtf.Errorf("failed to marshal request body: %w", err)
}
+ // 输出响应体,这一步是可选的
+ fmt.Println("Response Body:", string(requestBody))
+ fmt.Println("Response Body:", string(requestBody))
+ fmt.Println("Response Body:", string(requestBody))
+ fmt.Println("Response Body:", string(requestBody))
+ fmt.Println("Response Body:", string(requestBody))
+ fmt.Println("Response Body:", string(requestBody))
+ fmt.Println("Response path:", string(baseURL))
+ fmt.Println("Response path:", string(baseURL))
+
// 发送POST请求
resp, err := http.Post(u.String(), "application/json", bytes.NewBuffer(requestBody))
if err != nil {
@@ -1077,15 +1933,17 @@ func SendPrivateMessageSSE(UserID int64, message structs.InterfaceBody, promptst
return fmt.Errorf("failed to read response body: %w", err)
}
+ fmt.Println("返回 返回返回返回返回返回Body:", string(bodyBytes))
+
// 解析响应体以获取message_id
- var responseData ResponseData
+ var responseData ResponseDataSP
if err := json.Unmarshal(bodyBytes, &responseData); err != nil {
return fmt.Errorf("failed to unmarshal response data: %w", err)
}
messageID := responseData.Data.MessageID
// 添加messageID到全局变量
- AddMessageID(UserID, messageID)
+ AddMessageIDSP(UserID, messageID)
// 输出响应体,这一步是可选的
fmt.Println("Response Body:", string(bodyBytes))
@@ -1428,6 +2286,63 @@ func SendSSEPrivateMessage(userID int64, content string, promptstr string, selfi
}
}
+// SendSSEPrivateMessage 分割并发送消息的核心逻辑,直接遍历字符串
+func SendSSEPrivateMessageSP(userID string, content string, promptstr string, selfid string) {
+ punctuations := []rune{'。', '!', '?', ',', ',', '.', '!', '?', '~'}
+ splitProbability := config.GetSplitByPuntuations()
+
+ var parts []string
+ var currentPart strings.Builder
+
+ for _, runeValue := range content {
+ currentPart.WriteRune(runeValue)
+ if strings.ContainsRune(string(punctuations), runeValue) {
+ // 根据概率决定是否分割
+ if rand.Intn(100) < splitProbability {
+ parts = append(parts, currentPart.String())
+ currentPart.Reset()
+ }
+ }
+ }
+ // 添加最后一部分(如果有的话)
+ if currentPart.Len() > 0 {
+ parts = append(parts, currentPart.String())
+ }
+
+ // 根据parts长度处理状态
+ for i, part := range parts {
+ state := 1
+ if i == len(parts)-2 { // 倒数第二部分
+ state = 11
+ } else if i == len(parts)-1 { // 最后一部分
+ state = 20
+ }
+
+ // 构造消息体并发送
+ messageSSE := structs.InterfaceBody{
+ Content: part,
+ State: state,
+ }
+
+ if state == 20 { // 对最后一部分特殊处理
+ MemoryLoadCommand := config.GetMemoryLoadCommand()
+ promptKeyboard := config.GetPromptkeyboard()
+
+ if len(MemoryLoadCommand) > 0 {
+ selectedRestoreResponse := MemoryLoadCommand[rand.Intn(len(MemoryLoadCommand))]
+ if len(promptKeyboard) > 0 {
+ promptKeyboard[0] = selectedRestoreResponse
+ }
+ }
+
+ messageSSE.PromptKeyboard = promptKeyboard
+ }
+
+ // 发送SSE消息函数
+ SendPrivateMessageSSESP(userID, messageSSE, promptstr, selfid)
+ }
+}
+
// SendSSEPrivateMessageWithKeyboard 分割并发送消息的核心逻辑,直接遍历字符串
func SendSSEPrivateMessageWithKeyboard(userID int64, content string, keyboard []string, promptstr string, selfid string) {
punctuations := []rune{'。', '!', '?', ',', ',', '.', '!', '?', '~'}
@@ -1451,8 +2366,118 @@ func SendSSEPrivateMessageWithKeyboard(userID int64, content string, keyboard []
parts = append(parts, currentPart.String())
}
- // 根据parts长度处理状态
- for i, part := range parts {
+ // 根据parts长度处理状态
+ for i, part := range parts {
+ state := 1
+ if i == len(parts)-2 { // 倒数第二部分
+ state = 11
+ } else if i == len(parts)-1 { // 最后一部分
+ state = 20
+ }
+
+ // 构造消息体并发送
+ messageSSE := structs.InterfaceBody{
+ Content: part,
+ State: state,
+ }
+
+ if state == 20 { // 对最后一部分特殊处理
+ var promptKeyboard []string
+ if len(keyboard) == 0 {
+ RestoreResponses := config.GetRestoreCommand()
+ promptKeyboard = config.GetPromptkeyboard()
+
+ if len(RestoreResponses) > 0 {
+ selectedRestoreResponse := RestoreResponses[rand.Intn(len(RestoreResponses))]
+ if len(promptKeyboard) > 0 {
+ promptKeyboard[0] = selectedRestoreResponse
+ }
+ }
+ } else {
+ promptKeyboard = keyboard
+ }
+
+ messageSSE.PromptKeyboard = promptKeyboard
+ }
+
+ // 发送SSE消息函数
+ SendPrivateMessageSSE(userID, messageSSE, promptstr, selfid)
+ }
+}
+
+// SendSSEPrivateMessageWithKeyboard 分割并发送消息的核心逻辑,直接遍历字符串
+func SendSSEPrivateMessageWithKeyboardSP(userID string, content string, keyboard []string, promptstr string, selfid string) {
+ punctuations := []rune{'。', '!', '?', ',', ',', '.', '!', '?', '~'}
+ splitProbability := config.GetSplitByPuntuations()
+
+ var parts []string
+ var currentPart strings.Builder
+
+ for _, runeValue := range content {
+ currentPart.WriteRune(runeValue)
+ if strings.ContainsRune(string(punctuations), runeValue) {
+ // 根据概率决定是否分割
+ if rand.Intn(100) < splitProbability {
+ parts = append(parts, currentPart.String())
+ currentPart.Reset()
+ }
+ }
+ }
+ // 添加最后一部分(如果有的话)
+ if currentPart.Len() > 0 {
+ parts = append(parts, currentPart.String())
+ }
+
+ // 根据parts长度处理状态
+ for i, part := range parts {
+ state := 1
+ if i == len(parts)-2 { // 倒数第二部分
+ state = 11
+ } else if i == len(parts)-1 { // 最后一部分
+ state = 20
+ }
+
+ // 构造消息体并发送
+ messageSSE := structs.InterfaceBody{
+ Content: part,
+ State: state,
+ }
+
+ if state == 20 { // 对最后一部分特殊处理
+ var promptKeyboard []string
+ if len(keyboard) == 0 {
+ RestoreResponses := config.GetRestoreCommand()
+ promptKeyboard = config.GetPromptkeyboard()
+
+ if len(RestoreResponses) > 0 {
+ selectedRestoreResponse := RestoreResponses[rand.Intn(len(RestoreResponses))]
+ if len(promptKeyboard) > 0 {
+ promptKeyboard[0] = selectedRestoreResponse
+ }
+ }
+ } else {
+ promptKeyboard = keyboard
+ }
+
+ messageSSE.PromptKeyboard = promptKeyboard
+ }
+
+ // 发送SSE消息函数
+ SendPrivateMessageSSESP(userID, messageSSE, promptstr, selfid)
+ }
+}
+
+// SendSSEPrivateMessageByline 分割并发送消息的核心逻辑,直接遍历字符串
+func SendSSEPrivateMessageByLine(userID int64, content string, keyboard []string, promptstr string, selfid string) {
+ // 直接使用 strings.Split 按行分割字符串
+ parts := strings.Split(content, "\n")
+
+ // 根据parts长度处理状态
+ for i, part := range parts {
+ if part == "" {
+ continue // 跳过空行
+ }
+
state := 1
if i == len(parts)-2 { // 倒数第二部分
state = 11
@@ -1462,7 +2487,7 @@ func SendSSEPrivateMessageWithKeyboard(userID int64, content string, keyboard []
// 构造消息体并发送
messageSSE := structs.InterfaceBody{
- Content: part,
+ Content: part + "\n",
State: state,
}
@@ -1491,7 +2516,7 @@ func SendSSEPrivateMessageWithKeyboard(userID int64, content string, keyboard []
}
// SendSSEPrivateMessageByline 分割并发送消息的核心逻辑,直接遍历字符串
-func SendSSEPrivateMessageByLine(userID int64, content string, keyboard []string, promptstr string, selfid string) {
+func SendSSEPrivateMessageByLineSP(userID string, content string, keyboard []string, promptstr string, selfid string) {
// 直接使用 strings.Split 按行分割字符串
parts := strings.Split(content, "\n")
@@ -1534,7 +2559,7 @@ func SendSSEPrivateMessageByLine(userID int64, content string, keyboard []string
}
// 发送SSE消息函数
- SendPrivateMessageSSE(userID, messageSSE, promptstr, selfid)
+ SendPrivateMessageSSESP(userID, messageSSE, promptstr, selfid)
}
}
@@ -1604,6 +2629,72 @@ func SendSSEPrivateSafeMessage(userID int64, saveresponse string, promptstr stri
SendPrivateMessageSSE(userID, messageSSE, promptstr, selfid)
}
+// SendSSEPrivateSafeMessage 分割并发送安全消息的核心逻辑,直接遍历字符串
+func SendSSEPrivateSafeMessageSP(userID string, saveresponse string, promptstr string, selfid string) {
+ // 将字符串转换为rune切片,以正确处理多字节字符
+ runes := []rune(saveresponse)
+
+ // 计算每部分应该包含的rune数量
+ partLength := len(runes) / 3
+
+ // 初始化用于存储分割结果的切片
+ parts := make([]string, 3)
+
+ // 按字符分割字符串
+ for i := 0; i < 3; i++ {
+ if i < 2 { // 前两部分
+ start := i * partLength
+ end := start + partLength
+ parts[i] = string(runes[start:end])
+ } else { // 最后一部分,包含所有剩余的字符
+ start := i * partLength
+ parts[i] = string(runes[start:])
+ }
+ }
+ // 开头
+ messageSSE := structs.InterfaceBody{
+ Content: parts[0],
+ State: 1,
+ }
+
+ SendPrivateMessageSSESP(userID, messageSSE, promptstr, selfid)
+
+ // 中间
+ messageSSE = structs.InterfaceBody{
+ Content: parts[1],
+ State: 11,
+ }
+ SendPrivateMessageSSESP(userID, messageSSE, promptstr, selfid)
+
+ // 从配置中获取恢复响应数组
+ RestoreResponses := config.GetRestoreCommand()
+
+ var selectedRestoreResponse string
+ // 如果RestoreResponses至少有一个成员,则随机选择一个
+ if len(RestoreResponses) > 0 {
+ selectedRestoreResponse = RestoreResponses[rand.Intn(len(RestoreResponses))]
+ }
+
+ // 从配置中获取promptkeyboard
+ promptkeyboard := config.GetPromptkeyboard()
+
+ // 确保promptkeyboard至少有一个成员
+ if len(promptkeyboard) > 0 {
+ // 使用随机选中的RestoreResponse替换promptkeyboard的第一个成员
+ promptkeyboard[0] = selectedRestoreResponse
+ }
+
+ // 创建InterfaceBody结构体实例
+ messageSSE = structs.InterfaceBody{
+ Content: parts[2], // 假设空格字符串是期望的内容
+ State: 20, // 假设的状态码
+ PromptKeyboard: promptkeyboard, // 使用更新后的promptkeyboard
+ }
+
+ // 发送SSE私人消息
+ SendPrivateMessageSSESP(userID, messageSSE, promptstr, selfid)
+}
+
// SendSSEPrivateRestoreMessage 分割并发送重置消息的核心逻辑,直接遍历字符串
func SendSSEPrivateRestoreMessage(userID int64, RestoreResponse string, promptstr string, selfid string) {
// 将字符串转换为rune切片,以正确处理多字节字符
@@ -1656,6 +2747,58 @@ func SendSSEPrivateRestoreMessage(userID int64, RestoreResponse string, promptst
SendPrivateMessageSSE(userID, messageSSE, promptstr, selfid)
}
+// SendSSEPrivateRestoreMessage 分割并发送重置消息的核心逻辑,直接遍历字符串
+func SendSSEPrivateRestoreMessageSP(userID string, RestoreResponse string, promptstr string, selfid string) {
+ // 将字符串转换为rune切片,以正确处理多字节字符
+ runes := []rune(RestoreResponse)
+
+ // 计算每部分应该包含的rune数量
+ partLength := len(runes) / 3
+
+ // 初始化用于存储分割结果的切片
+ parts := make([]string, 3)
+
+ // 按字符分割字符串
+ for i := 0; i < 3; i++ {
+ if i < 2 { // 前两部分
+ start := i * partLength
+ end := start + partLength
+ parts[i] = string(runes[start:end])
+ } else { // 最后一部分,包含所有剩余的字符
+ start := i * partLength
+ parts[i] = string(runes[start:])
+ }
+ }
+
+ // 开头
+ messageSSE := structs.InterfaceBody{
+ Content: parts[0],
+ State: 1,
+ }
+
+ SendPrivateMessageSSESP(userID, messageSSE, promptstr, selfid)
+
+ //中间
+ messageSSE = structs.InterfaceBody{
+ Content: parts[1],
+ State: 11,
+ }
+ SendPrivateMessageSSESP(userID, messageSSE, promptstr, selfid)
+
+ // 从配置中获取promptkeyboard
+ promptkeyboard := config.GetPromptkeyboard()
+
+ // 创建InterfaceBody结构体实例
+ messageSSE = structs.InterfaceBody{
+ Content: parts[2], // 假设空格字符串是期望的内容
+ State: 20, // 假设的状态码
+ PromptKeyboard: promptkeyboard, // 使用更新后的promptkeyboard
+ }
+
+ // 发送SSE私人消息
+ SendPrivateMessageSSESP(userID, messageSSE, promptstr, selfid)
+}
+
// LanguageIntercept 检查文本语言,如果不在允许列表中,则返回 true 并发送消息
func LanguageIntercept(text string, message structs.OnebotGroupMessage, selfid string, promptstr string) bool {
hintWords := config.GetGroupHintWords()
@@ -1693,6 +2836,43 @@ func LanguageIntercept(text string, message structs.OnebotGroupMessage, selfid s
return true // 拦截
}
+// LanguageIntercept 检查文本语言,如果不在允许列表中,则返回 true 并发送消息
+func LanguageInterceptSP(text string, message structs.OnebotGroupMessageS, selfid string, promptstr string) bool {
+ hintWords := config.GetGroupHintWords()
+ // 遍历所有触发词,将其从文本中剔除
+ for _, word := range hintWords {
+ text = strings.Replace(text, word, "", -1)
+ }
+ info := whatlanggo.Detect(text)
+ lang := whatlanggo.LangToString(info.Lang)
+ fmtf.Printf("LanguageIntercept:%v\n", lang)
+
+ allowedLanguages := config.GetAllowedLanguages()
+ for _, allowed := range allowedLanguages {
+ if strings.Contains(allowed, lang) {
+ return false // 语言允许,不拦截
+ }
+ }
+
+ // 语言不允许,进行拦截
+ responseMessage := config.GetLanguagesResponseMessages()
+ friendlyName := FriendlyLanguageNameCN(info.Lang)
+ responseMessage = strings.Replace(responseMessage, "**", friendlyName, -1)
+
+ // 发送响应消息
+ if message.RealMessageType == "group_private" || message.MessageType == "private" {
+ if !config.GetUsePrivateSSE() {
+ SendPrivateMessageSP(message.UserID, responseMessage, selfid, promptstr)
+ } else {
+ SendSSEPrivateMessageSP(message.UserID, responseMessage, promptstr, selfid)
+ }
+ } else {
+ SendGroupMessageSP(message.GroupID, message.UserID, responseMessage, selfid, promptstr)
+ }
+
+ return true // 拦截
+}
+
// FriendlyLanguageNameCN 将语言代码映射为中文名称
func FriendlyLanguageNameCN(lang whatlanggo.Lang) string {
langMapCN := map[whatlanggo.Lang]string{
@@ -1752,6 +2932,29 @@ func LengthIntercept(text string, message structs.OnebotGroupMessage, selfid str
return false // 长度符合要求,不拦截
}
+// LengthIntercept 检查文本长度,如果超过最大长度,则返回 true 并发送消息
+func LengthInterceptSP(text string, message structs.OnebotGroupMessageS, selfid string, promptstr string) bool {
+ maxLen := config.GetQuestionMaxLenth()
+ if len(text) > maxLen {
+ // 长度超出限制,获取并发送响应消息
+ responseMessage := config.GetQmlResponseMessages()
+
+ // 根据消息类型发送响应
+ if message.RealMessageType == "group_private" || message.MessageType == "private" {
+ if !config.GetUsePrivateSSE() {
+ SendPrivateMessageSP(message.UserID, responseMessage, selfid, promptstr)
+ } else {
+ SendSSEPrivateMessageSP(message.UserID, responseMessage, promptstr, selfid)
+ }
+ } else {
+ SendGroupMessageSP(message.GroupID, message.UserID, responseMessage, selfid, promptstr)
+ }
+
+ return true // 拦截
+ }
+ return false // 长度符合要求,不拦截
+}
+
// AddMessageID 为指定user_id添加新的消息ID
func AddMessageID(userID int64, messageID int64) {
muUserIDMessageIDs.Lock()
@@ -1768,6 +2971,22 @@ func AddMessageID(userID int64, messageID int64) {
UserIDMessageIDs[userID] = append(UserIDMessageIDs[userID], messageInfo)
}
+// AddMessageID 为指定user_id添加新的消息ID
+func AddMessageIDSP(userID string, messageID string) {
+ muUserIDMessageIDs.Lock()
+ defer muUserIDMessageIDs.Unlock()
+
+ // 消息ID的有效期是120秒
+ expiration := time.Now().Add(120 * time.Second)
+ messageInfo := MessageIDInfoSP{MessageID: messageID, Expires: expiration}
+
+ // 清理已过期的消息ID
+ cleanExpiredMessageIDsSP(userID)
+
+ // 添加新的消息ID
+ UserIDMessageIDsSP[userID] = append(UserIDMessageIDsSP[userID], messageInfo)
+}
+
// cleanExpiredMessageIDs 清理指定user_id的已过期消息ID
func cleanExpiredMessageIDs(userID int64) {
validMessageIDs := []MessageIDInfo{}
@@ -1779,6 +2998,17 @@ func cleanExpiredMessageIDs(userID int64) {
UserIDMessageIDs[userID] = validMessageIDs
}
+// cleanExpiredMessageIDs 清理指定user_id的已过期消息ID
+func cleanExpiredMessageIDsSP(userID string) {
+ validMessageIDs := []MessageIDInfoSP{}
+ for _, messageInfo := range UserIDMessageIDsSP[userID] {
+ if messageInfo.Expires.After(time.Now()) {
+ validMessageIDs = append(validMessageIDs, messageInfo)
+ }
+ }
+ UserIDMessageIDsSP[userID] = validMessageIDs
+}
+
// GetLatestValidMessageID 获取指定user_id当前有效的最新消息ID
func GetLatestValidMessageID(userID int64) (int64, bool) {
muUserIDMessageIDs.RLock()
@@ -1795,6 +3025,22 @@ func GetLatestValidMessageID(userID int64) (int64, bool) {
return 0, false
}
+// GetLatestValidMessageID 获取指定user_id当前有效的最新消息ID
+func GetLatestValidMessageIDSP(userID string) (string, bool) {
+ muUserIDMessageIDsSP.RLock()
+ defer muUserIDMessageIDsSP.RUnlock()
+
+ // 确保已过期的消息ID被清理
+ cleanExpiredMessageIDsSP(userID)
+
+ // 获取最新的消息ID
+ if len(UserIDMessageIDsSP[userID]) > 0 {
+ latestMessageInfo := UserIDMessageIDsSP[userID][len(UserIDMessageIDsSP[userID])-1]
+ return latestMessageInfo.MessageID, true
+ }
+ return "", false
+}
+
// sendDeleteRequest 发送删除消息的请求,并输出响应内容
func sendDeleteRequest(url string, requestBody []byte) error {
// 发送POST请求
@@ -1885,6 +3131,69 @@ func DeleteLatestMessage(messageType string, id int64, userid int64, selfid stri
return sendDeleteRequest(u.String(), requestBodyBytes)
}
+func DeleteLatestMessageSP(messageType string, id string, userid string, selfid string) error {
+ var baseURL string
+ if len(config.GetHttpPaths()) > 0 {
+ baseURL, _ = GetBaseURLByUserID(selfid)
+ } else {
+ // 获取基础URL
+ baseURL = config.GetHttpPath() // 假设config.getHttpPath()返回基础URL
+ }
+
+ // 构建完整的URL
+ baseURL = baseURL + "/delete_msg"
+
+ // 获取PathToken并检查其是否为空
+ pathToken := config.GetPathToken()
+ // 使用net/url包构建URL
+ u, err := url.Parse(baseURL)
+ if err != nil {
+ panic("URL parsing failed: " + err.Error())
+ }
+
+ // 添加access_token参数
+ query := u.Query()
+ if pathToken != "" {
+ query.Set("access_token", pathToken)
+ }
+ u.RawQuery = query.Encode()
+
+ // 获取最新的有效消息ID
+ messageID, valid := GetLatestValidMessageIDSP(userid)
+ if !valid {
+ return fmt.Errorf("no valid message ID found for user/group/guild ID: %d", id)
+ }
+
+ // 构造请求体
+ requestBody := make(map[string]interface{})
+ requestBody["message_id"] = messageID
+
+ // 根据type填充相应的ID字段
+ switch messageType {
+ case "group_private":
+ requestBody["user_id"] = id
+ case "group":
+ requestBody["group_id"] = id
+ case "guild":
+ requestBody["channel_id"] = id
+ case "guild_private":
+ requestBody["guild_id"] = id
+ case "interaction":
+ requestBody["group_id"] = id
+ default:
+ return fmt.Errorf("unsupported message type: %s", messageType)
+ }
+
+ requestBodyBytes, err := json.Marshal(requestBody)
+ if err != nil {
+ return fmt.Errorf("failed to marshal request body: %w", err)
+ }
+ fmtf.Printf("发送撤回请求:%v", string(requestBodyBytes))
+
+ // 发送删除消息请求
+ return sendDeleteRequest(u.String(), requestBodyBytes)
+}
+
// MakeAlternating ensures that roles alternate between "user" and "assistant".
func MakeAlternating(messages []structs.MessageContent) []structs.MessageContent {
if len(messages) < 2 {