From 0fff1ed54c8d1ade8131a48acd3f13e5356edf03 Mon Sep 17 00:00:00 2001 From: Penguin-71630 Date: Sun, 7 Dec 2025 17:22:42 +0800 Subject: [PATCH] init DiscordGo (2) --- api/client.go | 218 ++++++++++++++++++++++++++++++++++++++++++++++++ bot/commands.go | 128 ++++++++++++++++++++++++++++ go.sum | 17 ++++ 3 files changed, 363 insertions(+) create mode 100644 api/client.go create mode 100644 bot/commands.go create mode 100644 go.sum diff --git a/api/client.go b/api/client.go new file mode 100644 index 0000000..a6d9bc5 --- /dev/null +++ b/api/client.go @@ -0,0 +1,218 @@ +package api + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "time" +) + +type Client struct { + baseURL string + httpClient *http.Client + token string +} + +type Image struct { + ID string `json:"id"` + UploadedUserID string `json:"uploaded_user_id"` + UploadedAt time.Time `json:"uploaded_at"` + Aliases []string `json:"aliases"` + URL string `json:"url"` +} + +type ImagesResponse struct { + Images []Image `json:"images"` +} + +type AliasesResponse struct { + Aliases []string `json:"aliases"` +} + +func NewClient(baseURL string) *Client { + return &Client{ + baseURL: baseURL, + httpClient: &http.Client{ + Timeout: 30 * time.Second, + }, + } +} + +func (c *Client) SetToken(token string) { + c.token = token +} + +func (c *Client) GetImages(search string, limit, page int) (*ImagesResponse, error) { + url := fmt.Sprintf("%s/api/images?search=%s&limit=%d&page=%d", c.baseURL, search, limit, page) + + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return nil, err + } + + if c.token != "" { + req.Header.Set("Authorization", "Bearer "+c.token) + } + + resp, err := c.httpClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("API returned status %d", resp.StatusCode) + } + + var result ImagesResponse + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return nil, err + } + + return &result, nil +} + +func (c *Client) GetImage(id string) (*Image, error) { + url := fmt.Sprintf("%s/api/images/%s", c.baseURL, id) + + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return nil, err + } + + if c.token != "" { + req.Header.Set("Authorization", "Bearer "+c.token) + } + + resp, err := c.httpClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("API returned status %d", resp.StatusCode) + } + + var image Image + if err := json.NewDecoder(resp.Body).Decode(&image); err != nil { + return nil, err + } + + return &image, nil +} + +func (c *Client) GetAliases() (*AliasesResponse, error) { + url := fmt.Sprintf("%s/api/aliases", c.baseURL) + + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return nil, err + } + + if c.token != "" { + req.Header.Set("Authorization", "Bearer "+c.token) + } + + resp, err := c.httpClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("API returned status %d", resp.StatusCode) + } + + var result AliasesResponse + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return nil, err + } + + return &result, nil +} + +func (c *Client) UploadImage(imageData []byte, aliases []string) (*Image, error) { + // TODO: Implement multipart form upload + return nil, fmt.Errorf("not implemented") +} + +func (c *Client) DeleteImage(id string) error { + url := fmt.Sprintf("%s/api/images/%s", c.baseURL, id) + + req, err := http.NewRequest("DELETE", url, nil) + if err != nil { + return err + } + + if c.token != "" { + req.Header.Set("Authorization", "Bearer "+c.token) + } + + resp, err := c.httpClient.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusNoContent { + return fmt.Errorf("API returned status %d", resp.StatusCode) + } + + return nil +} + +func (c *Client) AddAlias(imageID, alias string) (*Image, error) { + url := fmt.Sprintf("%s/api/images/%s/aliases", c.baseURL, imageID) + + body := map[string]string{"alias": alias} + jsonBody, err := json.Marshal(body) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonBody)) + if err != nil { + return nil, err + } + + req.Header.Set("Content-Type", "application/json") + if c.token != "" { + req.Header.Set("Authorization", "Bearer "+c.token) + } + + resp, err := c.httpClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("API returned status %d", resp.StatusCode) + } + + var image Image + if err := json.NewDecoder(resp.Body).Decode(&image); err != nil { + return nil, err + } + + return &image, nil +} + +func (c *Client) GetImageFile(id string) ([]byte, error) { + url := fmt.Sprintf("%s/api/images/%s/file", c.baseURL, id) + + resp, err := c.httpClient.Get(url) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("API returned status %d", resp.StatusCode) + } + + return io.ReadAll(resp.Body) +} diff --git a/bot/commands.go b/bot/commands.go new file mode 100644 index 0000000..49e6f3a --- /dev/null +++ b/bot/commands.go @@ -0,0 +1,128 @@ +package bot + +import ( + "fmt" + "strings" + + "github.com/bwmarrin/discordgo" +) + +func (b *Bot) onMessageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { + // Ignore messages from the bot itself + if m.Author.ID == s.State.User.ID { + return + } + + // Check if message starts with prefix + if !strings.HasPrefix(m.Content, b.config.Prefix) { + return + } + + // Parse command + content := strings.TrimPrefix(m.Content, b.config.Prefix) + args := strings.Fields(content) + if len(args) == 0 { + return + } + + command := strings.ToLower(args[0]) + commandArgs := args[1:] + + // Route commands + switch command { + case "help": + b.handleHelp(s, m) + case "meme": + b.handleMeme(s, m, commandArgs) + case "upload": + b.handleUpload(s, m, commandArgs) + case "search": + b.handleSearch(s, m, commandArgs) + case "aliases": + b.handleAliases(s, m) + case "ping": + b.handlePing(s, m) + default: + s.ChannelMessageSend(m.ChannelID, fmt.Sprintf("Unknown command: `%s`. Use `%shelp` for available commands.", command, b.config.Prefix)) + } +} + +func (b *Bot) handleHelp(s *discordgo.Session, m *discordgo.MessageCreate) { + embed := &discordgo.MessageEmbed{ + Title: "🤖 MemeBot Commands", + Description: "Available commands for the MemeBot", + Color: 0x00ff00, + Fields: []*discordgo.MessageEmbedField{ + { + Name: fmt.Sprintf("%shelp", b.config.Prefix), + Value: "Show this help message", + Inline: false, + }, + { + Name: fmt.Sprintf("%smeme ", b.config.Prefix), + Value: "Get a meme by alias", + Inline: false, + }, + { + Name: fmt.Sprintf("%ssearch ", b.config.Prefix), + Value: "Search for memes by keyword", + Inline: false, + }, + { + Name: fmt.Sprintf("%supload ", b.config.Prefix), + Value: "Upload a meme with aliases", + Inline: false, + }, + { + Name: fmt.Sprintf("%saliases", b.config.Prefix), + Value: "List all available aliases", + Inline: false, + }, + { + Name: fmt.Sprintf("%sping", b.config.Prefix), + Value: "Check if bot is responsive", + Inline: false, + }, + }, + } + + s.ChannelMessageSendEmbed(m.ChannelID, embed) +} + +func (b *Bot) handleMeme(s *discordgo.Session, m *discordgo.MessageCreate, args []string) { + if len(args) == 0 { + s.ChannelMessageSend(m.ChannelID, fmt.Sprintf("Usage: `%smeme `", b.config.Prefix)) + return + } + + alias := strings.Join(args, " ") + + // TODO: Implement API call to get meme by alias + s.ChannelMessageSend(m.ChannelID, fmt.Sprintf("🔍 Searching for meme with alias: `%s`\n(API integration pending)", alias)) +} + +func (b *Bot) handleSearch(s *discordgo.Session, m *discordgo.MessageCreate, args []string) { + if len(args) == 0 { + s.ChannelMessageSend(m.ChannelID, fmt.Sprintf("Usage: `%ssearch `", b.config.Prefix)) + return + } + + keyword := strings.Join(args, " ") + + // TODO: Implement API call to search memes + s.ChannelMessageSend(m.ChannelID, fmt.Sprintf("🔍 Searching for: `%s`\n(API integration pending)", keyword)) +} + +func (b *Bot) handleUpload(s *discordgo.Session, m *discordgo.MessageCreate, args []string) { + // TODO: Implement meme upload functionality + s.ChannelMessageSend(m.ChannelID, "📤 Upload functionality coming soon!\n(API integration pending)") +} + +func (b *Bot) handleAliases(s *discordgo.Session, m *discordgo.MessageCreate) { + // TODO: Implement API call to get all aliases + s.ChannelMessageSend(m.ChannelID, "📋 Fetching all aliases...\n(API integration pending)") +} + +func (b *Bot) handlePing(s *discordgo.Session, m *discordgo.MessageCreate) { + s.ChannelMessageSend(m.ChannelID, "🏓 Pong! Bot is online.") +} diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..d666e98 --- /dev/null +++ b/go.sum @@ -0,0 +1,17 @@ +github.com/bwmarrin/discordgo v0.27.1 h1:ib9AIc/dom1E/fSIulrBwnez0CToJE113ZGt4HoliGY= +github.com/bwmarrin/discordgo v0.27.1/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=