Skip to content

Commit

Permalink
Implementar bot con comandos y algunos comentarios
Browse files Browse the repository at this point in the history
  • Loading branch information
aaavril committed Nov 12, 2024
1 parent 2bff001 commit a9c2bbf
Show file tree
Hide file tree
Showing 16 changed files with 589 additions and 33 deletions.
51 changes: 49 additions & 2 deletions src/Library/Commands/BattleCommand.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,53 @@
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using ClassLibrary;

namespace Library.Commands;

public class BattleCommand
/// <summary>
/// Esta clase implementa el comando 'battle' del bot. Este comando une al
/// jugador que envía el mensaje con el oponente que se recibe como parámetro,
/// si lo hubiera, en una batalla; si no se recibe un oponente, lo une con
/// cualquiera que esté esperando para jugar.
/// </summary>
// ReSharper disable once UnusedType.Global
public class BattleCommand : ModuleBase<SocketCommandContext>
{

/// <summary>
/// Implementa el comando 'battle'. Este comando une al jugador que envía el
/// mensaje a la lista de jugadores esperando para jugar.
/// </summary>
[Command("battle")]
[Summary(
"""
Une al jugador que envía el mensaje con el oponente que se recibe
como parámetro, si lo hubiera, en una batalla; si no se recibe un
oponente, lo une con cualquiera que esté esperando para jugar.
""")]
// ReSharper disable once UnusedMember.Global
public async Task ExecuteAsync(
[Remainder]
[Summary("Display name del oponente, opcional")]
string? opponentDisplayName = null)

Check warning on line 32 in src/Library/Commands/BattleCommand.cs

View workflow job for this annotation

GitHub Actions / build-test-generate-docs

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
{
string displayName = CommandHelper.GetDisplayName(Context);

SocketGuildUser? opponentUser = CommandHelper.GetUser(
Context, opponentDisplayName);

string result;
if (opponentUser != null)
{
result = Facade.Instance.StartBattle(displayName, opponentUser.DisplayName);
await Context.Message.Author.SendMessageAsync(result);
await opponentUser.SendMessageAsync(result);
}
else
{
result = $"No hay un usuario {opponentDisplayName}";
}

await ReplyAsync(result);
}
}
51 changes: 49 additions & 2 deletions src/Library/Commands/CommandHelper.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,53 @@
using Discord.Commands;
using Discord.WebSocket;

namespace Library.Commands;

public class CommandHelper
public static class CommandHelper
{

public static string GetDisplayName(
SocketCommandContext context,
string? name = null)

Check warning on line 10 in src/Library/Commands/CommandHelper.cs

View workflow job for this annotation

GitHub Actions / build-test-generate-docs

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
{
if (name == null)
{
name = context.Message.Author.Username;
}

foreach (SocketGuildUser user in context.Guild.Users)
{
if (user.Username == name
|| user.DisplayName == name
|| user.Nickname == name
|| user.GlobalName == name)
{
return user.DisplayName;
}
}

return name;
}

public static SocketGuildUser? GetUser(

Check warning on line 31 in src/Library/Commands/CommandHelper.cs

View workflow job for this annotation

GitHub Actions / build-test-generate-docs

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
SocketCommandContext context,
string? name)

Check warning on line 33 in src/Library/Commands/CommandHelper.cs

View workflow job for this annotation

GitHub Actions / build-test-generate-docs

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
{
if (name == null)
{
return null;
}

foreach (SocketGuildUser user in context.Guild.Users)
{
if (user.Username == name
|| user.DisplayName == name
|| user.Nickname == name
|| user.GlobalName == name)
{
return user;
}
}

return null;
}
}
24 changes: 22 additions & 2 deletions src/Library/Commands/JoinCommand.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,26 @@
using Discord.Commands;
using ClassLibrary;

namespace Library.Commands;

public class JoinCommand
/// <summary>
/// Esta clase implementa el comando 'join' del bot. Este comando une al jugador
/// que envía el mensaje a la lista de jugadores esperando para jugar.
/// </summary>
// ReSharper disable once UnusedType.Global
public class JoinCommand : ModuleBase<SocketCommandContext>
{

/// <summary>
/// Implementa el comando 'join'. Este comando une al jugador que envía el
/// mensaje a la lista de jugadores esperando para jugar.
/// </summary>
[Command("join")]
[Summary("Une el usuario que envía el mensaje a la lista de espera")]
// ReSharper disable once UnusedMember.Global
public async Task ExecuteAsync()
{
string displayName = CommandHelper.GetDisplayName(Context);
string result = Facade.Instance.AddPlayerToWaitingList(displayName);
await ReplyAsync(result);
}
}
24 changes: 22 additions & 2 deletions src/Library/Commands/LeaveCommand.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,26 @@
using Discord.Commands;
using ClassLibrary;

