Initial commit
This commit is contained in:
commit
49485129f7
|
@ -0,0 +1 @@
|
|||
*.env*
|
|
@ -0,0 +1,8 @@
|
|||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="Go" enabled="true" />
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/go-discord-bot.iml" filepath="$PROJECT_DIR$/.idea/go-discord-bot.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,62 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"code.lowsec.club/okawari/go-discord-bot/lib/bot"
|
||||
"code.lowsec.club/okawari/go-discord-bot/lib/commands"
|
||||
"code.lowsec.club/okawari/go-discord-bot/lib/helpers"
|
||||
"code.lowsec.club/okawari/go-discord-bot/lib/logging"
|
||||
"context"
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/rs/zerolog/log"
|
||||
"gorm.io/gorm"
|
||||
"os"
|
||||
"os/signal"
|
||||
)
|
||||
|
||||
var discord *discordgo.Session
|
||||
|
||||
func main() {
|
||||
_ = logging.InitLogging()
|
||||
err := godotenv.Load()
|
||||
if err != nil {
|
||||
log.Err(err).Msg("Unable to load dot env files")
|
||||
}
|
||||
|
||||
log.Info().Msg("Starting discord bot")
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
db, err := helpers.CreateConnection(&gorm.Config{
|
||||
Logger: logging.DBLogger{Logger: log.With().Str("module", "sql").Logger()},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Panic().Str("module", "sql").Err(err).Msg("unable to connect to database")
|
||||
}
|
||||
|
||||
helpers.AutoMigrate(db)
|
||||
helpers.ImportFromFio(db)
|
||||
|
||||
ctx = helpers.AddDb(ctx, db)
|
||||
discord, err := bot.New(ctx, "trader", os.Getenv("DISCORD_TOKEN"))
|
||||
if err != nil {
|
||||
log.Err(err).Msg("Unable to create discord bot")
|
||||
}
|
||||
|
||||
discord.AddCommand(commands.StockCommand{}, true)
|
||||
discord.AddCommand(commands.PriceCommand{}, true)
|
||||
|
||||
err = discord.Open()
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Unable to start discord session")
|
||||
}
|
||||
defer discord.Close()
|
||||
|
||||
stop := make(chan os.Signal, 1)
|
||||
signal.Notify(stop, os.Interrupt)
|
||||
log.Info().Msg("Press Ctrl+C to exit")
|
||||
<-stop
|
||||
|
||||
log.Info().Msg("Shutting down")
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
module code.lowsec.club/okawari/go-discord-bot
|
||||
|
||||
go 1.22.5
|
||||
|
||||
require github.com/rs/zerolog v1.33.0
|
||||
|
||||
require (
|
||||
github.com/bwmarrin/discordgo v0.28.1 // indirect
|
||||
github.com/gorilla/websocket v1.4.2 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/pgx/v5 v5.6.0 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/joho/godotenv v1.5.1 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.22 // indirect
|
||||
golang.org/x/crypto v0.25.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/sys v0.22.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
gorm.io/driver/postgres v1.5.9 // indirect
|
||||
gorm.io/driver/sqlite v1.5.6 // indirect
|
||||
gorm.io/gorm v1.25.11 // indirect
|
||||
)
|
|
@ -0,0 +1,63 @@
|
|||
github.com/bwmarrin/discordgo v0.28.1 h1:gXsuo2GBO7NbR6uqmrrBDplPUx2T3nzu775q/Rd1aG4=
|
||||
github.com/bwmarrin/discordgo v0.28.1/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY=
|
||||
github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=
|
||||
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
|
||||
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
||||
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
||||
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/postgres v1.5.9 h1:DkegyItji119OlcaLjqN11kHoUgZ/j13E0jkJZgD6A8=
|
||||
gorm.io/driver/postgres v1.5.9/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI=
|
||||
gorm.io/driver/sqlite v1.5.6 h1:fO/X46qn5NUEEOZtnjJRWRzZMe8nqJiQ9E+0hi+hKQE=
|
||||
gorm.io/driver/sqlite v1.5.6/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4=
|
||||
gorm.io/gorm v1.25.11 h1:/Wfyg1B/je1hnDx3sMkX+gAlxrlZpn6X0BXRlwXlvHg=
|
||||
gorm.io/gorm v1.25.11/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
|
|
@ -0,0 +1,137 @@
|
|||
package bot
|
||||
|
||||
import (
|
||||
"code.lowsec.club/okawari/go-discord-bot/lib/commands"
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
type Bot struct {
|
||||
discord *discordgo.Session
|
||||
logger zerolog.Logger
|
||||
guildId string
|
||||
commands []struct {
|
||||
Command *commands.SlashCommand
|
||||
removeOnClose bool
|
||||
}
|
||||
registeredCmds []*discordgo.ApplicationCommand
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func (b *Bot) AddCommand(command commands.SlashCommand, removeOnClose bool) *Bot {
|
||||
b.commands = append(b.commands, struct {
|
||||
Command *commands.SlashCommand
|
||||
removeOnClose bool
|
||||
}{Command: &command, removeOnClose: removeOnClose})
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *Bot) AddCommands(commands []commands.SlashCommand, removeOnClose bool) *Bot {
|
||||
for _, cmd := range commands {
|
||||
b.AddCommand(cmd, removeOnClose)
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *Bot) registerCommand(command commands.SlashCommand, removeOnClose bool) error {
|
||||
acc := command.CreateCommand()
|
||||
logger := b.logger.With().Str("cmd", acc.Name).Logger()
|
||||
|
||||
registeredCmd, err := b.discord.ApplicationCommandCreate(b.discord.State.User.ID, b.guildId, acc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Debug().Msg("Command registered")
|
||||
|
||||
if removeOnClose == true {
|
||||
b.registeredCmds = append(b.registeredCmds, registeredCmd)
|
||||
}
|
||||
|
||||
b.discord.AddHandler(func(s *discordgo.Session, r *discordgo.InteractionCreate) {
|
||||
logger = logger.With().
|
||||
Str("user", r.Member.Nick).
|
||||
Str("component", "Handler").
|
||||
Logger()
|
||||
if r.ApplicationCommandData().ID == registeredCmd.ID {
|
||||
ctx := logger.WithContext(b.ctx)
|
||||
command.Handle(ctx, s, r)
|
||||
}
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bot) Open() error {
|
||||
b.discord.AddHandler(b.onReady())
|
||||
return b.discord.Open()
|
||||
}
|
||||
|
||||
func (b *Bot) onReady() func(s *discordgo.Session, r *discordgo.Ready) {
|
||||
return func(s *discordgo.Session, r *discordgo.Ready) {
|
||||
log.Info().
|
||||
Str("user", fmt.Sprintf("%v#%v", s.State.User.Username, s.State.User.Discriminator)).
|
||||
Msg("Logged inn")
|
||||
|
||||
log.Info().Int("commands", len(b.commands)).Msg("Registering commands")
|
||||
for _, cmd := range b.commands {
|
||||
err := b.registerCommand(*cmd.Command, cmd.removeOnClose)
|
||||
if err != nil {
|
||||
b.logger.Fatal().Err(err).Msg("Unable to register command")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bot) Close() {
|
||||
b.logger.Debug().Msg("Shutting down")
|
||||
|
||||
b.logger.Debug().Int("commands", len(b.registeredCmds)).Msg("Registered commands")
|
||||
|
||||
for _, cmd := range b.registeredCmds {
|
||||
log.Info().
|
||||
Str("name", cmd.Name).
|
||||
Msg("Unregistering command")
|
||||
|
||||
err := b.discord.ApplicationCommandDelete(b.discord.State.User.ID, b.guildId, cmd.ID)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Err(err).
|
||||
Str("name", cmd.Name).Msg("Failed to unregister command")
|
||||
}
|
||||
}
|
||||
|
||||
err := b.discord.Close()
|
||||
if err != nil {
|
||||
b.logger.Error().Err(err).Msg("Unable to close discord")
|
||||
}
|
||||
}
|
||||
|
||||
func New(ctx context.Context, name string, token string) (*Bot, error) {
|
||||
|
||||
discord, err := discordgo.New("Bot " + token)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to create discord bot: %s", err)
|
||||
}
|
||||
|
||||
return (&Bot{
|
||||
discord: discord,
|
||||
logger: log.With().Str("bot", name).Logger(),
|
||||
ctx: ctx,
|
||||
guildId: "758249214160797706",
|
||||
commands: make([]struct {
|
||||
Command *commands.SlashCommand
|
||||
removeOnClose bool
|
||||
}, 0),
|
||||
registeredCmds: make([]*discordgo.ApplicationCommand, 0),
|
||||
}), nil
|
||||
}
|
||||
|
||||
func (b *Bot) init() *Bot {
|
||||
|
||||
return b
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/bwmarrin/discordgo"
|
||||
)
|
||||
|
||||
type SlashCommand interface {
|
||||
CreateCommand() *discordgo.ApplicationCommand
|
||||
Handle(ctx context.Context, session *discordgo.Session, command *discordgo.InteractionCreate)
|
||||
}
|
||||
|
||||
func Commands() []SlashCommand {
|
||||
return []SlashCommand{
|
||||
StockCommand{},
|
||||
PriceCommand{},
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"code.lowsec.club/okawari/go-discord-bot/lib/helpers"
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"github.com/rs/zerolog/log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type PriceCommand struct {
|
||||
}
|
||||
|
||||
func (p PriceCommand) CreateCommand() *discordgo.ApplicationCommand {
|
||||
return &discordgo.ApplicationCommand{
|
||||
ID: "price",
|
||||
Name: "price",
|
||||
Description: "Change stock of traded commodity",
|
||||
Options: []*discordgo.ApplicationCommandOption{
|
||||
{
|
||||
Type: discordgo.ApplicationCommandOptionString,
|
||||
Name: "commodity",
|
||||
Description: "Ticker of wanted commodity",
|
||||
Required: true,
|
||||
MinLength: helpers.Pointer(1),
|
||||
MaxLength: 3,
|
||||
},
|
||||
{
|
||||
Type: discordgo.ApplicationCommandOptionString,
|
||||
Name: "price",
|
||||
Description: "Price of commodity in stock",
|
||||
Required: true,
|
||||
MinValue: helpers.Pointer(0.0),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (p PriceCommand) Handle(ctx context.Context, session *discordgo.Session, command *discordgo.InteractionCreate) {
|
||||
logger := log.Ctx(ctx)
|
||||
db := helpers.DB(ctx)
|
||||
|
||||
commodityString := strings.ToUpper(command.ApplicationCommandData().Options[0].StringValue())
|
||||
price, err := helpers.ParseNumericString(command.ApplicationCommandData().Options[1].StringValue())
|
||||
|
||||
if err != nil {
|
||||
helpers.InteractionErrorResponse(session, command, logger, err)
|
||||
return
|
||||
}
|
||||
|
||||
logger.Info().Str("commodity", commodityString).Float64("price", price).Msg("Got price change request")
|
||||
|
||||
_, exists, err := helpers.FindCommodity(db, commodityString)
|
||||
if err != nil {
|
||||
helpers.InteractionErrorResponse(session, command, logger, err)
|
||||
return
|
||||
}
|
||||
|
||||
if !exists {
|
||||
helpers.InteractionErrorResponse(session, command, logger, fmt.Errorf("Can't find any commodities by the name `%s`", commodityString))
|
||||
return
|
||||
}
|
||||
|
||||
err = session.InteractionRespond(command.Interaction, helpers.EphemeralResponse("%s", "test"))
|
||||
if err != nil {
|
||||
logger.Error().Str("user", command.User.Username).Err(err).Msg("Unable to respond to request from user")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"code.lowsec.club/okawari/go-discord-bot/lib/helpers"
|
||||
"context"
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
type StockCommand struct{}
|
||||
|
||||
func (s StockCommand) CreateCommand() *discordgo.ApplicationCommand {
|
||||
return &discordgo.ApplicationCommand{
|
||||
ID: "stock",
|
||||
Name: "stock",
|
||||
Description: "Change stock of traded commodity",
|
||||
Options: []*discordgo.ApplicationCommandOption{
|
||||
{
|
||||
Type: discordgo.ApplicationCommandOptionString,
|
||||
Name: "commodity",
|
||||
Description: "Ticker of wanted commodity",
|
||||
Required: true,
|
||||
MinLength: helpers.Pointer(1),
|
||||
MaxLength: 3,
|
||||
},
|
||||
{
|
||||
Type: discordgo.ApplicationCommandOptionInteger,
|
||||
Name: "amount",
|
||||
Description: "Amount of commodity in stock",
|
||||
Required: true,
|
||||
MinValue: helpers.Pointer(0.0),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s StockCommand) Handle(ctx context.Context, session *discordgo.Session, command *discordgo.InteractionCreate) {
|
||||
logger := log.Ctx(ctx)
|
||||
err := session.InteractionRespond(command.Interaction, helpers.EphemeralResponse("Surprise 2!"))
|
||||
if err != nil {
|
||||
logger.Error().Str("user", command.User.Username).Err(err).Msg("Unable to respond to request from user")
|
||||
}
|
||||
}
|
||||
|
||||
func NewStockCommand() StockCommand {
|
||||
return StockCommand{}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package helpers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ParseNumericString(value string) (float64, error) {
|
||||
|
||||
value = strings.Replace(value, " ", "", 9999)
|
||||
value = strings.Replace(value, ",", ".", 9999)
|
||||
|
||||
out, err := strconv.ParseFloat(value, 64)
|
||||
if err != nil {
|
||||
result, err := strconv.ParseInt(value, 10, 64)
|
||||
out = float64(result)
|
||||
if err != nil {
|
||||
return 0.0, fmt.Errorf("not numeric")
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package helpers
|
||||
|
||||
import (
|
||||
"code.lowsec.club/okawari/go-discord-bot/lib/models"
|
||||
"context"
|
||||
"github.com/rs/zerolog/log"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
"os"
|
||||
)
|
||||
|
||||
func CreateConnection(config *gorm.Config) (*gorm.DB, error) {
|
||||
log.Debug().Msg("Connecting to DB")
|
||||
return gorm.Open(postgres.Open(os.Getenv("DATABASE_URL")), config)
|
||||
}
|
||||
|
||||
func AddDb(ctx context.Context, db *gorm.DB) context.Context {
|
||||
return context.WithValue(ctx, "db", db)
|
||||
}
|
||||
|
||||
func DB(ctx context.Context) *gorm.DB {
|
||||
if val, ok := ctx.Value("db").(*gorm.DB); ok {
|
||||
return val
|
||||
}
|
||||
log.Fatal().Msg("Could not find DB object")
|
||||
return nil
|
||||
}
|
||||
|
||||
func AutoMigrate(db *gorm.DB) {
|
||||
hasError := false
|
||||
|
||||
logger := log.With().Str("module", "sql-migrator").Logger()
|
||||
|
||||
for _, model := range []any{
|
||||
models.Commodity{},
|
||||
} {
|
||||
err := db.AutoMigrate(model)
|
||||
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Interface("model", model).Msg("Failed to auto migrate")
|
||||
hasError = true
|
||||
}
|
||||
}
|
||||
|
||||
if hasError {
|
||||
log.Panic().Msg("Exiting")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func FindCommodity(db *gorm.DB, term string) (*models.Commodity, bool, error) {
|
||||
result := models.Commodity{}
|
||||
tx := db.Model(models.Commodity{}).Where("ticker = ?", term).First(&result)
|
||||
|
||||
if tx.Error != nil {
|
||||
return nil, false, tx.Error
|
||||
}
|
||||
return &result, tx.RowsAffected > 0, nil
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package helpers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"code.lowsec.club/okawari/go-discord-bot/lib/models"
|
||||
"encoding/json"
|
||||
"github.com/rs/zerolog/log"
|
||||
"gorm.io/gorm"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func GetPayloadFromFIO() ([]models.FIOCommodity, error) {
|
||||
client := http.Client{}
|
||||
|
||||
body := bytes.NewBufferString("")
|
||||
request, err := http.NewRequest("GET", "https://rest.fnar.net/material/allmaterials", body)
|
||||
if err != nil {
|
||||
return []models.FIOCommodity{}, err
|
||||
}
|
||||
resp, err := client.Do(request)
|
||||
results := make([]models.FIOCommodity, 0)
|
||||
err = json.NewDecoder(resp.Body).Decode(&results)
|
||||
if err != nil {
|
||||
return []models.FIOCommodity{}, err
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func ImportFromFio(db *gorm.DB) error {
|
||||
|
||||
var count int64 = 0
|
||||
db.Table("commodities").Count(&count)
|
||||
|
||||
if count > 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
items, err := GetPayloadFromFIO()
|
||||
if err != nil {
|
||||
log.Panic().Err(err).Msg("Couldnt get payload from fio")
|
||||
}
|
||||
|
||||
for _, item := range items {
|
||||
log.Info().Str("ticker", item.Ticker).Msg("Importing item")
|
||||
|
||||
com := models.Commodity{}
|
||||
db.Where(models.Commodity{Ticker: item.Ticker}).Assign(item.ToCommodity()).FirstOrCreate(&com)
|
||||
|
||||
}
|
||||
|
||||
count = 0
|
||||
db.Table("commodities").Count(&count)
|
||||
log.Info().Int64("count", count).Msg("Counting rows in commodities")
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package helpers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
func Pointer[T any](v T) *T {
|
||||
return &v
|
||||
}
|
||||
|
||||
func EphemeralResponse(format string, any ...any) *discordgo.InteractionResponse {
|
||||
return &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Flags: discordgo.MessageFlagsEphemeral,
|
||||
Content: fmt.Sprintf(format, any...),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func InteractionErrorResponse(session *discordgo.Session, command *discordgo.InteractionCreate, logger *zerolog.Logger, err error) {
|
||||
err = session.InteractionRespond(command.Interaction, EphemeralResponse("%s", err))
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Msg("Unable to respond to request from user")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package logging
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/rs/zerolog"
|
||||
"gorm.io/gorm/logger"
|
||||
"time"
|
||||
)
|
||||
|
||||
type DBLogger struct {
|
||||
Logger zerolog.Logger
|
||||
}
|
||||
|
||||
func (D DBLogger) LogMode(_ logger.LogLevel) logger.Interface {
|
||||
return D
|
||||
}
|
||||
|
||||
func (D DBLogger) Info(ctx context.Context, s string, i ...interface{}) {
|
||||
D.Logger.Info().Ctx(ctx).Msgf(s, i...)
|
||||
}
|
||||
|
||||
func (D DBLogger) Warn(ctx context.Context, s string, i ...interface{}) {
|
||||
D.Logger.Warn().Ctx(ctx).Msgf(s, i...)
|
||||
}
|
||||
|
||||
func (D DBLogger) Error(ctx context.Context, s string, i ...interface{}) {
|
||||
D.Logger.Error().Ctx(ctx).Msgf(s, i...)
|
||||
}
|
||||
|
||||
func (D DBLogger) Trace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {
|
||||
tupl, rows := fc()
|
||||
D.Logger.Trace().Ctx(ctx).
|
||||
Time("begin", begin).
|
||||
Err(err).
|
||||
Str("sql", tupl).
|
||||
Int64("rows", rows).
|
||||
Msg("Query performed")
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package logging
|
||||
|
||||
import (
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"os"
|
||||
)
|
||||
|
||||
func InitLogging() error {
|
||||
|
||||
switch os.Getenv("LOG_FORMAT") {
|
||||
|
||||
default:
|
||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: zerolog.TimeFieldFormat})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package models
|
||||
|
||||
import "gorm.io/gorm"
|
||||
|
||||
type Commodity struct {
|
||||
gorm.Model
|
||||
|
||||
Ticker string `gorm:"index,unique"`
|
||||
Name string
|
||||
|
||||
Weight float64
|
||||
Volume float64
|
||||
|
||||
Category string
|
||||
}
|
||||
|
||||
type FIOCommodity struct {
|
||||
MaterialId string `json:"MaterialId"`
|
||||
Ticker string `json:"Ticker"`
|
||||
Name string `json:"Name"`
|
||||
Category string `json:"CategoryName"`
|
||||
|
||||
Volume float64 `json:"Volume"`
|
||||
Weight float64 `json:"Weight"`
|
||||
}
|
||||
|
||||
func (C FIOCommodity) ToCommodity() Commodity {
|
||||
return Commodity{
|
||||
Ticker: C.Ticker,
|
||||
Category: C.Category,
|
||||
Weight: C.Weight,
|
||||
Volume: C.Volume,
|
||||
Name: C.Name,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
package models
|
||||
|
||||
type User struct {
|
||||
}
|
Loading…
Reference in New Issue