-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 9126d43
Showing
17 changed files
with
950 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
name: Build and Publish | ||
|
||
env: | ||
REGISTRY: ghcr.io | ||
PACKAGE_NAME: ${{ github.repository }} | ||
GITHUB_SHA: ${{ github.sha }} | ||
|
||
on: | ||
push: | ||
branches: [ "main" ] | ||
|
||
jobs: | ||
publish-image: | ||
runs-on: ubuntu-latest | ||
permissions: | ||
contents: read | ||
packages: write | ||
|
||
steps: | ||
- name: Checkout repository | ||
uses: actions/checkout@v4 | ||
|
||
- name: Print Go version | ||
run: | | ||
go version | ||
- name: Log in to the Container registry | ||
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1 | ||
with: | ||
registry: ${{ env.REGISTRY }} | ||
username: ${{ github.actor }} | ||
password: ${{ secrets.GITHUB_TOKEN }} | ||
|
||
- name: Set image name | ||
run: | | ||
echo "IMAGE_NAME=${REGISTRY}/${PACKAGE_NAME,,}:${GITHUB_SHA}" >> ${GITHUB_ENV} | ||
- name: Extract metadata (tags, labels) for Docker | ||
id: meta | ||
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7 | ||
with: | ||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} | ||
|
||
- name: Build and push Docker image | ||
uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4 | ||
with: | ||
context: . | ||
push: true | ||
tags: ${{ env.IMAGE_NAME }} | ||
labels: ${{ steps.meta.outputs.labels }} | ||
|
||
- name: Log image name | ||
run: | | ||
echo "Image URI: ${IMAGE_NAME}" >> $GITHUB_STEP_SUMMARY |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
.idea | ||
.env | ||
main |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
# Build container | ||
FROM golang:1.22 AS builder | ||
|
||
RUN go version | ||
|
||
RUN apt-get update && apt-get upgrade -y && apt-get install -y ca-certificates git zlib1g-dev | ||
|
||
COPY . /go/src/github.com/TicketsBot/misconduct-detector | ||
WORKDIR /go/src/github.com/TicketsBot/misconduct-detector | ||
|
||
RUN git submodule update --init --recursive --remote | ||
|
||
RUN set -Eeux && \ | ||
go mod download && \ | ||
go mod verify | ||
|
||
RUN GOOS=linux GOARCH=amd64 \ | ||
go build \ | ||
-trimpath \ | ||
-o main cmd/misconduct-detector/main.go | ||
|
||
# Prod container | ||
FROM ubuntu:latest | ||
|
||
RUN apt-get update && apt-get upgrade -y && apt-get install -y ca-certificates curl | ||
|
||
COPY --from=builder /go/src/github.com/TicketsBot/misconduct-detector/main /srv/misconduct-detector/main | ||
|
||
RUN chmod +x /srv/misconduct-detector/main | ||
|
||
RUN useradd -m container | ||
USER container | ||
WORKDIR /srv/misconduct-detector | ||
|
||
CMD ["/srv/misconduct-detector/main"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"github.com/TicketsBot/common/observability" | ||
"github.com/TicketsBot/common/rpc" | ||
"github.com/TicketsBot/misconduct-detector/internal/config" | ||
"github.com/TicketsBot/misconduct-detector/internal/processor" | ||
"github.com/TicketsBot/misconduct-detector/internal/processor/rules" | ||
"github.com/TicketsBot/misconduct-detector/internal/queue" | ||
"github.com/getsentry/sentry-go" | ||
"github.com/jackc/pgx/v4/pgxpool" | ||
"github.com/rxdn/gdl/cache" | ||
"github.com/rxdn/gdl/objects/guild" | ||
"go.uber.org/zap" | ||
"go.uber.org/zap/zapcore" | ||
"os" | ||
"os/signal" | ||
"sync" | ||
"syscall" | ||
"time" | ||
) | ||
|
||
func main() { | ||
config := must(config.LoadFromEnv()) | ||
|
||
// Build logger | ||
if config.SentryDsn != nil { | ||
if err := sentry.Init(sentry.ClientOptions{ | ||
Dsn: *config.SentryDsn, | ||
}); err != nil { | ||
panic(fmt.Errorf("sentry.Init: %w", err)) | ||
} | ||
} | ||
|
||
var logger *zap.Logger | ||
var err error | ||
if config.JsonLogs { | ||
loggerConfig := zap.NewProductionConfig() | ||
loggerConfig.Level.SetLevel(config.LogLevel) | ||
|
||
logger, err = loggerConfig.Build( | ||
zap.AddCaller(), | ||
zap.AddStacktrace(zap.ErrorLevel), | ||
zap.WrapCore(observability.ZapSentryAdapter(observability.EnvironmentProduction)), | ||
) | ||
} else { | ||
loggerConfig := zap.NewDevelopmentConfig() | ||
loggerConfig.Level.SetLevel(config.LogLevel) | ||
loggerConfig.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder | ||
|
||
logger, err = loggerConfig.Build(zap.AddCaller(), zap.AddStacktrace(zap.ErrorLevel)) | ||
} | ||
|
||
if err != nil { | ||
panic(fmt.Errorf("failed to initialise zap logger: %w", err)) | ||
} | ||
|
||
// Build app context | ||
appContext := must(buildAppContext(config, logger)) | ||
|
||
// Connect to Kafka | ||
guildCh := make(chan guild.Guild, 10) | ||
consumer := queue.NewConsumer(config, logger.With(zap.String("module", "consumer")), guildCh) | ||
|
||
logger.Info("Starting RPC client") | ||
rpcClient, err := rpc.NewClient( | ||
logger.With(zap.String("service", "rpc-client")), | ||
rpc.Config{ | ||
Brokers: config.Kafka.Brokers, | ||
ConsumerConcurrency: 1, | ||
}, | ||
map[string]rpc.Listener{ | ||
config.Kafka.EventsTopic: consumer, | ||
}, | ||
) | ||
if err != nil { | ||
logger.Fatal("Failed to start RPC client", zap.Error(err)) | ||
return | ||
} | ||
|
||
logger.Info("RPC client started") | ||
|
||
wg := &sync.WaitGroup{} | ||
delegators := make([]*processor.Delegator, config.ConcurrentTasks) | ||
for i := 0; i < config.ConcurrentTasks; i++ { | ||
wg.Add(1) | ||
|
||
delegator := processor.NewDelegator( | ||
config, | ||
logger.With(zap.String("module", "delegator")), | ||
appContext, | ||
rules.Ruleset, | ||
queue.NewKafkaProducer(config, rpcClient), | ||
guildCh, | ||
) | ||
|
||
go func() { | ||
defer wg.Done() | ||
delegator.Run() | ||
}() | ||
|
||
delegators[i] = delegator | ||
} | ||
|
||
awaitShutdown(logger, wg, delegators) | ||
} | ||
|
||
func awaitShutdown(logger *zap.Logger, wg *sync.WaitGroup, delegators []*processor.Delegator) { | ||
done := make(chan os.Signal, 1) | ||
signal.Notify(done, syscall.SIGINT, syscall.SIGTERM) | ||
<-done | ||
|
||
logger.Info("Shutting down") | ||
for _, delegator := range delegators { | ||
delegator.Shutdown() | ||
} | ||
|
||
wgCh := make(chan struct{}) | ||
go func() { | ||
wg.Wait() | ||
close(wgCh) | ||
}() | ||
|
||
select { | ||
case <-wgCh: | ||
logger.Info("All delegators have shut down") | ||
case <-time.After(10 * time.Second): | ||
logger.Warn("Some delegators have not shut down, but timeout has expired") | ||
} | ||
} | ||
|
||
func buildAppContext(config config.Config, logger *zap.Logger) (*rules.AppContext, error) { | ||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) | ||
defer cancel() | ||
|
||
logger.Info("Connecting to cache") | ||
pool, err := pgxpool.Connect(ctx, config.Cache.Uri) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
cache := cache.NewPgCache(pool, cache.CacheOptions{ | ||
Guilds: true, | ||
Users: true, | ||
Members: true, | ||
Channels: true, | ||
}) | ||
logger.Info("Connected to cache") | ||
|
||
return rules.NewAppContext(config, &cache), nil | ||
} | ||
|
||
func must[T any](t T, err error) T { | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
return t | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
module github.com/TicketsBot/misconduct-detector | ||
|
||
go 1.22 | ||
|
||
require ( | ||
github.com/TicketsBot/common v0.0.0-20241104184641-e39c64bdcf3e | ||
github.com/caarlos0/env/v11 v11.2.2 | ||
github.com/getsentry/sentry-go v0.21.0 | ||
github.com/prometheus/client_golang v1.20.5 | ||
github.com/rxdn/gdl v0.0.0-20241027214923-02dff700595b | ||
github.com/stretchr/testify v1.9.0 | ||
go.uber.org/zap v1.27.0 | ||
golang.org/x/sync v0.8.0 | ||
) | ||
|
||
require ( | ||
github.com/beorn7/perks v1.0.1 // indirect | ||
github.com/cespare/xxhash/v2 v2.3.0 // indirect | ||
github.com/davecgh/go-spew v1.1.1 // indirect | ||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect | ||
github.com/go-redis/redis/v8 v8.11.3 // indirect | ||
github.com/json-iterator/go v1.1.12 // indirect | ||
github.com/klauspost/compress v1.17.9 // indirect | ||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect | ||
github.com/modern-go/reflect2 v1.0.2 // indirect | ||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect | ||
github.com/panjf2000/ants/v2 v2.10.0 // indirect | ||
github.com/pierrec/lz4/v4 v4.1.21 // indirect | ||
github.com/pmezard/go-difflib v1.0.0 // indirect | ||
github.com/prometheus/client_model v0.6.1 // indirect | ||
github.com/prometheus/common v0.55.0 // indirect | ||
github.com/prometheus/procfs v0.15.1 // indirect | ||
github.com/twmb/franz-go v1.18.0 // indirect | ||
github.com/twmb/franz-go/pkg/kmsg v1.9.0 // indirect | ||
go.uber.org/atomic v1.4.0 // indirect | ||
go.uber.org/multierr v1.11.0 // indirect | ||
golang.org/x/sys v0.26.0 // indirect | ||
golang.org/x/text v0.19.0 // indirect | ||
google.golang.org/protobuf v1.34.2 // indirect | ||
gopkg.in/yaml.v3 v3.0.1 // indirect | ||
) | ||
|
||
replace github.com/TicketsBot/common => ../common |
Oops, something went wrong.