namespace Library.Commands;

public class LeaveCommand
/// <summary>
/// Esta clase implementa el comando 'leave' del bot. Este comando remueve el
/// jugador que envía el mensaje de la lista de jugadores esperando para jugar.
/// </summary>
// ReSharper disable once UnusedType.Global
public class LeaveCommand : ModuleBase<SocketCommandContext>
{

/// <summary>
/// Implementa el comando 'leave' del bot. Este comando remueve el jugador
/// que envía el mensaje de la lista de jugadores esperando para jugar.
/// </summary>
[Command("leave")]
[Summary("Remueve el usuario que envía el mensaje a la lista de espera")]
// ReSharper disable once UnusedMember.Global
public async Task ExecuteAsync()
{
string displayName = CommandHelper.GetDisplayName(Context);
string result = Facade.Instance.RemovePlayerFromWaitingList(displayName);
await ReplyAsync(result);
}
}
89 changes: 86 additions & 3 deletions src/Library/Commands/PokemonNameCommand.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,89 @@
using System.Net;
using System.Net.Http.Headers;
using System.Text.Json.Nodes;
using Microsoft.Extensions.Logging;
using Discord.Commands;

namespace Library.Commands;

public class PokemonNameCommand
/// <summary>
/// Esta clase implementa el comando 'name' del bot. Este comando retorna el
/// nombre de un Pokémon dado su identificador.
/// </summary>
// ReSharper disable once UnusedType.Global
public class PokemonNameCommand : ModuleBase<SocketCommandContext>
{

}
private readonly ILogger<PokemonNameCommand> logger;
private readonly HttpClient httpClient;

/// <summary>
/// Inicializa una nueva instancia de la clase
/// <see cref="PokemonNameCommand"/> con los valores recibidos como
/// argumento.
/// </summary>
/// <param name="logger">El servicio de logging a utilizar.</param>
public PokemonNameCommand(ILogger<PokemonNameCommand> logger)
{
this.logger = logger;

httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
httpClient.DefaultRequestHeaders.Add("User-Agent", "DiscordBot");
}

/// <summary>
/// Implementa el comando 'name'. Este comando retorna el nombre de un
/// Pokémon dado su identificador.
/// </summary>
/// <param name="id">El identificador del Pokémon a buscar.</param>
[Command("name")]
[Summary("Busca el nombre de un Pokémon por identificador usando la PokéAPI")]
// ReSharper disable once UnusedMember.Global
public async Task ExecuteAsync([Remainder][Summary("ID")] int id = 0)
{
if (id <= 0)
{
await ReplyAsync("Uso: !name <id>");
return;
}

try
{
var response = await httpClient.GetStringAsync($"https://pokeapi.co/api/v2/pokemon/{id}");

if (string.IsNullOrEmpty(response))
{
await ReplyAsync($"No encontré nada para {id}");
return;
}

JsonNode? pokemonNode = JsonNode.Parse(response);
JsonNode? nameNode = pokemonNode?["name"];

if (pokemonNode == null || nameNode == null)
{
await ReplyAsync($"No encontré el nombre de {id}");
}
else
{
await ReplyAsync(nameNode.GetValue<string>());
}
}
catch (HttpRequestException exception)
{
if (exception.StatusCode == HttpStatusCode.NotFound)
{
await ReplyAsync("No lo encontré");
}
else
{
logger.LogError("HTTP error: {Message}", exception.Message);
}

}
catch (Exception exception)
{
logger.LogError("Exception: {Message}", exception.Message);
}
}
}
44 changes: 42 additions & 2 deletions src/Library/Commands/UserInfoCommand.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,46 @@
using Discord.Commands;
using Discord.WebSocket;
using ClassLibrary;

namespace Library.Commands;

