Skip to content

Commit a674363

Browse files
committed
feat(sources): discord basics
1 parent 21d896f commit a674363

File tree

9 files changed

+480
-80
lines changed

9 files changed

+480
-80
lines changed

cmd/hyperdocs/main.go

+10-80
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
package main
22

33
import (
4-
"bytes"
54
"fmt"
65
"log"
76
"os"
87
"os/signal"
9-
"strings"
108
"syscall"
11-
"text/template"
129

1310
"github.com/bwmarrin/discordgo"
1411
"github.com/joho/godotenv"
1512

13+
"hyperdocs/internal/sources"
1614
discordgoutil "hyperdocs/pkg/discordgo"
1715
)
1816

@@ -30,81 +28,7 @@ var (
3028

3129
var discordDocTmpl = `https://discord.dev/{{ .topic.Value }}/{{ .page.Value }}{{ with $x := (index . "paragraph-id").Value }}#{{ $x }}{{ end }}`
3230

33-
var (
34-
commands = []*discordgo.ApplicationCommand{
35-
{
36-
Name: "docs",
37-
Description: "Search through documentation",
38-
Options: []*discordgo.ApplicationCommandOption{
39-
{
40-
Name: "discord",
41-
Description: "Discord API documentation",
42-
Type: discordgo.ApplicationCommandOptionSubCommand,
43-
Options: []*discordgo.ApplicationCommandOption{
44-
{
45-
Name: "topic",
46-
Description: "Topic name",
47-
Type: discordgo.ApplicationCommandOptionString,
48-
Required: true,
49-
},
50-
{
51-
Name: "page",
52-
Description: "Page name",
53-
Type: discordgo.ApplicationCommandOptionString,
54-
Required: true,
55-
},
56-
{
57-
Name: "paragraph-id",
58-
Description: "Id of the paragraph to retrieve",
59-
Type: discordgo.ApplicationCommandOptionString,
60-
},
61-
},
62-
},
63-
},
64-
},
65-
}
66-
commandHandlers = discordgoutil.InteractionHandlerMap{
67-
"docs discord": func(s *discordgo.Session, i *discordgo.InteractionCreate) {
68-
optionsMap := map[string]*discordgo.ApplicationCommandInteractionDataOption{}
69-
for _, opt := range i.ApplicationCommandData().Options[0].Options {
70-
if opt.Type == discordgo.ApplicationCommandOptionString {
71-
nesting := strings.Split(opt.StringValue(), ":")
72-
for i, element := range nesting {
73-
nesting[i] = strings.Join(strings.Split(strings.TrimSpace(element), " "), "-")
74-
}
75-
76-
opt.Value = strings.ToLower(strings.Join(nesting, "-"))
77-
}
78-
optionsMap[opt.Name] = opt
79-
80-
}
81-
82-
err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
83-
Type: discordgo.InteractionResponseChannelMessageWithSource,
84-
Data: &discordgo.InteractionResponseData{
85-
Content: executeTemplate(discordDocTmpl, optionsMap),
86-
},
87-
})
88-
89-
if err != nil {
90-
panic(err)
91-
}
92-
},
93-
}
94-
)
95-
96-
func executeTemplate(tmpl string, data interface{}) string {
97-
98-
buf := new(bytes.Buffer)
99-
err := template.Must(template.New("doc-url").Parse(tmpl)).Execute(buf, data)
100-
if err != nil {
101-
panic(fmt.Errorf("template: %w", err))
102-
}
103-
104-
return buf.String()
105-
}
106-
107-
func registerCommands(session *discordgo.Session) {
31+
func registerCommands(session *discordgo.Session, commands []*discordgo.ApplicationCommand) {
10832
guild := os.Getenv("DISCORD_GUILD")
10933
app := os.Getenv("DISCORD_ID")
11034
for _, cmd := range commands {
@@ -135,9 +59,15 @@ func main() {
13559
panic(fmt.Errorf("cannot construct session: %w", err))
13660
}
13761

138-
registerCommands(session)
62+
registerCommands(session, []*discordgo.ApplicationCommand{
63+
{
64+
Name: "docs",
65+
Description: "Open sesame the documentation vault.",
66+
Options: optionsFromSourceList(sources.Sources),
67+
},
68+
})
13969

140-
session.AddHandler(discordgoutil.NewCommandHandler(commandHandlers))
70+
session.AddHandler(discordgoutil.NewCommandHandler(makeHandlersMap(sources.Sources)))
14171
session.AddHandler(func(s *discordgo.Session, r *discordgo.Ready) {
14272
log.Println("Bot is up!")
14373
})

cmd/hyperdocs/sources.go

+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
"hyperdocs/internal/sources"
8+
discordgoutil "hyperdocs/pkg/discordgo"
9+
10+
"github.com/bwmarrin/discordgo"
11+
)
12+
13+
func newCommandFromSource(src sources.Source) *discordgo.ApplicationCommandOption {
14+
return &discordgo.ApplicationCommandOption{
15+
Type: discordgo.ApplicationCommandOptionSubCommand,
16+
Name: src.Name(),
17+
Description: src.Description(),
18+
Options: src.Options(),
19+
}
20+
}
21+
22+
func optionsFromSourceList(list []sources.Source) (res []*discordgo.ApplicationCommandOption) {
23+
for _, v := range list {
24+
res = append(res, newCommandFromSource(v))
25+
}
26+
return
27+
}
28+
29+
func makeInteractionHandler(src sources.Source) func(s *discordgo.Session, i *discordgo.InteractionCreate) {
30+
return func(s *discordgo.Session, i *discordgo.InteractionCreate) {
31+
var symbol sources.Symbol
32+
var err error
33+
src.Process(context.TODO(), s, i)
34+
symbol, err = src.Search(context.TODO(), s, i)
35+
36+
if errors.Is(err, sources.ErrSymbolNotFound) {
37+
err = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
38+
Type: discordgo.InteractionResponseChannelMessageWithSource,
39+
Data: &discordgo.InteractionResponseData{
40+
Content: "**Error: nothing had been found by this query**",
41+
Flags: 1 << 6,
42+
},
43+
})
44+
if err != nil {
45+
fmt.Println(err)
46+
}
47+
return
48+
}
49+
desc, fields := symbol.Render()
50+
err = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
51+
Type: discordgo.InteractionResponseChannelMessageWithSource,
52+
Data: &discordgo.InteractionResponseData{
53+
Embeds: []*discordgo.MessageEmbed{
54+
{
55+
Title: symbol.GetName(),
56+
URL: symbol.GetLink(),
57+
Description: desc,
58+
Fields: fields,
59+
Color: 0x0F0D0D,
60+
},
61+
},
62+
},
63+
})
64+
65+
if err != nil {
66+
fmt.Println(err)
67+
}
68+
69+
// TODO: context
70+
71+
// ctx, cancel := context.WithTimeout(context.Background(), discordgo.InteractionDeadline-time.Second)
72+
73+
// var symbol sources.Symbol, err error
74+
75+
// go func() {
76+
// src.Process(ctx, s, i)
77+
// symbol, err = src.Search(ctx, s, i)
78+
// cancel()
79+
// }()
80+
81+
// <-ctx.Done()
82+
// if errors.Is(ctx.Err(), context.DeadlineExceeded) {
83+
// err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
84+
// Type: discordgo.InteractionResponseDeferredChannelMessageWithSource,
85+
// })
86+
87+
// if err != nil {
88+
// fmt.Println(err) // TODO: logrus
89+
// }
90+
// } else {
91+
// s.InteractionRespond()
92+
// }
93+
// case <-end:
94+
// if errors.Is(ctx.Err(), context.DeadlineExceeded) {
95+
// } else {
96+
97+
// }
98+
// }
99+
}
100+
}
101+
102+
func makeHandlersMap(sourcesList []sources.Source) (handlerMap discordgoutil.InteractionHandlerMap) {
103+
handlerMap = make(discordgoutil.InteractionHandlerMap, len(sourcesList))
104+
for _, src := range sourcesList {
105+
handlerMap["docs"+" "+src.Name()] = makeInteractionHandler(src)
106+
}
107+
return
108+
}

