v0.0.9 Интегрирован сервис модерации контента при создании поста
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
madipo2611 2025-07-22 08:28:11 +03:00
parent 82d7866796
commit 1a3267a4cc
7 changed files with 681 additions and 0 deletions

4
go.mod
View File

@ -14,6 +14,7 @@ require (
github.com/prometheus/client_golang v1.22.0 github.com/prometheus/client_golang v1.22.0
github.com/vektah/gqlparser/v2 v2.5.25 github.com/vektah/gqlparser/v2 v2.5.25
golang.org/x/crypto v0.37.0 golang.org/x/crypto v0.37.0
google.golang.org/grpc v1.73.0
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
) )
@ -30,7 +31,10 @@ require (
github.com/prometheus/common v0.62.0 // indirect github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect github.com/prometheus/procfs v0.15.1 // indirect
github.com/sosodev/duration v1.3.1 // indirect github.com/sosodev/duration v1.3.1 // indirect
golang.org/x/net v0.39.0 // indirect
golang.org/x/sys v0.32.0 // indirect golang.org/x/sys v0.32.0 // indirect
golang.org/x/text v0.24.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect
google.golang.org/protobuf v1.36.6 // indirect google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
) )

24
go.sum
View File

@ -25,10 +25,16 @@ github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54 h1:SG7nF6SRlWhcT7c
github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8= github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8=
github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
@ -70,12 +76,30 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/vektah/gqlparser/v2 v2.5.25 h1:FmWtFEa+invTIzWlWK6Vk7BVEZU/97QBzeI8Z1JjGt8= github.com/vektah/gqlparser/v2 v2.5.25 h1:FmWtFEa+invTIzWlWK6Vk7BVEZU/97QBzeI8Z1JjGt8=
github.com/vektah/gqlparser/v2 v2.5.25/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo= github.com/vektah/gqlparser/v2 v2.5.25/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 h1:e0AIkUUhxyBKh6ssZNrAMeqhA7RKUj42346d1y02i2g=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=

View File

@ -14,6 +14,7 @@ import (
"tailly_back_v2/internal/domain" "tailly_back_v2/internal/domain"
"tailly_back_v2/internal/repository" "tailly_back_v2/internal/repository"
"tailly_back_v2/internal/utils" "tailly_back_v2/internal/utils"
"tailly_back_v2/pkg/moderation"
"time" "time"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
@ -80,6 +81,22 @@ func (s *postService) Create(ctx context.Context, authorID int, title, content s
return nil, errors.New("image too large (max 10MB)") return nil, errors.New("image too large (max 10MB)")
} }
// Создаем клиент модерации
modClient, err := moderation.NewModerationClient("localhost:50051")
if err != nil {
return nil, fmt.Errorf("failed to create moderation client: %v", err)
}
defer modClient.Close()
// Проверяем изображение
allowed, err := modClient.CheckImage(ctx, imgBase64)
if err != nil {
return nil, fmt.Errorf("image moderation failed: %v", err)
}
if !allowed {
return nil, errors.New("image rejected by moderation service")
}
randName := utils.GenerateId() + ".jpg" randName := utils.GenerateId() + ".jpg"
buf := new(bytes.Buffer) buf := new(bytes.Buffer)

View File

@ -0,0 +1,40 @@
package moderation
import (
"context"
"google.golang.org/grpc"
pb "tailly_back_v2/pkg/moderation/proto"
)
type ModerationClient struct {
conn *grpc.ClientConn
client pb.ModerationServiceClient
}
func NewModerationClient(addr string) (*ModerationClient, error) {
conn, err := grpc.Dial(addr, grpc.WithInsecure())
if err != nil {
return nil, err
}
return &ModerationClient{
conn: conn,
client: pb.NewModerationServiceClient(conn),
}, nil
}
func (c *ModerationClient) CheckImage(ctx context.Context, imageData []byte) (bool, error) {
resp, err := c.client.CheckImage(ctx, &pb.ImageRequest{
ImageData: imageData,
})
if err != nil {
return false, err
}
return resp.OverallDecision == "allowed", nil
}
func (c *ModerationClient) Close() {
c.conn.Close()
}

View File

