Looking at how to send messages to specific channels. After searching around it doesn't seem to be too complicated but for some reason channel in this case is returning null, and as such an object reference not set to an instance of an object error when its used. The ID should be correct so I'm not really sure whats wrong.
[Command("say")]
public async Task sayAsync([Remainder] string message)
{
DiscordSocketClient client = new DiscordSocketClient();
ulong channelID = 123456789123456789; //Example ID
var channel = client.GetChannel(channelID) as SocketTextChannel;
await channel.SendMessageAsync(message);
}
If anyone finds this I'll put the answer I found on the discord API discord.
I made the mistake of creating a new client and trying to find the channels inside that.
DiscordSocketClient client = new DiscordSocketClient();
I should have been using the client that was used for the connection and thus
var client = Context.Client;
Was all that was needed to fix the issue.
I am a beginner in using Signalr and am checking out some examples.
Is it possible to send a message to the client from the server and wait for a return from it? Or is it possible to guarantee that after the answer the same session will be used?
My question is because in a given process, within a transaction, I need to ask the user if he wants to continue with the changes. I have not been able to ask this question before because validations should be done in the same session where changes have been made (but not yet confirmed).
Reiterating the comment from Jaime Yule, WebSockets are bidirectional communication and do not follow the Request/Response architecture for messaging. Given the very fluid nature of communication around WebSockets, these bullet points are good to keep in mind for your current (& future) scenarios:
SignalR is great if you're going to use it for fire & forget (Display a pop-up to a user and that's it)
It's not designed around request-response like you're asking, and trying to use it as such is an anti-pattern
Messages may be sent from either end of the connection at any time,
and there is no native support for one message to indicate it is
related to another
This makes the protocol poorly suited for transactional requirements
It is possible, but i would not recommend (relying on) it.
And it's not a pretty solution (using a static event and being pretty complex for such a simple thing).
Story goes like this:
Make sure client and server know the connectionId - They probably know that already, but i could not figure out a way to access it.
Await NotificationService.ConfirmAsync
... which will call confirm on the client
... which will await the user supplied answer
... and send it back to the server using Callback from The hub.
... which will notify the Callback from the NotificationService over a static event
... which will hand off the message back to ConfirmAsync (using a AutoResetEvent)
... which is hopefully still waiting :)
Client and server both have a 10 second timeout set.
The hub:
// Setup as /notification-hub
public class NotificationHub : Hub {
public string ConnectionId() => Context.ConnectionId;
public static event Action<string, string> Response;
public void Callback(string connectionId, string message) {
Response?.Invoke(connectionId, message);
}
}
Service:
// Wire it up using DI
public class NotificationService {
private readonly IHubContext<NotificationHub> _notificationHubContext;
public NotificationService(IHubContext<NotificationHub> notificationHubContext) {
_notificationHubContext = notificationHubContext;
}
public async Task<string> ConfirmAsync(string connectionId, string text, IEnumerable<string> choices) {
await _notificationHubContext.Clients.Client(connectionId)
.SendAsync("confirm", text, choices);
var are = new AutoResetEvent(false);
string response = null;
void Callback(string connId, string message) {
if (connectionId == connId) {
response = message;
are.Set();
}
}
NotificationHub.Response += Callback;
are.WaitOne(TimeSpan.FromSeconds(10));
NotificationHub.Response -= Callback;
return response;
}
}
Client side js:
var conn = new signalR.HubConnectionBuilder().withUrl("/notification-hub").build();
var connId;
// using Noty v3 (https://ned.im/noty/)
function confirm(text, choices) {
return new Promise(function (resolve, reject) {
var n = new Noty({
text: text,
timeout: 10000,
buttons: choices.map(function (b) {
return Noty.button(b, 'btn', function () {
resolve(b);
n.close();
});
})
});
n.show();
});
}
conn.on('confirm', function(text, choices) {
confirm(text, choices).then(function(choice) {
conn.invoke("Callback", connId, choice);
});
});
conn.start().then(function() {
conn.invoke("ConnectionId").then(function (connectionId) {
connId = connectionId;
// Picked up by a form and posted to the server
document.querySelector(".connection-id-input").value = connectionId;
});
});
For me this is way to complex to put it into the project i am working on.
It really looks like something that will come back and bite you later...
Is it possible to send a message to the client from the server and wait for a return from it? Or is it possible to guarantee that after the answer the same session will be used?
None of this is possible. Currently there's no way to wait for the client's response or even to get to know if the client received the message. There's some discussion implementing this on GitHub. Also here's the feature request.
Until then, the workaround is to send a "notification" from the server with a fire and forget attitude and let the client get the required data via a HTTP request to the server.
This is now possible with .NET 7 using Client Results.
Today, I've highlighted this issue in dotnet's Github page and got a good response from one of the developers of SignalR.
This requires the server to use ISingleClientProxy.InvokeAsync to be able to make request to the client and wait for response.
Quote from the documentation
In addition to making calls to clients, the server can request a
result from a client. This requires the server to use
ISingleClientProxy.InvokeAsync and the client to return a result from
its .On handler.
From the client (js/ts)
hubConnection.on("GetMessage", async () => {
let promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(new { data: "message" });
}, 100);
});
return promise;
});
From the server (C#)
//By calling Client(...) on an instance of IHubContext<T>
async Task<object> SomeMethod(IHubContext<MyHub> context)
{
string result = await context.Clients.Client(connectionID).InvokeAsync<string>(
"GetMessage");
return result;
}
//---------------------------//
//Or by calling Client(...) or Caller on the Clients property in a Hub method
public class ChatHub : Hub
{
public async Task<string> WaitForMessage(string connectionId)
{
var message = await Clients.Client(connectionId).InvokeAsync<string>(
"GetMessage");
return message;
}
}
Using the following form with Invoke waits for and returns the response directly (just like a "real" synchronous method call)
var persons = hubProxy.Invoke<IEnumerable<Person>>("GetPersonsSynchronous", SearchCriteria, noteFields).Result;
foreach (Person person in persons)
{
Console.WriteLine($"{person.LastName}, {person.FirstName}");
}
I'm trying to replicate behavior like a client browser but in C# (Performance reason). What I'm trying to set out to achieve is that for every new events received, my program should trigger the server side (Hub) which would then notify the client. Rather than having a while loop which would repeatedly hit the hub method every time even if theres no messages, is there a way to treat it as a trigger/detection so that once message is detected then execute Hub method ? Hope this makes sense
Snapshot of Client code below:
IHubProxy _hub;
string url = #"http://localhost:8080/";
var connection = new HubConnection(url);
_hub = connection.CreateHubProxy("PersonHub");
connection.Start().Wait();
//client side method
_hub.On("checkedIn", x => Console.WriteLine(x));
Console.WriteLine("Enter Person Name");
var answer = Console.ReadLine();
while (true) // Better way doing this? trigger or detect new message?
{
//server side method
_hub.Invoke("GetByName", answer).Wait();
}
Snapshot of Hub code below:
[HubName("PersonHub")]
public class PersonHub: Hub
{
public Person GetByName(string name)
{
//logic and etc ...
Clients.All.checkedIn(name);
}
}
By setting the while loop to true this means this will always call the server side method (Hub method) which I dont want to. If theres new events triggered then it should hit the hub method. Is there a way to somehow listen for new message but not to execute if no messages has been detected?
A possible solution is:
string line;
while ((line = Console.ReadLine()) != "exit")
{
_hub.Invoke("GetByName", line).Wait();
}
I have created a Notification Hub using this guide and these instructions (for how to add Firebase to Azure).
When I send using Test Send on Azure, the push notification is send successfully. But when I send it using their Console Example in the previous mentioned guide, it simply crashes when using SendGcmNativeNotificationAsync-method.
What can be wrong?
My namespace contains letters and a -, but my name for the hub contains _ too. Can that be the problem (and if it is, why did they not tell me during creation)?
EDIT: Modified code
var connectionStr = ServiceBusConnectionStringBuilder.CreateUsingSharedAccessKey(new Uri({uri}), "DefaultSendSharedAccessSignature", "Ln4em6ZqeukRS3y1Hgq/3m5V2S51IBIkG7tk+MAfO/Y=");
var hub = NotificationHubClient.CreateClientFromConnectionString(connectionStr, {hub-name});
await hub.SendGcmNativeNotificationAsync("{ \"data\" : {\"message\":\"Hello from Azure!\"}}");
Console.ReadLine();
Try something like this:
private static async void SendNotificationAsync()
{
NotificationHubClient hub = NotificationHubClient
.CreateClientFromConnectionString("<connection string with full access>", "<hub name>");
var notif = "{ \"data\" : {\"message\":\"" + "Hola" + "\"}}";
await hub.SendGcmNativeNotificationAsync(notif);
}
Obtained from: http://www.c-sharpcorner.com/blogs/sending-notification-from-a-console-application-using-notification-hubs
The connection string can be found in your Notification Hub (Access Policies):
It worked for me for a simple console App Test.
i have stored some client data on server in datagrid using signalr (whenever client connects details of all clients updated on server like ipaddress, name etc)... so i want to send that datagrid details to all clients and the condition is whenever new clients connect to server then all client including current client must get updated list ....here is my code basically what i have done till now,
public override Task OnConnected()
{
object ipaddress;
var a=Context.QueryString["name"];
var b= Context.QueryString["AnotherValue"];
if (Context.Request.Environment.TryGetValue("server.RemoteIpAddress", out ipaddress))
{
//ipcollections = new List<string[]>();
userhandler.ipcol.Add(new string[] { ipaddress.ToString(), a, b });
Program.MainForm.writetodatagrid(userhandler.ipcol);
}
Program.MainForm.WriteToConsole("Client connected: " + Context.ConnectionId );
return base.OnConnected();
}
and showing this list on server itself in datagird...i have to send this list to all clients...please help me...thank you....or is there any other way or am i doing things wrong please tell me..
On the server you would have a Hub and a method on the Hub to broadcast.
public class MyHub : Hub
{
public void Send(string ipaddress, string name)
{
Clients.All.addMessage(ipaddress, name);
}
}
Take a look at the following post. It has a an example of what you would do on your winforms client.
https://code.msdn.microsoft.com/windowsdesktop/Using-SignalR-in-WinForms-f1ec847b#content
and the source code for the winforms client:
https://code.msdn.microsoft.com/windowsdesktop/Using-SignalR-in-WinForms-f1ec847b/sourcecode?fileId=119892&pathId=583880341