feat: Agregar ejemplos de uso de feature flags en comandos y mejorar la configuración del proyecto

This commit is contained in:
Shni
2025-10-31 21:18:46 -05:00
parent 89d475ba66
commit 76ce4e4e4d
5 changed files with 891 additions and 297 deletions

View File

@@ -0,0 +1,311 @@
# 🎮 Feature Flags - Uso en Comandos Slash y Mensajes
## 🚀 Uso Universal
El sistema funciona **idénticamente** para comandos slash y comandos de mensaje.
---
## 📦 3 Formas de Usar
### 1⃣ Wrapper (Recomendado - Más Limpio)
```typescript
import { withFeatureFlag } from "@/core/lib/featureFlagCommandWrapper";
import { CommandSlash } from "@/core/types/commands";
// COMANDO SLASH
export const command: CommandSlash = {
name: 'shop',
description: 'Abre la tienda',
type: 'slash',
cooldown: 10,
run: withFeatureFlag('new_shop_system', async (interaction, client) => {
// Tu código aquí - solo se ejecuta si el flag está enabled
await interaction.reply('🛒 Tienda!');
}, {
fallbackMessage: '🔧 Tienda en mantenimiento'
})
};
// COMANDO DE MENSAJE
export const command: CommandMessage = {
name: 'shop',
type: 'message',
cooldown: 10,
run: withFeatureFlag('new_shop_system', async (message, args, client) => {
// Mismo código, funciona igual
await message.reply('🛒 Tienda!');
}, {
fallbackMessage: '🔧 Tienda en mantenimiento'
})
};
```
### 2⃣ Guard (Respuesta Automática)
```typescript
import { guardFeatureFlag } from "@/core/lib/featureFlagCommandWrapper";
// COMANDO SLASH
export const command: CommandSlash = {
name: 'mine',
description: 'Minea recursos',
type: 'slash',
cooldown: 10,
run: async (interaction, client) => {
// Guard responde automáticamente si está disabled
if (!await guardFeatureFlag('new_mining', interaction)) {
return; // Ya respondió al usuario
}
// Tu código aquí
await interaction.reply('⛏️ Minando...');
}
};
// COMANDO DE MENSAJE - EXACTAMENTE IGUAL
export const command: CommandMessage = {
name: 'mine',
type: 'message',
cooldown: 10,
run: async (message, args, client) => {
// Mismo guard funciona para mensajes
if (!await guardFeatureFlag('new_mining', message)) {
return;
}
await message.reply('⛏️ Minando...');
}
};
```
### 3⃣ Check Manual (Más Control)
```typescript
import { checkFeatureFlag } from "@/core/lib/featureFlagCommandWrapper";
// COMANDO SLASH
export const command: CommandSlash = {
name: 'inventory',
description: 'Tu inventario',
type: 'slash',
cooldown: 5,
run: async (interaction, client) => {
const useNewUI = await checkFeatureFlag('inventory_v2', interaction);
if (useNewUI) {
await interaction.reply('📦 Inventario v2');
} else {
await interaction.reply('📦 Inventario v1');
}
}
};
// COMANDO DE MENSAJE - IGUAL
export const command: CommandMessage = {
name: 'inventory',
type: 'message',
cooldown: 5,
run: async (message, args, client) => {
const useNewUI = await checkFeatureFlag('inventory_v2', message);
if (useNewUI) {
await message.reply('📦 Inventario v2');
} else {
await message.reply('📦 Inventario v1');
}
}
};
```
---
## 🔥 A/B Testing
```typescript
import { abTestCommand } from "@/core/lib/featureFlagCommandWrapper";
// COMANDO SLASH
export const command: CommandSlash = {
name: 'attack',
description: 'Ataca',
type: 'slash',
cooldown: 10,
run: async (interaction, client) => {
await abTestCommand('new_combat', interaction, {
variant: async () => {
// Nueva versión (50% usuarios)
await interaction.reply('⚔️ Daño nuevo: 100');
},
control: async () => {
// Versión antigua (50% usuarios)
await interaction.reply('⚔️ Daño viejo: 50');
}
});
}
};
// COMANDO DE MENSAJE - IGUAL
export const command: CommandMessage = {
name: 'attack',
type: 'message',
cooldown: 10,
run: async (message, args, client) => {
await abTestCommand('new_combat', message, {
variant: async () => {
await message.reply('⚔️ Daño nuevo: 100');
},
control: async () => {
await message.reply('⚔️ Daño viejo: 50');
}
});
}
};
```
---
## 💡 Ejemplo Real: Comando Universal
```typescript
import { checkFeatureFlag } from "@/core/lib/featureFlagCommandWrapper";
import { CommandSlash, CommandMessage } from "@/core/types/commands";
// Función de negocio (reutilizable)
async function executeShop(source: any) {
const useNewShop = await checkFeatureFlag('new_shop_system', source);
const items = useNewShop
? ['⚔️ Espada Legendaria', '🛡️ Escudo Épico']
: ['Espada', 'Escudo'];
const response = `🛒 **Tienda**\n${items.join('\n')}`;
// Detectar tipo y responder
if ('options' in source) {
await source.reply(response);
} else {
await source.reply(response);
}
}
// COMANDO SLASH
export const shopSlash: CommandSlash = {
name: 'shop',
description: 'Tienda',
type: 'slash',
cooldown: 10,
run: async (interaction, client) => {
await executeShop(interaction);
}
};
// COMANDO DE MENSAJE
export const shopMessage: CommandMessage = {
name: 'shop',
type: 'message',
cooldown: 10,
run: async (message, args, client) => {
await executeShop(message);
}
};
```
---
## 📊 Configurar Flags
```bash
# Crear flag
/featureflags create name:new_shop_system status:disabled target:global
# Habilitar
/featureflags update flag:new_shop_system status:enabled
# Rollout 25% de usuarios
/featureflags rollout flag:new_shop_system strategy:percentage percentage:25
# A/B testing (50/50)
/featureflags rollout flag:new_combat strategy:percentage percentage:50
# Ver estadísticas
/featureflags stats flag:new_shop_system
```
---
## ✨ Ventajas del Sistema
**Un solo código** para ambos tipos de comandos
**No rompe** comandos existentes
**Rollouts progresivos** sin redeploys
**Kill switches** instantáneos
**A/B testing** automático
**Estadísticas** de uso en tiempo real
---
## 🎯 Casos de Uso
### Migración Gradual
```typescript
run: async (interaction, client) => {
const useNew = await checkFeatureFlag('new_system', interaction);
if (useNew) {
await newSystem(interaction);
} else {
await oldSystem(interaction);
}
}
```
### Kill Switch
```bash
# Si hay un bug crítico
/featureflags update flag:problematic_feature status:maintenance
# Inmediatamente deshabilitado sin redeploy
```
### Beta Testing
```bash
# Solo para guilds específicos
/featureflags create name:beta_features status:rollout target:guild
/featureflags rollout flag:beta_features strategy:whitelist
# Luego añadir IDs de guilds en el config
```
### Eventos Temporales
```typescript
// Crear con fechas
await featureFlagService.setFlag({
name: 'halloween_event',
status: 'enabled',
startDate: new Date('2025-10-25'),
endDate: new Date('2025-11-01')
});
// Se auto-desactiva el 1 de noviembre
```
---
## 🔧 Integración en tu Bot
Simplemente usa los helpers en cualquier comando:
```typescript
import { withFeatureFlag } from '@/core/lib/featureFlagCommandWrapper';
export const command: CommandSlash = {
name: 'tu_comando',
description: 'Descripción',
type: 'slash',
cooldown: 10,
run: withFeatureFlag('tu_flag', async (interaction, client) => {
// Tu código existente aquí
})
};
```
**Eso es todo.** El sistema funciona transparentemente para ambos tipos de comandos. 🎮