dsx137 49ec272f88
feat: afterMsgSaveDB webhook (#3788)
* feat: afterMsgSaveDB webhook

fix(config): add default webhook event filters

fix(core): handle group mentions and webhook enqueue errors

Use group IDs when filtering group-message attention callbacks, and report failures when asynchronous webhook tasks cannot be queued or posted.

feat(webhook): add standalone mock webhook server CLI

test(webhook): add mock server and test suite for all callback commands

feat(msgtransfer): trigger afterMsgSaveDB webhook when message saved to DB

feat(config): add afterMsgSaveDB webhook configuration

feat(callbackstruct): add CallbackAfterMsgSaveDB command and request structs

* refactor(core): simplify asynchronous webhook enqueueing
2026-08-21 19:36:11 +08:00

104 lines
3.4 KiB
Go

// Copyright © 2023 OpenIM. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"context"
"encoding/json"
"flag"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"strings"
"syscall"
"time"
"github.com/gin-gonic/gin"
cbapi "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct"
"github.com/openimsdk/open-im-server/v3/test/webhook"
"github.com/openimsdk/protocol/constant"
)
func main() {
port := flag.Int("port", 10006, "Webhook server listening port")
sensitiveWord := flag.String("mask-word", "xxx", "Keyword to mask in callbackBeforeMsgModifyCommand")
flag.Parse()
gin.SetMode(gin.ReleaseMode)
srv := webhook.NewServer()
// 1. Log every incoming webhook request in real-time
srv.OnRequest(func(command string, reqBody []byte, c *gin.Context) {
log.Printf("[Webhook] %s %s | Command: %s | Body: %s",
c.Request.Method, c.Request.URL.Path, command, string(reqBody))
})
// 2. Example: Custom hook for message content modification (sensitive word filtering)
if *sensitiveWord != "" {
webhook.RegisterCallback(srv, cbapi.CallbackBeforeMsgModifyCommand, func(c *gin.Context, req *cbapi.CallbackMsgModifyCommandReq) (*cbapi.CallbackMsgModifyCommandResp, error) {
var resp cbapi.CallbackMsgModifyCommandResp
if req.ContentType != constant.Text {
return &resp, nil
}
var textElem struct {
Content string `json:"content"`
}
if err := json.Unmarshal([]byte(req.Content), &textElem); err != nil {
return nil, err
}
if strings.Contains(textElem.Content, *sensitiveWord) {
textElem.Content = strings.ReplaceAll(textElem.Content, *sensitiveWord, strings.Repeat("*", len(*sensitiveWord)))
masked, err := json.Marshal(&textElem)
if err != nil {
return nil, err
}
s := string(masked)
resp.Content = &s
log.Printf("[Webhook Mask] Masked content: %s -> %s", req.Content, s)
}
return &resp, nil
})
}
addr := fmt.Sprintf(":%d", *port)
log.Printf("=================================================================")
log.Printf(" OpenIM Mock Webhook Server is listening on %s", addr)
log.Printf(" Callback URL in config/webhooks.yml:")
log.Printf(" url: 'http://127.0.0.1:%d/callbackExample'", *port)
log.Printf(" Supported callbacks: 48 registered OpenIM callback commands")
log.Printf(" Press Ctrl+C to stop")
log.Printf("=================================================================")
go func() {
if err := srv.Start(addr); err != nil && err != http.ErrServerClosed {
log.Fatalf("Server listen error: %v", err)
}
}()
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Println("Shutting down Webhook Server...")
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Fatalf("Server forced to shutdown: %v", err)
}
log.Println("Webhook Server exited cleanly.")
}