|
11 | 11 | using System.Net.Http.Headers; |
12 | 12 | using System.Net; |
13 | 13 | using System.IO; |
14 | | -using System.Runtime.InteropServices; |
15 | 14 |
|
16 | 15 | namespace WebUntisAPI.Client |
17 | 16 | { |
@@ -81,6 +80,205 @@ public async Task<MessagePreview[]> GetMessageInboxAsync(CancellationToken ct = |
81 | 80 | return new JsonSerializer().Deserialize<List<MessagePreview>>(jsonMsg.CreateReader()).ToArray(); |
82 | 81 | } |
83 | 82 |
|
| 83 | + /// <summary> |
| 84 | + /// Send a draft |
| 85 | + /// </summary> |
| 86 | + /// <param name="draft">The draft that you want to send</param> |
| 87 | + /// <param name="recipients">The recipients for the message</param> |
| 88 | + /// <param name="timeout">The time out for the attachment download (when the draft had attachments they must be downloaded to send them)</param> |
| 89 | + /// <param name="ct">Cancellation token</param> |
| 90 | + /// <returns>The preview of the sent message</returns> |
| 91 | + /// <exception cref="ObjectDisposedException">Thrown when the instance was disposed</exception> |
| 92 | + /// <exception cref="UnauthorizedAccessException">Thrown when you're logged in</exception> |
| 93 | + /// <exception cref="HttpRequestException">Thrown when an error happened while the http request</exception> |
| 94 | + public async Task<MessagePreview> SendMessageAsync(Draft draft, MessagePerson[] recipients, int timeout = 2000, CancellationToken ct = default) |
| 95 | + { |
| 96 | + Tuple<string, MemoryStream>[] attachments = new Tuple<string, MemoryStream>[0]; |
| 97 | + if (draft.Attachments.Count > 0) |
| 98 | + { |
| 99 | + Dictionary<string, Task<MemoryStream>> attachmentTasks = new Dictionary<string, Task<MemoryStream>>(); |
| 100 | + |
| 101 | + foreach (Attachment attachment in draft.Attachments) |
| 102 | + attachmentTasks.Add(attachment.Name, attachment.DownloadContentAsStreamAsync(this, timeout, ct)); |
| 103 | + |
| 104 | + await Task.WhenAll(attachmentTasks.Values); |
| 105 | + attachments = attachmentTasks.Select(attachment => new Tuple<string, MemoryStream>(attachment.Key, attachment.Value.Result)).ToArray(); |
| 106 | + } |
| 107 | + |
| 108 | + return await SendMessageAsync(draft.Subject, draft.Content, recipients, draft.ForbidReply, attachments, ct); |
| 109 | + } |
| 110 | + |
| 111 | + /// <summary> |
| 112 | + /// Send a message |
| 113 | + /// </summary> |
| 114 | + /// <param name="subject">The subject</param> |
| 115 | + /// <param name="content">The content (use <![CDATA[<br>]]> for line breaks</param> |
| 116 | + /// <param name="recipients">The recipients for the message</param> |
| 117 | + /// <param name="forbidReply">Is a reply forbidden (it need a permission to to that)</param> |
| 118 | + /// <param name="attachments">The attachments to send (Item1 is the name and Item2 the content)</param> |
| 119 | + /// <param name="ct">Cancellation token</param> |
| 120 | + /// <returns>The preview of the sent message</returns> |
| 121 | + /// <exception cref="ObjectDisposedException">Thrown when the instance was disposed</exception> |
| 122 | + /// <exception cref="UnauthorizedAccessException">Thrown when you're logged in</exception> |
| 123 | + /// <exception cref="HttpRequestException">Thrown when an error happened while the http request</exception> |
| 124 | + public async Task<MessagePreview> SendMessageAsync(string subject, string content, MessagePerson[] recipients, bool forbidReply, Tuple<string, MemoryStream>[] attachments = null, CancellationToken ct = default) |
| 125 | + { |
| 126 | + // Check for disposing |
| 127 | + if (_disposedValue) |
| 128 | + throw new ObjectDisposedException(GetType().FullName); |
| 129 | + |
| 130 | + // Check if you logged in |
| 131 | + if (!LoggedIn) |
| 132 | + throw new UnauthorizedAccessException("You're not logged in"); |
| 133 | + |
| 134 | + MultipartFormDataContent requestContent = new MultipartFormDataContent(); |
| 135 | + |
| 136 | + // Json part |
| 137 | + StringWriter sw = new StringWriter(); |
| 138 | + using (JsonWriter writer = new JsonTextWriter(sw)) |
| 139 | + { |
| 140 | + writer.WriteStartObject(); |
| 141 | + |
| 142 | + writer.WritePropertyName("subject"); |
| 143 | + writer.WriteValue(subject); |
| 144 | + |
| 145 | + writer.WritePropertyName("content"); |
| 146 | + writer.WriteValue(content); |
| 147 | + |
| 148 | + writer.WritePropertyName("requestConfirmation"); |
| 149 | + writer.WriteValue(false); |
| 150 | + |
| 151 | + writer.WritePropertyName("recipientUserIds"); |
| 152 | + writer.WriteStartArray(); |
| 153 | + foreach (MessagePerson recipient in recipients) |
| 154 | + writer.WriteValue(recipient.Id); |
| 155 | + writer.WriteEndArray(); |
| 156 | + |
| 157 | + writer.WritePropertyName("oneDriveAttachments"); |
| 158 | + writer.WriteStartArray(); |
| 159 | + writer.WriteEndArray(); |
| 160 | + |
| 161 | + writer.WritePropertyName("forbidReply"); |
| 162 | + writer.WriteValue(forbidReply); |
| 163 | + |
| 164 | + writer.WriteEndObject(); |
| 165 | + |
| 166 | + StringContent jsonContent = new StringContent(sw.GetStringBuilder().ToString(), Encoding.UTF8, "application/json"); |
| 167 | + requestContent.Add(jsonContent, "request", "blob"); |
| 168 | + } |
| 169 | + |
| 170 | + // Attachment part |
| 171 | + foreach (Tuple<string, MemoryStream> attachment in attachments) |
| 172 | + { |
| 173 | + ByteArrayContent fileContent = new ByteArrayContent(attachment.Item2.ToArray()); |
| 174 | + fileContent.Headers.Add("Content-Type", "application/x-msdownload"); |
| 175 | + requestContent.Add(fileContent, "attachments", attachment.Item1); |
| 176 | + } |
| 177 | + |
| 178 | + HttpRequestMessage request = new HttpRequestMessage() |
| 179 | + { |
| 180 | + Method = HttpMethod.Post, |
| 181 | + RequestUri = new Uri(ServerUrl + "/WebUntis/api/rest/view/v2/messages/users"), |
| 182 | + Content = requestContent |
| 183 | + }; |
| 184 | + request.Headers.Add("JSESSIONID", _sessonId); |
| 185 | + request.Headers.Add("schoolname", _schoolName); |
| 186 | + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _bearerToken); |
| 187 | + |
| 188 | + HttpResponseMessage response = await _client.SendAsync(request, ct); |
| 189 | + |
| 190 | + // Verify response |
| 191 | + if (response.StatusCode == HttpStatusCode.Unauthorized || response.StatusCode == HttpStatusCode.Forbidden) |
| 192 | + { |
| 193 | + _ = LogoutAsync(); |
| 194 | + throw new UnauthorizedAccessException("You're not logged in"); |
| 195 | + } |
| 196 | + |
| 197 | + if (response.StatusCode != HttpStatusCode.OK) |
| 198 | + throw new HttpRequestException($"There was an error while the http request (Code: {response.StatusCode})."); |
| 199 | + |
| 200 | + return JsonConvert.DeserializeObject<MessagePreview>(await response.Content.ReadAsStringAsync()); |
| 201 | + } |
| 202 | + |
| 203 | + /// <summary> |
| 204 | + /// Revoke a message (move back into drafts)(only for self-sent messages!) |
| 205 | + /// </summary> |
| 206 | + /// <param name="message">The message to revoke</param> |
| 207 | + /// <param name="ct">Cancellation token</param> |
| 208 | + /// <exception cref="ObjectDisposedException">Thrown when the instance was disposed</exception> |
| 209 | + /// <exception cref="UnauthorizedAccessException">Thrown when you're logged in</exception> |
| 210 | + /// <exception cref="HttpRequestException">Thrown when an error happened while the http request</exception> |
| 211 | + public async Task RevokeMessageAsync(MessagePreview message, CancellationToken ct = default) |
| 212 | + { |
| 213 | + await RevokeMessageAsync(new Message() { Id = message.Id }, ct); |
| 214 | + } |
| 215 | + |
| 216 | + /// <summary> |
| 217 | + /// Revoke a message (move back into drafts)(only for self-sent messages!) |
| 218 | + /// </summary> |
| 219 | + /// <param name="message">The message to revoke</param> |
| 220 | + /// <param name="ct">Cancellation token</param> |
| 221 | + /// <exception cref="ObjectDisposedException">Thrown when the instance was disposed</exception> |
| 222 | + /// <exception cref="UnauthorizedAccessException">Thrown when you're logged in</exception> |
| 223 | + /// <exception cref="HttpRequestException">Thrown when an error happened while the http request</exception> |
| 224 | + public async Task RevokeMessageAsync(Message message, CancellationToken ct = default) |
| 225 | + { |
| 226 | + // Check for disposing |
| 227 | + if (_disposedValue) |
| 228 | + throw new ObjectDisposedException(GetType().FullName); |
| 229 | + |
| 230 | + // Check if you logged in |
| 231 | + if (!LoggedIn) |
| 232 | + throw new UnauthorizedAccessException("You're not logged in"); |
| 233 | + |
| 234 | + HttpRequestMessage request = new HttpRequestMessage() |
| 235 | + { |
| 236 | + Method = HttpMethod.Post, |
| 237 | + RequestUri = new Uri(ServerUrl + $"/WebUntis/api/rest/view/v1/messages/{message.Id}/revoke"), |
| 238 | + }; |
| 239 | + request.Headers.Add("JSESSIONID", _sessonId); |
| 240 | + request.Headers.Add("schoolname", _schoolName); |
| 241 | + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _bearerToken); |
| 242 | + |
| 243 | + HttpResponseMessage response = await _client.SendAsync(request, ct); |
| 244 | + |
| 245 | + // Verify response |
| 246 | + if (response.StatusCode == HttpStatusCode.Unauthorized || response.StatusCode == HttpStatusCode.Forbidden) |
| 247 | + { |
| 248 | + _ = LogoutAsync(); |
| 249 | + throw new UnauthorizedAccessException("You're not logged in"); |
| 250 | + } |
| 251 | + |
| 252 | + if (response.StatusCode != HttpStatusCode.OK) |
| 253 | + throw new HttpRequestException($"There was an error while the http request (Code: {response.StatusCode})."); |
| 254 | + } |
| 255 | + |
| 256 | + /// <summary> |
| 257 | + /// Delete a message |
| 258 | + /// </summary> |
| 259 | + /// <param name="message">Message to delete</param> |
| 260 | + /// <param name="ct">Cancellation token</param> |
| 261 | + /// <exception cref="ObjectDisposedException">Thrown when the instance was disposed</exception> |
| 262 | + /// <exception cref="UnauthorizedAccessException">Thrown when you're logged in</exception> |
| 263 | + /// <exception cref="HttpRequestException">Thrown when an error happened while the http request</exception> |
| 264 | + public async Task DeleteMessageAsync(MessagePreview message, CancellationToken ct = default) |
| 265 | + { |
| 266 | + await DeleteDraftAsync(new Draft() { Id = message.Id }, ct); |
| 267 | + } |
| 268 | + |
| 269 | + /// <summary> |
| 270 | + /// Delete a message |
| 271 | + /// </summary> |
| 272 | + /// <param name="message">Message to delete</param> |
| 273 | + /// <param name="ct">Cancellation token</param> |
| 274 | + /// <exception cref="ObjectDisposedException">Thrown when the instance was disposed</exception> |
| 275 | + /// <exception cref="UnauthorizedAccessException">Thrown when you're logged in</exception> |
| 276 | + /// <exception cref="HttpRequestException">Thrown when an error happened while the http request</exception> |
| 277 | + public async Task DeleteMessageAsync(Message message, CancellationToken ct = default) |
| 278 | + { |
| 279 | + await DeleteDraftAsync(new Draft() { Id = message.Id }, ct); |
| 280 | + } |
| 281 | + |
84 | 282 | /// <summary> |
85 | 283 | /// Get all your saved drafts |
86 | 284 | /// </summary> |
@@ -111,7 +309,7 @@ public async Task<DraftPreview[]> GetSavedDraftsAsync(CancellationToken ct = def |
111 | 309 | /// <exception cref="ObjectDisposedException">Thrown when the instance was disposed</exception> |
112 | 310 | /// <exception cref="UnauthorizedAccessException">Thrown when you're logged in</exception> |
113 | 311 | /// <exception cref="HttpRequestException">Thrown when an error happened while the http request</exception> |
114 | | - public async Task<DraftPreview> CreateDraftAsync(string subject, string content, string recipientOption, bool forbidReply, bool copyToStudent, Tuple<string, MemoryStream>[] attachments, CancellationToken ct = default) |
| 312 | + public async Task<DraftPreview> CreateDraftAsync(string subject, string content, string recipientOption, bool forbidReply, bool copyToStudent, Tuple<string, MemoryStream>[] attachments = null, CancellationToken ct = default) |
115 | 313 | { |
116 | 314 | // Check for disposing |
117 | 315 | if (_disposedValue) |
@@ -197,13 +395,13 @@ public async Task<DraftPreview> CreateDraftAsync(string subject, string content, |
197 | 395 | /// <remarks>Change all in the <paramref name="draft"/> excepted the attachments</remarks> |
198 | 396 | /// <param name="draft">The draft to update</param> |
199 | 397 | /// <param name="newAttachments">The new attachments (Item1 is the file name and Item2 the content)</param> |
200 | | - /// <param name="attachmentIdsToDelete">The <see cref="Attachment.Id"/> from the attachment you want to delete</param> |
| 398 | + /// <param name="attachmentToDelete">The attachments from the draft you want to delete</param> |
201 | 399 | /// <param name="ct">Cancellation token</param> |
202 | 400 | /// <returns>The preview of the created draft</returns> |
203 | 401 | /// <exception cref="ObjectDisposedException">Thrown when the instance was disposed</exception> |
204 | 402 | /// <exception cref="UnauthorizedAccessException">Thrown when you're logged in</exception> |
205 | 403 | /// <exception cref="HttpRequestException">Thrown when an error happened while the http request</exception> |
206 | | - public async Task<DraftPreview> UpdateDraftAsync(Draft draft, Tuple<string, MemoryStream>[] newAttachments = null, string[] attachmentIdsToDelete = null, CancellationToken ct = default) |
| 404 | + public async Task<DraftPreview> UpdateDraftAsync(Draft draft, Tuple<string, MemoryStream>[] newAttachments = null, Attachment[] attachmentToDelete = null, CancellationToken ct = default) |
207 | 405 | { |
208 | 406 | // Check for disposing |
209 | 407 | if (_disposedValue) |
@@ -242,8 +440,8 @@ public async Task<DraftPreview> UpdateDraftAsync(Draft draft, Tuple<string, Memo |
242 | 440 |
|
243 | 441 | writer.WritePropertyName("attachmentIdsToDelete"); |
244 | 442 | writer.WriteStartArray(); |
245 | | - foreach (string attachmentId in attachmentIdsToDelete) |
246 | | - writer.WriteValue(attachmentId); |
| 443 | + foreach (Attachment attachment in attachmentToDelete ?? new Attachment[0]) |
| 444 | + writer.WriteValue(attachment._id); |
247 | 445 | writer.WriteEndArray(); |
248 | 446 |
|
249 | 447 | writer.WritePropertyName("forbidReply"); |
|
0 commit comments