Im creting discord bot via discord.Net v 1.0.2
And I want to get specific channel from my server Im trying to get that channel via Client.GetChannel(%channelID%) (Client is property of type DiscordSocketClient in my bot class) but that method return me only null and I just cant figure out why. I searched on lots of similiar post on internet but nothing work. There is start method of my bot and method where Im trying to get channel:
public async Task StartAsync()
{
await Client.StartAsync();
Client.MessageReceived += Client_MessageReceived;
Client.Connected += Client_Connected;
Logger.WriteLog("Bot started");
await Task.Delay(-1);
}
private async Task Client_Connected()
{
var channel = Client.GetChannel(414543303187496962);
}
Use Client.Ready event Handler instead, and make sure that the bot is indeed in that specific channel, and it should work this time.
So it would look like this:
public async Task StartAsync()
{
await Client.StartAsync();
Client.MessageReceived += Client_MessageReceived;
Client.Ready += Client_Ready;
Logger.WriteLog("Bot started");
await Task.Delay(-1);
}
private async Task Client_Ready()
{
var channel = Client.GetChannel(414543303187496962);
}
Related
I'm encountering some problems with a signalR hub , i need to invoke a certain method
from a signalR hub whenever i receive a request from an external service .
Whenever i do receive such "request" i connect to the signalRhub and then invoke one of its methods.
string hostname = Environment.GetEnvironmentVariable("FRONTEND_HOSTNAME");
HubConnection connection;
connection = new HubConnectionBuilder()
.WithUrl($"{hostname}/xxx")
.Build();
await connection.StartAsync();
var resp = new
{
xx:'xx',
};
await connection.InvokeAsync("SendReport", resp);
connection.Closed += async (error) =>
{
await Task.Delay(new Random().Next(0, 5) * 1000);
await connection.StartAsync();
};
The whole implementation has already been done , although it seems that i'm able to connect & send messages ALMOST always without problems,
however from time to time the StartAsync() method fails and throws an exception "Name or service not known" , to me it looks like it's unable to connect due to network issues , if you had to implement a way to retry the StartAsync a few times to then invoke the hub method, how would you do it?
I'm not asking for a straight solution , an example of something / links to documentation would still be great, i did search for a way to handle this but with no luck
Sorry it appears i didn't look properly within the documentation ,
here's the answer i was looking for:
public static async Task<bool> ConnectWithRetryAsync(HubConnection connection, CancellationToken token){
// Keep trying to until we can start or the token is canceled.
while (true)
{
try
{
await connection.StartAsync(token);
Debug.Assert(connection.State == HubConnectionState.Connected);
return true;
}
catch when (token.IsCancellationRequested)
{
return false;
}
catch
{
// Failed to connect, trying again in 5000 ms.
Debug.Assert(connection.State == HubConnectionState.Disconnected);
await Task.Delay(5000);
}
}}
https://learn.microsoft.com/en-us/aspnet/core/signalr/dotnet-client?view=aspnetcore-6.0&tabs=visual-studio
I've been working on a discord bot for the past few days, and I managed to get one of the functions working: (a command that sets a message it's supposed to dm users when they join). I can't seem to get the bot to send the actual message.
private async Task Join(SocketGuildUser UID)
{
if (UID.IsBot || UID.IsWebhook) return;
Welcometxt= File.ReadAllText([FILE]);
await UID.SendMessageAsync("Your Message Was Sucessfully set!");
}
private async Task HandleCommandAsync(SocketMessage arg)
{
var message = arg as SocketUserMessage;
var context = new SocketCommandContext(_client, message);
if (message.Author.IsBot) return;
int argPos = 0;
if (message.HasStringPrefix("!", ref argPos))
{
var result = await _commands.ExecuteAsync(context, argPos, _services);
if (!result.IsSuccess) Console.WriteLine(result.ErrorReason);
}
}
When i check the logs it gives a null reference error with the var message and context, I tried googling the error and changing up the code but to no avail any advice? I believe the error is in one of these two methods but im not 100% positive
I guess you can use UserJoined event to achieve this.
Define event handler for UserJoined
public async Task UserJoined(SocketGuildUser user)
{
await user.SendMessageAsync("Hello");
}
Register it
private readonly DiscordSocketClient _client = new DiscordSocketClient();
private readonly CommandService _commandService = new CommandService();
public async Task MainAsync()
{
....
_client.UserJoined += UserJoined;
....
}
I'm not sure enough if this is related, but check if the Server Member Intents is on
I want my Discord bot to greet members when they join a channel. I have been unable to find an event that fires when this happens. I have tried myClient.UserJoined += MyMethod; and others but they never get fired as I hope. Here is my main code:
public class Program
{
private DiscordSocketClient _client;
private CommandService _commands;
private IServiceProvider _services;
static void Main(string[] args)
=> new Program().RunBotAsync().GetAwaiter().GetResult();
public async Task RunBotAsync()
{
_client = new DiscordSocketClient();
_commands = new CommandService();
_services = new ServiceCollection()
.AddSingleton(_client)
.AddSingleton(_commands)
.BuildServiceProvider();
string botToken = // removed
_client.Log += Log;
await RegisterCommandsAsync();
await _client.LoginAsync(TokenType.Bot, botToken);
await _client.StartAsync();
await Task.Delay(-1);
}
private Task Log(LogMessage arg)
{
Console.WriteLine(arg);
return Task.CompletedTask;
}
public async Task RegisterCommandsAsync()
{
_client.MessageReceived += HandleCommandAsync;
_client.UserJoined += JoinedAsync; // Something like this to notify bot when someone has joined chat?
await _commands.AddModulesAsync(Assembly.GetEntryAssembly());
}
private Task JoinedAsync(SocketGuildUser arg)
{
throw new NotImplementedException();
}
private async Task HandleCommandAsync(SocketMessage arg)
{
var message = arg as SocketUserMessage;
if(message is null || message.Author.IsBot)
{
return;
}
int argPos = 0;
if (message.HasStringPrefix("!", ref argPos))
{
var context = new SocketCommandContext(_client, message);
await _commands.ExecuteAsync(context, argPos);
}
}
}
Thanks, and let me know if I can provide any more information.
Edit: The suggested link implements the UserJoined event, which only seems to trigger when a new member joins the channel. I need something that triggers everytime anyone logs in to the channel, even existing members.
Judging by the edit, I think you may have a slight mis conception of how the channels work.
Users join a guild, after which, they have become part of the guild.
After they join a guild, they are part of it, and the channels they are allowed to see. Hence there is no need to log into channels anymore.
Now what I think you want to achieve is sending a message in a channel / to a user whenever they switch from the offline state to the online state.
For this you could use the UserUpdated event. Where you can check the previous and the current status of a user, and send a message accordingly.
_client.UserUpdated += async (before, after) =>
{
// Check if the user was offline, and now no longer is
if(before.Status == UserStatus.Offline && after.Status != UserStatus.Offline)
{
// Find some channel to send the message to
var channel = e.Server.FindChannels("Hello-World", ChannelType.Text);
// Send the message you wish to send
await channel.SendMessage(after.Name + " has come online!");
}
}
I'm programming Discord bot and I went into a big problem. I am programming RPG bot so I need to write player's HP and enemy's hp really often so I've made a function.
private void showHP(CommandEventArgs e)
{
client.ExecuteAndWait(async() =>
{
await e.Channel.SendMessage(userInside + " - " + player.HP);
});
}
That CommandEventArgs e comes from Command function since I need to access the channel where that command has been executed.
commands.CreateCommand("attack").Do(async (e) =>
{
await e.Channel.SendMessage("You have attacked");
showHP(e);
}
Everything seems to work pretty well... Until it is executed. The whole bot stucks and it is endlessly waiting, so I think I've used async and wait commands wrongly. If I don't use them at all the message is falling into timeout.
client.ExecuteAndWait() blocks the calling context until the parent DiscordClient, (in your case, client) disconnects.
This is not the proper way to create an asynchronous context - the easiest way of doing so would be to wrap your code in a Task.Run, like so:
private void showHP(CommandEventArgs e)
{
Task.Run(async () => {
await e.Channel.SendMessage($"{userInside} - {player.HP}");
});
}
If you want to follow the right pattern for async then modify your code as follow:
private Task showHP(CommandEventArgs e)
{
return e.Channel.SendMessage($"{userInside} - {player.HP}");
}
And do the call as follow:
commands.CreateCommand("attack")
.Do(async (e) =>
{
await e.Channel.SendMessage("You have attacked");
await showHP(e);
}
Creating a task to run a function as async and don't await that task is just useless, just call the async function without await and return the task, it will prevent the task planner to split the code on different context switches.
I am trying to invoke a method from another .dll file .
It is sending a message through the VPN then Return the RecievedMessage from another computer.
As you now it takes time to sending and receiving message and VpnObject just send message and I should wait for listener to invoke the RecievedMessage.
This method is like this!
public string RecievedMessage()
{
string Recieved ;
// Some VPN Code and then return the result;
return Recieved;
}
public string SendAndRecieveMessage(string MessageToSend)
{
string RecievedAnswer = string.Empty;
// Now Sending Message through the VPN
VpnObject.SendMessage(MessageToSend);
//Then want to Recieve the answer and return the answer here .
return RecievedAnswer;
}
I'm just thinking how can wait for RecievedMessage to invoke then return the result .
You know it is simple to use a variable and assign it value and check for while but it reduced the performance dramatically .
Is there anyway to continue from SendAndRecieveMessage just when RecievedMessage invoked ? (I think it is something with async and await but don't know how!)
Edit :VpnObject is just a sender and receiver through the vpn . it contains a simple socket send and a listener that invoke a method(RecievedMessage) when new message received .
Whether or not you have an alternative to polling depends on whether the library you are using provides any events or callbacks that will tell you when the request has completed.
Either way, the standard approach to exposing the deferred result of an asynchronous operation is to use a Task. Your method signature would look like this:
public Task<string> SendAndRecieveMessage(string MessageToSend)
Now, how you actually implement the method depends on what API VpnObject exposes. TaskCompletionSource is very useful for this kind of thing.
If VpnObject has an event that fires when the request completes:
public Task<string> SendAndReceiveMessage(string messageToSend)
{
var tcs = new TaskCompletionSource<string>();
...
VpnObject.OnMessageReceived += (s, e) => tcs.SetResult(e.Message);
...
return tcs.Task;
}
If VpnObject can accept a callback that it will invoke when the request completes:
public Task<string> SendAndReceiveMessage(string messageToSend)
{
var tcs = new TaskCompletionSource<string>();
...
VpnObject.OnMessageReceived(message => tcs.SetResult(message));
...
return tcs.Task;
}
If VpnObject doesn't support any of this, you can fall back to polling:
public async Task<string> SendAndReceiveMessage(string messageToSend)
{
var tcs = new TaskCompletionSource<string>();
...
while(!VpnObject.IsMessageReceived)
await Task.Delay(500); // Adjust to a reasonable polling interval
...
return VpnObject.Message;
}
You know it is simple to use a variable and assign it value and check for while but it reduced the performance dramatically .
A spin while loop is definitely not the way to implement this. Even with a sleep, it's clunky, and C# has multiple ways to solve this problem.
It's not entirely clear how your VPN Send and Receive method works, but the idea for solving this is to either use a callback approach, or as you noted, use C# async framework.
Without more details on the VPN Object, I'll just have to have some stub methods. The idea is to create a Task that returns the string, mark it as an async task, then await for it to complete. In your case, the task is receiving the VPN response string.
Something like this.
public Task<string> ReceivedMessage()
{
//get the response from the VPN Object.
string Received = VpnObject.GetResponse();
var ts = new TaskCompletionSource<string>();
ts.SetResult(Received);
// Some VPN Code and then return the result;
return ts.Task;
}
public async Task<string> SendAndReceiveMessageAsync(string MessageToSend)
{
string result = string.Empty;
// Now Sending Message through the VPN
VpnObject.SendMessage(MessageToSend);
result = await ReceivedMessage();
return result;
}