Skip to content

Commit 0af3c8f

Browse files
leamirSouthclaws
authored andcommitted
Update wiki command
1 parent 92ab292 commit 0af3c8f

File tree

1 file changed

+86
-62
lines changed

1 file changed

+86
-62
lines changed

bot/commands/cmd_wiki.go

Lines changed: 86 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -4,53 +4,27 @@ import (
44
"encoding/json"
55
"fmt"
66
"html"
7-
"net/http"
87
"strings"
98

109
"github.com/bwmarrin/discordgo"
1110

1211
"github.com/Southclaws/cj/types"
12+
13+
"github.com/algolia/algoliasearch-client-go/v4/algolia/search"
14+
15+
"go.uber.org/zap"
1316
)
1417

1518
const cmdUsage = "USAGE: /wiki [function/callback]"
1619

17-
type Results struct {
18-
Status struct {
19-
Total int `json:"total"`
20-
Failed int `json:"failed"`
21-
Successful int `json:"successful"`
22-
} `json:"status"`
23-
Request struct {
24-
Query struct {
25-
Query string `json:"query"`
26-
} `json:"query"`
27-
Size int `json:"size"`
28-
From int `json:"from"`
29-
Highlight struct {
30-
Style interface{} `json:"style"`
31-
Fields interface{} `json:"fields"`
32-
} `json:"highlight"`
33-
Fields interface{} `json:"fields"`
34-
Facets interface{} `json:"facets"`
35-
Explain bool `json:"explain"`
36-
Sort []string `json:"sort"`
37-
IncludeLocations bool `json:"includeLocations"`
38-
SearchAfter interface{} `json:"search_after"`
39-
SearchBefore interface{} `json:"search_before"`
40-
} `json:"request"`
41-
Hits []Hit `json:"hits"`
42-
TotalHits int `json:"total"`
43-
Took int64 `json:"took"`
44-
}
45-
46-
type Hit struct {
47-
Url string `json:"url"`
48-
Title string `json:"title"`
49-
Description string `json:"desc"`
50-
TitleFragments string `json:"title_fragment"`
51-
DescriptionFragments string `json:"desc_fragment"`
52-
Score float64 `json:"score"`
53-
}
20+
// Algolia config
21+
const (
22+
algoliaAppID = "AOKXGK39Z7"
23+
algoliaAPIKey = "54204f37e5c8fc2871052d595ee0505e" //Safe to commit
24+
algoliaIndexName = "open"
25+
algoliaContextualSearch = true
26+
algoliaInsights = false
27+
)
5428

