复制项目
This commit is contained in:
177
internal/api/bot/bot.go
Normal file
177
internal/api/bot/bot.go
Normal file
@@ -0,0 +1,177 @@
|
||||
package bot
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"git.imall.cloud/openim/chat/internal/api/util"
|
||||
"git.imall.cloud/openim/chat/pkg/botstruct"
|
||||
"git.imall.cloud/openim/chat/pkg/common/imwebhook"
|
||||
"git.imall.cloud/openim/chat/pkg/protocol/bot"
|
||||
"git.imall.cloud/openim/protocol/constant"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/openimsdk/tools/a2r"
|
||||
"github.com/openimsdk/tools/apiresp"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
func New(botClient bot.BotClient, api *util.Api) *Api {
|
||||
return &Api{
|
||||
Api: api,
|
||||
botClient: botClient,
|
||||
}
|
||||
}
|
||||
|
||||
type Api struct {
|
||||
*util.Api
|
||||
botClient bot.BotClient
|
||||
}
|
||||
|
||||
func (o *Api) CreateAgent(c *gin.Context) {
|
||||
a2r.Call(c, bot.BotClient.CreateAgent, o.botClient)
|
||||
}
|
||||
|
||||
func (o *Api) DeleteAgent(c *gin.Context) {
|
||||
a2r.Call(c, bot.BotClient.DeleteAgent, o.botClient)
|
||||
}
|
||||
|
||||
func (o *Api) UpdateAgent(c *gin.Context) {
|
||||
a2r.Call(c, bot.BotClient.UpdateAgent, o.botClient)
|
||||
}
|
||||
|
||||
func (o *Api) PageFindAgent(c *gin.Context) {
|
||||
a2r.Call(c, bot.BotClient.PageFindAgent, o.botClient)
|
||||
}
|
||||
|
||||
func (o *Api) AfterSendSingleMsg(c *gin.Context) {
|
||||
var (
|
||||
req = imwebhook.CallbackAfterSendSingleMsgReq{}
|
||||
)
|
||||
|
||||
if err := c.BindJSON(&req); err != nil {
|
||||
apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap())
|
||||
return
|
||||
}
|
||||
if req.ContentType != constant.Text {
|
||||
apiresp.GinSuccess(c, nil)
|
||||
return
|
||||
}
|
||||
isAgent := botstruct.IsAgentUserID(req.RecvID)
|
||||
if !isAgent {
|
||||
apiresp.GinSuccess(c, nil)
|
||||
return
|
||||
}
|
||||
|
||||
var elem botstruct.TextElem
|
||||
err := json.Unmarshal([]byte(req.Content), &elem)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, errs.ErrArgs.WrapMsg("json unmarshal error: "+err.Error()))
|
||||
return
|
||||
}
|
||||
convID := getConversationIDByMsg(req.SessionType, req.SendID, req.RecvID, "")
|
||||
|
||||
key, ok := c.GetQuery(botstruct.Key)
|
||||
if !ok {
|
||||
apiresp.GinError(c, errs.ErrArgs.WithDetail("missing key in query").Wrap())
|
||||
return
|
||||
}
|
||||
res, err := o.botClient.SendBotMessage(c, &bot.SendBotMessageReq{
|
||||
AgentID: req.RecvID,
|
||||
ConversationID: convID,
|
||||
ContentType: req.ContentType,
|
||||
Content: elem.Content,
|
||||
Ex: req.Ex,
|
||||
Key: key,
|
||||
})
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
apiresp.GinSuccess(c, res)
|
||||
}
|
||||
|
||||
func (o *Api) AfterSendGroupMsg(c *gin.Context) {
|
||||
var (
|
||||
req = imwebhook.CallbackAfterSendGroupMsgReq{}
|
||||
)
|
||||
if err := c.BindJSON(&req); err != nil {
|
||||
apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap())
|
||||
return
|
||||
}
|
||||
|
||||
if req.ContentType != constant.AtText {
|
||||
apiresp.GinSuccess(c, nil)
|
||||
}
|
||||
key, ok := c.GetQuery(botstruct.Key)
|
||||
if !ok {
|
||||
apiresp.GinError(c, errs.ErrArgs.WithDetail("missing key in query").Wrap())
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
elem botstruct.AtElem
|
||||
reqs []*bot.SendBotMessageReq
|
||||
)
|
||||
|
||||
convID := getConversationIDByMsg(req.SessionType, req.SendID, "", req.GroupID)
|
||||
err := json.Unmarshal([]byte(req.Content), &elem)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, errs.ErrArgs.WrapMsg("json unmarshal error: "+err.Error()))
|
||||
}
|
||||
for _, userID := range elem.AtUserList {
|
||||
if botstruct.IsAgentUserID(userID) {
|
||||
reqs = append(reqs, &bot.SendBotMessageReq{
|
||||
AgentID: userID,
|
||||
ConversationID: convID,
|
||||
ContentType: req.ContentType,
|
||||
Content: elem.Text,
|
||||
Ex: req.Ex,
|
||||
Key: key,
|
||||
})
|
||||
}
|
||||
}
|
||||
if len(reqs) == 0 {
|
||||
apiresp.GinSuccess(c, nil)
|
||||
}
|
||||
|
||||
g := errgroup.Group{}
|
||||
g.SetLimit(min(len(reqs), 5))
|
||||
for i := 0; i < len(reqs); i++ {
|
||||
i := i
|
||||
g.Go(func() error {
|
||||
_, err := o.botClient.SendBotMessage(c, reqs[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
err = g.Wait()
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
apiresp.GinSuccess(c, nil)
|
||||
}
|
||||
|
||||
func getConversationIDByMsg(sessionType int32, sendID, recvID, groupID string) string {
|
||||
switch sessionType {
|
||||
case constant.SingleChatType:
|
||||
l := []string{sendID, recvID}
|
||||
sort.Strings(l)
|
||||
return "si_" + strings.Join(l, "_") // single chat
|
||||
case constant.WriteGroupChatType:
|
||||
return "g_" + groupID // group chat
|
||||
case constant.ReadGroupChatType:
|
||||
return "sg_" + groupID // super group chat
|
||||
case constant.NotificationChatType:
|
||||
l := []string{sendID, recvID}
|
||||
sort.Strings(l)
|
||||
return "sn_" + strings.Join(l, "_")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
139
internal/api/bot/start.go
Normal file
139
internal/api/bot/start.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package bot
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
chatmw "git.imall.cloud/openim/chat/internal/api/mw"
|
||||
"git.imall.cloud/openim/chat/internal/api/util"
|
||||
"git.imall.cloud/openim/chat/pkg/common/config"
|
||||
"git.imall.cloud/openim/chat/pkg/common/kdisc"
|
||||
disetcd "git.imall.cloud/openim/chat/pkg/common/kdisc/etcd"
|
||||
adminclient "git.imall.cloud/openim/chat/pkg/protocol/admin"
|
||||
botclient "git.imall.cloud/openim/chat/pkg/protocol/bot"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/openimsdk/tools/discovery/etcd"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/mw"
|
||||
"github.com/openimsdk/tools/system/program"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
"github.com/openimsdk/tools/utils/runtimeenv"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
ApiConfig config.APIBot
|
||||
Discovery config.Discovery
|
||||
Share config.Share
|
||||
Redis config.Redis
|
||||
|
||||
RuntimeEnv string
|
||||
}
|
||||
|
||||
func Start(ctx context.Context, index int, cfg *Config) error {
|
||||
cfg.RuntimeEnv = runtimeenv.PrintRuntimeEnvironment()
|
||||
apiPort, err := datautil.GetElemByIndex(cfg.ApiConfig.Api.Ports, index)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client, err := kdisc.NewDiscoveryRegister(&cfg.Discovery, cfg.RuntimeEnv, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
botConn, err := client.GetConn(ctx, cfg.Discovery.RpcService.Bot, grpc.WithTransportCredentials(insecure.NewCredentials()), mw.GrpcClient())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
adminConn, err := client.GetConn(ctx, cfg.Discovery.RpcService.Admin, grpc.WithTransportCredentials(insecure.NewCredentials()), mw.GrpcClient())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
adminClient := adminclient.NewAdminClient(adminConn)
|
||||
botClient := botclient.NewBotClient(botConn)
|
||||
base := util.Api{
|
||||
ImUserID: cfg.Share.OpenIM.AdminUserID,
|
||||
ProxyHeader: cfg.Share.ProxyHeader,
|
||||
ChatAdminUserID: cfg.Share.ChatAdmin[0],
|
||||
}
|
||||
botApi := New(botClient, &base)
|
||||
mwApi := chatmw.New(adminClient)
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
engine := gin.New()
|
||||
engine.Use(gin.Recovery(), mw.CorsHandler(), mw.GinParseOperationID(), func(c *gin.Context) {
|
||||
// 确保 operationID 被正确设置到 context 中
|
||||
operationID := c.GetHeader("operationid")
|
||||
if operationID != "" {
|
||||
c.Set("operationID", operationID)
|
||||
}
|
||||
c.Next()
|
||||
})
|
||||
SetBotRoute(engine, botApi, mwApi)
|
||||
|
||||
var (
|
||||
netDone = make(chan struct{}, 1)
|
||||
netErr error
|
||||
)
|
||||
server := http.Server{Addr: fmt.Sprintf(":%d", apiPort), Handler: engine}
|
||||
go func() {
|
||||
err = server.ListenAndServe()
|
||||
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
netErr = errs.WrapMsg(err, fmt.Sprintf("api start err: %s", server.Addr))
|
||||
netDone <- struct{}{}
|
||||
}
|
||||
}()
|
||||
if cfg.Discovery.Enable == kdisc.ETCDCONST {
|
||||
cm := disetcd.NewConfigManager(client.(*etcd.SvcDiscoveryRegistryImpl).GetClient(),
|
||||
[]string{
|
||||
config.ChatAPIBotCfgFileName,
|
||||
config.DiscoveryConfigFileName,
|
||||
config.ShareFileName,
|
||||
config.LogConfigFileName,
|
||||
},
|
||||
)
|
||||
cm.Watch(ctx)
|
||||
}
|
||||
shutdown := func() error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer cancel()
|
||||
err := server.Shutdown(ctx)
|
||||
if err != nil {
|
||||
return errs.WrapMsg(err, "shutdown err")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
disetcd.RegisterShutDown(shutdown)
|
||||
|
||||
sigs := make(chan os.Signal, 1)
|
||||
signal.Notify(sigs, syscall.SIGTERM)
|
||||
select {
|
||||
case <-sigs:
|
||||
program.SIGTERMExit()
|
||||
if err := shutdown(); err != nil {
|
||||
return err
|
||||
}
|
||||
case <-netDone:
|
||||
close(netDone)
|
||||
return netErr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func SetBotRoute(router gin.IRouter, bot *Api, mw *chatmw.MW) {
|
||||
account := router.Group("/agent")
|
||||
account.POST("/create", mw.CheckAdmin, bot.CreateAgent)
|
||||
account.POST("/delete", mw.CheckAdmin, bot.DeleteAgent)
|
||||
account.POST("/update", mw.CheckAdmin, bot.UpdateAgent)
|
||||
account.POST("/page", mw.CheckToken, bot.PageFindAgent)
|
||||
|
||||
imwebhook := router.Group("/im_callback")
|
||||
imwebhook.POST("/callbackAfterSendSingleMsgCommand", bot.AfterSendSingleMsg)
|
||||
imwebhook.POST("/callbackAfterSendGroupMsgCommand", bot.AfterSendGroupMsg)
|
||||
}
|
||||
Reference in New Issue
Block a user