feat: discord commands
This commit is contained in:
		
							parent
							
								
									0b2b0a1358
								
							
						
					
					
						commit
						512e977d0d
					
				
					 2 changed files with 169 additions and 0 deletions
				
			
		|  | @ -2,6 +2,7 @@ package main | |||
| 
 | ||||
| import ( | ||||
| 	"codeberg.org/u1f320/pronouns.cc/backend/routes/auth" | ||||
| 	"codeberg.org/u1f320/pronouns.cc/backend/routes/bot" | ||||
| 	"codeberg.org/u1f320/pronouns.cc/backend/routes/user" | ||||
| 	"codeberg.org/u1f320/pronouns.cc/backend/server" | ||||
| 	"github.com/go-chi/chi/v5" | ||||
|  | @ -14,5 +15,6 @@ func mountRoutes(s *server.Server) { | |||
| 	s.Router.Route("/v1", func(r chi.Router) { | ||||
| 		auth.Mount(s, r) | ||||
| 		user.Mount(s, r) | ||||
| 		bot.Mount(s, r) | ||||
| 	}) | ||||
| } | ||||
|  |  | |||
							
								
								
									
										167
									
								
								backend/routes/bot/bot.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										167
									
								
								backend/routes/bot/bot.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,167 @@ | |||
| package bot | ||||
| 
 | ||||
| import ( | ||||
| 	"crypto/ed25519" | ||||
| 	"encoding/hex" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 
 | ||||
| 	"codeberg.org/u1f320/pronouns.cc/backend/db" | ||||
| 	"codeberg.org/u1f320/pronouns.cc/backend/log" | ||||
| 	"codeberg.org/u1f320/pronouns.cc/backend/server" | ||||
| 	"github.com/bwmarrin/discordgo" | ||||
| 	"github.com/go-chi/chi/v5" | ||||
| 	"github.com/go-chi/render" | ||||
| ) | ||||
| 
 | ||||
| type Bot struct { | ||||
| 	*server.Server | ||||
| 
 | ||||
| 	publicKey ed25519.PublicKey | ||||
| 	baseURL   string | ||||
| } | ||||
| 
 | ||||
| func Mount(srv *server.Server, r chi.Router) { | ||||
| 	publicKey, err := hex.DecodeString(os.Getenv("DISCORD_PUBLIC_KEY")) | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	b := &Bot{ | ||||
| 		Server:    srv, | ||||
| 		publicKey: publicKey, | ||||
| 		baseURL:   os.Getenv("BASE_URL"), | ||||
| 	} | ||||
| 
 | ||||
| 	r.HandleFunc("/interactions", b.handle) | ||||
| } | ||||
| 
 | ||||
| func (bot *Bot) handle(w http.ResponseWriter, r *http.Request) { | ||||
| 	if !discordgo.VerifyInteraction(r, bot.publicKey) { | ||||
| 		http.Error(w, "Forbidden", http.StatusForbidden) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	var ev *discordgo.InteractionCreate | ||||
| 
 | ||||
| 	if err := json.NewDecoder(r.Body).Decode(&ev); err != nil { | ||||
| 		http.Error(w, "Bad Request", http.StatusBadRequest) | ||||
| 	} | ||||
| 
 | ||||
| 	// we can always respond to ping with pong | ||||
| 	if ev.Type == discordgo.InteractionPing { | ||||
| 		log.Debug("received ping interaction") | ||||
| 		render.JSON(w, r, discordgo.InteractionResponse{ | ||||
| 			Type: discordgo.InteractionResponsePong, | ||||
| 		}) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if ev.Type != discordgo.InteractionApplicationCommand { | ||||
| 		return | ||||
| 	} | ||||
| 	data := ev.ApplicationCommandData() | ||||
| 
 | ||||
| 	switch data.Name { | ||||
| 	case "Show user's pronouns": | ||||
| 		bot.userPronouns(w, r, ev) | ||||
| 	case "Show author's pronouns": | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (bot *Bot) userPronouns(w http.ResponseWriter, r *http.Request, ev *discordgo.InteractionCreate) { | ||||
| 	ctx := r.Context() | ||||
| 
 | ||||
| 	var du *discordgo.User | ||||
| 	for _, user := range ev.ApplicationCommandData().Resolved.Users { | ||||
| 		du = user | ||||
| 		break | ||||
| 	} | ||||
| 	if du == nil { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	u, err := bot.DB.DiscordUser(ctx, du.ID) | ||||
| 	if err != nil { | ||||
| 		if err == db.ErrUserNotFound { | ||||
| 			respond(w, r, &discordgo.MessageEmbed{ | ||||
| 				Description: du.String() + " does not have any pronouns set.", | ||||
| 			}) | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		log.Errorf("getting discord user: %v", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	avatarURL := du.AvatarURL("") | ||||
| 	if u.AvatarURL != nil { | ||||
| 		avatarURL = *u.AvatarURL | ||||
| 	} | ||||
| 	name := u.Username | ||||
| 	if u.DisplayName != nil { | ||||
| 		name = fmt.Sprintf("%s (%s)", *u.DisplayName, u.Username) | ||||
| 	} | ||||
| 	url := bot.baseURL | ||||
| 	if url != "" { | ||||
| 		url += "/@" + u.Username | ||||
| 	} | ||||
| 
 | ||||
| 	e := &discordgo.MessageEmbed{ | ||||
| 		Author: &discordgo.MessageEmbedAuthor{ | ||||
| 			Name:    name, | ||||
| 			IconURL: avatarURL, | ||||
| 			URL:     url, | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	if u.Bio != nil { | ||||
| 		e.Fields = append(e.Fields, &discordgo.MessageEmbedField{ | ||||
| 			Name:  "Bio", | ||||
| 			Value: *u.Bio, | ||||
| 		}) | ||||
| 	} | ||||
| 
 | ||||
| 	fields, err := bot.DB.UserFields(ctx, u.ID) | ||||
| 	if err != nil { | ||||
| 		respond(w, r, e) | ||||
| 
 | ||||
| 		log.Errorf("getting user fields: %v", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	for _, field := range fields { | ||||
| 		if len(field.Favourite) == 0 { | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		var value string | ||||
| 		for _, fav := range field.Favourite { | ||||
| 			if len(value) > 500 { | ||||
| 				break | ||||
| 			} | ||||
| 
 | ||||
| 			value += fav + "\n" | ||||
| 		} | ||||
| 
 | ||||
| 		e.Fields = append(e.Fields, &discordgo.MessageEmbedField{ | ||||
| 			Name:   field.Name, | ||||
| 			Value:  value, | ||||
| 			Inline: true, | ||||
| 		}) | ||||
| 	} | ||||
| 
 | ||||
| 	respond(w, r, e) | ||||
| } | ||||
| 
 | ||||
| func respond(w http.ResponseWriter, r *http.Request, embeds ...*discordgo.MessageEmbed) { | ||||
| 	render.JSON(w, r, discordgo.InteractionResponse{ | ||||
| 		Type: discordgo.InteractionResponseChannelMessageWithSource, | ||||
| 		Data: &discordgo.InteractionResponseData{ | ||||
| 			Embeds: embeds, | ||||
| 			Flags:  uint64(discordgo.MessageFlagsEphemeral), | ||||
| 		}, | ||||
| 	}) | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue