举报垃圾消息

pull/3727/head
hawklin2017 2 months ago
parent a3bda263a7
commit 7e65b21c5e

@ -379,3 +379,15 @@ func (m *MessageApi) GetStreamMsg(c *gin.Context) {
func (m *MessageApi) AppendStreamMsg(c *gin.Context) {
a2r.Call(c, msg.MsgClient.GetServerTime, m.Client)
}
func (m *MessageApi) ReportSpam(c *gin.Context) {
a2r.Call(c, msg.MsgClient.ReportSpam, m.Client)
}
func (m *MessageApi) GetSpamReports(c *gin.Context) {
a2r.Call(c, msg.MsgClient.GetSpamReports, m.Client)
}
func (m *MessageApi) HandleSpamReport(c *gin.Context) {
a2r.Call(c, msg.MsgClient.HandleSpamReport, m.Client)
}

@ -288,6 +288,9 @@ func newGinRouter(ctx context.Context, client discovery.SvcDiscoveryRegistry, co
msgGroup.POST("/batch_send_msg", m.BatchSendMsg)
msgGroup.POST("/check_msg_is_send_success", m.CheckMsgIsSendSuccess)
msgGroup.POST("/get_server_time", m.GetServerTime)
msgGroup.POST("/report_spam", m.ReportSpam)
msgGroup.POST("/get_spam_reports", m.GetSpamReports)
msgGroup.POST("/handle_spam_report", m.HandleSpamReport)
}
// Conversation
{

@ -0,0 +1,143 @@
// Copyright © 2024 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 msg
import (
"context"
"crypto/rand"
"time"
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
"github.com/openimsdk/protocol/msg"
"github.com/openimsdk/tools/errs"
"github.com/openimsdk/tools/mcontext"
"github.com/openimsdk/tools/utils/datautil"
)
func genReportID() string {
const dataLen = 12
data := make([]byte, dataLen)
rand.Read(data)
chars := []byte("0123456789abcdefghijklmnopqrstuvwxyz")
for i := 0; i < len(data); i++ {
data[i] = chars[data[i]%byte(len(chars))]
}
return string(data)
}
func (m *msgServer) ReportSpam(ctx context.Context, req *msg.ReportSpamReq) (*msg.ReportSpamResp, error) {
if req.ReportedUserID == "" {
return nil, errs.ErrArgs.WrapMsg("reportedUserID is required")
}
if req.ReasonType <= 0 {
return nil, errs.ErrArgs.WrapMsg("reasonType must be positive")
}
reporterUserID := mcontext.GetOpUserID(ctx)
report := &model.SpamReport{
ReporterUserID: reporterUserID,
ReportedUserID: req.ReportedUserID,
ConversationID: req.ConversationID,
ClientMsgID: req.ClientMsgID,
Seq: req.Seq,
ReasonType: req.ReasonType,
Reason: req.Reason,
Status: model.SpamReportStatusPending,
CreateTime: time.Now(),
Ex: req.Ex,
}
// Generate a unique reportID.
for i := 0; i < 20; i++ {
id := genReportID()
existing, err := m.spamReportDB.Get(ctx, id)
if err == nil && existing != nil {
continue
}
report.ReportID = id
break
}
if report.ReportID == "" {
return nil, errs.ErrInternalServer.WrapMsg("failed to generate report ID")
}
if err := m.spamReportDB.Create(ctx, report); err != nil {
return nil, err
}
return &msg.ReportSpamResp{ReportID: report.ReportID}, nil
}
func (m *msgServer) GetSpamReports(ctx context.Context, req *msg.GetSpamReportsReq) (*msg.GetSpamReportsResp, error) {
if err := authverify.CheckAdmin(ctx, m.config.Share.IMAdminUserID); err != nil {
return nil, err
}
var start, end time.Time
if req.StartTime > 0 {
start = time.UnixMilli(req.StartTime)
}
if req.EndTime > 0 {
end = time.UnixMilli(req.EndTime)
}
total, reports, err := m.spamReportDB.Find(ctx, req.Status, req.ReportedUserID, req.ReporterUserID,
start, end, req.Pagination)
if err != nil {
return nil, err
}
pbReports := datautil.Slice(reports, func(r *model.SpamReport) *msg.SpamReportInfo {
return &msg.SpamReportInfo{
ReportID: r.ReportID,
ReporterUserID: r.ReporterUserID,
ReportedUserID: r.ReportedUserID,
ConversationID: r.ConversationID,
ClientMsgID: r.ClientMsgID,
Seq: r.Seq,
ReasonType: r.ReasonType,
Reason: r.Reason,
Status: r.Status,
CreateTime: r.CreateTime.UnixMilli(),
HandleTime: r.HandleTime.UnixMilli(),
HandlerUserID: r.HandlerUserID,
Ex: r.Ex,
}
})
return &msg.GetSpamReportsResp{
Reports: pbReports,
Total: uint32(total),
}, nil
}
func (m *msgServer) HandleSpamReport(ctx context.Context, req *msg.HandleSpamReportReq) (*msg.HandleSpamReportResp, error) {
if err := authverify.CheckAdmin(ctx, m.config.Share.IMAdminUserID); err != nil {
return nil, err
}
if req.ReportID == "" {
return nil, errs.ErrArgs.WrapMsg("reportID is required")
}
if req.Status != model.SpamReportStatusHandled && req.Status != model.SpamReportStatusIgnored {
return nil, errs.ErrArgs.WrapMsg("status must be 1 (handled) or 2 (ignored)")
}
handlerUserID := mcontext.GetOpUserID(ctx)
if err := m.spamReportDB.UpdateStatus(ctx, req.ReportID, req.Status, handlerUserID, time.Now()); err != nil {
return nil, err
}
return &msg.HandleSpamReportResp{}, nil
}

@ -22,6 +22,7 @@ import (
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/cache/redis"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database/mgo"
"github.com/openimsdk/open-im-server/v3/pkg/common/webhook"
"github.com/openimsdk/protocol/sdkws"
@ -69,6 +70,7 @@ type msgServer struct {
config *Config // Global configuration settings.
webhookClient *webhook.Client
conversationClient *rpcli.ConversationClient
spamReportDB database.SpamReport
}
func (m *msgServer) addInterceptorHandler(interceptorFunc ...MessageInterceptorFunc) {
@ -121,6 +123,10 @@ func Start(ctx context.Context, config *Config, client discovery.SvcDiscoveryReg
return err
}
conversationClient := rpcli.NewConversationClient(conversationConn)
spamReportDB, err := mgo.NewSpamReportMongo(mgocli.GetDB())
if err != nil {
return err
}
s := &msgServer{
MsgDatabase: msgDatabase,
RegisterCenter: client,
@ -131,6 +137,7 @@ func Start(ctx context.Context, config *Config, client discovery.SvcDiscoveryReg
config: config,
webhookClient: webhook.NewWebhookClient(config.WebhooksConfig.URL),
conversationClient: conversationClient,
spamReportDB: spamReportDB,
}
s.notificationSender = notification.NewNotificationSender(&config.NotificationConfig, notification.WithLocalSendMsg(s.SendMsg))

@ -0,0 +1,110 @@
// Copyright © 2024 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 mgo
import (
"context"
"time"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
"github.com/openimsdk/tools/db/mongoutil"
"github.com/openimsdk/tools/db/pagination"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
func NewSpamReportMongo(db *mongo.Database) (database.SpamReport, error) {
coll := db.Collection(database.SpamReportName)
_, err := coll.Indexes().CreateMany(context.Background(), []mongo.IndexModel{
{
Keys: bson.D{{Key: "report_id", Value: 1}},
Options: options.Index().SetUnique(true),
},
{
Keys: bson.D{
{Key: "reporter_user_id", Value: 1},
{Key: "create_time", Value: -1},
},
},
{
Keys: bson.D{
{Key: "reported_user_id", Value: 1},
{Key: "create_time", Value: -1},
},
},
{
Keys: bson.D{
{Key: "status", Value: 1},
{Key: "create_time", Value: -1},
},
},
})
if err != nil {
return nil, err
}
return &SpamReportMgo{coll: coll}, nil
}
type SpamReportMgo struct {
coll *mongo.Collection
}
func (s *SpamReportMgo) Create(ctx context.Context, report *model.SpamReport) error {
return mongoutil.InsertOne(ctx, s.coll, report)
}
func (s *SpamReportMgo) Find(ctx context.Context, status int32, reportedUserID, reporterUserID string,
start, end time.Time, pagination pagination.Pagination) (int64, []*model.SpamReport, error) {
filter := bson.M{}
if status >= 0 {
filter["status"] = status
}
if reportedUserID != "" {
filter["reported_user_id"] = reportedUserID
}
if reporterUserID != "" {
filter["reporter_user_id"] = reporterUserID
}
if !start.IsZero() || !end.IsZero() {
timeFilter := bson.M{}
if !start.IsZero() {
timeFilter["$gte"] = start
}
if !end.IsZero() {
timeFilter["$lte"] = end
}
filter["create_time"] = timeFilter
}
return mongoutil.FindPage[*model.SpamReport](ctx, s.coll, filter, pagination,
options.Find().SetSort(bson.D{{Key: "create_time", Value: -1}}))
}
func (s *SpamReportMgo) UpdateStatus(ctx context.Context, reportID string, status int32, handlerUserID string, handleTime time.Time) error {
return mongoutil.UpdateOne(ctx, s.coll,
bson.M{"report_id": reportID},
bson.M{"$set": bson.M{
"status": status,
"handler_user_id": handlerUserID,
"handle_time": handleTime,
}},
false,
)
}
func (s *SpamReportMgo) Get(ctx context.Context, reportID string) (*model.SpamReport, error) {
return mongoutil.FindOne[*model.SpamReport](ctx, s.coll, bson.M{"report_id": reportID})
}

@ -21,4 +21,5 @@ const (
PhoneSNInfoName = "phone_sn_info"
SignalInvitationName = "signal_invitation"
SignalRecordName = "signal_record"
SpamReportName = "spam_report"
)

@ -0,0 +1,35 @@
// Copyright © 2024 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 database
import (
"context"
"time"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
"github.com/openimsdk/tools/db/pagination"
)
type SpamReport interface {
// Create inserts a new spam report record.
Create(ctx context.Context, report *model.SpamReport) error
// Find queries spam reports with optional filters, returns total count and records.
Find(ctx context.Context, status int32, reportedUserID, reporterUserID string,
start, end time.Time, pagination pagination.Pagination) (int64, []*model.SpamReport, error)
// UpdateStatus updates the handling status of a spam report.
UpdateStatus(ctx context.Context, reportID string, status int32, handlerUserID string, handleTime time.Time) error
// Get retrieves a single spam report by its reportID.
Get(ctx context.Context, reportID string) (*model.SpamReport, error)
}

@ -0,0 +1,53 @@
// Copyright © 2024 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 model
import (
"time"
"go.mongodb.org/mongo-driver/bson/primitive"
)
// SpamReport status constants.
const (
SpamReportStatusPending int32 = 0 // 待处理
SpamReportStatusHandled int32 = 1 // 已处理
SpamReportStatusIgnored int32 = 2 // 已忽略
)
// SpamReport reason type constants.
const (
SpamReasonTypeSpam int32 = 1 // 垃圾消息
SpamReasonTypePorn int32 = 2 // 色情内容
SpamReasonTypeIllegal int32 = 3 // 违法内容
SpamReasonTypeOther int32 = 4 // 其他
)
type SpamReport struct {
ID primitive.ObjectID `bson:"_id"`
ReportID string `bson:"report_id"`
ReporterUserID string `bson:"reporter_user_id"`
ReportedUserID string `bson:"reported_user_id"`
ConversationID string `bson:"conversation_id"` // 举报具体消息时填写
ClientMsgID string `bson:"client_msg_id"` // 举报具体消息时填写
Seq int64 `bson:"seq"`
ReasonType int32 `bson:"reason_type"` // 1垃圾 2色情 3违法 4其他
Reason string `bson:"reason"`
Status int32 `bson:"status"` // 0待处理 1已处理 2已忽略
CreateTime time.Time `bson:"create_time"`
HandleTime time.Time `bson:"handle_time"`
HandlerUserID string `bson:"handler_user_id"`
Ex string `bson:"ex"`
}
Loading…
Cancel
Save