fix:修复报错

This commit is contained in:
renzhiyuan 2025-12-10 11:29:54 +08:00
parent 09d98fb8eb
commit e6c142f3a1
15 changed files with 283 additions and 180 deletions

View File

@ -88,7 +88,7 @@ permissionConfig:
permission_url: "http://api.test.user.1688sup.cn:8001/v1/menu/myCodes?systemId=" permission_url: "http://api.test.user.1688sup.cn:8001/v1/menu/myCodes?systemId="
ding_talk_bots: #ding_talk_bots:
public: # public:
client_id: "dingchg59zwwvmuuvldx", # client_id: "dingchg59zwwvmuuvldx",
client_secret: "ZwetAnRiTQobNFVlNrshRagSMAJIFpBAepWkWI7on7Tt_o617KHtTjBLp8fQfplz", # client_secret: "ZwetAnRiTQobNFVlNrshRagSMAJIFpBAepWkWI7on7Tt_o617KHtTjBLp8fQfplz",

View File

@ -2,29 +2,61 @@ package biz
import ( import (
"ai_scheduler/internal/biz/do" "ai_scheduler/internal/biz/do"
"ai_scheduler/internal/data/constants"
"ai_scheduler/internal/data/impl"
"ai_scheduler/internal/data/model"
"ai_scheduler/internal/entitys" "ai_scheduler/internal/entitys"
"ai_scheduler/internal/pkg/mapstructure"
"context" "context"
"fmt"
"github.com/gofiber/fiber/v2/log"
"github.com/open-dingtalk/dingtalk-stream-sdk-go/chatbot" "github.com/open-dingtalk/dingtalk-stream-sdk-go/chatbot"
"xorm.io/builder"
) )
// AiRouterBiz 智能路由服务 // AiRouterBiz 智能路由服务
type DingTalkBotBiz struct { type DingTalkBotBiz struct {
do *do.Do do *do.Do
handle *do.Handle handle *do.Handle
botConfigImpl *impl.BotConfigImpl
replier *chatbot.ChatbotReplier
log log.Logger
} }
// NewDingTalkBotBiz // NewDingTalkBotBiz
func NewDingTalkBotBiz( func NewDingTalkBotBiz(
do *do.Do, do *do.Do,
handle *do.Handle, handle *do.Handle,
botConfigImpl *impl.BotConfigImpl,
) *DingTalkBotBiz { ) *DingTalkBotBiz {
return &DingTalkBotBiz{ return &DingTalkBotBiz{
do: do, do: do,
handle: handle, handle: handle,
botConfigImpl: botConfigImpl,
replier: chatbot.NewChatbotReplier(),
} }
} }
func (d *DingTalkBotBiz) GetDingTalkBotCfgList() (dingBotList []entitys.DingTalkBot, err error) {
botConfig := make([]model.AiBotConfig, 0)
cond := builder.NewCond()
cond = cond.And(builder.Eq{"status": constants.Enable})
cond = cond.And(builder.Eq{"bot_type": constants.BotTypeDingTalk})
err = d.botConfigImpl.GetRangeToMapStruct(&cond, &botConfig)
for _, v := range botConfig {
var config entitys.DingTalkBot
err = mapstructure.Decode(v, &config)
if err != nil {
d.log.Info("初始化“%s”失败:%s", v.BotName, err.Error())
}
dingBotList = append(dingBotList, config)
}
return
}
func (d *DingTalkBotBiz) InitRequire(ctx context.Context, data *chatbot.BotCallbackDataModel) (requireData *entitys.RequireDataDingTalkBot, err error) { func (d *DingTalkBotBiz) InitRequire(ctx context.Context, data *chatbot.BotCallbackDataModel) (requireData *entitys.RequireDataDingTalkBot, err error) {
requireData = &entitys.RequireDataDingTalkBot{ requireData = &entitys.RequireDataDingTalkBot{
Req: data, Req: data,
@ -37,6 +69,74 @@ func (d *DingTalkBotBiz) InitRequire(ctx context.Context, data *chatbot.BotCallb
return return
} }
func (d *DingTalkBotBiz) Recognize(ctx context.Context, rec *entitys.Recognize, ch chan entitys.Response) (match *entitys.Match, err error) { func (d *DingTalkBotBiz) Recognize(ctx context.Context, bot *chatbot.BotCallbackDataModel) (match entitys.Match, err error) {
return d.handle.RecognizeBot(ctx, rec, ch)
return d.handle.Recognize(ctx, nil, &do.WithDingTalkBot{})
}
func (d *DingTalkBotBiz) HandleRes(ctx context.Context, data *chatbot.BotCallbackDataModel, resp entitys.Response) error {
switch resp.Type {
case entitys.ResponseText:
return d.replyText(ctx, data.SessionWebhook, resp.Content)
case entitys.ResponseStream:
return d.replySteam(ctx, data.SessionWebhook, resp.Content)
case entitys.ResponseImg:
return d.replyImg(ctx, data.SessionWebhook, resp.Content)
case entitys.ResponseFile:
return d.replyFile(ctx, data.SessionWebhook, resp.Content)
case entitys.ResponseMarkdown:
return d.replyMarkdown(ctx, data.SessionWebhook, resp.Content)
case entitys.ResponseActionCard:
return d.replyActionCard(ctx, data.SessionWebhook, resp.Content)
default:
return nil
}
}
func (d *DingTalkBotBiz) replySteam(ctx context.Context, SessionWebhook string, content string, arg ...string) error {
msg := content
if len(arg) > 0 {
msg = fmt.Sprintf(content, arg)
}
return d.replier.SimpleReplyText(ctx, SessionWebhook, []byte(msg))
}
func (d *DingTalkBotBiz) replyText(ctx context.Context, SessionWebhook string, content string, arg ...string) error {
msg := content
if len(arg) > 0 {
msg = fmt.Sprintf(content, arg)
}
return d.replier.SimpleReplyText(ctx, SessionWebhook, []byte(msg))
}
func (d *DingTalkBotBiz) replyImg(ctx context.Context, SessionWebhook string, content string, arg ...string) error {
msg := content
if len(arg) > 0 {
msg = fmt.Sprintf(content, arg)
}
return d.replier.SimpleReplyText(ctx, SessionWebhook, []byte(msg))
}
func (d *DingTalkBotBiz) replyFile(ctx context.Context, SessionWebhook string, content string, arg ...string) error {
msg := content
if len(arg) > 0 {
msg = fmt.Sprintf(content, arg)
}
return d.replier.SimpleReplyText(ctx, SessionWebhook, []byte(msg))
}
func (d *DingTalkBotBiz) replyMarkdown(ctx context.Context, SessionWebhook string, content string, arg ...string) error {
msg := content
if len(arg) > 0 {
msg = fmt.Sprintf(content, arg)
}
return d.replier.SimpleReplyText(ctx, SessionWebhook, []byte(msg))
}
func (d *DingTalkBotBiz) replyActionCard(ctx context.Context, SessionWebhook string, content string, arg ...string) error {
msg := content
if len(arg) > 0 {
msg = fmt.Sprintf(content, arg)
}
return d.replier.SimpleReplyText(ctx, SessionWebhook, []byte(msg))
} }

View File

@ -1,6 +1,7 @@
package do package do
import ( import (
"ai_scheduler/internal/biz/handle"
"ai_scheduler/internal/entitys" "ai_scheduler/internal/entitys"
"ai_scheduler/internal/pkg" "ai_scheduler/internal/pkg"
"context" "context"
@ -42,7 +43,7 @@ func (f *WithSys) CreatePrompt(ctx context.Context, rec *entitys.Recognize) (mes
func (f *WithSys) getUserContent(ctx context.Context, rec *entitys.Recognize) (content strings.Builder, err error) { func (f *WithSys) getUserContent(ctx context.Context, rec *entitys.Recognize) (content strings.Builder, err error) {
var hasFile bool var hasFile bool
if len(rec.UserContent.FileUrl) > 0 || rec.UserContent.File != nil { if rec.UserContent.File != nil && (len(rec.UserContent.File.FileUrl) > 0 || rec.UserContent.File.File != nil) {
hasFile = true hasFile = true
} }
content.WriteString(rec.UserContent.Text) content.WriteString(rec.UserContent.Text)
@ -64,7 +65,65 @@ func (f *WithSys) getUserContent(ctx context.Context, rec *entitys.Recognize) (c
if hasFile { if hasFile {
content.WriteString("\n") content.WriteString("\n")
content.WriteString("### 文件内容:\n") content.WriteString("### 文件内容:\n")
hand.WriteString(rec.UserContent.FileUrl, rec.UserContent.FileUrl) handle.HandleRecognizeFile(rec.UserContent.File)
//...do something with file
}
return
}
type WithDingTalkBot struct {
}
func (f *WithDingTalkBot) CreatePrompt(ctx context.Context, rec *entitys.Recognize) (mes []api.Message, err error) {
var (
prompt = make([]api.Message, 0) // 初始化一个空的api.Message切片
)
// 获取用户内容,如果出错则直接返回错误
content, err := f.getUserContent(ctx, rec)
if err != nil {
return nil, err
}
// 构建提示消息列表,包含系统提示、助手回复和用户内容
mes = append(prompt, api.Message{
Role: "system", // 系统角色
Content: rec.SystemPrompt, // 系统提示内容
}, api.Message{
Role: "assistant", // 助手角色
Content: "### 聊天记录:" + pkg.JsonStringIgonErr(rec.ChatHis), // 助手回复内容
}, api.Message{
Role: "user", // 用户角色
Content: content.String(), // 用户输入内容
})
return
}
func (f *WithDingTalkBot) getUserContent(ctx context.Context, rec *entitys.Recognize) (content strings.Builder, err error) {
var hasFile bool
if rec.UserContent.File != nil && (len(rec.UserContent.File.FileUrl) > 0 || rec.UserContent.File.File != nil) {
hasFile = true
}
content.WriteString(rec.UserContent.Text)
if hasFile {
content.WriteString("\n")
}
if len(rec.UserContent.Tag) > 0 {
content.WriteString("\n")
content.WriteString("### 工具必须使用:")
content.WriteString(rec.UserContent.Tag)
}
if len(rec.ChatHis.Messages) > 0 {
content.WriteString("### 引用历史聊天记录:\n")
content.WriteString(pkg.JsonStringIgonErr(rec.ChatHis))
}
if hasFile {
content.WriteString("\n")
content.WriteString("### 文件内容:\n")
handle.HandleRecognizeFile(rec.UserContent.File)
//...do something with file
} }
return return
} }

View File

@ -19,12 +19,7 @@ type Config struct {
DB DB `mapstructure:"db"` DB DB `mapstructure:"db"`
DefaultPrompt SysPrompt `mapstructure:"default_prompt"` DefaultPrompt SysPrompt `mapstructure:"default_prompt"`
PermissionConfig PermissionConfig `mapstructure:"permissionConfig"` PermissionConfig PermissionConfig `mapstructure:"permissionConfig"`
DingTalkBots map[string]*DingTalkBot `mapstructure:"ding_talk_bots"` // DingTalkBots map[string]*DingTalkBot `mapstructure:"ding_talk_bots"`
}
type DingTalkBot struct {
ClientId string `mapstructure:"client_id"`
ClientSecret string `mapstructure:"client_secret"`
} }
type SysPrompt struct { type SysPrompt struct {

View File

@ -25,3 +25,9 @@ var ChatStyleMap = map[ChatStyle]string{
ChatStyleCute: "可爱", ChatStyleCute: "可爱",
ChatStyleAngry: "愤怒", ChatStyleAngry: "愤怒",
} }
type BotType int
const (
BotTypeDingTalk BotType = 1 // 系统的bug/优化建议
)

View File

@ -22,7 +22,7 @@ BaseModel 是一个泛型结构体用于封装GORM数据库通用操作。
// 定义受支持的PO类型集合可根据需要扩展, 只有包含表结构才能使用BaseModel避免使用出现问题 // 定义受支持的PO类型集合可根据需要扩展, 只有包含表结构才能使用BaseModel避免使用出现问题
type PO interface { type PO interface {
model.AiChatHi | model.AiChatHi |
model.AiSy | model.AiSession | model.AiTask | model.AiBot model.AiSy | model.AiSession | model.AiTask | model.AiBotConfig
} }
type BaseModel[P PO] struct { type BaseModel[P PO] struct {

View File

@ -10,6 +10,6 @@ type BotChatHisImpl struct {
dataTemp.DataTemp dataTemp.DataTemp
} }
func NewBotChatHisImpl(db *utils.Db) *TaskImpl { func NewBotChatHisImpl(db *utils.Db) *BotChatHisImpl {
return &TaskImpl{*dataTemp.NewDataTemp(db, new(model.AiBotChatHi))} return &BotChatHisImpl{*dataTemp.NewDataTemp(db, new(model.AiBotChatHi))}
} }

View File

@ -0,0 +1,17 @@
package impl
import (
"ai_scheduler/internal/data/model"
"ai_scheduler/tmpl/dataTemp"
"ai_scheduler/utils"
)
type BotConfigImpl struct {
dataTemp.DataTemp
}
func NewBotConfigImpl(db *utils.Db) *BotConfigImpl {
return &BotConfigImpl{
DataTemp: *dataTemp.NewDataTemp(db, new(model.AiBotConfig)),
}
}

View File

@ -1,28 +0,0 @@
package impl
import (
"ai_scheduler/internal/data/model"
"ai_scheduler/tmpl/dataTemp"
"ai_scheduler/utils"
"gorm.io/gorm"
)
type BotImpl struct {
dataTemp.DataTemp
BaseRepository[model.AiBot]
}
func NewBotImpl(db *utils.Db) *BotImpl {
return &BotImpl{
DataTemp: *dataTemp.NewDataTemp(db, new(model.AiBot)),
BaseRepository: NewBaseModel[model.AiBot](db.Client),
}
}
// WithSysId 系统id
func (s *BotImpl) WithSysId(sysId interface{}) CondFunc {
return func(db *gorm.DB) *gorm.DB {
return db.Where("sys_id = ?", sysId)
}
}

View File

@ -4,4 +4,10 @@ import (
"github.com/google/wire" "github.com/google/wire"
) )
var ProviderImpl = wire.NewSet(NewSessionImpl, NewSysImpl, NewTaskImpl, NewChatHisImpl) var ProviderImpl = wire.NewSet(
NewSessionImpl,
NewSysImpl,
NewTaskImpl,
NewChatHisImpl,
NewBotConfigImpl,
)

View File

@ -8,13 +8,13 @@ import (
"time" "time"
) )
const TableNameAiBot = "ai_bot" const TableNameAiBotConfig = "ai_bot_config"
// AiBot mapped from table <ai_bot> // AiBotConfig mapped from table <ai_bot_config>
type AiBot struct { type AiBotConfig struct {
BotID int32 `gorm:"column:bot_id;primaryKey;autoIncrement:true" json:"bot_id"` BotID int32 `gorm:"column:bot_id;primaryKey;autoIncrement:true" json:"bot_id"`
SysID int32 `gorm:"column:sys_id;not null" json:"sys_id"` SysID int32 `gorm:"column:sys_id;not null" json:"sys_id"`
BotType int32 `gorm:"column:bot_type" json:"bot_type"` BotType int32 `gorm:"column:bot_type;not null;default:1" json:"bot_type"`
BotName string `gorm:"column:bot_name;not null" json:"bot_name"` BotName string `gorm:"column:bot_name;not null" json:"bot_name"`
BotConfig string `gorm:"column:bot_config;not null" json:"bot_config"` BotConfig string `gorm:"column:bot_config;not null" json:"bot_config"`
CreateAt time.Time `gorm:"column:create_at;default:CURRENT_TIMESTAMP" json:"create_at"` CreateAt time.Time `gorm:"column:create_at;default:CURRENT_TIMESTAMP" json:"create_at"`
@ -23,7 +23,7 @@ type AiBot struct {
DeleteAt time.Time `gorm:"column:delete_at" json:"delete_at"` DeleteAt time.Time `gorm:"column:delete_at" json:"delete_at"`
} }
// TableName AiBot's table name // TableName AiBotConfig's table name
func (*AiBot) TableName() string { func (*AiBotConfig) TableName() string {
return TableNameAiBot return TableNameAiBotConfig
} }

View File

@ -22,3 +22,8 @@ type RequireDataDingTalkBot struct {
ImgByte []api.ImageData ImgByte []api.ImageData
ImgUrls []string ImgUrls []string
} }
type DingTalkBot struct {
ClientId string `mapstructure:"client_id"`
ClientSecret string `mapstructure:"client_secret"`
}

View File

@ -1,7 +1,7 @@
package server package server
import ( import (
"ai_scheduler/internal/config" "ai_scheduler/internal/entitys"
"ai_scheduler/internal/services" "ai_scheduler/internal/services"
"context" "context"
@ -11,7 +11,7 @@ import (
) )
type DingBotServiceInterface interface { type DingBotServiceInterface interface {
GetServiceCfg(cfg map[string]*config.DingTalkBot) (*config.DingTalkBot, string) GetServiceCfg() ([]entitys.DingTalkBot, error)
OnChatBotMessageReceived(ctx context.Context, data *chatbot.BotCallbackDataModel) (content []byte, err error) OnChatBotMessageReceived(ctx context.Context, data *chatbot.BotCallbackDataModel) (content []byte, err error)
} }
@ -19,15 +19,19 @@ type DingTalkBotServer struct {
Clients []*client.StreamClient Clients []*client.StreamClient
} }
// NewDingTalkBotServer 批量注册钉钉客户端cli
// 这里支持两种方式一种是完全独立service,一种是直接用现成的service
// 独立的service,在本页的ProvideAllDingBotServices方法进行注册
// 现成的service参考services->dtalk_bot.go
// 具体使用请根据实际业务需求
func NewDingTalkBotServer( func NewDingTalkBotServer(
cfg *config.Config,
services []DingBotServiceInterface, services []DingBotServiceInterface,
) *DingTalkBotServer { ) *DingTalkBotServer {
clients := make([]*client.StreamClient, 0) clients := make([]*client.StreamClient, 0)
for _, service := range services { for _, service := range services {
serviceConf, index := service.GetServiceCfg(cfg.DingTalkBots) serviceConfigs, index := service.GetServiceCfg()
if serviceConf == nil { for _, serviceConf := range serviceConfigs {
log.Info("未找到%s配置", index) if serviceConf.ClientId == "" || serviceConf.ClientSecret == "" {
continue continue
} }
cli := DingBotServerInit(serviceConf.ClientId, serviceConf.ClientSecret, service) cli := DingBotServerInit(serviceConf.ClientId, serviceConf.ClientSecret, service)
@ -37,6 +41,7 @@ func NewDingTalkBotServer(
} }
clients = append(clients, cli) clients = append(clients, cli)
} }
}
return &DingTalkBotServer{ return &DingTalkBotServer{
Clients: clients, Clients: clients,
} }

View File

@ -3,6 +3,7 @@ package services
import ( import (
"ai_scheduler/internal/biz" "ai_scheduler/internal/biz"
"ai_scheduler/internal/config" "ai_scheduler/internal/config"
"ai_scheduler/internal/entitys" "ai_scheduler/internal/entitys"
"context" "context"
"fmt" "fmt"
@ -12,40 +13,38 @@ import (
type DingBotService struct { type DingBotService struct {
config *config.Config config *config.Config
replier *chatbot.ChatbotReplier
env string dingTalkBotBiz *biz.DingTalkBotBiz
DingTalkBotBiz *biz.DingTalkBotBiz
} }
func NewDingBotService(config *config.Config, DingTalkBotBiz *biz.DingTalkBotBiz) *DingBotService { func NewDingBotService(config *config.Config, DingTalkBotBiz *biz.DingTalkBotBiz) *DingBotService {
return &DingBotService{config: config, replier: chatbot.NewChatbotReplier(), env: "public", DingTalkBotBiz: DingTalkBotBiz} return &DingBotService{config: config, dingTalkBotBiz: DingTalkBotBiz}
} }
func (d *DingBotService) GetServiceCfg(cfg map[string]*config.DingTalkBot) (*config.DingTalkBot, string) { func (d *DingBotService) GetServiceCfg() ([]entitys.DingTalkBot, error) {
return cfg[d.env], d.env return d.dingTalkBotBiz.GetDingTalkBotCfgList()
} }
func (d *DingBotService) OnChatBotMessageReceived(ctx context.Context, data *chatbot.BotCallbackDataModel) (content []byte, err error) { func (d *DingBotService) OnChatBotMessageReceived(ctx context.Context, data *chatbot.BotCallbackDataModel) (content []byte, err error) {
requireData, err := d.DingTalkBotBiz.InitRequire(ctx, data) requireData, err := d.dingTalkBotBiz.InitRequire(ctx, data)
if err != nil { if err != nil {
return return
} }
go func() { go func() {
defer close(requireData.Ch) defer close(requireData.Ch)
//if match, _err := d.dingTalkBotBiz.Recognize(ctx, data); _err != nil {
if recognizeErr := d.DingTalkBotBiz.Recognize(ctx, requireData); recognizeErr != nil { // requireData.Ch <- entitys.Response{
requireData.Ch <- entitys.Response{ // Type: entitys.ResponseEnd,
Type: entitys.ResponseEnd, // Content: fmt.Sprintf("处理消息时出错: %s", _err.Error()),
Content: fmt.Sprintf("处理消息时出错: %v", recognizeErr), // }
} //}
} ////向下传递
//向下传递 //if err = d.dingTalkBotBiz.HandleMatch(ctx, nil, requireData); err != nil {
if err = d.handle.HandleMatch(ctx, nil, requireData); err != nil { // requireData.Ch <- entitys.Response{
requireData.Ch <- entitys.Response{ // Type: entitys.ResponseEnd,
Type: entitys.ResponseEnd, // Content: fmt.Sprintf("匹配失败: %v", err),
Content: fmt.Sprintf("匹配失败: %v", err), // }
} //}
}
}() }()
for { for {
select { select {
@ -59,77 +58,10 @@ func (d *DingBotService) OnChatBotMessageReceived(ctx context.Context, data *cha
if resp.Type == entitys.ResponseLog { if resp.Type == entitys.ResponseLog {
return return
} }
if err := d.handleRes(ctx, data, resp); err != nil { if err := d.dingTalkBotBiz.HandleRes(ctx, data, resp); err != nil {
return nil, fmt.Errorf("回复失败: %w", err) return nil, fmt.Errorf("回复失败: %w", err)
} }
} }
} }
return return
} }
func (d *DingBotService) handleRes(ctx context.Context, data *chatbot.BotCallbackDataModel, resp entitys.Response) error {
switch resp.Type {
case entitys.ResponseText:
return d.replyText(ctx, data.SessionWebhook, resp.Content)
case entitys.ResponseStream:
return d.replySteam(ctx, data.SessionWebhook, resp.Content)
case entitys.ResponseImg:
return d.replyImg(ctx, data.SessionWebhook, resp.Content)
case entitys.ResponseFile:
return d.replyFile(ctx, data.SessionWebhook, resp.Content)
case entitys.ResponseMarkdown:
return d.replyMarkdown(ctx, data.SessionWebhook, resp.Content)
case entitys.ResponseActionCard:
return d.replyActionCard(ctx, data.SessionWebhook, resp.Content)
default:
return nil
}
}
func (d *DingBotService) replyText(ctx context.Context, SessionWebhook string, content string, arg ...string) error {
msg := content
if len(arg) > 0 {
msg = fmt.Sprintf(content, arg)
}
return d.replier.SimpleReplyText(ctx, SessionWebhook, []byte(msg))
}
func (d *DingBotService) replySteam(ctx context.Context, SessionWebhook string, content string, arg ...string) error {
msg := content
if len(arg) > 0 {
msg = fmt.Sprintf(content, arg)
}
return d.replier.SimpleReplyText(ctx, SessionWebhook, []byte(msg))
}
func (d *DingBotService) replyImg(ctx context.Context, SessionWebhook string, content string, arg ...string) error {
msg := content
if len(arg) > 0 {
msg = fmt.Sprintf(content, arg)
}
return d.replier.SimpleReplyText(ctx, SessionWebhook, []byte(msg))
}
func (d *DingBotService) replyFile(ctx context.Context, SessionWebhook string, content string, arg ...string) error {
msg := content
if len(arg) > 0 {
msg = fmt.Sprintf(content, arg)
}
return d.replier.SimpleReplyText(ctx, SessionWebhook, []byte(msg))
}
func (d *DingBotService) replyMarkdown(ctx context.Context, SessionWebhook string, content string, arg ...string) error {
msg := content
if len(arg) > 0 {
msg = fmt.Sprintf(content, arg)
}
return d.replier.SimpleReplyText(ctx, SessionWebhook, []byte(msg))
}
func (d *DingBotService) replyActionCard(ctx context.Context, SessionWebhook string, content string, arg ...string) error {
msg := content
if len(arg) > 0 {
msg = fmt.Sprintf(content, arg)
}
return d.replier.SimpleReplyText(ctx, SessionWebhook, []byte(msg))
}

View File

@ -6,4 +6,10 @@ import (
"github.com/google/wire" "github.com/google/wire"
) )
var ProviderSetServices = wire.NewSet(NewChatService, NewSessionService, gateway.NewGateway, NewTaskService, NewCallbackService, NewHistoryService) var ProviderSetServices = wire.NewSet(
NewChatService,
NewSessionService, gateway.NewGateway,
NewTaskService,
NewCallbackService,
NewDingBotService,
NewHistoryService)