turso
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
package lib
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/bwmarrin/discordgo"
|
||||
@ -10,34 +11,54 @@ type CommandFunc func(s *discordgo.Session, i *discordgo.InteractionCreate) (str
|
||||
|
||||
func HandleCommand(commandName string, cooldownDuration time.Duration, handler CommandFunc) func(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||
return func(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||
// Get user information first
|
||||
user, userErr := GetUser(i)
|
||||
if userErr != nil {
|
||||
RespondWithError(s, i, "Error processing command: "+userErr.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Store user in database
|
||||
dbErr := StoreUser(user.ID, user.Username)
|
||||
if dbErr != nil {
|
||||
// Log the error but don't stop command execution
|
||||
log.Printf("Error storing user: %v", dbErr)
|
||||
}
|
||||
|
||||
// Rest of your existing HandleCommand logic...
|
||||
if !CheckAndApplyCooldown(s, i, commandName, cooldownDuration) {
|
||||
return
|
||||
}
|
||||
|
||||
if !CheckAndApplyCooldown(s, i, commandName, cooldownDuration) {
|
||||
return
|
||||
}
|
||||
|
||||
// Acknowledge the interaction immediately
|
||||
err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
interactErr := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseDeferredChannelMessageWithSource,
|
||||
})
|
||||
if err != nil {
|
||||
ThrowWithError(commandName, "Error deferring response: "+err.Error())
|
||||
|
||||
if interactErr != nil {
|
||||
ThrowWithError(commandName, "Error deferring response: "+interactErr.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Execute the command handler
|
||||
response, err := handler(s, i)
|
||||
response, handlerErr := handler(s, i)
|
||||
|
||||
if err != nil {
|
||||
RespondWithError(s, i, "Error processing command: "+err.Error())
|
||||
if handlerErr != nil {
|
||||
RespondWithError(s, i, "Error processing command: "+handlerErr.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Send the follow-up message with the response
|
||||
_, err = s.FollowupMessageCreate(i.Interaction, true, &discordgo.WebhookParams{
|
||||
_, followErr := s.FollowupMessageCreate(i.Interaction, true, &discordgo.WebhookParams{
|
||||
Content: response,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
ThrowWithError(commandName, "Error sending follow-up message: "+err.Error())
|
||||
if followErr != nil {
|
||||
ThrowWithError(commandName, "Error sending follow-up message: "+followErr.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
169
lib/db.go
Normal file
169
lib/db.go
Normal file
@ -0,0 +1,169 @@
|
||||
package lib
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/tursodatabase/go-libsql"
|
||||
)
|
||||
|
||||
var DBClient *sql.DB
|
||||
|
||||
func InitDB() error {
|
||||
dbUrl := os.Getenv("DATABASE_URL")
|
||||
dbToken := os.Getenv("DATABASE_AUTH_TOKEN")
|
||||
|
||||
if dbUrl == "" || dbToken == "" {
|
||||
return fmt.Errorf("database configuration missing")
|
||||
}
|
||||
|
||||
connector, err := libsql.NewEmbeddedReplicaConnector(
|
||||
"./himbot.db",
|
||||
dbUrl,
|
||||
libsql.WithAuthToken(dbToken),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create client: %w", err)
|
||||
}
|
||||
|
||||
client := sql.OpenDB(connector)
|
||||
DBClient = client
|
||||
|
||||
return runMigrations()
|
||||
}
|
||||
|
||||
type Migration struct {
|
||||
Version int
|
||||
Up string
|
||||
Down string
|
||||
}
|
||||
|
||||
func loadMigrations() ([]Migration, error) {
|
||||
var migrations []Migration
|
||||
migrationFiles, err := filepath.Glob("migrations/*.up.sql")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read migration files: %w", err)
|
||||
}
|
||||
|
||||
for _, upFile := range migrationFiles {
|
||||
// Extract version from filename (000001_create_users_table.up.sql -> 1)
|
||||
baseName := filepath.Base(upFile)
|
||||
version := 0
|
||||
fmt.Sscanf(baseName, "%d_", &version)
|
||||
|
||||
downFile := strings.Replace(upFile, ".up.sql", ".down.sql", 1)
|
||||
|
||||
upSQL, err := ioutil.ReadFile(upFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read migration file %s: %w", upFile, err)
|
||||
}
|
||||
|
||||
downSQL, err := ioutil.ReadFile(downFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read migration file %s: %w", downFile, err)
|
||||
}
|
||||
|
||||
migrations = append(migrations, Migration{
|
||||
Version: version,
|
||||
Up: string(upSQL),
|
||||
Down: string(downSQL),
|
||||
})
|
||||
}
|
||||
|
||||
sort.Slice(migrations, func(i, j int) bool {
|
||||
return migrations[i].Version < migrations[j].Version
|
||||
})
|
||||
|
||||
return migrations, nil
|
||||
}
|
||||
|
||||
func runMigrations() error {
|
||||
// Create migrations table if it doesn't exist
|
||||
_, err := DBClient.Exec(`
|
||||
CREATE TABLE IF NOT EXISTS schema_migrations (
|
||||
version INTEGER PRIMARY KEY,
|
||||
applied_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)`)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create schema_migrations table: %w", err)
|
||||
}
|
||||
|
||||
migrations, err := loadMigrations()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, migration := range migrations {
|
||||
var exists bool
|
||||
err := DBClient.QueryRow(
|
||||
"SELECT EXISTS(SELECT 1 FROM schema_migrations WHERE version = ?)",
|
||||
migration.Version).Scan(&exists)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check migration status: %w", err)
|
||||
}
|
||||
|
||||
if !exists {
|
||||
tx, err := DBClient.Begin()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start transaction: %w", err)
|
||||
}
|
||||
|
||||
// Run migration
|
||||
_, err = tx.Exec(migration.Up)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return fmt.Errorf("failed to apply migration %d: %w", migration.Version, err)
|
||||
}
|
||||
|
||||
// Record migration
|
||||
_, err = tx.Exec(
|
||||
"INSERT INTO schema_migrations (version) VALUES (?)",
|
||||
migration.Version)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return fmt.Errorf("failed to record migration %d: %w", migration.Version, err)
|
||||
}
|
||||
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to commit migration %d: %w", migration.Version, err)
|
||||
}
|
||||
|
||||
log.Printf("Applied migration %d", migration.Version)
|
||||
}
|
||||
}
|
||||
|
||||
log.Println("Database migrations completed successfully")
|
||||
return nil
|
||||
}
|
||||
|
||||
func StoreUser(discordID, username string) error {
|
||||
// Check if user exists
|
||||
var exists bool
|
||||
err := DBClient.QueryRow(
|
||||
"SELECT EXISTS(SELECT 1 FROM users WHERE discord_id = ?)",
|
||||
discordID).Scan(&exists)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check user existence: %w", err)
|
||||
}
|
||||
|
||||
// If user doesn't exist, insert them
|
||||
if !exists {
|
||||
_, err = DBClient.Exec(
|
||||
"INSERT INTO users (discord_id, username) VALUES (?, ?)",
|
||||
discordID, username)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to store user: %w", err)
|
||||
}
|
||||
log.Printf("New user stored: %s (%s)", username, discordID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
Reference in New Issue
Block a user