gitea-register-account-bot/main.go

174 lines
5.2 KiB
Go

package main
import (
"crypto/rand"
"fmt"
"math/big"
"os"
"regexp"
"strings"
"code.gitea.io/sdk/gitea"
"github.com/bwmarrin/discordgo"
)
var (
discordToken string
giteaToken string
giteaURL string
)
func main() {
giteaToken = os.Getenv("GITEA_TOKEN")
giteaURL = os.Getenv("GITEA_URL")
discordToken = os.Getenv("DISCORD_TOKEN")
if giteaToken == "" || giteaURL == "" || discordToken == "" {
fmt.Println("Environment variables GITEA_TOKEN, GITEA_URL, and DISCORD_TOKEN must be set.")
os.Exit(1)
}
dg, err := discordgo.New("Bot " + discordToken)
if err != nil {
fmt.Println("error creating Discord session,", err)
return
}
dg.Identify.Intents = discordgo.IntentsGuildMessages | discordgo.IntentsDirectMessages
dg.AddHandler(messageCreate)
err = dg.Open()
if err != nil {
fmt.Println("error opening connection,", err)
return
}
fmt.Println("Bot is now running. Press CTRL+C to exit.")
select {}
}
// validateUsername checks if the username complies with Gitea requirements
func validateUsername(username string) (bool, error) {
// Regular expression for valid username
var validUsernameRegex = regexp.MustCompile(`^[a-zA-Z0-9]+([_.-]?[a-zA-Z0-9]+)*$`)
// Check length constraints (1 to 39 characters)
if len(username) < 1 || len(username) > 39 {
return false, nil
}
return validUsernameRegex.MatchString(username), nil
}
func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
// Ignore messages from the bot itself
if m.Author.ID == s.State.User.ID {
return
}
// Check if the command is !register
if strings.HasPrefix(m.Content, "!register") {
args := strings.Fields(m.Content)
var username, email string
// Loop through arguments to find username and email
for _, arg := range args[1:] { // Skip the command itself
if strings.HasPrefix(arg, "--username=") {
username = strings.TrimPrefix(arg, "--username=")
} else if strings.HasPrefix(arg, "--email=") {
email = strings.TrimPrefix(arg, "--email=")
}
}
// If no username parameter is provided, use the user's Discord nickname or username
if username == "" {
member, err := s.GuildMember(m.GuildID, m.Author.ID)
if err != nil {
username = m.Author.Username
} else if member.Nick != "" {
username = member.Nick
} else {
username = m.Author.Username
}
}
// Remove trailing period from username if present
username = strings.TrimRight(username, ".")
// If no email parameter is provided, use the default email format
if email == "" {
email = fmt.Sprintf("%s@git.beisel.it", username)
}
// Validate the username
isValid, err := validateUsername(username)
if err != nil {
s.ChannelMessageSend(m.ChannelID, "Invalid username. Username must consist of alphanumeric characters, dashes, underscores, and dots. It cannot begin or end with non-alphanumeric characters, and consecutive non-alphanumeric characters are forbidden.")
return
}
if !isValid {
s.ChannelMessageSend(m.ChannelID, "Invalid username. Username must consist of alphanumeric characters, dashes, underscores, and dots. It cannot begin or end with non-alphanumeric characters, and consecutive non-alphanumeric characters are forbidden.")
return
}
fmt.Println("Creating user:", username)
// Auto-generate a password
password, err := generatePassword(12) // You can choose the length of the password
if err != nil {
s.ChannelMessageSend(m.ChannelID, "Failed to generate a secure password.")
return
}
// Create user in Gitea
err = createUserInGitea(username, email, password)
if err != nil {
s.ChannelMessageSend(m.ChannelID, "Failed to create user in Gitea.")
return
}
// Send DM with login details and instructions
dmChannel, err := s.UserChannelCreate(m.Author.ID)
if err != nil {
s.ChannelMessageSend(m.ChannelID, "Failed to send DM.")
return
}
dmMessage := fmt.Sprintf("You can now log in to %s using:\n\nUsername: %s\nPassword: %s\n\nYour E-Mail has temporarily been set to \"%s\". Please make sure to set this to your correct e-mail address and update your password immediately.", giteaURL, username, password, email)
_, err = s.ChannelMessageSend(dmChannel.ID, dmMessage)
if err != nil {
s.ChannelMessageSend(m.ChannelID, "Failed to send login details via DM.")
return
}
}
}
// generatePassword creates a secure, random password of a given length
func generatePassword(length int) (string, error) {
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
var password strings.Builder
for i := 0; i < length; i++ {
charIndex, err := rand.Int(rand.Reader, big.NewInt(int64(len(charset))))
if err != nil {
return "", err
}
password.WriteByte(charset[charIndex.Int64()])
}
return password.String(), nil
}
func createUserInGitea(username, email, password string) error {
client, _ := gitea.NewClient(giteaURL, gitea.SetToken(giteaToken))
_, _, err := client.AdminCreateUser(gitea.CreateUserOption{
LoginName: username,
Username: username,
Email: email,
Password: password,
})
return err
}