@ -0,0 +1,400 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.6
// protoc v3.21.12
// source: moderation.proto
package __
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type ImageRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
ImageData []byte `protobuf:"bytes,1,opt,name=image_data,json=imageData,proto3" json:"image_data,omitempty"`
ImageUrl string `protobuf:"bytes,2,opt,name=image_url,json=imageUrl,proto3" json:"image_url,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ImageRequest) Reset() {
*x = ImageRequest{}
mi := &file_moderation_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ImageRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ImageRequest) ProtoMessage() {}
func (x *ImageRequest) ProtoReflect() protoreflect.Message {
mi := &file_moderation_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ImageRequest.ProtoReflect.Descriptor instead.
func (*ImageRequest) Descriptor() ([]byte, []int) {
return file_moderation_proto_rawDescGZIP(), []int{0}
}
func (x *ImageRequest) GetImageData() []byte {
if x != nil {
return x.ImageData
}
return nil
}
func (x *ImageRequest) GetImageUrl() string {
if x != nil {
return x.ImageUrl
}
return ""
}
type VideoRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
VideoUrl string `protobuf:"bytes,1,opt,name=video_url,json=videoUrl,proto3" json:"video_url,omitempty"`
FrameSampleRate int32 `protobuf:"varint,2,opt,name=frame_sample_rate,json=frameSampleRate,proto3" json:"frame_sample_rate,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *VideoRequest) Reset() {
*x = VideoRequest{}
mi := &file_moderation_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *VideoRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*VideoRequest) ProtoMessage() {}
func (x *VideoRequest) ProtoReflect() protoreflect.Message {
mi := &file_moderation_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use VideoRequest.ProtoReflect.Descriptor instead.
func (*VideoRequest) Descriptor() ([]byte, []int) {
return file_moderation_proto_rawDescGZIP(), []int{1}
}
func (x *VideoRequest) GetVideoUrl() string {
if x != nil {
return x.VideoUrl
}
return ""
}
func (x *VideoRequest) GetFrameSampleRate() int32 {
if x != nil {
return x.FrameSampleRate
}
return 0
}
type ModerationResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
OverallDecision string `protobuf:"bytes,1,opt,name=overall_decision,json=overallDecision,proto3" json:"overall_decision,omitempty"`
Predictions []*Prediction `protobuf:"bytes,2,rep,name=predictions,proto3" json:"predictions,omitempty"`
Message string `protobuf:"bytes,3,opt,name=message,proto3" json:"message,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ModerationResponse) Reset() {
*x = ModerationResponse{}
mi := &file_moderation_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ModerationResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ModerationResponse) ProtoMessage() {}
func (x *ModerationResponse) ProtoReflect() protoreflect.Message {
mi := &file_moderation_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ModerationResponse.ProtoReflect.Descriptor instead.
func (*ModerationResponse) Descriptor() ([]byte, []int) {
return file_moderation_proto_rawDescGZIP(), []int{2}
}
func (x *ModerationResponse) GetOverallDecision() string {
if x != nil {
return x.OverallDecision
}
return ""
}
func (x *ModerationResponse) GetPredictions() []*Prediction {
if x != nil {
return x.Predictions
}
return nil
}
func (x *ModerationResponse) GetMessage() string {
if x != nil {
return x.Message
}
return ""
}
type Prediction struct {
state protoimpl.MessageState `protogen:"open.v1"`
Category string `protobuf:"bytes,1,opt,name=category,proto3" json:"category,omitempty"`
Score float32 `protobuf:"fixed32,2,opt,name=score,proto3" json:"score,omitempty"`
Decision string `protobuf:"bytes,3,opt,name=decision,proto3" json:"decision,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Prediction) Reset() {
*x = Prediction{}
mi := &file_moderation_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *Prediction) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Prediction) ProtoMessage() {}
func (x *Prediction) ProtoReflect() protoreflect.Message {
mi := &file_moderation_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Prediction.ProtoReflect.Descriptor instead.
func (*Prediction) Descriptor() ([]byte, []int) {
return file_moderation_proto_rawDescGZIP(), []int{3}
}
func (x *Prediction) GetCategory() string {
if x != nil {
return x.Category
}
return ""
}
func (x *Prediction) GetScore() float32 {
if x != nil {
return x.Score
}
return 0
}
func (x *Prediction) GetDecision() string {
if x != nil {
return x.Decision
}
return ""
}
type ModerationSettings struct {
state protoimpl.MessageState `protogen:"open.v1"`
CategoryThresholds map[string]float32 `protobuf:"bytes,1,rep,name=category_thresholds,json=categoryThresholds,proto3" json:"category_thresholds,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"fixed32,2,opt,name=value"`
AlwaysBlockCategories []string `protobuf:"bytes,2,rep,name=always_block_categories,json=alwaysBlockCategories,proto3" json:"always_block_categories,omitempty"`
AlwaysAllowCategories []string `protobuf:"bytes,3,rep,name=always_allow_categories,json=alwaysAllowCategories,proto3" json:"always_allow_categories,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ModerationSettings) Reset() {
*x = ModerationSettings{}
mi := &file_moderation_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ModerationSettings) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ModerationSettings) ProtoMessage() {}
func (x *ModerationSettings) ProtoReflect() protoreflect.Message {
mi := &file_moderation_proto_msgTypes[4]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ModerationSettings.ProtoReflect.Descriptor instead.
func (*ModerationSettings) Descriptor() ([]byte, []int) {
return file_moderation_proto_rawDescGZIP(), []int{4}
}
func (x *ModerationSettings) GetCategoryThresholds() map[string]float32 {
if x != nil {
return x.CategoryThresholds
}
return nil
}
func (x *ModerationSettings) GetAlwaysBlockCategories() []string {
if x != nil {
return x.AlwaysBlockCategories
}
return nil
}
func (x *ModerationSettings) GetAlwaysAllowCategories() []string {
if x != nil {
return x.AlwaysAllowCategories
}
return nil
}
var File_moderation_proto protoreflect.FileDescriptor
const file_moderation_proto_rawDesc = "" +
"\n" +
"\x10moderation.proto\x12\n" +
"moderation\"J\n" +
"\fImageRequest\x12\x1d\n" +
"\n" +
"image_data\x18\x01 \x01(\fR\timageData\x12\x1b\n" +
"\timage_url\x18\x02 \x01(\tR\bimageUrl\"W\n" +
"\fVideoRequest\x12\x1b\n" +
"\tvideo_url\x18\x01 \x01(\tR\bvideoUrl\x12*\n" +
"\x11frame_sample_rate\x18\x02 \x01(\x05R\x0fframeSampleRate\"\x93\x01\n" +
"\x12ModerationResponse\x12)\n" +
"\x10overall_decision\x18\x01 \x01(\tR\x0foverallDecision\x128\n" +
"\vpredictions\x18\x02 \x03(\v2\x16.moderation.PredictionR\vpredictions\x12\x18\n" +
"\amessage\x18\x03 \x01(\tR\amessage\"Z\n" +
"\n" +
"Prediction\x12\x1a\n" +
"\bcategory\x18\x01 \x01(\tR\bcategory\x12\x14\n" +
"\x05score\x18\x02 \x01(\x02R\x05score\x12\x1a\n" +
"\bdecision\x18\x03 \x01(\tR\bdecision\"\xb4\x02\n" +
"\x12ModerationSettings\x12g\n" +
"\x13category_thresholds\x18\x01 \x03(\v26.moderation.ModerationSettings.CategoryThresholdsEntryR\x12categoryThresholds\x126\n" +
"\x17always_block_categories\x18\x02 \x03(\tR\x15alwaysBlockCategories\x126\n" +
"\x17always_allow_categories\x18\x03 \x03(\tR\x15alwaysAllowCategories\x1aE\n" +
"\x17CategoryThresholdsEntry\x12\x10\n" +
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
"\x05value\x18\x02 \x01(\x02R\x05value:\x028\x012\xa7\x01\n" +
"\x11ModerationService\x12H\n" +
"\n" +
"CheckImage\x12\x18.moderation.ImageRequest\x1a\x1e.moderation.ModerationResponse\"\x00\x12H\n" +
"\n" +
"CheckVideo\x12\x18.moderation.VideoRequest\x1a\x1e.moderation.ModerationResponse\"\x00B\x03Z\x01.b\x06proto3"
var (
file_moderation_proto_rawDescOnce sync.Once
file_moderation_proto_rawDescData []byte
)
func file_moderation_proto_rawDescGZIP() []byte {
file_moderation_proto_rawDescOnce.Do(func() {
file_moderation_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_moderation_proto_rawDesc), len(file_moderation_proto_rawDesc)))
})
return file_moderation_proto_rawDescData
}
var file_moderation_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
var file_moderation_proto_goTypes = []any{
(*ImageRequest)(nil), // 0: moderation.ImageRequest
(*VideoRequest)(nil), // 1: moderation.VideoRequest
(*ModerationResponse)(nil), // 2: moderation.ModerationResponse
(*Prediction)(nil), // 3: moderation.Prediction
(*ModerationSettings)(nil), // 4: moderation.ModerationSettings
nil, // 5: moderation.ModerationSettings.CategoryThresholdsEntry
}
var file_moderation_proto_depIdxs = []int32{
3, // 0: moderation.ModerationResponse.predictions:type_name -> moderation.Prediction
5, // 1: moderation.ModerationSettings.category_thresholds:type_name -> moderation.ModerationSettings.CategoryThresholdsEntry
0, // 2: moderation.ModerationService.CheckImage:input_type -> moderation.ImageRequest
1, // 3: moderation.ModerationService.CheckVideo:input_type -> moderation.VideoRequest
2, // 4: moderation.ModerationService.CheckImage:output_type -> moderation.ModerationResponse
2, // 5: moderation.ModerationService.CheckVideo:output_type -> moderation.ModerationResponse
4, // [4:6] is the sub-list for method output_type
2, // [2:4] is the sub-list for method input_type
2, // [2:2] is the sub-list for extension type_name
2, // [2:2] is the sub-list for extension extendee
0, // [0:2] is the sub-list for field type_name
}
func init() { file_moderation_proto_init() }
func file_moderation_proto_init() {
if File_moderation_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_moderation_proto_rawDesc), len(file_moderation_proto_rawDesc)),
NumEnums: 0,
NumMessages: 6,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_moderation_proto_goTypes,
DependencyIndexes: file_moderation_proto_depIdxs,
MessageInfos: file_moderation_proto_msgTypes,
}.Build()
File_moderation_proto = out.File
file_moderation_proto_goTypes = nil
file_moderation_proto_depIdxs = nil
}

View File

@ -0,0 +1,37 @@
syntax = "proto3";
package moderation;
option go_package = ".";
service ModerationService {
rpc CheckImage (ImageRequest) returns (ModerationResponse) {}
rpc CheckVideo (VideoRequest) returns (ModerationResponse) {}
}
message ImageRequest {
bytes image_data = 1;
string image_url = 2;
}
message VideoRequest {
string video_url = 1;
int32 frame_sample_rate = 2;
}
message ModerationResponse {
string overall_decision = 1;
repeated Prediction predictions = 2;
string message = 3;
}
message Prediction {
string category = 1;
float score = 2;
string decision = 3;
}
message ModerationSettings {
map<string, float> category_thresholds = 1;
repeated string always_block_categories = 2;
repeated string always_allow_categories = 3;
}

View File

@ -0,0 +1,159 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc v3.21.12
// source: moderation.proto
package __
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
ModerationService_CheckImage_FullMethodName = "/moderation.ModerationService/CheckImage"
ModerationService_CheckVideo_FullMethodName = "/moderation.ModerationService/CheckVideo"
)
// ModerationServiceClient is the client API for ModerationService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type ModerationServiceClient interface {
CheckImage(ctx context.Context, in *ImageRequest, opts ...grpc.CallOption) (*ModerationResponse, error)
CheckVideo(ctx context.Context, in *VideoRequest, opts ...grpc.CallOption) (*ModerationResponse, error)
}
type moderationServiceClient struct {
cc grpc.ClientConnInterface
}
func NewModerationServiceClient(cc grpc.ClientConnInterface) ModerationServiceClient {
return &moderationServiceClient{cc}
}
func (c *moderationServiceClient) CheckImage(ctx context.Context, in *ImageRequest, opts ...grpc.CallOption) (*ModerationResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ModerationResponse)
err := c.cc.Invoke(ctx, ModerationService_CheckImage_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *moderationServiceClient) CheckVideo(ctx context.Context, in *VideoRequest, opts ...grpc.CallOption) (*ModerationResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ModerationResponse)
err := c.cc.Invoke(ctx, ModerationService_CheckVideo_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// ModerationServiceServer is the server API for ModerationService service.
// All implementations must embed UnimplementedModerationServiceServer
// for forward compatibility.
type ModerationServiceServer interface {
CheckImage(context.Context, *ImageRequest) (*ModerationResponse, error)
CheckVideo(context.Context, *VideoRequest) (*ModerationResponse, error)
mustEmbedUnimplementedModerationServiceServer()
}
// UnimplementedModerationServiceServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedModerationServiceServer struct{}
func (UnimplementedModerationServiceServer) CheckImage(context.Context, *ImageRequest) (*ModerationResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CheckImage not implemented")
}
func (UnimplementedModerationServiceServer) CheckVideo(context.Context, *VideoRequest) (*ModerationResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CheckVideo not implemented")
}
func (UnimplementedModerationServiceServer) mustEmbedUnimplementedModerationServiceServer() {}
func (UnimplementedModerationServiceServer) testEmbeddedByValue() {}
// UnsafeModerationServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to ModerationServiceServer will
// result in compilation errors.
type UnsafeModerationServiceServer interface {
mustEmbedUnimplementedModerationServiceServer()
}
func RegisterModerationServiceServer(s grpc.ServiceRegistrar, srv ModerationServiceServer) {
// If the following call pancis, it indicates UnimplementedModerationServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&ModerationService_ServiceDesc, srv)
}
func _ModerationService_CheckImage_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ImageRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ModerationServiceServer).CheckImage(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ModerationService_CheckImage_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ModerationServiceServer).CheckImage(ctx, req.(*ImageRequest))
}
return interceptor(ctx, in, info, handler)
}
func _ModerationService_CheckVideo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(VideoRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ModerationServiceServer).CheckVideo(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ModerationService_CheckVideo_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ModerationServiceServer).CheckVideo(ctx, req.(*VideoRequest))
}
return interceptor(ctx, in, info, handler)
}
// ModerationService_ServiceDesc is the grpc.ServiceDesc for ModerationService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var ModerationService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "moderation.ModerationService",
HandlerType: (*ModerationServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "CheckImage",
Handler: _ModerationService_CheckImage_Handler,
},
{
MethodName: "CheckVideo",
Handler: _ModerationService_CheckVideo_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "moderation.proto",
}