go.mod

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ module hyperdocs
33
go 1.15
44

55
require (
6+
github.com/PuerkitoBio/goquery v1.7.1
67
github.com/bwmarrin/discordgo v0.23.3-0.20210821175000-0fad116c6c2a
8+
github.com/gomarkdown/markdown v0.0.0-20210820032736-385812cbea76
79
github.com/joho/godotenv v1.3.0
810
)

go.sum

+12
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,26 @@
1+
github.com/PuerkitoBio/goquery v1.7.1 h1:oE+T06D+1T7LNrn91B4aERsRIeCLJ/oPSa6xB9FPnz4=
2+
github.com/PuerkitoBio/goquery v1.7.1/go.mod h1:XY0pP4kfraEmmV1O7Uf6XyjoslwsneBbgeDjLYuN8xY=
3+
github.com/andybalholm/cascadia v1.2.0 h1:vuRCkM5Ozh/BfmsaTm26kbjm0mIOM3yS5Ek/F5h18aE=
4+
github.com/andybalholm/cascadia v1.2.0/go.mod h1:YCyR8vOZT9aZ1CHEd8ap0gMVm2aFgxBp0T0eFw1RUQY=
15
github.com/bwmarrin/discordgo v0.23.3-0.20210821175000-0fad116c6c2a h1:L7EuIzka83l5Z7LQqpSBfvmTNvUdr9tGhBa0mDBgSsc=
26
github.com/bwmarrin/discordgo v0.23.3-0.20210821175000-0fad116c6c2a/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY=
7+
github.com/gomarkdown/markdown v0.0.0-20210820032736-385812cbea76 h1:nrGXmvqQjFCxD27hosXjoVaDtPL1tVvJ6iGXucNXCVE=
8+
github.com/gomarkdown/markdown v0.0.0-20210820032736-385812cbea76/go.mod h1:aii0r/K0ZnHv7G0KF7xy1v0A7s2Ljrb5byB7MO5p6TU=
39
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
410
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
511
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
612
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
13+
golang.org/dl v0.0.0-20190829154251-82a15e2f2ead/go.mod h1:IUMfjQLJQd4UTqG1Z90tenwKoCX93Gn3MAQJMOSBsDQ=
714
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg=
815
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
16+
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
917
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
18+
golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
19+
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
1020
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
1121
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
22+
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
1223
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
1324
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
25+
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
1426
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

0 commit comments

Comments
 (0)