-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathBroadcastClient.cs
More file actions
218 lines (190 loc) · 8.29 KB
/
Copy pathBroadcastClient.cs
File metadata and controls
218 lines (190 loc) · 8.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
using Microsoft.AspNetCore.SignalR.Client;
using Newtonsoft.Json;
using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace Bitfox.AzureBroadcast
{
/// <summary>
/// A client to send messages via a predefined Azure Functions and Azure SignalR service.
/// </summary>
/// <typeparam name="T">string or your custom message class.</typeparam>
public class BroadcastClient<T>
{
private HubConnection connection;
private HttpClient httpClient;
private string baseAzureFunctionUrl;
private string userId;
/// <summary>
/// Add a handler to receive your messages.
/// </summary>
public Action<T, IBroadcastInfo> onMessage = null;
/// <summary>
/// Return the connection status of this client.
/// </summary>
public bool IsConnected {
get {
if (connection==null) { return false; }
return (connection.State == HubConnectionState.Connected);
}
}
/// <summary>
/// If true then messages originating from this userid, are not notified on the onMessage handler.
/// </summary>
public bool FilterOwnMessages { get; set; } = false;
/// <summary>
/// Creates a new BroadcastClient
/// </summary>
/// <param name="AzureFunctionUrl">The URL with /api of the Azure Functions</param>
/// <param name="FunctionHostKey">The host key to access the protected Azure Functions</param>
public BroadcastClient(string AzureFunctionUrl, string FunctionHostKey) :
this(AzureFunctionUrl, FunctionHostKey, Guid.NewGuid().ToString())
{
}
/// <summary>
/// Creates a new BroadcastClient with a specific userid
/// </summary>
/// <param name="AzureFunctionUrl">The URL with /api of the Azure Functions</param>
/// <param name="FunctionHostKey">The host key to access the protected Azure Functions</param>
/// <param name="userId">A custom userid</param>
public BroadcastClient(string AzureFunctionUrl, string FunctionHostKey, string userId)
{
this.baseAzureFunctionUrl = urlEndsWithSlash(AzureFunctionUrl);
this.userId = userId;
httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Add("x-ms-signalr-userid", userId);
httpClient.DefaultRequestHeaders.Add("x-functions-key", FunctionHostKey);
}
private string urlEndsWithSlash(string url)
{
return url.EndsWith("/") ? url : url + "/";
}
private async Task<SignalRConnectionInfo> GetSignalRConnectionInfo()
{
var url = $"{baseAzureFunctionUrl}api/negotiate";
var response = await httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
var connectionInfo = JsonConvert.DeserializeObject<SignalRConnectionInfo>(content);
return connectionInfo;
}
/// <summary>
/// Setup and connect to SignalR. Starts listening for incoming messages.
/// </summary>
public async void Start()
{
//Prevent multiple starts.
if (connection != null) { return; }
//Get the Azure SignalR service Url and Token.
var connectionInfo = await GetSignalRConnectionInfo();
//Build normal SignalR connection, with provided Token.
connection = new HubConnectionBuilder()
.WithUrl(connectionInfo.Url, (options) =>
{
options.AccessTokenProvider = () => Task.FromResult(connectionInfo.AccessToken);
})
.Build();
//Auto reconnected on disconnect
connection.Closed += async (error) =>
{
await Task.Delay(new Random().Next(0, 5) * 1000);
await connection.StartAsync();
};
//Call the delegate when receiving message.
connection.On<string>("newMessage", (wrappedmessage) =>
{
BroadcastMessage messageObject = JsonConvert.DeserializeObject<BroadcastMessage>(wrappedmessage);
if (!((messageObject.fromUser == userId) & FilterOwnMessages))
{
T message = JsonConvert.DeserializeObject<T>(messageObject.jsonmessage);
onMessage?.Invoke(message, messageObject);
}
});
await connection.StartAsync();
}
/// <summary>
/// Send message to every connected client.
/// </summary>
/// <param name="message"></param>
public async void Send(T message)
{
var url = $"{baseAzureFunctionUrl}api/broadcast";
var wrapped = new BroadcastMessage() {
toGroupName="",
toUser="",
fromUser=userId,
jsonmessage = JsonConvert.SerializeObject(message)
};
var msg = JsonConvert.SerializeObject(wrapped);
HttpContent c = new StringContent(msg, Encoding.UTF8, "application/json");
await httpClient.PostAsync(url, c);
}
/// <summary>
/// Send message to a named group
/// </summary>
/// <param name="message"></param>
/// <param name="groupName">The name of the group</param>
public async void SendToGroup(T message, string groupName)
{
var url = $"{baseAzureFunctionUrl}api/broadcast";
var wrapped = new BroadcastMessage(){
toGroupName=groupName,
toUser="",
fromUser=userId,
jsonmessage = JsonConvert.SerializeObject(message)
};
var msg = JsonConvert.SerializeObject(wrapped);
HttpContent c = new StringContent(msg, Encoding.UTF8, "application/json");
await httpClient.PostAsync(url, c);
}
/// <summary>
/// Send message to specific user(id)
/// </summary>
/// <param name="message"></param>
/// <param name="user">The userid receiving this message. UserId can be (optionally) specified in the constructor of the client</param>
public async void SendToUser(T message, string user)
{
var url = $"{baseAzureFunctionUrl}api/broadcast";
var wrapped = new BroadcastMessage(){
toGroupName="",
toUser=user,
fromUser=userId,
jsonmessage = JsonConvert.SerializeObject(message)
};
var msg = JsonConvert.SerializeObject(wrapped);
HttpContent c = new StringContent(msg, Encoding.UTF8, "application/json");
await httpClient.PostAsync(url, c);
}
/// <summary>
/// This client starts listening for messages designated to the group specified.
/// </summary>
/// <param name="groupName">The name of the group</param>
public async void JoinGroup(string groupName){
var url = $"{baseAzureFunctionUrl}api/groupaction";
GroupActionMessage gam = new GroupActionMessage() {
groupAction= GroupAction.Add,
groupName = groupName
};
//convert gam to json
var msg = JsonConvert.SerializeObject(gam);
HttpContent c = new StringContent(msg, Encoding.UTF8, "application/json");
await httpClient.PostAsync(url, c);
}
/// <summary>
/// This client stops listening for messages designated to the group specified.
/// </summary>
/// <param name="groupName">The name of the group</param>
public async void LeaveGroup(string groupName){
var url = $"{baseAzureFunctionUrl}api/groupaction";
GroupActionMessage gam = new GroupActionMessage() {
groupAction= GroupAction.Remove,
groupName = groupName
};
//convert gam to json
var msg = JsonConvert.SerializeObject(gam);
HttpContent c = new StringContent(msg, Encoding.UTF8, "application/json");
await httpClient.PostAsync(url, c);
}
}
}