@@ -218,6 +218,118 @@ public async Task<MessagePreview> SendMessageAsync(string subject, string conten
218218
219219 return JsonConvert . DeserializeObject < MessagePreview > ( await response . Content . ReadAsStringAsync ( ) ) ;
220220 }
221+
222+ /// <summary>
223+ /// Get a message instance as template for the reply
224+ /// </summary>
225+ /// <param name="replyMessage">The message you want to reply</param>
226+ /// <param name="ct">Cancellation token</param>
227+ /// <returns>The template</returns>
228+ /// <exception cref="ObjectDisposedException">Thrown when the instance was disposed</exception>
229+ /// <exception cref="UnauthorizedAccessException">Thrown when you're logged in</exception>
230+ /// <exception cref="HttpRequestException">Thrown when an error happened while the http request</exception>
231+ public async Task < Message > GetReplyFormAsync ( MessagePreview replyMessage , CancellationToken ct = default )
232+ {
233+ return await GetReplyFormAsync ( new Message ( ) { Id = replyMessage . Id } , ct ) ;
234+ }
235+
236+ /// <summary>
237+ /// Get a message instance as template for the reply
238+ /// </summary>
239+ /// <param name="replyMessage">The message you want to reply</param>
240+ /// <param name="ct">Cancellation token</param>
241+ /// <returns>The template</returns>
242+ /// <exception cref="ObjectDisposedException">Thrown when the instance was disposed</exception>
243+ /// <exception cref="UnauthorizedAccessException">Thrown when you're logged in</exception>
244+ /// <exception cref="HttpRequestException">Thrown when an error happened while the http request</exception>
245+ public async Task < Message > GetReplyFormAsync ( Message replyMessage , CancellationToken ct = default )
246+ {
247+ string responseString = await MakeAPIGetRequestAsync ( $ "/WebUntis/api/rest/view/v1/messages/{ replyMessage . Id } /reply-form", ct ) ;
248+ return JObject . Parse ( responseString ) . ToObject < Message > ( ) ;
249+ }
250+
251+ /// <summary>
252+ /// Reply a message
253+ /// </summary>
254+ /// <remarks>
255+ /// Use this only for incoming messages and check if it is allowed
256+ /// </remarks>
257+ /// <param name="replyMessage">The message to reply</param>
258+ /// <param name="subject">The subject</param>
259+ /// <param name="content">The content (use <![CDATA[<br>]]> for line breaks</param>
260+ /// <param name="attachments">The attachments to send (Item1 is the name and Item2 the content)</param>
261+ /// <param name="ct">Cancellation token</param>
262+ /// <returns>The preview of the sent message</returns>
263+ /// <exception cref="ObjectDisposedException">Thrown when the instance was disposed</exception>
264+ /// <exception cref="UnauthorizedAccessException">Thrown when you're logged in</exception>
265+ /// <exception cref="HttpRequestException">Thrown when an error happened while the http request</exception>
266+ public async Task ReplyMessageAsync ( Message replyMessage , string subject , string content , Tuple < string , Stream > [ ] attachments = null , CancellationToken ct = default )
267+ {
268+ // Check for disposing
269+ if ( _disposedValue )
270+ throw new ObjectDisposedException ( GetType ( ) . FullName ) ;
271+
272+ // Check if you logged in
273+ if ( ! LoggedIn )
274+ throw new UnauthorizedAccessException ( "You're not logged in" ) ;
275+
276+ MultipartFormDataContent requestContent = new MultipartFormDataContent ( ) ;
277+
278+ // Json part
279+ StringWriter sw = new StringWriter ( ) ;
280+ using ( JsonWriter writer = new JsonTextWriter ( sw ) )
281+ {
282+ writer . WriteStartObject ( ) ;
283+
284+ writer . WritePropertyName ( "subject" ) ;
285+ writer . WriteValue ( subject ) ;
286+
287+ writer . WritePropertyName ( "content" ) ;
288+ writer . WriteValue ( content ) ;
289+
290+ writer . WritePropertyName ( "oneDriveAttachments" ) ;
291+ writer . WriteStartArray ( ) ;
292+ writer . WriteEndArray ( ) ;
293+
294+ writer . WriteEndObject ( ) ;
295+
296+ StringContent jsonContent = new StringContent ( sw . GetStringBuilder ( ) . ToString ( ) , Encoding . UTF8 , "application/json" ) ;
297+ requestContent . Add ( jsonContent , "request" , "blob" ) ;
298+ }
299+
300+ // Attachment part
301+ foreach ( Tuple < string , Stream > attachment in attachments )
302+ {
303+ byte [ ] buffer = new byte [ attachment . Item2 . Length ] ;
304+ int bytesRead = await attachment . Item2 . ReadAsync ( buffer , 0 , buffer . Length ) ;
305+ ByteArrayContent fileContent = new ByteArrayContent ( buffer , 0 , bytesRead ) ;
306+
307+ fileContent . Headers . Add ( "Content-Type" , "application/x-msdownload" ) ;
308+ requestContent . Add ( fileContent , "attachments" , attachment . Item1 ) ;
309+ }
310+
311+ HttpRequestMessage request = new HttpRequestMessage ( )
312+ {
313+ Method = HttpMethod . Post ,
314+ RequestUri = new Uri ( ServerUrl + $ "/WebUntis/api/rest/view/v2/messages/{ replyMessage . Id } /reply") ,
315+ Content = requestContent
316+ } ;
317+ request . Headers . Add ( "JSESSIONID" , _sessonId ) ;
318+ request . Headers . Add ( "schoolname" , _schoolName ) ;
319+ request . Headers . Authorization = new AuthenticationHeaderValue ( "Bearer" , _bearerToken ) ;
320+
321+ HttpResponseMessage response = await _client . SendAsync ( request , ct ) ;
322+
323+ // Verify response
324+ if ( response . StatusCode == HttpStatusCode . Unauthorized || response . StatusCode == HttpStatusCode . Forbidden )
325+ {
326+ _ = LogoutAsync ( ) ;
327+ throw new UnauthorizedAccessException ( "You're not logged in" ) ;
328+ }
329+
330+ if ( response . StatusCode != HttpStatusCode . OK )
331+ throw new HttpRequestException ( $ "There was an error while the http request (Code: { response . StatusCode } ).") ;
332+ }
221333
222334 /// <summary>
223335 /// Revoke a message (move back into drafts)(only for self-sent messages!)
0 commit comments