Skip to content

Commit

Permalink
feat: update .golangci.yml and files (#23)
Browse files Browse the repository at this point in the history
  • Loading branch information
rancoud committed Feb 5, 2024
1 parent 9d5fe1d commit 4cc1078
Show file tree
Hide file tree
Showing 7 changed files with 173 additions and 94 deletions.
55 changes: 17 additions & 38 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -1,42 +1,21 @@
run:
timeout: 30s

linters:
enable:
- gofmt
- govet
enable-all: true
disable:
- depguard
- goimports
- ineffassign
- misspell
- unused
- revive
- staticcheck
- typecheck
- nolintlint
- gosec
- forbidigo
- importas
- errname
- makezero
- whitespace
disable-all: true
- exhaustruct
- gofumpt
# deprecated
- deadcode
- exhaustivestruct
- golint
- ifshort
- interfacer
- maligned
- nosnakecase
- scopelint
- structcheck
- varcheck

linters-settings:
depguard:
rules:
main:
deny:
# The io/ioutil package has been deprecated.
# https://go.dev/doc/go1.16#ioutil
- pkg: "io/ioutil"
desc: The io/ioutil package has been deprecated.
forbidigo:
forbid:
- '^fmt\.Errorf(# use errors\.Errorf instead)?$'
importas:
no-unaliased: true

# show all
max-issues-per-linter: 0
max-same-issues: 0
lll:
line-length: 160
43 changes: 28 additions & 15 deletions configuration/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package configuration
import (
"encoding/json"
"errors"
"fmt"
"os"
"reflect"
"runtime"
Expand All @@ -12,34 +13,41 @@ import (
"github.com/blueprintue/discord-bot/welcome"
)

var (
ErrDiscordName = errors.New("invalid json value: discord.name is empty")
ErrDiscordToken = errors.New("invalid json value: discord.token is empty")
ErrLogFilename = errors.New("invalid json value: log.filename is empty")
)

// Support only field's type string, int, bool, []string

// Configuration contains Discord, Log and Modules parameters
// Configuration contains Discord, Log and Modules parameters.
type Configuration struct {
Discord struct {
Name string `json:"name" env:"DBOT_DISCORD_NAME"`
Token string `json:"token" env:"DBOT_DISCORD_TOKEN"`
Name string `env:"DBOT_DISCORD_NAME" json:"name"`
Token string `env:"DBOT_DISCORD_TOKEN" json:"token"`
} `json:"discord"`
Log struct {
Filename string `json:"filename" env:"DBOT_LOG_FILENAME"`
Level string `json:"level" env:"DBOT_LOG_LEVEL"`
}
Filename string `env:"DBOT_LOG_FILENAME" json:"filename"`
Level string `env:"DBOT_LOG_LEVEL" json:"level"`
} `json:"log"`
Modules struct {
WelcomeConfiguration welcome.Configuration `json:"welcome"`
} `json:"modules"`
}

// ReadConfiguration read config.json file and update some values with env if found
// ReadConfiguration read config.json file and update some values with env if found.
func ReadConfiguration(filename string) (*Configuration, error) {
filedata, err := os.ReadFile(filename)
if err != nil {
return nil, err
return nil, fmt.Errorf("%w", err)
}

config := Configuration{}

err = json.Unmarshal(filedata, &config)
if err != nil {
return nil, err
return nil, fmt.Errorf("%w", err)
}

eraseConfigurationValuesWithEnv(&config)
Expand All @@ -52,7 +60,8 @@ func ReadConfiguration(filename string) (*Configuration, error) {
return &config, nil
}

func eraseConfigurationValuesWithEnv(obj interface{}) interface{} {
//nolint:cyclop
func eraseConfigurationValuesWithEnv(obj any) {
var val reflect.Value
if reflect.TypeOf(obj).Kind() == reflect.Ptr {
val = reflect.ValueOf(obj).Elem()
Expand All @@ -63,15 +72,18 @@ func eraseConfigurationValuesWithEnv(obj interface{}) interface{} {
for idxNumField := 0; idxNumField < val.NumField(); idxNumField++ {
if val.Field(idxNumField).Kind() == reflect.Struct {
eraseConfigurationValuesWithEnv(val.Field(idxNumField).Addr().Interface())

continue
}

envKey := reflect.TypeOf(obj).Elem().Field(idxNumField).Tag.Get("env")

envValue, ok := os.LookupEnv(envKey)
if !ok {
continue
}

//nolint:exhaustive
switch val.Field(idxNumField).Kind() {
case reflect.String:
val.Field(idxNumField).SetString(envValue)
Expand All @@ -90,28 +102,29 @@ func eraseConfigurationValuesWithEnv(obj interface{}) interface{} {
if runtime.GOOS == "windows" {
splitter = ";"
}

stringEnvValues := strings.Split(envValue, splitter)

val.Field(idxNumField).Set(reflect.MakeSlice(val.Field(idxNumField).Type(), len(stringEnvValues), len(stringEnvValues)))

for idxSlice := 0; idxSlice < len(stringEnvValues); idxSlice++ {
val.Field(idxNumField).Index(idxSlice).SetString(stringEnvValues[idxSlice])
}
}
}

return obj
}

func checkBasicConfiguration(config Configuration) error {
if config.Discord.Name == "" {
return errors.New("invalid json value: discord.name is empty")
return ErrDiscordName
}

if config.Discord.Token == "" {
return errors.New("invalid json value: discord.token is empty")
return ErrDiscordToken
}

if config.Log.Filename == "" {
return errors.New("invalid json value: log.filename is empty")
return ErrLogFilename
}

return nil
Expand Down
6 changes: 3 additions & 3 deletions configuration/configuration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import (
"testing"

"github.com/blueprintue/discord-bot/configuration"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestReadConfiguration(t *testing.T) {
t.Parallel()

conf, err := configuration.ReadConfiguration("")
assert.Error(t, err)
assert.Nil(t, conf)
require.Error(t, err)
require.Nil(t, conf)
}
46 changes: 27 additions & 19 deletions helpers/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,52 +2,60 @@ package helpers

import (
"encoding/json"
"fmt"
"net/url"
"strconv"
"strings"

"github.com/bwmarrin/discordgo"
)

// MessageReactionsAll retrieve all Reactions from Message taking into account pagination
func MessageReactionsAll(session *discordgo.Session, channelID, messageID, emojiID string) (st []*discordgo.User, err error) {
var body []byte
const limitMessageReactions = "100"

// MessageReactionsAll retrieve all Reactions from Message taking into account pagination.
func MessageReactionsAll(session *discordgo.Session, channelID, messageID, emojiID string) ([]*discordgo.User, error) {
var listUsers []*discordgo.User
emojiID = strings.Replace(emojiID, "#", "%23", -1)

emojiID = strings.ReplaceAll(emojiID, "#", "%23")
uri := discordgo.EndpointMessageReactions(channelID, messageID, emojiID)

v := url.Values{}
urlValues := url.Values{}

v.Set("limit", strconv.Itoa(100))
urlValues.Set("limit", limitMessageReactions)

for {
tempURI := uri
if len(v) > 0 {
tempURI += "?" + v.Encode()
if len(urlValues) > 0 {
tempURI += "?" + urlValues.Encode()
}

body, err := session.RequestWithBucketID("GET", tempURI, nil, discordgo.EndpointMessageReaction(channelID, "", "", ""))
if err != nil {
return listUsers, fmt.Errorf("%w", err)
}

body, err = session.RequestWithBucketID("GET", tempURI, nil, discordgo.EndpointMessageReaction(channelID, "", "", ""))
var listUsersFromAPI []*discordgo.User

err = unmarshal(body, &listUsersFromAPI)
if err != nil {
return
return listUsers, err
}
err = unmarshal(body, &listUsers)

if len(listUsers) == 0 {
if len(listUsersFromAPI) == 0 {
break
}

for k := range listUsers {
ptr := *listUsers[k]
st = append(st, &ptr)
for k := range listUsersFromAPI {
ptr := *listUsersFromAPI[k]
listUsers = append(listUsers, &ptr)
}

v.Set("after", listUsers[len(listUsers)-1].ID)
urlValues.Set("after", listUsersFromAPI[len(listUsersFromAPI)-1].ID)
}

return
return listUsers, nil
}

func unmarshal(data []byte, v interface{}) error {
func unmarshal(data []byte, v any) error {
err := json.Unmarshal(data, v)
if err != nil {
return discordgo.ErrJSONUnmarshal
Expand Down
11 changes: 9 additions & 2 deletions logger/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,33 @@ import (
"github.com/rs/zerolog/log"
)

// Configure configures logger
const numberFilesRotation = 5

// Configure configures logger.
func Configure(configuration *configuration.Configuration) {
var err error

logFile := path.Clean(configuration.Log.Filename)
if err := os.MkdirAll(path.Dir(logFile), os.ModePerm); err != nil {
log.Fatal().Err(err).Msgf("Cannot create log folder")
}
rwriter, err := rotatewriter.NewRotateWriter(logFile, 5)

rwriter, err := rotatewriter.NewRotateWriter(logFile, numberFilesRotation)
if err != nil {
log.Fatal().Err(err).Msgf("Cannot create log file writer")
}

sighupChan := make(chan os.Signal, 1)

signal.Notify(sighupChan, syscall.SIGHUP)

go func() {
for {
_, ok := <-sighupChan
if !ok {
return
}

if err := rwriter.Rotate(nil); err != nil {
log.Error().Err(err).Msgf("Cannot rotate log")
}
Expand Down
17 changes: 15 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,21 @@ import (
"github.com/rs/zerolog/log"
)

const waitStateFilled = 250 * time.Millisecond
const configurationFilename = "config.json"
const (
waitStateFilled = 250 * time.Millisecond
configurationFilename = "config.json"
)

var version = "edge"

//nolint:funlen
func main() {
var err error

log.Info().Str("version", version).Msg("Starting discord-bot")

log.Info().Msgf("Read configuration from file: %s", configurationFilename)

config, err := configuration.ReadConfiguration(configurationFilename)
if err != nil {
log.Fatal().Err(err).Msg("Error on configuration")
Expand All @@ -33,12 +37,14 @@ func main() {
logger.Configure(config)

log.Info().Msg("Create discordgo session")

session, err := discordgo.New("Bot " + config.Discord.Token)
if err != nil {
log.Fatal().Err(err).Msg("Could not create discordgo session")
}

log.Info().Msg("Open discordgo session")

err = session.Open()
if err != nil {
log.Fatal().Err(err).Msg("Could not open discordgo session")
Expand All @@ -47,26 +53,32 @@ func main() {
for {
log.Info().Msg("Pending session to be ready...")
time.Sleep(waitStateFilled)

if hasRequiredStateFieldsFilled(session) {
break
}
}

log.Info().Msg("Create Welcome Manager")

welcomeManager := welcome.NewWelcomeManager(session, config.Discord.Name, config.Modules.WelcomeConfiguration)
if welcomeManager == nil {
log.Error().Msg("Could not start Welcome Manager")
} else {
log.Info().Msg("Run Welcome Manager")

err = welcomeManager.Run()
if err != nil {
log.Error().Err(err).Msg("Could not run Welcome Manager")

closeSessionDiscord(session)

return
}
}

log.Info().Msg("Bot is now running. Press CTRL+C to stop")

sc := make(chan os.Signal, 1)
signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt)
<-sc
Expand All @@ -80,6 +92,7 @@ func hasRequiredStateFieldsFilled(session *discordgo.Session) bool {

func closeSessionDiscord(session *discordgo.Session) {
log.Info().Msg("Close discordgo session")

err := session.Close()
if err != nil {
log.Fatal().Err(err).Msg("Could not close discordgo session")
Expand Down
Loading

0 comments on commit 4cc1078

Please sign in to comment.