Files
amayo/README/FEATURE_FLAGS_COMANDOS.md

7.1 KiB
Raw Blame History

🎮 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)

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)

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)

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

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

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

# 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

run: async (interaction, client) => {
  const useNew = await checkFeatureFlag('new_system', interaction);
  
  if (useNew) {
    await newSystem(interaction);
  } else {
    await oldSystem(interaction);
  }
}

Kill Switch

# Si hay un bug crítico
/featureflags update flag:problematic_feature status:maintenance
# Inmediatamente deshabilitado sin redeploy

Beta Testing

# 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

// 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:

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. 🎮