Improved role selection
This commit is contained in:
		@ -39,8 +39,9 @@ type StatusPortal struct {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type RoleSelectionConfig struct {
 | 
					type RoleSelectionConfig struct {
 | 
				
			||||||
	Title       string `yaml:"title"`
 | 
						Title              string  `yaml:"title"`
 | 
				
			||||||
	Description string `yaml:"description"`
 | 
						Description        string  `yaml:"description"`
 | 
				
			||||||
 | 
						GenerateColorEmoji Feature `yaml:"generate_color_emoji"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	SelectionChannel string       `yaml:"discord_channel"`
 | 
						SelectionChannel string       `yaml:"discord_channel"`
 | 
				
			||||||
	Roles            []RoleConfig `yaml:"roles"`
 | 
						Roles            []RoleConfig `yaml:"roles"`
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										33
									
								
								core/image.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								core/image.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,33 @@
 | 
				
			|||||||
 | 
					package core
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"encoding/base64"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"image"
 | 
				
			||||||
 | 
						"image/color"
 | 
				
			||||||
 | 
						"image/draw"
 | 
				
			||||||
 | 
						"image/jpeg"
 | 
				
			||||||
 | 
						"log"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ImageToBase64(img image.Image) string {
 | 
				
			||||||
 | 
						data := &bytes.Buffer{}
 | 
				
			||||||
 | 
						if err := jpeg.Encode(data, img, nil); err != nil {
 | 
				
			||||||
 | 
							log.Println("Error encoding avatar: ", err)
 | 
				
			||||||
 | 
							return ""
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return fmt.Sprintf("data:image/png;base64,%s", base64.StdEncoding.EncodeToString(data.Bytes()))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ColorToImage creates a 16x16 image filled with the specified color.
 | 
				
			||||||
 | 
					func ColorToImage(c color.Color) image.Image {
 | 
				
			||||||
 | 
						// Create a new rectangle with the desired dimensions.
 | 
				
			||||||
 | 
						rect := image.Rect(0, 0, 16, 16)
 | 
				
			||||||
 | 
						// Create a new RGBA image with the specified rectangle.
 | 
				
			||||||
 | 
						img := image.NewRGBA(rect)
 | 
				
			||||||
 | 
						// Use the draw.Draw function to fill the image with the specified color.
 | 
				
			||||||
 | 
						draw.Draw(img, img.Bounds(), &image.Uniform{C: c}, image.Point{}, draw.Src)
 | 
				
			||||||
 | 
						return img
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -30,3 +30,27 @@ func (discord *Discord) CreateMessageComponent(channelID string, content string,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return result.ID
 | 
						return result.ID
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// UpdateMessageComponent updates a discord component
 | 
				
			||||||
 | 
					func (discord *Discord) UpdateMessageComponent(messageID string, channelID string, content string, components []Component) string {
 | 
				
			||||||
 | 
						dComponents := make([]discordgo.MessageComponent, len(components))
 | 
				
			||||||
 | 
						for i, v := range components {
 | 
				
			||||||
 | 
							dComponents[i] = v.toMessageComponent()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						result, err := discord.session.ChannelMessageEditComplex(&discordgo.MessageEdit{
 | 
				
			||||||
 | 
							Components: dComponents,
 | 
				
			||||||
 | 
							Content:    &content,
 | 
				
			||||||
 | 
							Channel:    channelID,
 | 
				
			||||||
 | 
							ID:         messageID,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Printf("Error updating message component: %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if result != nil {
 | 
				
			||||||
 | 
							return result.ID
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -9,6 +9,7 @@ type Button struct {
 | 
				
			|||||||
	Label string
 | 
						Label string
 | 
				
			||||||
	ID    string
 | 
						ID    string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Emoji   *Emoji
 | 
				
			||||||
	discord *Discord
 | 
						discord *Discord
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -18,6 +19,17 @@ func (discord *Discord) NewButton(id string, label string) *Button {
 | 
				
			|||||||
		discord: discord,
 | 
							discord: discord,
 | 
				
			||||||
		ID:      id,
 | 
							ID:      id,
 | 
				
			||||||
		Label:   label,
 | 
							Label:   label,
 | 
				
			||||||
 | 
							Emoji:   nil,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewButtonWithEmoji creates a new button component with a emoji
 | 
				
			||||||
 | 
					func (discord *Discord) NewButtonWithEmoji(id string, label string, emoji *Emoji) *Button {
 | 
				
			||||||
 | 
						return &Button{
 | 
				
			||||||
 | 
							discord: discord,
 | 
				
			||||||
 | 
							ID:      id,
 | 
				
			||||||
 | 
							Label:   label,
 | 
				
			||||||
 | 
							Emoji:   emoji,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -41,9 +53,16 @@ func (button *Button) OnClick(action func(user common.User)) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (button *Button) toMessageComponent() discordgo.MessageComponent {
 | 
					func (button *Button) toMessageComponent() discordgo.MessageComponent {
 | 
				
			||||||
	return discordgo.Button{
 | 
						cmp := discordgo.Button{
 | 
				
			||||||
		Label:    button.Label,
 | 
							Label:    button.Label,
 | 
				
			||||||
		CustomID: button.ID,
 | 
							CustomID: button.ID,
 | 
				
			||||||
		Style:    discordgo.PrimaryButton,
 | 
							Style:    discordgo.PrimaryButton,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if button.Emoji != nil {
 | 
				
			||||||
 | 
							cmp.Emoji = &discordgo.ComponentEmoji{
 | 
				
			||||||
 | 
								Name: button.Emoji.Name,
 | 
				
			||||||
 | 
								ID:   button.Emoji.ID,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return cmp
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										78
									
								
								discord/emoji.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								discord/emoji.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,78 @@
 | 
				
			|||||||
 | 
					package discord
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"image"
 | 
				
			||||||
 | 
						"log"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/bwmarrin/discordgo"
 | 
				
			||||||
 | 
						"github.com/yeslayla/birdbot/core"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Emoji struct {
 | 
				
			||||||
 | 
						discord *Discord
 | 
				
			||||||
 | 
						ID      string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Name  string
 | 
				
			||||||
 | 
						Roles []string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetEmoji returns a emoji that exists on Discord
 | 
				
			||||||
 | 
					func (discord *Discord) GetEmoji(name string) *Emoji {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						emojis, err := discord.session.GuildEmojis(discord.guildID)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Printf("Error occured listing roles: %s", err)
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, emoji := range emojis {
 | 
				
			||||||
 | 
							if emoji.Managed {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if emoji.Name == name {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return &Emoji{
 | 
				
			||||||
 | 
									ID:      emoji.ID,
 | 
				
			||||||
 | 
									discord: discord,
 | 
				
			||||||
 | 
									Name:    emoji.Name,
 | 
				
			||||||
 | 
									Roles:   emoji.Roles,
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// CreateEmoji creates a new emoji on Discord
 | 
				
			||||||
 | 
					func (discord *Discord) CreateEmoji(name string, image image.Image) *Emoji {
 | 
				
			||||||
 | 
						result, err := discord.session.GuildEmojiCreate(discord.guildID, &discordgo.EmojiParams{
 | 
				
			||||||
 | 
							Name:  name,
 | 
				
			||||||
 | 
							Image: core.ImageToBase64(image),
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Printf("Failed to create emoji: %s", err)
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if result == nil {
 | 
				
			||||||
 | 
							log.Print("Failed to create emoji: result is nil")
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &Emoji{
 | 
				
			||||||
 | 
							ID:      result.ID,
 | 
				
			||||||
 | 
							Name:    result.Name,
 | 
				
			||||||
 | 
							Roles:   result.Roles,
 | 
				
			||||||
 | 
							discord: discord,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Save updates the emoji on Discord
 | 
				
			||||||
 | 
					func (emoji *Emoji) Save() {
 | 
				
			||||||
 | 
						if _, err := emoji.discord.session.GuildEmojiEdit(emoji.discord.guildID, emoji.ID, &discordgo.EmojiParams{
 | 
				
			||||||
 | 
							Name:  emoji.Name,
 | 
				
			||||||
 | 
							Roles: emoji.Roles,
 | 
				
			||||||
 | 
						}); err != nil {
 | 
				
			||||||
 | 
							log.Printf("Failed to save role: %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,15 +1,12 @@
 | 
				
			|||||||
package discord
 | 
					package discord
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"bytes"
 | 
					 | 
				
			||||||
	"encoding/base64"
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"image"
 | 
						"image"
 | 
				
			||||||
	"image/jpeg"
 | 
					 | 
				
			||||||
	"log"
 | 
						"log"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/bwmarrin/discordgo"
 | 
						"github.com/bwmarrin/discordgo"
 | 
				
			||||||
	"github.com/yeslayla/birdbot-common/common"
 | 
						"github.com/yeslayla/birdbot-common/common"
 | 
				
			||||||
 | 
						"github.com/yeslayla/birdbot/core"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewUser creates a new user object from a discordgo.User object
 | 
					// NewUser creates a new user object from a discordgo.User object
 | 
				
			||||||
@ -49,13 +46,7 @@ func (discord *Discord) GetAvatar(user common.User) image.Image {
 | 
				
			|||||||
func (discord *Discord) GetAvatarBase64(user common.User) string {
 | 
					func (discord *Discord) GetAvatarBase64(user common.User) string {
 | 
				
			||||||
	avatar := discord.GetAvatar(user)
 | 
						avatar := discord.GetAvatar(user)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fmtAvatar := &bytes.Buffer{}
 | 
						return core.ImageToBase64(avatar)
 | 
				
			||||||
	if err := jpeg.Encode(fmtAvatar, avatar, nil); err != nil {
 | 
					 | 
				
			||||||
		log.Println("Error encoding avatar: ", err)
 | 
					 | 
				
			||||||
		return ""
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return fmt.Sprintf("data:image/png;base64,%s", base64.StdEncoding.EncodeToString(fmtAvatar.Bytes()))
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// AssignRole adds a role to a user
 | 
					// AssignRole adds a role to a user
 | 
				
			||||||
 | 
				
			|||||||
@ -40,13 +40,43 @@ func (c *roleSelectionModule) Initialize(birdbot common.ModuleManager) error {
 | 
				
			|||||||
		role := c.session.GetRoleAndCreate(roleConfig.RoleName)
 | 
							role := c.session.GetRoleAndCreate(roleConfig.RoleName)
 | 
				
			||||||
		configColor, _ := core.HexToColor(roleConfig.Color)
 | 
							configColor, _ := core.HexToColor(roleConfig.Color)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							var emoji *discord.Emoji = nil
 | 
				
			||||||
 | 
							if c.cfg.GenerateColorEmoji.IsEnabledByDefault() {
 | 
				
			||||||
 | 
								emoji = c.session.GetEmoji(roleConfig.RoleName)
 | 
				
			||||||
 | 
								if emoji == nil {
 | 
				
			||||||
 | 
									emoji = c.session.CreateEmoji(roleConfig.RoleName, core.ColorToImage(configColor))
 | 
				
			||||||
 | 
									if emoji == nil {
 | 
				
			||||||
 | 
										log.Printf("Failed to create emoji for role: %s", roleConfig.RoleName)
 | 
				
			||||||
 | 
										return nil
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// If role.ID in emoji.Roles
 | 
				
			||||||
 | 
								var found bool
 | 
				
			||||||
 | 
								for _, r := range emoji.Roles {
 | 
				
			||||||
 | 
									if r == role.ID {
 | 
				
			||||||
 | 
										found = true
 | 
				
			||||||
 | 
										break
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if !found {
 | 
				
			||||||
 | 
									emoji.Roles = append(emoji.Roles, role.ID)
 | 
				
			||||||
 | 
									emoji.Save()
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if role.Color != configColor {
 | 
							if role.Color != configColor {
 | 
				
			||||||
			role.Color = configColor
 | 
								role.Color = configColor
 | 
				
			||||||
			role.Save()
 | 
								role.Save()
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Create button
 | 
							// Create button
 | 
				
			||||||
		btn := c.session.NewButton(fmt.Sprint(c.cfg.Title, role.Name), role.Name)
 | 
							var btn *discord.Button
 | 
				
			||||||
 | 
							if emoji != nil {
 | 
				
			||||||
 | 
								btn = c.session.NewButtonWithEmoji(fmt.Sprint(c.cfg.Title, roleConfig.RoleName), roleConfig.RoleName, emoji)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								btn = c.session.NewButton(fmt.Sprint(c.cfg.Title, role.Name), role.Name)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		btn.OnClick(func(user common.User) {
 | 
							btn.OnClick(func(user common.User) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Assign the roles asynchronously to avoid Discord's response timeout
 | 
								// Assign the roles asynchronously to avoid Discord's response timeout
 | 
				
			||||||
@ -97,10 +127,16 @@ func (c *roleSelectionModule) Initialize(birdbot common.ModuleManager) error {
 | 
				
			|||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if messageID == "" {
 | 
						// Update message
 | 
				
			||||||
		messageID = c.session.CreateMessageComponent(c.cfg.SelectionChannel, fmt.Sprintf("**%s**\n%s", c.cfg.Title, c.cfg.Description), components)
 | 
						if messageID != "" {
 | 
				
			||||||
		return c.db.SetDiscordMessage(localID, messageID)
 | 
							resultID := c.session.UpdateMessageComponent(messageID, c.cfg.SelectionChannel, fmt.Sprintf("**%s**\n%s", c.cfg.Title, c.cfg.Description), components)
 | 
				
			||||||
 | 
							if resultID != "" {
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						// Create new message
 | 
				
			||||||
 | 
						messageID = c.session.CreateMessageComponent(c.cfg.SelectionChannel, fmt.Sprintf("**%s**\n%s", c.cfg.Title, c.cfg.Description), components)
 | 
				
			||||||
 | 
						return c.db.SetDiscordMessage(localID, messageID)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user