#FAQ says CommandsNext will be deprecated, and I should use Commands, but I cant find that library

1 messages · Page 1 of 1 (latest)

devout bison
#

I am confused, when reading the FAQ, it says CommandsNext will be deprecated, and Commands should be used, but I cannot find such a library to reference, nor a namespace in the main library. I much prefer how Commands is apparently scoped per command execution, and would strongly prefer to use that, but cannot figure out how.

sharp acorn
#

Turn on prereleases

#

Its currently only in our nightly v5 build

devout bison
#

ok, thank you, how is the stability of v5 builds?

sharp acorn
#

more stable that our "stable" version

devout bison
#

ok, thanks for the quick response, will give that a try then

#

does there exist any examples using v5 commands anywhere, its not the same setup as how commandsnext work?

vale urchin
devout bison
#

thanks

#

yeah, this is definitely an improvement

vale urchin
#

cc @crisp palm

crisp palm
#

Usually we test our commits in the PR stage before they're merged

#

We only make official releases when there aren't any outstanding bugs or features waiting to be merged to the master branch

devout bison
#

yeah, its similar to how we have made our release channel be the defactor "release" for our civ4 mod, we almost never release properly

vale urchin
#

(really, unstable/stable here mostly refers to the API surface, reliability is about the same, if anything nightly tends to be more stable because we actually fix bugs there)

#

civ 4, excellent

devout bison
#

it goes to release branch which pushes to svn for us, and then people use svn to keep mod updated

crisp palm
devout bison
#

Im part of the team that works on Caveman2Cosmos

crisp palm
#

I'm too burnt out to write any more articles

#

And other people are in desperate need of them

crisp palm
#

I'm gonna make a few final touches then we'll merge

crisp palm
devout bison
#

Ill get this v5 version into my self built "discordbot host" that uses IHostedService, and ill see how it flies. I run the bot as a hosted service addon to a webapi

crisp palm
#

Hmm

devout bison
crisp palm
devout bison
#

the bot I am building is a cs2 gather bot, I dont want to expose all commands through discord, such as adding gameservers and whatnot

#

I prefer to have that on rest api with admin panel, and authentication that way

crisp palm
#

Right right

#

I was more concerned about switching over the Discord commands you have right now to HTTP Interactions

#

