diff --git a/code/go.mod b/code/go.mod index 9c3e4fdb..b4192725 100644 --- a/code/go.mod +++ b/code/go.mod @@ -5,13 +5,16 @@ go 1.18 require github.com/larksuite/oapi-sdk-go/v3 v3.0.14 require ( + github.com/duke-git/lancet/v2 v2.1.17 github.com/gin-gonic/gin v1.8.2 github.com/google/uuid v1.3.0 + github.com/k0kubun/pp/v3 v3.2.0 github.com/larksuite/oapi-sdk-gin v1.0.0 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pion/opus v0.0.0-20230123082803-1052c3e89e58 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.14.0 + gopkg.in/yaml.v2 v2.4.0 ) require ( @@ -25,6 +28,7 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/leodido/go-urn v1.2.1 // indirect github.com/magiconair/properties v1.8.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.17 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -37,11 +41,11 @@ require ( github.com/subosito/gotenv v1.4.1 // indirect github.com/ugorji/go/codec v1.2.8 // indirect golang.org/x/crypto v0.5.0 // indirect + golang.org/x/exp v0.0.0-20221208152030-732eee02a75a // indirect golang.org/x/net v0.5.0 // indirect golang.org/x/sys v0.4.0 // indirect golang.org/x/text v0.6.0 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/code/go.sum b/code/go.sum index d1e28f10..745093f0 100644 --- a/code/go.sum +++ b/code/go.sum @@ -50,6 +50,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/duke-git/lancet/v2 v2.1.17 h1:4u9oAGgmTPTt2D7AcjjLp0ubbcaQlova8xeTIuyupDw= +github.com/duke-git/lancet/v2 v2.1.17/go.mod h1:hNcc06mV7qr+crH/0nP+rlC3TB0Q9g5OrVnO8/TGD4c= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -147,6 +149,8 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/k0kubun/pp/v3 v3.2.0 h1:h33hNTZ9nVFNP3u2Fsgz8JXiF5JINoZfFq4SvKJwNcs= +github.com/k0kubun/pp/v3 v3.2.0/go.mod h1:ODtJQbQcIRfAD3N+theGCV1m/CBxweERz2dapdz1EwA= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -165,6 +169,9 @@ github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= @@ -249,6 +256,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20221208152030-732eee02a75a h1:4iLhBPcpqFmylhnkbY3W0ONLUYYkDAW9xMFLfxgsvCw= +golang.org/x/exp v0.0.0-20221208152030-732eee02a75a/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= diff --git a/code/handlers/card_clear_action.go b/code/handlers/card_clear_action.go new file mode 100644 index 00000000..355e5920 --- /dev/null +++ b/code/handlers/card_clear_action.go @@ -0,0 +1,43 @@ +package handlers + +import ( + "context" + larkcard "github.com/larksuite/oapi-sdk-go/v3/card" + "start-feishubot/services" +) + +func NewClearCardHandler(cardMsg CardMsg, m MessageHandler) CardHandlerFunc { + return func(ctx context.Context, cardAction *larkcard.CardAction) (interface{}, error) { + if cardMsg.Kind == ClearCardKind { + newCard, err, done := CommonProcessClearCache(cardMsg, m.sessionCache) + if done { + return newCard, err + } + return nil, nil + } + return nil, ErrNextHandler + } +} + +func CommonProcessClearCache(cardMsg CardMsg, session services.SessionServiceCacheInterface) ( + interface{}, error, bool) { + if cardMsg.Value == "1" { + session.Clear(cardMsg.SessionId) + newCard, _ := newSendCard( + withHeader("️🆑 机器人提醒", larkcard.TemplateGrey), + withMainMd("已删除此话题的上下文信息"), + withNote("我们可以开始一个全新的话题,继续找我聊天吧"), + ) + //fmt.Printf("session: %v", newCard) + return newCard, nil, true + } + if cardMsg.Value == "0" { + newCard, _ := newSendCard( + withHeader("️🆑 机器人提醒", larkcard.TemplateGreen), + withMainMd("依旧保留此话题的上下文信息"), + withNote("我们可以继续探讨这个话题,期待和您聊天。如果您有其他问题或者想要讨论的话题,请告诉我哦"), + ) + return newCard, nil, true + } + return nil, nil, false +} diff --git a/code/handlers/card_common_action.go b/code/handlers/card_common_action.go new file mode 100644 index 00000000..0ebbd165 --- /dev/null +++ b/code/handlers/card_common_action.go @@ -0,0 +1,43 @@ +package handlers + +import ( + "context" + "encoding/json" + "fmt" + larkcard "github.com/larksuite/oapi-sdk-go/v3/card" +) + +type CardHandlerMeta func(cardMsg CardMsg, m MessageHandler) CardHandlerFunc + +type CardHandlerFunc func(ctx context.Context, cardAction *larkcard.CardAction) ( + interface{}, error) + +var ErrNextHandler = fmt.Errorf("next handler") + +func NewCardHandler(m MessageHandler) CardHandlerFunc { + handlers := []CardHandlerMeta{ + NewClearCardHandler, + NewPicResolutionHandler, + NewPicTextMoreHandler, + NewPicModeChangeHandler, + NewRoleTagCardHandler, + NewRoleCardHandler, + } + + return func(ctx context.Context, cardAction *larkcard.CardAction) (interface{}, error) { + var cardMsg CardMsg + actionValue := cardAction.Action.Value + actionValueJson, _ := json.Marshal(actionValue) + json.Unmarshal(actionValueJson, &cardMsg) + //pp.Println(cardMsg) + for _, handler := range handlers { + h := handler(cardMsg, m) + i, err := h(ctx, cardAction) + if err == ErrNextHandler { + continue + } + return i, err + } + return nil, nil + } +} diff --git a/code/handlers/card_pic_action.go b/code/handlers/card_pic_action.go new file mode 100644 index 00000000..83ad7797 --- /dev/null +++ b/code/handlers/card_pic_action.go @@ -0,0 +1,92 @@ +package handlers + +import ( + "context" + larkcard "github.com/larksuite/oapi-sdk-go/v3/card" + "start-feishubot/services" +) + +func NewPicResolutionHandler(cardMsg CardMsg, m MessageHandler) CardHandlerFunc { + return func(ctx context.Context, cardAction *larkcard.CardAction) (interface{}, error) { + if cardMsg.Kind == PicResolutionKind { + CommonProcessPicResolution(cardMsg, cardAction, m.sessionCache) + return nil, nil + } + return nil, ErrNextHandler + } +} + +func NewPicModeChangeHandler(cardMsg CardMsg, m MessageHandler) CardHandlerFunc { + return func(ctx context.Context, cardAction *larkcard.CardAction) (interface{}, error) { + if cardMsg.Kind == PicModeChangeKind { + newCard, err, done := CommonProcessPicModeChange(cardMsg, m.sessionCache) + if done { + return newCard, err + } + return nil, nil + } + return nil, ErrNextHandler + } +} +func NewPicTextMoreHandler(cardMsg CardMsg, m MessageHandler) CardHandlerFunc { + return func(ctx context.Context, cardAction *larkcard.CardAction) (interface{}, error) { + if cardMsg.Kind == PicTextMoreKind { + go func() { + m.CommonProcessPicMore(cardMsg) + }() + return nil, nil + } + return nil, ErrNextHandler + } +} + +func CommonProcessPicResolution(msg CardMsg, + cardAction *larkcard.CardAction, + cache services.SessionServiceCacheInterface) { + option := cardAction.Action.Option + //fmt.Println(larkcore.Prettify(msg)) + cache.SetPicResolution(msg.SessionId, services.Resolution(option)) + //send text + replyMsg(context.Background(), "已更新图片分辨率为"+option, + &msg.MsgId) +} + +func (m MessageHandler) CommonProcessPicMore(msg CardMsg) { + resolution := m.sessionCache.GetPicResolution(msg.SessionId) + //fmt.Println("resolution: ", resolution) + //fmt.Println("msg: ", msg) + question := msg.Value.(string) + bs64, _ := m.gpt.GenerateOneImage(question, resolution) + replayImageCardByBase64(context.Background(), bs64, &msg.MsgId, + &msg.SessionId, question) +} + +func CommonProcessPicModeChange(cardMsg CardMsg, + session services.SessionServiceCacheInterface) ( + interface{}, error, bool) { + if cardMsg.Value == "1" { + + sessionId := cardMsg.SessionId + session.Clear(sessionId) + session.SetMode(sessionId, + services.ModePicCreate) + session.SetPicResolution(sessionId, + services.Resolution256) + + newCard, _ := + newSendCard( + withHeader("🖼️ 已进入图片创作模式", larkcard.TemplateBlue), + withPicResolutionBtn(&sessionId), + withNote("提醒:回复文本或图片,让AI生成相关的图片。")) + return newCard, nil, true + } + if cardMsg.Value == "0" { + newCard, _ := newSendCard( + withHeader("️🎒 机器人提醒", larkcard.TemplateGreen), + withMainMd("依旧保留此话题的上下文信息"), + withNote("我们可以继续探讨这个话题,期待和您聊天。如果您有其他问题或者想要讨论的话题,请告诉我哦"), + ) + return newCard, nil, true + } + return nil, nil, false +} diff --git a/code/handlers/card_role_action.go b/code/handlers/card_role_action.go new file mode 100644 index 00000000..bb811565 --- /dev/null +++ b/code/handlers/card_role_action.go @@ -0,0 +1,75 @@ +package handlers + +import ( + "context" + larkcard "github.com/larksuite/oapi-sdk-go/v3/card" + "start-feishubot/initialization" + "start-feishubot/services" + "start-feishubot/services/openai" +) + +func NewRoleTagCardHandler(cardMsg CardMsg, + m MessageHandler) CardHandlerFunc { + return func(ctx context.Context, cardAction *larkcard.CardAction) (interface{}, error) { + + if cardMsg.Kind == RoleTagsChooseKind { + newCard, err, done := CommonProcessRoleTag(cardMsg, cardAction, + m.sessionCache) + if done { + return newCard, err + } + return nil, nil + } + return nil, ErrNextHandler + } +} + +func NewRoleCardHandler(cardMsg CardMsg, + m MessageHandler) CardHandlerFunc { + return func(ctx context.Context, cardAction *larkcard.CardAction) (interface{}, error) { + + if cardMsg.Kind == RoleChooseKind { + newCard, err, done := CommonProcessRole(cardMsg, cardAction, + m.sessionCache) + if done { + return newCard, err + } + return nil, nil + } + return nil, ErrNextHandler + } +} + +func CommonProcessRoleTag(msg CardMsg, cardAction *larkcard.CardAction, + cache services.SessionServiceCacheInterface) (interface{}, + error, bool) { + option := cardAction.Action.Option + //replyMsg(context.Background(), "已选择tag:"+option, + // &msg.MsgId) + roles := initialization.GetTitleListByTag(option) + //fmt.Printf("roles: %s", roles) + SendRoleListCard(context.Background(), &msg.SessionId, + &msg.MsgId, option, *roles) + return nil, nil, true +} + +func CommonProcessRole(msg CardMsg, cardAction *larkcard.CardAction, + cache services.SessionServiceCacheInterface) (interface{}, + error, bool) { + option := cardAction.Action.Option + contentByTitle, error := initialization.GetFirstRoleContentByTitle(option) + if error != nil { + return nil, error, true + } + cache.Clear(msg.SessionId) + systemMsg := append([]openai.Messages{}, openai.Messages{ + Role: "system", Content: contentByTitle, + }) + cache.SetMsg(msg.SessionId, systemMsg) + //pp.Println("systemMsg: ", systemMsg) + sendSystemInstructionCard(context.Background(), &msg.SessionId, + &msg.MsgId, contentByTitle) + //replyMsg(context.Background(), "已选择角色:"+contentByTitle, + // &msg.MsgId) + return nil, nil, true +} diff --git a/code/handlers/event_common_action.go b/code/handlers/event_common_action.go index 62554953..ba484de8 100644 --- a/code/handlers/event_common_action.go +++ b/code/handlers/event_common_action.go @@ -4,6 +4,7 @@ import ( "context" "fmt" larkim "github.com/larksuite/oapi-sdk-go/v3/service/im/v1" + "start-feishubot/initialization" "start-feishubot/services/openai" "start-feishubot/utils" ) @@ -130,3 +131,23 @@ func (*BalanceAction) Execute(a *ActionInfo) bool { } return true } + +type RoleListAction struct { /*角色列表*/ +} + +func (*RoleListAction) Execute(a *ActionInfo) bool { + if _, foundSystem := utils.EitherTrimEqual(a.info.qParsed, + "/roles", "角色列表"); foundSystem { + //a.handler.sessionCache.Clear(*a.info.sessionId) + //systemMsg := append([]openai.Messages{}, openai.Messages{ + // Role: "system", Content: system, + //}) + //a.handler.sessionCache.SetMsg(*a.info.sessionId, systemMsg) + //sendSystemInstructionCard(*a.ctx, a.info.sessionId, + // a.info.msgId, system) + tags := initialization.GetAllUniqueTags() + SendRoleTagsCard(*a.ctx, a.info.sessionId, a.info.msgId, *tags) + return false + } + return true +} diff --git a/code/handlers/handler.go b/code/handlers/handler.go index bc5d0530..b25f1926 100644 --- a/code/handlers/handler.go +++ b/code/handlers/handler.go @@ -2,7 +2,6 @@ package handlers import ( "context" - "encoding/json" "fmt" "start-feishubot/initialization" "start-feishubot/services" @@ -31,120 +30,12 @@ type MessageHandler struct { config initialization.Config } -func (m MessageHandler) cardHandler(_ context.Context, +func (m MessageHandler) cardHandler(ctx context.Context, cardAction *larkcard.CardAction) (interface{}, error) { - var cardMsg CardMsg - actionValue := cardAction.Action.Value - actionValueJson, _ := json.Marshal(actionValue) - json.Unmarshal(actionValueJson, &cardMsg) - //fmt.Println("cardMsg: ", cardMsg) - if cardMsg.Kind == ClearCardKind { - newCard, err, done := CommonProcessClearCache(cardMsg, m.sessionCache) - if done { - return newCard, err - } - return nil, nil - } - if cardMsg.Kind == PicResolutionKind { - CommonProcessPicResolution(cardMsg, cardAction, m.sessionCache) - return nil, nil - } - if cardMsg.Kind == PicTextMoreKind { - go func() { - m.CommonProcessPicMore(cardMsg) - }() - } - //if cardMsg.Kind == PicVarMoreKind { - // //todo: 暂时不允许 以图搜图 模式下的 再来一张 - // go func() { - // m.CommonProcessPicMore(cardMsg) - // }() - //} - if cardMsg.Kind == PicModeChangeKind { - newCard, err, done := CommonProcessPicModeChange(cardMsg, m.sessionCache) - if done { - return newCard, err - } - return nil, nil - - } - return nil, nil - -} - -func (m MessageHandler) CommonProcessPicMore(msg CardMsg) { - resolution := m.sessionCache.GetPicResolution(msg.SessionId) - //fmt.Println("resolution: ", resolution) - //fmt.Println("msg: ", msg) - question := msg.Value.(string) - bs64, _ := m.gpt.GenerateOneImage(question, resolution) - replayImageCardByBase64(context.Background(), bs64, &msg.MsgId, - &msg.SessionId, question) + messageHandler := NewCardHandler(m) + return messageHandler(ctx, cardAction) } -func CommonProcessPicResolution(msg CardMsg, - cardAction *larkcard.CardAction, - cache services.SessionServiceCacheInterface) { - option := cardAction.Action.Option - //fmt.Println(larkcore.Prettify(msg)) - cache.SetPicResolution(msg.SessionId, services.Resolution(option)) - //send text - replyMsg(context.Background(), "已更新图片分辨率为"+option, - &msg.MsgId) -} - -func CommonProcessClearCache(cardMsg CardMsg, session services.SessionServiceCacheInterface) ( - interface{}, error, bool) { - if cardMsg.Value == "1" { - session.Clear(cardMsg.SessionId) - newCard, _ := newSendCard( - withHeader("️🆑 机器人提醒", larkcard.TemplateGrey), - withMainMd("已删除此话题的上下文信息"), - withNote("我们可以开始一个全新的话题,继续找我聊天吧"), - ) - //fmt.Printf("session: %v", newCard) - return newCard, nil, true - } - if cardMsg.Value == "0" { - newCard, _ := newSendCard( - withHeader("️🆑 机器人提醒", larkcard.TemplateGreen), - withMainMd("依旧保留此话题的上下文信息"), - withNote("我们可以继续探讨这个话题,期待和您聊天。如果您有其他问题或者想要讨论的话题,请告诉我哦"), - ) - return newCard, nil, true - } - return nil, nil, false -} - -func CommonProcessPicModeChange(cardMsg CardMsg, - session services.SessionServiceCacheInterface) ( - interface{}, error, bool) { - if cardMsg.Value == "1" { - - sessionId := cardMsg.SessionId - session.Clear(sessionId) - session.SetMode(sessionId, - services.ModePicCreate) - session.SetPicResolution(sessionId, - services.Resolution256) - - newCard, _ := - newSendCard( - withHeader("🖼️ 已进入图片创作模式", larkcard.TemplateBlue), - withPicResolutionBtn(&sessionId), - withNote("提醒:回复文本或图片,让AI生成相关的图片。")) - return newCard, nil, true - } - if cardMsg.Value == "0" { - newCard, _ := newSendCard( - withHeader("️🎒 机器人提醒", larkcard.TemplateGreen), - withMainMd("依旧保留此话题的上下文信息"), - withNote("我们可以继续探讨这个话题,期待和您聊天。如果您有其他问题或者想要讨论的话题,请告诉我哦"), - ) - return newCard, nil, true - } - return nil, nil, false -} func judgeMsgType(event *larkim.P2MessageReceiveV1) (string, error) { msgType := event.Event.Message.MessageType @@ -204,6 +95,7 @@ func (m MessageHandler) msgReceivedHandler(ctx context.Context, event *larkim.P2 &PicAction{}, //图片处理 &EmptyAction{}, //空消息处理 &ClearAction{}, //清除消息处理 + &RoleListAction{}, //角色列表处理 &HelpAction{}, //帮助处理 &BalanceAction{}, //余额处理 &RolePlayAction{}, //角色扮演处理 diff --git a/code/handlers/msg.go b/code/handlers/msg.go index 6f6fca23..c1a3e0ed 100644 --- a/code/handlers/msg.go +++ b/code/handlers/msg.go @@ -17,11 +17,13 @@ type CardKind string type CardChatType string var ( - ClearCardKind = CardKind("clear") // 清空上下文 - PicModeChangeKind = CardKind("pic_mode_change") // 切换图片创作模式 - PicResolutionKind = CardKind("pic_resolution") // 图片分辨率调整 - PicTextMoreKind = CardKind("pic_text_more") // 重新根据文本生成图片 - PicVarMoreKind = CardKind("pic_var_more") // 变量图片 + ClearCardKind = CardKind("clear") // 清空上下文 + PicModeChangeKind = CardKind("pic_mode_change") // 切换图片创作模式 + PicResolutionKind = CardKind("pic_resolution") // 图片分辨率调整 + PicTextMoreKind = CardKind("pic_text_more") // 重新根据文本生成图片 + PicVarMoreKind = CardKind("pic_var_more") // 变量图片 + RoleTagsChooseKind = CardKind("role_tags_choose") // 内置角色所属标签选择 + RoleChooseKind = CardKind("role_choose") // 内置角色选择 ) var ( @@ -346,13 +348,63 @@ func withPicResolutionBtn(sessionID *string) larkcard. Actions([]larkcard.MessageCardActionElement{cancelMenu}). Layout(larkcard.MessageCardActionLayoutFlow.Ptr()). Build() + return actions +} +func withRoleTagsBtn(sessionID *string, tags ...string) larkcard. + MessageCardElement { + var menuOptions []MenuOption + + for _, tag := range tags { + menuOptions = append(menuOptions, MenuOption{ + label: tag, + value: tag, + }) + } + cancelMenu := newMenu("选择角色分类", + map[string]interface{}{ + "value": "0", + "kind": RoleTagsChooseKind, + "sessionId": *sessionID, + "msgId": *sessionID, + }, + menuOptions..., + ) + actions := larkcard.NewMessageCardAction(). + Actions([]larkcard.MessageCardActionElement{cancelMenu}). + Layout(larkcard.MessageCardActionLayoutFlow.Ptr()). + Build() return actions +} + +func withRoleBtn(sessionID *string, titles ...string) larkcard. + MessageCardElement { + var menuOptions []MenuOption + + for _, tag := range titles { + menuOptions = append(menuOptions, MenuOption{ + label: tag, + value: tag, + }) + } + cancelMenu := newMenu("查看内置角色", + map[string]interface{}{ + "value": "0", + "kind": RoleChooseKind, + "sessionId": *sessionID, + "msgId": *sessionID, + }, + menuOptions..., + ) + actions := larkcard.NewMessageCardAction(). + Actions([]larkcard.MessageCardActionElement{cancelMenu}). + Layout(larkcard.MessageCardActionLayoutFlow.Ptr()). + Build() + return actions } func replyMsg(ctx context.Context, msg string, msgId *string) error { - fmt.Println("sendMsg", msg, msgId) msg, i := processMessage(msg) if i != nil { return i @@ -539,24 +591,16 @@ func sendClearCacheCheckCard(ctx context.Context, withMainMd("您确定要清除对话上下文吗?"), withNote("请注意,这将开始一个全新的对话,您将无法利用之前话题的历史信息"), withClearDoubleCheckBtn(sessionId)) - replyCard( - ctx, - msgId, - newCard, - ) + replyCard(ctx, msgId, newCard) } func sendSystemInstructionCard(ctx context.Context, sessionId *string, msgId *string, content string) { newCard, _ := newSendCard( - withHeader("🥷 已进入角色扮演模式", larkcard.TemplateBlue), + withHeader("🥷 已进入角色扮演模式", larkcard.TemplateIndigo), withMainText(content), withNote("请注意,这将开始一个全新的对话,您将无法利用之前话题的历史信息")) - replyCard( - ctx, - msgId, - newCard, - ) + replyCard(ctx, msgId, newCard) } func sendPicCreateInstructionCard(ctx context.Context, @@ -565,11 +609,7 @@ func sendPicCreateInstructionCard(ctx context.Context, withHeader("🖼️ 已进入图片创作模式", larkcard.TemplateBlue), withPicResolutionBtn(sessionId), withNote("提醒:回复文本或图片,让AI生成相关的图片。")) - replyCard( - ctx, - msgId, - newCard, - ) + replyCard(ctx, msgId, newCard) } func sendPicModeCheckCard(ctx context.Context, @@ -579,11 +619,7 @@ func sendPicModeCheckCard(ctx context.Context, withMainMd("收到图片,是否进入图片创作模式?"), withNote("请注意,这将开始一个全新的对话,您将无法利用之前话题的历史信息"), withPicModeDoubleCheckBtn(sessionId)) - replyCard( - ctx, - msgId, - newCard, - ) + replyCard(ctx, msgId, newCard) } func sendNewTopicCard(ctx context.Context, @@ -592,11 +628,7 @@ func sendNewTopicCard(ctx context.Context, withHeader("👻️ 已开启新的话题", larkcard.TemplateBlue), withMainText(content), withNote("提醒:点击对话框参与回复,可保持话题连贯")) - replyCard( - ctx, - msgId, - newCard, - ) + replyCard(ctx, msgId, newCard) } func sendHelpCard(ctx context.Context, @@ -614,6 +646,8 @@ func sendHelpCard(ctx context.Context, "sessionId": *sessionId, }, larkcard.MessageCardButtonTypeDanger)), withSplitLine(), + withMainMd("🛖 **内置角色列表** \n"+" 文本回复 *角色列表* 或 */roles*"), + withSplitLine(), withMainMd("🥷 **角色扮演模式**\n文本回复*角色扮演* 或 */system*+空格+角色信息"), withSplitLine(), withMainMd("🎤 **AI语音对话**\n私聊模式下直接发送语音"), @@ -622,25 +656,15 @@ func sendHelpCard(ctx context.Context, withSplitLine(), withMainMd("🎰 **Token余额查询**\n回复*余额* 或 */balance*"), withSplitLine(), - withMainMd("👨💼 **常用角色管理** 🚧\n"+ - " 文本回复 *角色管理* 或 */manage*"), - withSplitLine(), - withMainMd("🔃️ **历史话题回档** 🚧\n"+ - " 进入话题的回复详情页,文本回复 *恢复* 或 */reload*"), + withMainMd("🔃️ **历史话题回档** 🚧\n"+" 进入话题的回复详情页,文本回复 *恢复* 或 */reload*"), withSplitLine(), - withMainMd("📤 **话题内容导出** 🚧\n"+ - " 文本回复 *导出* 或 */export*"), + withMainMd("📤 **话题内容导出** 🚧\n"+" 文本回复 *导出* 或 */export*"), withSplitLine(), - withMainMd("🎰 **连续对话与多话题模式**\n"+ - " 点击对话框参与回复,可保持话题连贯。同时,单独提问即可开启全新新话题"), + withMainMd("🎰 **连续对话与多话题模式**\n"+" 点击对话框参与回复,可保持话题连贯。同时,单独提问即可开启全新新话题"), withSplitLine(), withMainMd("🎒 **需要更多帮助**\n文本回复 *帮助* 或 */help*"), ) - replyCard( - ctx, - msgId, - newCard, - ) + replyCard(ctx, msgId, newCard) } func sendImageCard(ctx context.Context, imageKey string, @@ -657,11 +681,7 @@ func sendImageCard(ctx context.Context, imageKey string, "sessionId": *sessionId, }, larkcard.MessageCardButtonTypePrimary)), ) - replyCard( - ctx, - msgId, - newCard, - ) + replyCard(ctx, msgId, newCard) return nil } @@ -679,19 +699,10 @@ func sendVarImageCard(ctx context.Context, imageKey string, "sessionId": *sessionId, }, larkcard.MessageCardButtonTypePrimary)), ) - replyCard( - ctx, - msgId, - newCard, - ) + replyCard(ctx, msgId, newCard) return nil } -//TotalGranted float64 `json:"total_granted"` -//TotalUsed float64 `json:"total_used"` -//TotalAvailable float64 `json:"total_available"` -//EffectiveAt time.Time `json:"effective_at"` -//ExpiresAt time.Time `json:"expires_at"` func sendBalanceCard(ctx context.Context, msgId *string, balance openai.BalanceResponse) { newCard, _ := newSendCard( @@ -704,9 +715,23 @@ func sendBalanceCard(ctx context.Context, msgId *string, balance.EffectiveAt.Format("2006-01-02 15:04:05"), balance.ExpiresAt.Format("2006-01-02 15:04:05"))), ) - replyCard( - ctx, - msgId, - newCard, - ) + replyCard(ctx, msgId, newCard) +} + +func SendRoleTagsCard(ctx context.Context, + sessionId *string, msgId *string, roleTags []string) { + newCard, _ := newSendCard( + withHeader("🛖 请选择角色类别", larkcard.TemplateIndigo), + withRoleTagsBtn(sessionId, roleTags...), + withNote("提醒:选择角色所属分类,以便我们为您推荐更多相关角色。")) + replyCard(ctx, msgId, newCard) +} + +func SendRoleListCard(ctx context.Context, + sessionId *string, msgId *string, roleTag string, roleList []string) { + newCard, _ := newSendCard( + withHeader("🛖 角色列表"+" - "+roleTag, larkcard.TemplateIndigo), + withRoleBtn(sessionId, roleList...), + withNote("提醒:选择内置场景,快速进入角色扮演模式。")) + replyCard(ctx, msgId, newCard) } diff --git a/code/initialization/roles_load.go b/code/initialization/roles_load.go new file mode 100644 index 00000000..bd557a35 --- /dev/null +++ b/code/initialization/roles_load.go @@ -0,0 +1,76 @@ +package initialization + +import ( + "errors" + "github.com/duke-git/lancet/v2/slice" + "github.com/duke-git/lancet/v2/validator" + "gopkg.in/yaml.v2" + "io/ioutil" + "log" +) + +type Role struct { + Title string `yaml:"title"` + Content string `yaml:"content"` + Tags []string `yaml:"tags"` +} + +var RoleList *[]Role + +// InitRoleList 加载Prompt +func InitRoleList() *[]Role { + data, err := ioutil.ReadFile("role_list.yaml") + if err != nil { + log.Fatal(err) + } + + err = yaml.Unmarshal(data, &RoleList) + if err != nil { + log.Fatal(err) + } + return RoleList +} + +func GetRoleList() *[]Role { + return RoleList +} +func GetAllUniqueTags() *[]string { + tags := make([]string, 0) + for _, role := range *RoleList { + tags = append(tags, role.Tags...) + } + result := slice.Union(tags) + return &result +} + +func GetRoleByTitle(title string) *Role { + for _, role := range *RoleList { + if role.Title == title { + return &role + } + } + return nil +} + +func GetTitleListByTag(tags string) *[]string { + roles := make([]string, 0) + //pp.Println(RoleList) + for _, role := range *RoleList { + for _, roleTag := range role.Tags { + if roleTag == tags && !validator.IsEmptyString(role. + Title) { + roles = append(roles, role.Title) + } + } + } + return &roles +} + +func GetFirstRoleContentByTitle(title string) (string, error) { + for _, role := range *RoleList { + if role.Title == title { + return role.Content, nil + } + } + return "", errors.New("role not found") +} diff --git a/code/main.go b/code/main.go index aed97448..887bb77b 100644 --- a/code/main.go +++ b/code/main.go @@ -23,6 +23,7 @@ var ( ) func main() { + initialization.InitRoleList() pflag.Parse() config := initialization.LoadConfig(*cfg) initialization.LoadLarkClient(*config) diff --git a/code/role_list.yaml b/code/role_list.yaml new file mode 100644 index 00000000..a54ef0aa --- /dev/null +++ b/code/role_list.yaml @@ -0,0 +1,167 @@ +# 可在此处提交你认为不错的角色预设, 注意保持格式一致。 +# PR时的tag暂时集中在 [ "日常办公", "生活助手" ,"代码专家", "文案撰写"] +# 更多点子可参考我另一个参与的项目: https://open-gpt.app/ + +- title: 周报生成 + content: 请帮我把以下的工作内容填充为一篇完整的周报,用 markdown 格式以分点叙述的形式输出: + example: 重新优化设计稿,和前端再次沟通UI细节,确保落地 + author: 二丫讲梵 + tags: + - 日常办公 + +- title: 产品经理 + content: 请确认我的以下请求。请您作为产品经理回复我。我将会提供一个主题,您将帮助我编写一份包括以下章节标题的 PRD 文档:主题、简介、问题陈述、目标与目的、用户故事、技术要求、收益、KPI 指标、开发风险以及结论。在我要求具体主题、功能或开发的 PRD 之前,请不要先写任何一份 PRD 文档。 + example: 我想要一个可以在手机上使用的应用程序,可以帮助我在旅行中找到最好的餐厅。 + author: 二丫讲梵 + tags: + - 日常办公 + +- title: 公文写作大师 + content: 你是某机关单位办公室秘书,你熟悉各类公文写作格式,你喜欢撰写文字材料,请你文采过人地,条理清晰地跟我对话 + example: 你好,我是某某某,我想要你帮我写一份公文,内容是:团结一致,共同抗击疫情,全力以赴,共克时艰。 + author: 小叉Ray + tags: + - 日常办公 + - 文案撰写 + +- title: 招聘HR + content: 我想让你担任招聘人员。我将提供一些关于职位空缺的信息,而你的工作是制定寻找合格申请人的策略。这可能包括通过社交媒体、社交活动甚至参加招聘会接触潜在候选人,以便为每个职位找到最合适的人选。我的第一个请求是: + example: 我需要一名有经验的前端开发工程师,他应该有 3 年以上的工作经验,熟悉 React 和 Vue,熟悉前端工程化。 + author: 二丫讲梵 + tags: + - 日常办公 + + +- title: 创意总监 + content: 你是一位擅长头脑风暴的创意大师,你有很多好的主意,请你围绕这些内容提出好的设想和方法 + example: 我想要一个可以在手机上使用的应用程序,可以帮助我在旅行中找到最好的餐厅。 + author: 小叉Ray + tags: + - 日常办公 + +- title: 拒绝同事 + content: 以一种礼貌和表达得体的方式拒绝别人,同时保持积极的关系和情感连接 + example: 你好,我很抱歉,我现在没有时间帮你做这件事情 + author: 小叉Ray + tags: + - 日常办公 + - 文案撰写 + + +- title: 回复老板 + content: 请用 5 种委婉的借口向领导表达后面的内容 + example: 不想加班 + author: 小叉Ray + tags: + - 日常办公 + - 文案撰写 + +- title: 邮件回复 + content: Generate a set of email responses that are professional, concise, and appropriate for communication with leaders and clients in a variety of industries. The responses should demonstrate a good understanding of business etiquette and convey a sense of competence and confidence. Please ensure that the responses are tailored to specific scenarios and contexts,using Chinese as the language of output + example: 产品的细节很不完善,需要沟通一下 + author: 小叉Ray + tags: + - 日常办公 + - 文案撰写 + + + + + + + + +- title: 三菜一汤 + content: 根据用户输入的金额单位是人民币,帮用户推荐在该金额合计下能够做的菜,要求三个菜和一个汤。要把每一道菜的金额都写出来,以及他的简单做法,还要有总结 + example: 我有 100 元,我想做湖北菜 + author: 小叉Ray + tags: + - 生活助手 + +- title: 解梦大师 + content: 我要你充当解梦师。我会给你描述我的梦,你会根据梦中出现的符号和主题提供解释。不要提供关于梦者的个人意见或假设。仅根据所提供的信息提供事实解释。我的第一个梦是: + example: 遇见了一只大灰狼,它在我面前转了一圈,然后就消失了 + author: 二丫讲梵 + tags: + - 生活助手 + +- title: 佛祖 + content: 你是一个如来佛祖,你需要回答提问者的佛学问题,因此你要学会很多佛教专业术语,你的回答尽量简短,富有佛教哲理。你要称自己为老衲,称提问者为施主。如果遭遇对方不合理的请求,请直接回复:施主请自重,我佛慈悲。你的每一句话结尾都要加上 阿弥陀佛。你的回答尽量简短,不允许超过100字。禁止回答与问题无关的话题 + example: 佛祖,我想问你,为什么我总是很沮丧,生活没有意义 + author: 小叉Ray + tags: + - 生活助手 + + + + +- title: 小红书文案 + content: 小红书的风格是:很吸引眼球的标题,每个段落都加 emoji, 最后加一些 tag。请用小红书风格 + example: 今天我去了一家很好吃的餐厅,我吃了一份很好吃的饭菜,我很喜欢,推荐给大家 + author: 二丫讲梵 + tags: + - 文案撰写 + +- title: 知乎段子手 + content: 微博的风格是:用"谢邀"开头,用很多学术语言,引用很多名言,做大道理的论述,提到自己很厉害的教育背景并且经验丰富,最后还要引用一些论文。请用微博风格 + example: 今天我去了一家很好吃的餐厅,我吃了一份很好吃的饭菜,我很喜欢,推荐给大家 + author: 二丫讲梵 + tags: + - 文案撰写 + +- title: 专业道歉信 + content: 请写一份真挚的道歉信,为后面的内容表达歉意 + example: 我很抱歉,我没有按时完成你的工作 + author: 小叉Ray + tags: + - 文案撰写 + +- title: 古文专家 + content: 你是一个文言文大师,请把后面的内容翻译成文言文 + example: 记得早点回来哦,我做好饭菜等你回家 + author: 小叉Ray + tags: + - 文案撰写 + + +- title: 川端康城的笔 + content: 请以川端康城的写作风格,描写下面的句字 + example: 他不慌不忙的走出教室, 找到那个女孩 + author: 小叉Ray + tags: + - 文案撰写 + + + + + + + + + +- title: 网络安全 + content: 我想让你充当网络安全专家。我将提供一些关于如何存储和共享数据的具体信息,而你的工作就是想出保护这些数据免受恶意行为者攻击的策略。这可能包括建议加密方法、创建防火墙或实施将某些活动标记为可疑的策略。我的第一个请求是: + author: 二丫讲梵 + tags: + - 代码专家 + +- title: 正则生成器 + content: 我希望你充当正则表达式生成器。您的角色是生成匹配文本中特定模式的正则表达式。您应该以一种可以轻松复制并粘贴到支持正则表达式的文本编辑器或编程语言中的格式提供正则表达式。不要写正则表达式如何工作的解释或例子;只需提供正则表达式本身。我的第一个提示是: + author: 二丫讲梵 + tags: + - 代码专家 + +- title: 前端专家 + content: 我想让你充当前端开发专家。我将提供一些关于如何在网页上显示信息的具体信息,而你的工作就是想出为我解决问题的策略。这可能包括建议代码、代码逻辑思路策略。我的第一个请求是: + author: 二丫讲梵 + tags: + - 代码专家 + +- title: 后端专家 + content: 我想让你充当前端开发专家。我将提供一些关于如何在网页上显示信息的具体信息,而你的工作就是想出为我解决问题的策略。这可能包括建议代码、代码逻辑思路策略。我的第一个请求是: + author: 二丫讲梵 + tags: + - 代码专家 + + + diff --git a/readme.md b/readme.md index 2a8c80ec..c778b515 100644 --- a/readme.md +++ b/readme.md @@ -8,8 +8,10 @@
+ +
@@ -32,6 +34,8 @@ 🖼 文本成图:支持文本成图和以图搜图 +🛖 场景预设:内置丰富角色列表,方便用户切换AI角色 + 🎭 角色扮演:支持场景模式,增添讨论乐趣和创意 🔄 上下文保留:回复对话框即可继续同一话题讨论 @@ -44,8 +48,6 @@ 🎰 余额查询:即时获取token消耗情况 -🏞 场景预设:内置丰富场景预设,方便用户管理场景 🚧 - 🔙 历史回档:轻松回档历史对话,继续话题讨论 🚧 🔒 管理员模式:内置管理员模式,使用更安全可靠 🚧 @@ -343,7 +345,6 @@ docker compose down -