Skip to content

Commit eceabc6

Browse files
committed
add discord handlers
1 parent 16c6c39 commit eceabc6

23 files changed

+1361
-5
lines changed

api/cmd/experiments/main.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package main
33
import (
44
"log"
55

6-
"github.com/NdoleStudio/httpsms/pkg/di"
76
"github.com/joho/godotenv"
87
)
98

@@ -12,5 +11,4 @@ func main() {
1211
if err != nil {
1312
log.Fatal("Error loading .env file")
1413
}
15-
_ = di.NewContainer("http-sms", "")
1614
}

api/go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ require (
3131
github.com/redis/go-redis/v9 v9.0.2
3232
github.com/rs/zerolog v1.29.0
3333
github.com/sendgrid/sendgrid-go v3.12.0+incompatible
34+
github.com/stretchr/testify v1.8.2
3435
github.com/swaggo/swag v1.8.10
3536
github.com/thedevsaddam/govalidator v1.9.10
3637
github.com/uptrace/uptrace-go v1.13.0
@@ -105,6 +106,7 @@ require (
105106
github.com/modern-go/reflect2 v1.0.2 // indirect
106107
github.com/olekukonko/tablewriter v0.0.5 // indirect
107108
github.com/philhofer/fwd v1.1.2 // indirect
109+
github.com/pmezard/go-difflib v1.0.0 // indirect
108110
github.com/rivo/uniseg v0.4.4 // indirect
109111
github.com/russross/blackfriday/v2 v2.1.0 // indirect
110112
github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 // indirect

api/go.sum

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,7 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
403403
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
404404
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
405405
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
406+
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
406407
github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a/go.mod h1:lKJPbtWzJ9JhsTN1k1gZgleJWY/cqq0psdoMmaThG3w=
407408
github.com/swaggo/files v1.0.0 h1:1gGXVIeUFCS/dta17rnP0iOpr6CXFwKD7EO5ID233e4=
408409
github.com/swaggo/files v1.0.0/go.mod h1:N59U6URJLyU1PQgFqPM7wXLMhJx7QAolnvfQkqO13kc=

api/pkg/di/container.go

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import (
99
"strconv"
1010
"time"
1111

12+
"github.com/NdoleStudio/httpsms/pkg/discord"
13+
1214
mexporter "github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric"
1315
cloudtrace "github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace"
1416
"go.opentelemetry.io/otel/sdk/metric"
@@ -113,6 +115,7 @@ func NewContainer(projectID string, version string) (container *Container) {
113115
container.RegisterWebhookListeners()
114116

115117
container.RegisterLemonsqueezyRoutes()
118+
container.RegisterDiscordRoutes()
116119

117120
// this has to be last since it registers the /* route
118121
container.RegisterSwaggerRoutes()
@@ -241,6 +244,10 @@ func (container *Container) DB() (db *gorm.DB) {
241244
container.logger.Fatal(stacktrace.Propagate(err, fmt.Sprintf("cannot migrate %T", &entities.Webhook{})))
242245
}
243246

247+
if err = db.AutoMigrate(&entities.Discord{}); err != nil {
248+
container.logger.Fatal(stacktrace.Propagate(err, fmt.Sprintf("cannot migrate %T", &entities.Discord{})))
249+
}
250+
244251
return container.db
245252
}
246253

@@ -424,6 +431,16 @@ func (container *Container) BillingHandlerValidator() (validator *validators.Bil
424431
)
425432
}
426433

434+
// DiscordHandlerValidator creates a new instance of validators.DiscordHandlerValidator
435+
func (container *Container) DiscordHandlerValidator() (validator *validators.DiscordHandlerValidator) {
436+
container.logger.Debug(fmt.Sprintf("creating %T", validator))
437+
return validators.NewDiscordHandlerValidator(
438+
container.Logger(),
439+
container.Tracer(),
440+
container.DiscordClient(),
441+
)
442+
}
443+
427444
// WebhookHandlerValidator creates a new instance of validators.WebhookHandlerValidator
428445
func (container *Container) WebhookHandlerValidator() (validator *validators.WebhookHandlerValidator) {
429446
container.logger.Debug(fmt.Sprintf("creating %T", validator))
@@ -520,6 +537,16 @@ func (container *Container) BillingUsageRepository() (repository repositories.Bi
520537
)
521538
}
522539

540+
// DiscordRepository creates a new instance of repositories.DiscordRepository
541+
func (container *Container) DiscordRepository() (repository repositories.DiscordRepository) {
542+
container.logger.Debug("creating GORM repositories.DiscordRepository")
543+
return repositories.NewGormDiscordRepository(
544+
container.Logger(),
545+
container.Tracer(),
546+
container.DB(),
547+
)
548+
}
549+
523550
// WebhookRepository creates a new instance of repositories.WebhookRepository
524551
func (container *Container) WebhookRepository() (repository repositories.WebhookRepository) {
525552
container.logger.Debug("creating GORM repositories.WebhookRepository")
@@ -606,6 +633,17 @@ func (container *Container) BillingService() (service *services.BillingService)
606633
)
607634
}
608635

636+
// DiscordService creates a new instance of services.DiscordService
637+
func (container *Container) DiscordService() (service *services.DiscordService) {
638+
container.logger.Debug(fmt.Sprintf("creating %T", service))
639+
return services.NewDiscordService(
640+
container.Logger(),
641+
container.Tracer(),
642+
container.DiscordClient(),
643+
container.DiscordRepository(),
644+
)
645+
}
646+
609647
// WebhookService creates a new instance of services.WebhookService
610648
func (container *Container) WebhookService() (service *services.WebhookService) {
611649
container.logger.Debug(fmt.Sprintf("creating %T", service))
@@ -813,6 +851,18 @@ func (container *Container) LemonsqueezyHandler() (handler *handlers.Lemonsqueez
813851
)
814852
}
815853

854+
// DiscordHandler creates a new instance of handlers.DiscordHandler
855+
func (container *Container) DiscordHandler() (handler *handlers.DiscordHandler) {
856+
container.logger.Debug(fmt.Sprintf("creating %T", handler))
857+
858+
return handlers.NewDiscordHandler(
859+
container.Logger(),
860+
container.Tracer(),
861+
container.DiscordHandlerValidator(),
862+
container.DiscordService(),
863+
)
864+
}
865+
816866
// LemonsqueezyHandlerValidator creates a new instance of validators.LemonsqueezyHandlerValidator
817867
func (container *Container) LemonsqueezyHandlerValidator() (validator *validators.LemonsqueezyHandlerValidator) {
818868
container.logger.Debug(fmt.Sprintf("creating %T", validator))
@@ -833,12 +883,27 @@ func (container *Container) LemonsqueezyClient() (client *lemonsqueezy.Client) {
833883
)
834884
}
835885

836-
// RegisterLemonsqueezyRoutes registers routes for the /project-settings prefix
886+
// DiscordClient creates a new instance of discord.Client
887+
func (container *Container) DiscordClient() (client *discord.Client) {
888+
container.logger.Debug(fmt.Sprintf("creating %T", client))
889+
return discord.New(
890+
discord.WithHTTPClient(container.HTTPClient("discord")),
891+
discord.WithBotToken(os.Getenv("DISCORD_BOT_TOKEN")),
892+
)
893+
}
894+
895+
// RegisterLemonsqueezyRoutes registers routes for the /lemonsqueezy prefix
837896
func (container *Container) RegisterLemonsqueezyRoutes() {
838897
container.logger.Debug(fmt.Sprintf("registering %T routes", &handlers.LemonsqueezyHandler{}))
839898
container.LemonsqueezyHandler().RegisterRoutes(container.App())
840899
}
841900

901+
// RegisterDiscordRoutes registers routes for the /discord prefix
902+
func (container *Container) RegisterDiscordRoutes() {
903+
container.logger.Debug(fmt.Sprintf("registering %T routes", &handlers.DiscordHandler{}))
904+
container.DiscordHandler().RegisterRoutes(container.App(), container.AuthenticatedMiddleware())
905+
}
906+
842907
// RegisterMessageThreadListeners registers event listeners for listeners.MessageThreadListener
843908
func (container *Container) RegisterMessageThreadListeners() {
844909
container.logger.Debug(fmt.Sprintf("registering listners for %T", listeners.MessageThreadListener{}))

api/pkg/discord/application.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package discord
2+
3+
// CommandCreateRequest is the request for creating a new command
4+
type CommandCreateRequest struct {
5+
Name string `json:"name"`
6+
Type int `json:"type"`
7+
Description string `json:"description"`
8+
Options []CommandCreateRequestOptions `json:"options"`
9+
}
10+
11+
// CommandCreateRequestOptions are options for creating a command
12+
type CommandCreateRequestOptions struct {
13+
Name string `json:"name"`
14+
Description string `json:"description"`
15+
Type int `json:"type"`
16+
Required bool `json:"required"`
17+
}
18+
19+
// CommandCreateResponse is the response after creating a command
20+
type CommandCreateResponse struct {
21+
ID string `json:"id"`
22+
ApplicationID string `json:"application_id"`
23+
Version string `json:"version"`
24+
DefaultMemberPermissions any `json:"default_member_permissions"`
25+
Type int `json:"type"`
26+
Name string `json:"name"`
27+
NameLocalizations any `json:"name_localizations"`
28+
Description string `json:"description"`
29+
DescriptionLocalizations any `json:"description_localizations"`
30+
GuildID string `json:"guild_id"`
31+
Options []CommandCreateResponseOptions `json:"options"`
32+
Nsfw bool `json:"nsfw"`
33+
}
34+
35+
// CommandCreateResponseOptions are options after creating a command
36+
type CommandCreateResponseOptions struct {
37+
Type int `json:"type"`
38+
Name string `json:"name"`
39+
NameLocalizations any `json:"name_localizations"`
40+
Description string `json:"description"`
41+
DescriptionLocalizations any `json:"description_localizations"`
42+
Required bool `json:"required"`
43+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package discord
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"net/http"
8+
)
9+
10+
// ApplicationService is the API client for the interacting with commands
11+
type ApplicationService service
12+
13+
// CreateCommand creates a new guild command
14+
//
15+
// API Docs: https://discord.com/developers/docs/interactions/application-commands#create-guild-application-command
16+
func (service *ApplicationService) CreateCommand(ctx context.Context, serverID string, channelID string, params *CommandCreateRequest) (*CommandCreateResponse, *Response, error) {
17+
url := fmt.Sprintf("/applications/%s/guilds/%s/commands", serverID, channelID)
18+
request, err := service.client.newRequest(ctx, http.MethodPost, url, params)
19+
if err != nil {
20+
return nil, nil, err
21+
}
22+
23+
response, err := service.client.do(request)
24+
if err != nil {
25+
return nil, response, err
26+
}
27+
28+
message := new(CommandCreateResponse)
29+
if err = json.Unmarshal(*response.Body, message); err != nil {
30+
return nil, response, err
31+
}
32+
33+
return message, response, nil
34+
}

api/pkg/discord/channel_service.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package discord
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"net/http"
8+
)
9+
10+
// ChannelService is the API client for interacting with channels
11+
type ChannelService service
12+
13+
// CreateMessage sends a message to a guild text or DM channel.
14+
//
15+
// API Docs: https://discord.com/developers/docs/resources/channel#create-message
16+
func (service *ChannelService) CreateMessage(ctx context.Context, channelID string, payload map[string]any) (*map[string]any, *Response, error) {
17+
request, err := service.client.newRequest(ctx, http.MethodPost, fmt.Sprintf("/channels/%s/messages", channelID), payload)
18+
if err != nil {
19+
return nil, nil, err
20+
}
21+
22+
response, err := service.client.do(request)
23+
if err != nil {
24+
return nil, response, err
25+
}
26+
27+
message := new(map[string]any)
28+
if err = json.Unmarshal(*response.Body, message); err != nil {
29+
return nil, response, err
30+
}
31+
32+
return message, response, nil
33+
}
34+
35+
// Get a channel by ID
36+
//
37+
// API Docs: https://discord.com/developers/docs/resources/channel#get-channel
38+
func (service *ChannelService) Get(ctx context.Context, channelID string) (*map[string]any, *Response, error) {
39+
request, err := service.client.newRequest(ctx, http.MethodGet, fmt.Sprintf("/channels/%s", channelID), nil)
40+
if err != nil {
41+
return nil, nil, err
42+
}
43+
44+
response, err := service.client.do(request)
45+
if err != nil {
46+
return nil, response, err
47+
}
48+
49+
channel := new(map[string]any)
50+
if err = json.Unmarshal(*response.Body, channel); err != nil {
51+
return nil, response, err
52+
}
53+
54+
return channel, response, nil
55+
}

0 commit comments

Comments
 (0)