5529
func (cm *CommandManager) commandWiki(
5630
interaction *discordgo.InteractionCreate,
@@ -66,19 +40,32 @@ func (cm *CommandManager) commandWiki(
6640
return
6741
}
6842

69-
r, err := http.Get(fmt.Sprintf("https://api.open.mp/docs/search?q=%s", strings.ReplaceAll(searchTerm, " ", "%20")))
70-
if err != nil {
71-
cm.replyDirectly(interaction, fmt.Sprintf("Failed to GET result for search term %s\nError: %s", searchTerm, err.Error()))
43+
algoliaClient, algoliaErr := search.NewClient(algoliaAppID, algoliaAPIKey)
44+
45+
if algoliaErr != nil {
46+
cm.replyDirectly(interaction, fmt.Sprintf("Failed to initialise Search client: \n%s", algoliaErr.Error()))
47+
zap.L().Error("Failed to initialise Search client", zap.Error(algoliaErr))
7248
return
7349
}
7450

75-
var results Results
76-
if err = json.NewDecoder(r.Body).Decode(&results); err != nil {
77-
cm.replyDirectly(interaction, fmt.Sprintf("Failed to decode result for search term %s\nError: %s\n", searchTerm, err.Error()))
51+
response, err := algoliaClient.Search(algoliaClient.NewApiSearchRequest(
52+
search.NewEmptySearchMethodParams().SetRequests(
53+
[]search.SearchQuery{*search.SearchForHitsAsSearchQuery(
54+
search.NewEmptySearchForHits().
55+
SetIndexName(algoliaIndexName).
56+
SetQuery(searchTerm).
57+
SetHitsPerPage(3),
58+
)},
59+
),
60+
))
61+
if err != nil {
62+
cm.replyDirectly(interaction, fmt.Sprintf("Failed to search: %s", err.Error()))
7863
return
7964
}
8065

81-
if results.TotalHits == 0 {
66+
finalResult := response.Results[0]
67+
68+
if *finalResult.SearchResponse.NbHits == 0 {
8269
cm.replyDirectlyEmbed(interaction, "", &discordgo.MessageEmbed{
8370
Type: discordgo.EmbedTypeRich,
8471
Title: fmt.Sprintf("No results: %s", searchTerm),
@@ -89,24 +76,65 @@ func (cm *CommandManager) commandWiki(
8976

9077
desc := strings.Builder{}
9178

92-
rendered := 0
93-
for _, hit := range results.Hits {
94-
if rendered == 3 {
95-
break
79+
seenUrls := make(map[string]bool)
80+
81+
actuallyFoundResults := 0
82+
for _, hit := range finalResult.SearchResponse.Hits {
83+
var hitData map[string]interface{}
84+
hitJSON, err := json.Marshal(hit)
85+
if err != nil {
86+
// Would reply, but then it may error again - and we cant re-send messages
87+
// cm.replyDirectly(interaction, fmt.Sprintf("Error marshalling hit: %s", err.Error()))
88+
continue
89+
}
90+
91+
if err := json.Unmarshal(hitJSON, &hitData); err != nil {
92+
// Would reply, but then it may error again - and we cant re-send messages
93+
// cm.replyDirectly(interaction, fmt.Sprintf("Error unmarshalling hit: %s", err.Error()))
94+
continue
95+
}
96+
97+
if seenUrls[hitData["url_without_anchor"].(string)] { //Already presented to user - Algolia does this thing of sending twice the same thing
98+
continue
9699
}
97100

98-
// Skip searching translations
99-
if strings.Contains(hit.Url, "translations") {
101+
seenUrls[hitData["url_without_anchor"].(string)] = true
102+
103+
stringParts := strings.Split(strings.TrimSuffix(hitData["url_without_anchor"].(string), "/"), "/") //Algolia doesnt give the Function/Callback name - so I steal it from the URL
104+
105+
if stringParts[len(stringParts)-2] == "blog" { //Remove blog posts from results
100106
continue
101107
}
102108

109+
actuallyFoundResults++
110+
111+
content, ok := hitData["content"].(string)
112+
description := ""
113+
if !ok {
114+
description = "(No description found)"
115+
} else {
116+
description = formatDescription(&content)
117+
}
118+
103119
desc.WriteString(fmt.Sprintf(
104-
"[%s](https://open.mp/%s): %s\n",
105-
hit.Title,
106-
strings.TrimSuffix(hit.Url, ".md"),
107-
formatDescription(hit)))
108-
rendered++
120+
"[%s](%s) [%s/%s]: %s\n",
121+
stringParts[len(stringParts)-1],
122+
hitData["url_without_anchor"].(string),
123+
stringParts[len(stringParts)-3],
124+
stringParts[len(stringParts)-2],
125+
description,
126+
))
109127
}
128+
129+
if actuallyFoundResults == 0 { //Hitting this means all results were filtered out
130+
cm.replyDirectlyEmbed(interaction, "", &discordgo.MessageEmbed{
131+
Type: discordgo.EmbedTypeRich,
132+
Title: fmt.Sprintf("No results: %s", searchTerm),
133+
Description: "There were no results for that query.",
134+
})
135+
return
136+
}
137+
110138
embed := &discordgo.MessageEmbed{
111139
Type: discordgo.EmbedTypeRich,
112140
Title: fmt.Sprintf("Documentation Search Results: %s", searchTerm),
@@ -117,14 +145,10 @@ func (cm *CommandManager) commandWiki(
117145
return false, err // Todo: remove this
118146
}
119147

120-
func formatDescription(hit Hit) string {
121-
if len(hit.Description) == 0 {
122-
return "(No description found)"
123-
}
124-
148+
func formatDescription(hit *string) string {
125149
return html.UnescapeString(strings.ReplaceAll(
126150
strings.ReplaceAll(
127-
hit.Description,
151+
*hit,
128152
"<mark>", "**"),
129153
"</mark>", "**"))
130154
}

0 commit comments

Comments
 (0)