public class UserInfoCommand
/// <summary>
/// Esta clase implementa el comando 'userinfo', alias 'who' o 'whois' del bot.
/// Este comando retorna información sobre el usuario que envía el mensaje o sobre
/// otro usuario si se incluye como parámetro..
/// </summary>
// ReSharper disable once UnusedType.Global
public class UserInfoCommand : ModuleBase<SocketCommandContext>
{

/// <summary>
/// Implementa el comando 'userinfo', alias 'who' o 'whois' del bot.
/// </summary>
/// <param name="displayName">El nombre de usuario de Discord a buscar.</param>
[Command("who")]
[Summary(
"""
Devuelve información sobre el usuario que se indica como parámetro o
sobre el usuario que envía el mensaje si no se indica otro usuario.
""")]
// ReSharper disable once UnusedMember.Global
public async Task ExecuteAsync(
[Remainder][Summary("El usuario del que tener información, opcional")]
string? displayName = null)

Check warning on line 28 in src/Library/Commands/UserInfoCommand.cs

View workflow job for this annotation

GitHub Actions / build-test-generate-docs

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
{
if (displayName != null)
{
SocketGuildUser? user = CommandHelper.GetUser(Context, displayName);

Check warning on line 32 in src/Library/Commands/UserInfoCommand.cs

View workflow job for this annotation

GitHub Actions / build-test-generate-docs

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

if (user == null)
{
await ReplyAsync($"No puedo encontrar {displayName} en este servidor");
}
}

string userName = displayName ?? CommandHelper.GetDisplayName(Context);

string result = Facade.Instance.PlayerIsWaiting(userName);

await ReplyAsync(result);
}
}
24 changes: 22 additions & 2 deletions src/Library/Commands/WaitingCommand.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,26 @@
using Discord.Commands;
using ClassLibrary;

namespace Library.Commands;

public class WaitingCommand
/// <summary>
/// Esta clase implementa el comando 'waitinglist' del bot. Este comando muestra
/// la lista de jugadores esperando para jugar.
/// </summary>
// ReSharper disable once UnusedType.Global
public class WaitingCommand : ModuleBase<SocketCommandContext>
{

/// <summary>
/// Implementa el comando 'waitinglist'. Este comando muestra la lista de
/// jugadores esperando para jugar.
/// </summary>
[Command("waitinglist")]
[Summary("Muestra los usuarios en la lista de espera")]
// ReSharper disable once UnusedMember.Global
public async Task ExecuteAsync()
{
string result = Facade.Instance.GetAllPlayersWaiting();

await ReplyAsync(result);
}
}
6 changes: 3 additions & 3 deletions src/Library/Domain/Item/Item.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ namespace ClassLibrary
{
/// <summary>
/// La clase <c>Item</c> representa un ítem genérico que puede ser utilizado en el contexto del juego.
/// - Principio de Responsabilidad Única (SRP): Se encarga exclusivamente de modelar las propiedades y el comportamiento básico de un ítem.
/// - Patrón Polimorfismo (GRASP): Permite a las subclases sobrescribir el método <c>ApplyEffect</c> para definir comportamientos específicos de los ítems.
/// - Inmutabilidad: La propiedad <c>Name</c> es de solo lectura, garantizando que el nombre del ítem no pueda ser modificado después de su creación.
/// Sigue el principio de responsabilidad única ya que se encarga exclusivamente de modelar las propiedades y el comportamiento básico de un ítem.
/// Hicimos uso del patrón de diseño Strategy que permite diferentes estrategias de implementación
/// de un mismo método, usando el polimorfismo para sobrescribir el método <c>ApplyEffect</c> para definir comportamientos específicos de los ítems.
/// </summary>
public class Item
{
Expand Down
5 changes: 3 additions & 2 deletions src/Library/Domain/Move/Move.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ namespace ClassLibrary;
/// sistema de batallas, sin representar un movimiento específico por sí sola. Según el principio de responsabilidad única,
/// <c>Move</c> organiza los elementos comunes (como el nombre y precisión) y delega a sus subclases <c>MoveParalize</c>, <c>MoveSleep</c>,
/// <c>MovePosion</c> <c>MoveBurn</c> <c>MoveNormal</c> la implementación de efectos específicos, asegurando que cada ataque cumpla
/// su función particular sin duplicar código. Además, el diseño sigue los principios de Liskov y Abierto/Cerrado, permitiendo la adición de nuevos tipos de movimientos
/// sin modificar la estructura base, lo cual favorece un sistema extensible y polimórfico.
/// su función particular sin duplicar código. Hicimos uso del patrón de diseño Strategy que permite diferentes estrategias de implementación
/// de un mismo método. Además, el diseño sigue los principios de Liskov y Abierto/Cerrado, permitiendo la adición de nuevos tipos de movimientos
/// sin modificar la estructura base, lo cual favorece un sistema extensible y polimórfico.
/// </summary>
public abstract class Move
{
Expand Down
12 changes: 12 additions & 0 deletions src/Library/Library.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<UserSecretsId>PII_DiscordBot_Demo</UserSecretsId>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<PropertyGroup>
Expand All @@ -11,4 +12,15 @@
<AnalysisMode>All</AnalysisMode>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Discord.Net" Version="3.16.0"/>
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1"/>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Options" Version="8.0.2"/>
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0"/>
</ItemGroup>
</Project>
Loading

0 comments on commit a9c2bbf

Please sign in to comment.