I'm creating a discord bot, and I'm struggling to figure out how to kick a user. I can't find code or documentation on this. Here's the code
private void MuteUserCommand()
{
commands.CreateCommand("stfu")
.Parameter("PersonToKick", ParameterType.Required)
.Do(async (e) =>
{
string persontomute = e.GetArg("PersonToKick");
if (e.User.ServerPermissions.MuteMembers == false)
{
await e.Channel.SendMessage("Yoo cahnot tells " + persontomute + " to shut up, iz not enuff poweh!");
}
else
{
await e.Channel.SendMessage("Shut up " + persontomute + "!");
//How do I kick persontomute here?
}
});
}
Update: I'm using Discord.NET 0.9.6
This is the command I use to kick people, checking for the correct permissions etc. (Make sure the bot has kick permissions itself ofcourse)
_client.GetService<CommandService>().CreateGroup("user", cgb =>
{
cgb.CreateCommand("kick")
.Description("Kick a user from the Server.")
.Parameter("User", ParameterType.Required)
.AddCheck((command, user, channel) => !paused)
.Do(async e =>
{
try
{
if (e.User.ServerPermissions.KickMembers)
{
User user = null;
try
{
// try to find the user
user = e.Server.FindUsers(e.GetArg("User")).First();
}
catch (InvalidOperationException)
{
await e.Channel.SendMessage($"Couldn't kick user {e.GetArg("User")} (not found).");
return;
}
// double safety check
if (user == null) await e.Channel.SendMessage($"Couldn't kick user {e.GetArg("User")} (not found).");
await user.Kick();
await e.Channel.SendMessage($"{user.Name} was kicked from the server!");
}
else
{
await e.Channel.SendMessage($"{e.User.Name} you don't have the permission to kick.");
}
}
catch (Exception ex)
{
// needs a better error handling haven't changed it since i tested it xD
await e.Channel.SendMessage(ex.Message);
}
});
You seem to be using Discord.Net. You need to get the user from the server and then kick them, as explaned in the documentation.
EDIT: my bad, I took this snippet from a friend's bot and forgot it used a custom extension. I added a "vanilla" way, taken straight from the sample bot.
var user = await _client.FindUser(e, e.Args[0], e.Args[1]); //args[0]would be the username, args[1] would be the discriminator (the random number that follows the discord id)
if (user == null) return;
await user.Kick();
Old
string err;
User user = e.Server.Users.FindUser(persontomute, out err);
if(user != null)
{
await user.Kick();
await e.Channel.SendMessage("Shut up " + persontomute + "!");
}
Related
first off, I know, that my question is very bad formulated. I am sorry for that.
I am trying to get my bot to search the status of every user in a guild and if the users status contains a certain string, the user gets a role. Everything works, as it should, aside from one thing. I want to run the command once and the other command will run every x seconds, so the users get updated all the time.
Now, if I add the string to my status and run the bot, it gives me the role every run. when I remove it, while the bot is running, the bot somehow still keeps giving me the role (it is supposed to remove the role and shut up until I add the string again). So it is somehow not updating and thinks, I stil have the role.
I am open for suggestions.
heres the responisble code:
[Command("statuson")]
[Alias("x")]
public async Task StatusOn()
{
while (true)
{
Console.WriteLine("req refresh");
await JaLOL();
Thread.Sleep(10000);
}
}
public int count = 0;
public async Task JaLOL()
{
IReadOnlyCollection<SocketGuildUser> users = null;
IReadOnlyCollection<IActivity> userActivities = null;
IReadOnlyCollection<ulong> roles = null;
Console.WriteLine("refresh");
users = Context.Guild.Users;
foreach (IGuildUser user in users)
{
userActivities = user.Activities;
roles = user.RoleIds;
foreach (ulong role in roles)
{
if (role == 1020762686323167303)
{
foreach (var activity in userActivities)
{
if (activity.Type == ActivityType.CustomStatus)
{
if (!activity.ToString().Contains(".gg/anime-club"))
{
await user.RemoveRoleAsync(1020762686323167303);
Console.WriteLine("remove " + user.DisplayName);
}
}
}
}
}
foreach (var activity in userActivities)
{
if (activity.Type == ActivityType.CustomStatus)
{
if (activity.ToString().Contains(".gg/anime-club"))
{
await user.AddRoleAsync(1020762686323167303);
Console.WriteLine("add " + user.DisplayName);
}
}
}
}
count++;
Console.WriteLine("end " + count);
}
I'm currently working on a Discord bot to learn how to code one. I thought I had it down, but when I try to use the following command, it does nothing:
[Command("ping")]
public async Task Ping(IUser user)
{
await Context.Channel.SendMessageAsync(user.ToString());
}
It's part of a public class, and if I use any other parameter type (e.g. IChannel, bool, int) it works. It's just this one parameter type. It also doesn't log any errors or exceptions. Any ideas?
[Command("ping")]
public async Task Ping(IUser user)
{
await Context.Channel.SendMessageAsync(user.ToString());
}
Your code id perfect. But think about this, the user is of the type IUser and your conversion to sting makes it vague. Instead try this:
[Command("ping")]
public async Task Ping(SocketGuildUser user)
{
await Context.Channel.SendMessageAsync(user.Username);
}
If you want to ping the user try user.Mention.
Also when I started learning I made a bot as well. Here is the source code. Its very very very basic. It definitely will help.
You could try using this workaround for your bot:
public async Task SampleCommand(string user="", [Remainder]string message="")
{
IUser subject = null;
if (user != "")
{
var guilds = (await Context.Client.GetGuildsAsync(Discord.CacheMode.AllowDownload));
var users = new List<IUser>();
foreach (var g in guilds)
users.AddRange(await g.GetUsersAsync(CacheMode.AllowDownload));
users = users.GroupBy(o => o.Id).Select(o => o.First()).ToList();
var search = users.Where(o => o.Username.ToLower().Contains(user.ToLower()) || Context.Message.MentionedUserIds.Contains(o.Id) || o.ToString().ToLower().Contains(user.ToLower())).ToArray();
if (search.Length == 0)
{
await ReplyAsync("***Error!*** *Couldn't find that user.*");
return;
}
else if (search.Length > 1)
{
await ReplyAsync("***Error!*** *Found more than one matching users.*");
return;
}
subject = search.First();
}
// ...
// execute command
Or you could wrap that in a method for easier access and reusability.
Basically, what it does is it looks for available users that match the given string (in nickname, username or mentions. You could also make it check for IDs if you so desire).
Edit: In my case I'm allowing people to mention anyone who shares the server with the bot, but in your case it might be more benefitial to just use the Context.Guild instead and cancel the command in case of DMs.
I ended up taking Reynevan's advice, and wrote a method for converting a mention into an IUser. Just call CustomUserTypereader.GetUser(mention_parameter, Context.Guild);
using System.Threading.Tasks;
using Discord;
public class CustomUserTypereader
{
public static async Task<IUser> GetUserFromString(string s, IGuild server)
{
if (s.IndexOf('#') == -1 || s.Replace("<", "").Replace(">", "").Length != s.Length - 2)
throw new System.Exception("Not a valid user mention.");
string idStr = s.Replace("<", "").Replace(">", "").Replace("#", "");
try
{
ulong id = ulong.Parse(idStr);
return await server.GetUserAsync(id);
}
catch
{
throw new System.Exception("Could not parse User ID. Are you sure the user is still on the server?");
}
}
}
My Xamarin Android app utilizes a Web service, which it connects to using HttpClient. On no connection (for exmaple when the user has no cell nor WiFi connection), an excpetion is thrown. I'm using async/await to get the data from the server. Here is an excerpt from my code:
public async Task<String> doLogin(string username, string password)
{
String url = Constants.loginEndpoint + username + "/" + password + "/";
var uri = new Uri(string.Format(url, string.Empty));
return_string = "";
try
{
var response = await GetAsync(uri);
if (response.IsSuccessStatusCode)
{
return_string = "success";
// Process the positive response here
else
{ }
}
catch (Exception ex)
{
throw new ConnectionException();
}
return return_string;
}
I defined a custon ConnectionException and want to show an AlertDialog to the user to inform them, that the request failed due to no connection. After the user clicks OK I want to close the app. I tried to show the alert dialog in the following way, but it's not working:
public class ConnectionException : Exception
{
public ConnectionException()
{
AlertDialog.Builder alert = new AlertDialog.Builder(myApp.Context);
alert.SetTitle("Failure");
alert.SetMessage("Request failed. No connection.");
alert.SetPositiveButton("OK", (senderAlert, args) =>
{
});
Dialog dialog = alert.Create();
dialog.Show();
}
public ConnectionException(string message)
: base(message)
{ }
public ConnectionException(string message, Exception innerException)
: base(message, innerException)
{ }
}
Is this the right approach? Probably not, as it's not working. I would appreciate any help on how to achieve this. Also, I've not given it too much thought, but is this a preferred way to handle such exceptions?
Assuming that your myApp.Context is an Activity and it has no back stack, you can just call Finish()
var context = myApp.Context; // this needs to be an Activity-based context...
context.RunOnUiThread(() =>
{
var alertDialog = new AlertDialog.Builder(context)
.SetTitle("Failure")
.SetMessage("Request failed. No connection.")
.SetPositiveButton("OK", (senderAlert, args) =>
{
context.Finish();
})
.Create();
alertDialog.Show();
});
Are you reusing this exception in several places, or is this a one off?
If your only using this exception once, there is no real reason to build your own.
You may as well just capture the exception and post your alert from inside your catch.
I know that's not as pretty of a way to write the catch, but if it works why not use it.
Side note:
DisplayAlert may be easier for you as well. It'll be a one liner.
Example:
await DisplayAlert("Failure","Request failed. No connection.", "Ok");
The way you are handling possible errors contains multiple issues and is not the right approach for several reasons.
First: Your code doesn't follow C-Sharp conventions and contains several code-smells. I show you a better and more accepted style.
1) Methods in C# normally starts with an uppercase letter. doLogin becomes Login
2) To create a new Uri instance you do not need to format your url-string. The string.Empty won't be used. So the code can be simplified into await GetAsync(new Uri(...));
3) The return_string seems not to be used in any way outside the method. It is string.Empty or "success". Why not switch it to bool? That way you can easily check if the login was successful. The return-type becomes bool instead of string.
The method looks now like this:
public async Task<bool> Login(string username, string password)
{
//TODO: Do parameter check for username and password
try
{
var response = await GetAsync(new Uri(Constants.loginEndpoint + username + "/" + password + "/"));
if (response.IsSuccessStatusCode)
{
// Process the positive response here
return true;
}
else
{
return false;
}
}
catch (Exception ex)
{
throw new ConnectionException();
}
return false;
}
Second, as mentioned by #Jason, an exception should not contain any UI or business logic. Consider the following, which will break your current implementation.
public async Task<bool> Login(string username, string password)
{
var connectionEx = new ConnectionException();
try
{
...
}
catch (Exception ex)
{
throw connectionEx;
}
...
}
Now your user will see the exception even so there wasn't any.
The last thing is that I recommend not to catch the exception just to throw your custom exception. The reason is, that there might be other things that raise an exception too. For example something is null in the positive response handling.
Depending on how the Login method is used, for example directly in an Android Activity, I would do something like that:
public async Task Login(string username, string password)
{
//TODO: Do parameter check for username and password
try
{
var response = await GetAsync(new Uri(Constants.loginEndpoint + username + "/" + password + "/"));
if (response.IsSuccessStatusCode)
{
// Process the positive response here
}
else
{
var alertDialog = new AlertDialog.Builder(context)
.SetTitle("Failure")
.SetMessage("Request failed.")
.SetPositiveButton("OK", (senderAlert, args) =>
{
Finish();
})
.Create();
alertDialog.Show();
}
}
catch (Exception ex)
{
var alertDialog = new AlertDialog.Builder(context)
.SetTitle("Failure")
.SetMessage("Something went wrong (" + ex.Message +")")
.SetPositiveButton("OK", (senderAlert, args) =>
{
Finish();
})
.Create();
alertDialog.Show();
}
}
I am using a combination of Entity Framework and WCF to provide Data for a client. But for some reason the client hang up (only when result is a non empty list) then calling a simple server function:
public List<CardSet> GetCollactions(string
{
try
{
if (db.Users.Any(x => x.username == username))
{
User requestingUser = db.Users.FirstOrDefault(x => x.username == username);
List<CardSet> result = requestingUser.Collections;
if (result != null)
return result;
}
}
catch (Exception e)
{
db.Errors.Add(new Error() { exception = e.Message, innerException = e.InnerException.Message, source = "GetCollection for user '" + username + "'", time = DateTime.Now });
db.SaveChanges();
}
return new List<CardSet>();
}
On the server the function is perfectly executed, the result for the database is correct. I call the function on the client like this:
List<CardSet> collections = client.GetCollactions(username).ToList();
foreach (CardSet collection in collections)
{
CollectionList.Items.Add(collection.name);
}
Edit: Client is a WPF form.
You must request from service in async manner to avoid client hangs up, First request async data from list in a thread and when data received continued operation in UI thread:
Task.Factory.StartNew(() => client.GetCollactions(username).ToList())
.ContinueWith(result =>
{
foreach (CardSet collection in result.Result)
{
CollectionList.Items.Add(collection.name);
}
},TaskScheduler.FromCurrentSynchronizationContext());
I have encountered a problem with pusherClient.wp8 package I installed from Nuget. Every time the app is sent to the background, I disconnect from pusher and unsubscribe from the channel. when I resume the app, the code hang when connection is re-established with pusher.
I have tried to reset the pusher object, reset the channel, create a new instance of pusher, and still nothing works, it seems there is a problem with the package, or rather the websocket disconnect method is failing to disconnect from pusher, however, when I close the app, everything get reseted. but this does not help me in instances when a user open a photopicker from my app.
Does anyone has a suggestion or know of another pusherclient I can use for windows phone 8. I have been struggling with this problem for weeks now.
Here is the github link of the package I used: https://github.com/bszypelow/PusherClient.WP8/blob/master/PusherClient.WP8
Thank you
public ConnectionState Connect()
{
var task = Task.Run(async () => await pusher.ConnectAsync()); ;
try
{
task.Wait();
}
catch (Exception ex)
{
Debug.WriteLine("Exception " + ex.Message + " at " + ex.Source + "Inner exception " + ex.InnerException + " additional data " + ex.Data);
}
return task.Result;
}
From looking at the source code I have a guess:
All the async methods never use .ConfigureAwait(false) which could be a reason for the dead lock.
Especially if you call it from the UI thread using the .Wait() or .Result. From event for example.
I suggest you to update the code (it's MIT license, you can do that):
public Task<Channel> Subscribe(string channelName)
{
if (_connection.State != ConnectionState.Connected)
throw new PusherException("You must wait for Pusher to connect before you can subscribe to a channel", ErrorCodes.NotConnected);
if (Channels.ContainsKey(channelName))
{
return Task.FromResult(Channels[channelName]);
}
// If private or presence channel, check that auth endpoint has been set
var chanType = ChannelTypes.Public;
if (channelName.ToLower().StartsWith("private-"))
chanType = ChannelTypes.Private;
else if (channelName.ToLower().StartsWith("presence-"))
chanType = ChannelTypes.Presence;
return SubscribeToChannel(chanType, channelName); //await is not needed here
}
private async Task<Channel> SubscribeToChannel(ChannelTypes type, string channelName)
{
switch (type)
{
case ChannelTypes.Public:
Channels.Add(channelName, new Channel(channelName, this));
break;
case ChannelTypes.Private:
AuthEndpointCheck();
Channels.Add(channelName, new PrivateChannel(channelName, this));
break;
case ChannelTypes.Presence:
AuthEndpointCheck();
Channels.Add(channelName, new PresenceChannel(channelName, this));
break;
}
if (type == ChannelTypes.Presence || type == ChannelTypes.Private)
{
string jsonAuth = await _options.Authorizer.Authorize(channelName, _connection.SocketID)
.ConfigureAwait(false); //do not capture the context!!
var template = new { auth = String.Empty, channel_data = String.Empty };
var message = JsonConvert.DeserializeAnonymousType(jsonAuth, template);
_connection.Send(JsonConvert.SerializeObject(new { #event = Constants.CHANNEL_SUBSCRIBE, data = new { channel = channelName, auth = message.auth, channel_data = message.channel_data } }));
}
else
{
// No need for auth details. Just send subscribe event
_connection.Send(JsonConvert.SerializeObject(new { #event = Constants.CHANNEL_SUBSCRIBE, data = new { channel = channelName } }));
}
return Channels[channelName];
}
You can checkout the source code, than if you don't use it now you can install trial version of ReSharper and this plug-in. They will help you to find all the lines where .ConfigureAwait is missing.