How do I make a discord bot respond only to people with a certain role? [C#]

和自甴很熟 提交于 2020-06-01 05:08:00

问题


I want to make my discord bot respond exclusively to people with a @Member role, so when a person without that role writes a command (ex. >say Hello), the bot will not respond to it, but when a person with the @Member role writes that, it will.


回答1:


Ideally, if this is being used for a command or rather multiple commands, a precondition should be used (example administration commands). This allows you to not have to duplicate the checks within each command.

Ah example of such a precondition can be found here. You can read more on preconditions here




回答2:


You will need to add role validation to each of your commands.

The quick and easy way would be to do the following:

[Command("test")]
public async Task TestCommand()
{
   var user as Context.User as IGuildUser;
   var roleToValidate = Context.Guild.Roles.First(r => r.Name == "SomeRoleName");

   if (!user.RoleIDs.Any(r => r == roleToValidate.Id))
      return;

   // the rest of the code
}

Another approach (which I would recommend) is using PreconditionAttribute

/// CheckRole.cs

using Discord;
using Discord.Commands;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Example.Attributes
{
    public class CheckRole : PreconditionAttribute
    {
        private List<string> _roles;

        public CheckRole(params string[] roles)
        {
            _roles = roles.ToList();
        }

        public async override Task<PreconditionResult> CheckPermissionsAsync(ICommandContext context, CommandInfo command)
        {
            var user = context.User as IGuildUser;
            var discordRoles = context.Guild.Roles.Where(gr => _roles.Any(r => gr.Name == r));

            foreach (var role in discordRoles)
            {
                var userInRole = user.RoleIds.Any(ri => ri == role.Id);

                if (userInRole)
                {
                    return await Task.FromResult(PreconditionResult.FromSuccess());
                }
            }

            return await Task.FromResult(PreconditionResult.FromError("You do not have permission to use this role."));
        }
    }
}
/// WhateverClassThatYouWriteCommandsIn.cs 

[Command("test")]
[CheckRole("AdminRoleName", "ModeratorRole", "NormalRole")]
public async Task TestCommandForMostRoles()
{
   var user as Context.User as IGuildUser;
   var roleToValidate = Context.Guild.Roles.First(r => r.Name == "Some Role Name");

   if (!user.RoleIDs.Any(r => r == roleToValidate.Id))
      return;

   // the rest of the code
}

[Command("test")]
[CheckRole("AdminRoleName", "ModeratorRole")]
public async Task TestCommandForAdmins()
{
   var user as Context.User as IGuildUser;
   var roleToValidate = Context.Guild.Roles.First(r => r.Name == "Some Role Name");

   if (!user.RoleIDs.Any(r => r == roleToValidate.Id))
      return;

   // the rest of the code
}

This literal code may not work as I haven't tested it, however it is based on my own implementation of role preconditions authorisation which works.

To break down the code:

  1. I have a variable to store multiple role names and use params[] in the constructor to allow any amount of role names to be provided. They are stored in the variable.

    private List<string> _roles;
    
    public CheckRole(params string[] roles)
    {
        _roles = roles.ToList();
    }
    
  2. CheckPermissionsAsync is automatically called every time that particular command is called.

public async override Task<PreconditionResult> CheckPermissionsAsync(ICommandContext context, CommandInfo command)
  1. Get the actual Role objects from the context from the names, loop through them and check the user to see if they have that permission. The first time a role is found on a user it will return a success and the command code in the original command function will be run. If FromError is returned then the command code is not run.
var user = context.User as IGuildUser;
            var discordRoles = context.Guild.Roles.Where(gr => _roles.Any(r => gr.Name == r));

            foreach (var role in discordRoles)
            {
                var userInRole = user.RoleIds.Any(ri => ri == role.Id);

                if (userInRole)
                {
                    return await Task.FromResult(PreconditionResult.FromSuccess());
                }
            }

            return await Task.FromResult(PreconditionResult.FromError("You do not have permission to use this role."));

This might seem like a lot, but you do not need to re-write role authorisation code again and you can simply add this attribute to whatever commands you want. You can also add this attribute to the class if you want every command in that class to be authorised by the role:

[CheckRoles("Moderator", "LowLevelModerator")]
public class ModeratorCommands : ModuleBase<SocketCommandContext>
{
   [Command("CheckStats")]
   public async Task ModeratorCommandForCheckStats()
   {
      // the code
   }

   [Command("BanUser")]
   public async Task ModeratorCommandForBanUser()
   {
      // the code
   }

   [CheckRole("Admin")]
   [Command("BanUser")]
   public async Task ModeratorCommandOnlyForAdminsForBanModerator()
   {
      // the code
   }
}


来源:https://stackoverflow.com/questions/55524148/how-do-i-make-a-discord-bot-respond-only-to-people-with-a-certain-role-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!