(Briefly put: the only thing that changes is how they're received)

vale urchin
#

(and ideally how the original response is created, which leaks to the user if we don't by default integrate with some http server)

devout bison
#

I am on my 5th? rebuild on the bot right now, but most of my commands call mediatr, the reason I wanted scope, is because then I can cache things like player information so I dont have to re-fetch playerdata, leaguedata, and so on over and over

#

the first thing I do when player calls a command is to verify that the player is signed up, has steamid and all these things, then check if gameservers are available, or a gather is being created, I have had to re-fetch that data atleast twice with the old way it worked

#

I can try making use of HTTP interactions, I just need to be pointed to the docs

#

and ill give it a whack

#

Unfortunately, I know not nearly enough about the inner workings of how the discord api works, beyond creating commands, and acting on those commands

crisp palm
#

They're not available yet unfortunately 😔

#

But when we do add them, we'll be adding them as a command processor

#

The only thing you'd have to change is literally a single line

devout bison
#

I'll give it a try when they exist then

#

what is the advantage of HTTP interactions?

crisp palm
#

I'll let ya know when they do salute

devout bison
#

I can literally call the bot using https?

crisp palm
#

Only if your bot has a phone number

#

Sorry

#

It'd just be another API route

devout bison
#

ok

crisp palm
#

I think the biggest issue for us is integration with other webservers

#

But... Maybe other extension methods might be able to fix that

#

¯_(ツ)_/¯

vale urchin
devout bison
#

ahh I see, because currently all commands are sent through websocket, if i understand it correctly

vale urchin
#

the disadvantage is that they're pretty hard to implement in a way that's both intuitive and unrestrictive

crisp palm
#

In theory, less resource intensive too

crisp palm
#

All events in general, which includes interactions

#

God I really want to start on the gateway rewrite

devout bison
#

I have an alarming amount of experience creating services that are based on httpClient from actual work

#

I need to actually get familiar with how things work in the nitty gritty first though

crisp palm
#

We'd be using HttpListener or ASP.NET Core

#

Discord sends us http requests and we'd respond

vale urchin
devout bison
#

ahh, so its all webhook based

crisp palm
devout bison
#

big ty for help, I will give this a whirl, and maybe I can atleast create a library that will be able to just plug into .net core webapi

crisp palm
#

Discord sends up the http request (POST https://your-website.com/api/discord-interactions) and you respond like any other http request that your webserver handles

#

I hope I'm explaining it well lol

devout bison
#

oh, I see, so in very simple terms, the bot would have controllers that handle all the discord interactions (minimal api kinda makes more sense)

crisp palm
#

Yes!

#

There we go

devout bison
#

yeah, i'd go minimal api route for that

#

because then you can set up the project having webapi, and all the endpoints can be just imported in your "addDiscordBot" service extension

#

have to use ngrok tunnel or other "fun" things to do testing of that though

crisp palm
#

Ah yeah that's true

#

I mean with how I want to do it you could just receive the interactions through the gateway

#

Your code won't change

#

Only the command processors used

devout bison
#

no, I get that

#

its a good way to speed up the "production environment"

crisp palm
#

But testing the new http interaction processor is

#

Fun

crisp palm
#

It's great

devout bison
#

what I would do for that to fly, atleast earlystage, is to require the bot to be attached to webapi, and then use minimalapi endpoints that is mapped to your services

#

and then bob should be your uncle relatively easily

#

I am actually pretty sure that, you can set that up inverse of how I set things up where the webapi is the startup project, and discordbot is launched by webapi

#

instead you could have discordbot be startup project, launch var builder = WebApplication.CreateBuilder() and then you run a IServiceCollection extension onto builder.Services that maps your minimal api endpoints

#

you should be cooking on gas pretty rapidly then, you would also then not need to worry about all the nitty gritty of actually handling the entire http request pipeline (that is ass)

#

the real trick is to let the webapplicationbuilder handle the ultimate creation of your servicecollection

vale urchin
#

##1820 :)

plucky hillBOT
devout bison
#

give me 30min, I am gonna see how v5 actually behaves when I set it up as hosted service, if it behaves well, I am gonna see if I can be creative

crisp palm
devout bison
#

this has now turned into a howto send me off on a tangent thinking about a problem

crisp palm
#

LMFAO

#

Valid

devout bison
#

but yeah, if possible i would stay well clear of trying to "redesign the wheel" when it comes to httpListener

#

I speak from experience in extensive amount of idiocy on that one, the http request handling pipeline in .net is robust, and probably could (should?) solve your usecase

#

(redesigning wheels that is)

devout bison
#

oh yeah the foreach is just for shardedclient, because it runs multiple instances of the client, I get it now, never really thought about the difference until now

#

is there any reason to use DiscordClient intead of DiscordShardedClient? I see DiscordClient does not have .UseCommandsAsync

vale urchin
#

DiscordClient is a single shard

#

which is generally what you want if you don't plan on serving enough guilds to warrant multiple shards

devout bison
#

yeah, I only ever plan to serve in my own server

#

its just DiscordClient doesnt have UseCommandsAsync

vale urchin
#

it has the sync version thereof

devout bison
#

yeah, I found it

#

was just surprised that usecommands didnt have async in a single instance scenario

vale urchin
#

it doesn't need to

#

the only reason it's async on the sharded client is that it may make an API call to determine the amount of shards it should create

devout bison
#

yeah I am just normally using async everywhere, I am starting to see that may not always be smart 😛

crisp palm
devout bison
#

🙂

crisp palm
#

I'm going to try to use existing solutions for the convenience of our users though

#

For once

devout bison
#

No, I have long since come to terms, that even though, I am fairly bright, there is always someone out there with a better solution than whatever I have come up with, all I can do is come with ideas

crisp palm
devout bison
#

I assume using Task when not needed has some unneeded overhead?

vale urchin
#

quite a lot thereof

crisp palm
#

Aki can go more into depth about it but the rule of thumb is to use VT when possible

vale urchin
#

probably not enough for people to care

#

but i care :smile:

#

when code isn't actually async, just keeping it sync is best (unless otherwise mandated by other restrictions, like implementing an interface)

devout bison
#

yeah, most of my operations are some async operation anyway, like fetching from or saving to db

vale urchin
#

when it can run async, i would generally use ValueTask where possible, but sticking to Task is also reasonable to avoid needing to understand how ValueTask works

#

(im sure whoever reviews my ratelimit PR will have fun with all the async ValueTask hacks in there)

devout bison
#

ValueTask is so far, outside my wheelhouse

vale urchin
#

LMAO

vale urchin
devout bison
#
  public Task StartAsync(CancellationToken cancellationToken)
  {
      var commandsExtension = _discordClient.UseCommands(new CommandsConfiguration
      {
          ServiceProvider = _serviceProvider
      });

      commandsExtension.AddCommands(typeof(DiscordBotService).Assembly);

      var textCommandProcessor = new TextCommandProcessor(new ()
      {
          PrefixResolver = new DefaultPrefixResolver("!").ResolvePrefixAsync
      });
      _ = commandsExtension.AddProcessorsAsync(textCommandProcessor);

      return Task.CompletedTask;
  }

should do fine, _serviceProvider is fetched from the serviceCollection, gotta start the bot in there as well,

attaching it to webapi like this:

    public static IServiceCollection AddDiscordBot(this IServiceCollection services, IConfiguration configuration)
    {
        services.Configure<DiscordClientConfig>(configuration.GetSection(DiscordClientConfig.Section));
        services.Configure<DiscordCommandServiceConfig>(configuration.GetSection(DiscordCommandServiceConfig.Section));
        services.AddSingleton<DiscordBotService>();
        services.AddHostedService(p => p.GetRequiredService<DiscordBotService>());
        return services;
    }
#

how do I create my own attributes with this setup? That I have not found yet

vale urchin
#

attributes as in pre-execution checks?

devout bison
#

yeah

devout bison
#

ok now i gotta figure out how to convert this:

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class PlayerRegisteredAttribute : CheckBaseAttribute
{
    public bool RegistrationRequired { get; set; }
    public PlayerRegisteredAttribute(bool registrationRequired)
    {
        RegistrationRequired = registrationRequired;
    }
    public override async Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help)
    {
        var mediator = ctx.Services.GetService(typeof(IMediator)) as IMediator;
        var player = await mediator!.Send(new GetPlayerByDiscordIdRequest(ctx.User.Id));
        

        return player.Success == RegistrationRequired;
    }
}

to that

vale urchin
#

you make an attribute with just the property and constructor

#

and then you make a check impl that takes the mediator through regular DI and copy the body over

#

presently, you're made to return an error message, that might change to a result type whenever @crisp palm approves

devout bison
#

ty for clarifying

#

now I only need to fuck it up twice before it works instead of the customary 5 times 😄

#

its exactly what I want though, because then I can do the registered check in the attribute, and store that player in a scoped service, that holds extended data about the call (player, what gather league and so on) without having to re-fetch it ad infinitum

#

what I used to have to do, was do the same fetch from db atleast once, maybe twice more, and same with the information about the league in the channel

vale urchin
devout bison
#

it was driving me mad how inefficient it was to constantly have to re-fetch this data

#

not to mention dealing with the fun and games of scopes and entity framework

#

I just realized, if you run custom implementation of httpListener, connecting this to existing webapi, wont that be a challenge since they will compete on port?

#

being 2 different instances of the httpListener

#

atleast in a prod environment where 80 and 443 is expected, unless some proxy shenanigans are done infront

vale urchin
#

another day of being very glad i don't do much web-related development 😄

devout bison
#
public class PlayerIsRegisteredAttribute : ContextCheckAttribute
{
    public bool RegistrationRequired { get; set; }

    public PlayerIsRegisteredAttribute(bool registrationRequired = true) => RegistrationRequired = registrationRequired;
}

public class PlayerRegisteredCheck : IContextCheck<PlayerIsRegisteredAttribute>
{
    private readonly IMediator _mediator;
    public PlayerRegisteredCheck(IMediator mediator)
    {
        _mediator = mediator;
    }
    public async ValueTask<string?> ExecuteCheckAsync(PlayerIsRegisteredAttribute attribute, CommandContext context)
    {
        try
        {
            var player = await _mediator.Send(new GetPlayerByDiscordId(context.User.Id));
            // logic to add player into cache here
            return null;
        }
        catch (PlayerNotFoundException ex)
        {
            return "player is not registered";
        }
    }
}

should do it for registering checks then

vale urchin
#

you do need to register it with the extension, too

devout bison
#

yeah, that I figured

#

was just the core logic of it, if that was solid