Improved role selection
This commit is contained in:
		@ -41,6 +41,7 @@ type StatusPortal struct {
 | 
			
		||||
type RoleSelectionConfig struct {
 | 
			
		||||
	Title              string  `yaml:"title"`
 | 
			
		||||
	Description        string  `yaml:"description"`
 | 
			
		||||
	GenerateColorEmoji Feature `yaml:"generate_color_emoji"`
 | 
			
		||||
 | 
			
		||||
	SelectionChannel string       `yaml:"discord_channel"`
 | 
			
		||||
	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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
	ID    string
 | 
			
		||||
 | 
			
		||||
	Emoji   *Emoji
 | 
			
		||||
	discord *Discord
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -18,6 +19,17 @@ func (discord *Discord) NewButton(id string, label string) *Button {
 | 
			
		||||
		discord: discord,
 | 
			
		||||
		ID:      id,
 | 
			
		||||
		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 {
 | 
			
		||||
	return discordgo.Button{
 | 
			
		||||
	cmp := discordgo.Button{
 | 
			
		||||
		Label:    button.Label,
 | 
			
		||||
		CustomID: button.ID,
 | 
			
		||||
		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
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/base64"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"image"
 | 
			
		||||
	"image/jpeg"
 | 
			
		||||
	"log"
 | 
			
		||||
 | 
			
		||||
	"github.com/bwmarrin/discordgo"
 | 
			
		||||
	"github.com/yeslayla/birdbot-common/common"
 | 
			
		||||
	"github.com/yeslayla/birdbot/core"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// 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 {
 | 
			
		||||
	avatar := discord.GetAvatar(user)
 | 
			
		||||
 | 
			
		||||
	fmtAvatar := &bytes.Buffer{}
 | 
			
		||||
	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()))
 | 
			
		||||
	return core.ImageToBase64(avatar)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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)
 | 
			
		||||
		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 {
 | 
			
		||||
			role.Color = configColor
 | 
			
		||||
			role.Save()
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// 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) {
 | 
			
		||||
 | 
			
		||||
			// Assign the roles asynchronously to avoid Discord's response timeout
 | 
			
		||||
@ -97,10 +127,16 @@ func (c *roleSelectionModule) Initialize(birdbot common.ModuleManager) error {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if messageID == "" {
 | 
			
		||||
		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)
 | 
			
		||||
	// Update message
 | 
			
		||||
	if 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