volt/user.rs
1//! User Client
2//!
3//! Makes requests for different information about users
4//!
5//! ## Examples:
6//! For all of the examples you will need to replace the path with the path to
7//! your own .env file and add the key "USER_KEY" with you revolt authentication token
8//!
9//! This fetchs all the data about the user and prints it out
10//!
11//! ```rust
12//! use volt::user::UserClient;
13//! use dotenv::from_path;
14//! use std::env::var;
15//!
16//! let path = r#"C:\Users\conno\Downloads\volt\tests\.env"#;
17//! from_path(path).unwrap();
18//! let user: String = var("USER_KEY").unwrap();
19//! let mut client = UserClient::new(user);
20//! println!("{:?}", client.fetch_self().get());
21//! ```
22//!
23//! This returns just the id of the user as string
24//!
25//! ```rust
26//! use volt::user::UserClient;
27//! use dotenv::from_path;
28//! use std::env::var;
29//!
30//! let path = r#"C:\Users\conno\Downloads\volt\tests\.env"#;
31//! from_path(path).unwrap();
32//! let user: String = var("USER_KEY").unwrap();
33//! let mut client = UserClient::new(user);
34//! println!("{:?}", client.fetch_self().get().id());
35//! ```
36//!
37
38use crate::core::structs::user_structs::{User, UserFlags, UserProfile, UserDmChannels as Channels, UserDms, UserMutuals, Image};
39use crate::core::Cache;
40use crate::core::TokenBucket;
41
42use reqwest::{Client as ReqwestClient};
43use reqwest::header::{HeaderMap, HeaderValue};
44use serde::{Serialize, Deserialize};
45use tokio::time;
46
47use std::time::Duration;
48use std::sync::{Arc, Mutex};
49
50/// The client to acsess user information
51///
52/// ## Returns:
53///
54/// The desired information as the inbuild `UserResult` enum type
55
56#[derive(Debug, Clone)]
57pub struct UserClient {
58 auth: String,
59 cache: Cache,
60 url: String,
61 bucket: Arc<Mutex<TokenBucket>>,
62 user: String,
63 json: String,
64 data: Data,
65}
66
67#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
68pub(crate) enum Data {
69 Users(),
70 UsersFlags(),
71 UsersProfile(),
72 UserDmChannels(),
73 UserDms(),
74 UserMutuals(),
75 String(),
76}
77
78/// The enum for data returning
79///
80/// The variations of this enum are the types that are returned after .run() is called
81///
82/// **Note!:**
83///
84/// If any of the methods that impliment this enum are called the type will change
85
86#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
87pub enum UserResult {
88 User(User),
89 UserFlags(UserFlags),
90 UserProfile(UserProfile),
91 UserDmChannels(Vec<Channels>),
92 UserDms(UserDms),
93 UserMutuals(UserMutuals),
94 String(String),
95}
96
97impl UserClient {
98
99 /// Creates a new UserClient object
100 ///
101 /// What this function does is initialize all of the values for the client to use
102 ///
103 /// This function must be directly called after UserClient (ex: UserClient::new(auth))
104 ///
105 /// ## Parameters
106 ///
107 /// `auth: String` - The revolt authentication token of the user
108
109 pub fn new(auth: String) -> UserClient {
110 UserClient {
111 auth,
112 cache: Cache::new(),
113 url: String::new(),
114 bucket: Arc::new(Mutex::new(TokenBucket::new(20))),
115 user: String::new(),
116 json: String::new(),
117 data: Data::Users(), // Data::Users() is defult as it is the most common
118 }
119 }
120
121 /// Fetchs the data of the currently authenticated user
122 ///
123 /// Sets the UserResult enum to UserResult::User
124
125 pub fn fetch_self(&mut self) -> &mut Self{
126 self.url ="https://api.revolt.chat/users/@me".to_string();
127 self
128 }
129
130 /// Fetchs a user's data
131 ///
132 /// Sets the UserResult enum to UserResult::User
133 ///
134 /// ## Parameters:
135 ///
136 /// `target: &str` - The userid of the user you want data for on revolt
137
138 pub fn fetch_user(&mut self, target: &str) -> &mut Self{
139 self.url = format!("https://api.revolt.chat/users/{}", target);
140 self.user = target.to_string();
141 self
142 }
143
144 /// Edits a users details
145 ///
146 /// ## Parameters:
147 ///
148 /// - `target: &str` - The userid of your user
149 /// - `changes: &str` - A &str repersentation of the changes you want to make to your user
150 ///
151 /// **Flaged as experimental until further testing can be done!!**
152
153 #[cfg(feature="experimental")]
154 pub fn edit_user(&mut self, target: &str, changes: &str) -> &mut Self{
155 self.data = Data::String();
156 self.url = format!("https://api.revolt.chat/users/{}", target);
157 self.json = changes.to_string();
158 self
159 }
160
161 /// Fetchs the flags of a user
162 ///
163 /// Flags are information about the users account status on revolt
164 ///
165 /// - 1: Suspended
166 /// - 2: Deleted
167 /// - 4: Banned
168 ///
169 /// Sets the UserResult enum to UserResult::UsersFlags
170 ///
171 /// ## Parameters:
172 ///
173 /// `target: &str` - The userid of the user you want data for on revolt
174
175 pub fn fetch_user_flags(&mut self, target: &str) -> &mut Self{
176 self.data = Data::UsersFlags();
177 self.url = format!("https://api.revolt.chat/users/{}/flags", target);
178 self.user = target.to_string();
179 self
180 }
181
182 /// Changes a users username
183 ///
184 /// ## Parameters:
185 ///
186 /// - `username: &str` - The username you want to change to
187 /// - `pass: &str` - Your revolt password
188 ///
189 /// **Flaged as experimental until further testing can be done!!**
190
191 #[cfg(feature="experimental")]
192 pub fn change_username(&mut self, username: &str, pass: &str) -> &mut Self {
193 self.data = Data::String();
194 self.url ="https://api.revolt.chat/users/@me/username".to_string();
195 self.json = format!("{{\"username\": {}, \"password\": {},}}", username, pass);
196 self
197 }
198
199 /// Returns the binary data of a persons avatar
200 ///
201 /// This function in pair with 'avatar' in UserResult will return a string that you will have to parse to form an image
202 ///
203 /// ## Parameters:
204 ///
205 /// `target: &str` - The id of the user that you want the avatar of
206
207 pub fn fetch_default_avatar(&mut self, target: &str) -> &mut Self {
208 self.data = Data::String();
209 self.url = format!("https://api.revolt.chat/users/{}/default_avatar", target);
210 self.user = target.to_string();
211 self
212 }
213
214 /// Fetchs the profile data of a user
215 ///
216 /// ## Parameters:
217 ///
218 /// `target: &str` - The id of the user that you want to fetch the profile of
219
220 pub fn fetch_user_profile(&mut self, target: &str) -> &mut Self {
221 self.data = Data::UsersProfile();
222 self.url = format!("https://api.revolt.chat/users/{}/profile", target);
223 self.user = target.to_string();
224 self
225 }
226
227 /// **Flaged as experimental until further testing can be done!!**
228 #[cfg(feature="experimental")]
229 pub fn fetch_direct_message_channels(&mut self) -> &mut Self {
230 self.data = Data::UserDmChannels();
231 self.url ="https://api.revolt.chat/users/dms".to_string();
232 self
233 }
234
235 /// **Flaged as experimental until further testing can be done!!**
236 #[cfg(feature="experimental")]
237 pub fn open_direct_message(&mut self, target: &str) -> &mut Self {
238 self.data = Data::UserDms();
239 self.url = format!("https://api.revolt.chat/users/{}/dm", target);
240 self
241 }
242
243 /// Fetches the mutual users and servers of a person
244 ///
245 /// ## Parameters:
246 ///
247 /// `target: &str` - The user id of the person you want to compare
248
249 pub fn fetch_mutuals(&mut self, target: &str) -> &mut Self {
250 self.data = Data::UserMutuals();
251 self.url = format!("https://api.revolt.chat/users/{}/mutual", target);
252 self
253 }
254
255 /// Accepts or denies a friend request
256 ///
257 /// ## Prameters:
258 ///
259 /// `target: &str` - The persons whoes friend request you want to accep/deny
260 ///
261 /// **Note**
262 /// If you do `.put()` after it will accept the request, `.del()` will deny it or remove the friend
263
264 pub fn friend(&mut self, target: &str) -> &mut Self {
265 self.url = format!("https://api.revolt.chat/users/{}/friend", target);
266 self
267 }
268
269 /// Blocks or unblocks a user
270 ///
271 /// ## Prameters:
272 ///
273 /// `target: &str` - The person who you want to block/unblock
274 ///
275 /// **Note**
276 /// If you do `.put()` it will block the user, `.del()` will unblock them
277
278 pub fn block(&mut self, target: &str) -> &mut Self {
279 self.url = format!("https://api.revolt.chat/users/{}/block", target);
280 self
281 }
282
283 /// Sends a friend request to a user
284 ///
285 /// ## Prameters:
286 ///
287 /// `target: &str` - The username and discrimeter of the user you want to send a friend request to
288 /// ex: "Bird#9223"
289
290 pub fn friend_request(&mut self, target: &str) -> &mut Self {
291 self.url = "https://api.revolt.chat/users/friend".to_string();
292 self.json = format!("{{\"username\":\"{}\"}}", target);
293 self
294 }
295
296 /// Runs the client for a get request
297 ///
298 /// This function makes the request, applies rate limits, and caches/retrives values from the cache
299 ///
300 /// ## Retruns:
301 ///
302 /// This function returns a UserResult enum that can be printed out
303 /// or used to get more specific data from the json data provided
304
305 #[tokio::main]
306 pub async fn get(&mut self) -> UserResult{
307 // Checks the cache for the item
308 // Only used on GET requests
309 if self.cache.get(&self.user).is_some() {
310 let text = self.cache.get(&self.user).unwrap();
311 match text.1.to_lowercase().as_str(){
312 "user" => {
313 if self.data == Data::Users(){
314 let parsed_value: User = serde_json::from_str(&text.0).unwrap();
315 return UserResult::User(parsed_value);
316 }
317 }
318 "userflags" => {
319 if self.data == Data::UsersFlags(){
320 let parsed_value: UserFlags = serde_json::from_str(&text.0).unwrap();
321 return UserResult::UserFlags(parsed_value);
322 }
323 }
324 "userprofile" => {
325 if self.data == Data::UsersProfile(){
326 let parsed_value: UserProfile = serde_json::from_str(&text.0).unwrap();
327 return UserResult::UserProfile(parsed_value);
328 }
329 }
330 _ => panic!("Something went very wrong"),
331 }
332 }
333
334 let bucket = self.bucket.clone();
335 let mut bucket = bucket.lock().unwrap();
336
337 let mut header: HeaderMap = HeaderMap::new();
338 header.insert("x-session-token", HeaderValue::from_str(&self.auth).unwrap());
339
340 while !bucket.try_acquire() {
341 time::sleep(Duration::from_millis(100)).await;
342 }
343
344 let client = ReqwestClient::new().get(&self.url).headers(header).send().await.unwrap();
345
346 // Might be a problem in the future
347 let body = client.text().await.unwrap();
348
349 return self.return_data(body)
350 }
351
352 /// Runs the client for a patch request
353 ///
354 /// This function makes the request, applies rate limits, sends the data, and returns the result
355 ///
356 /// ## Retruns:
357 ///
358 /// This function returns a UserResult enum that can be printed out
359 /// or used to get more specific data from the json data provided
360
361 #[tokio::main]
362 pub async fn patch(&mut self) -> UserResult {
363 let bucket = self.bucket.clone();
364 let mut bucket = bucket.lock().unwrap();
365
366 let mut header: HeaderMap = HeaderMap::new();
367 header.insert("x-session-token", HeaderValue::from_str(&self.auth).unwrap());
368 header.insert("Content-Type", HeaderValue::from_str("application/json").unwrap());
369
370 while !bucket.try_acquire() {
371 time::sleep(Duration::from_millis(100)).await;
372 }
373
374 let client = ReqwestClient::new().patch(&self.url).headers(header).body(self.json.clone()).send().await.unwrap();
375
376 let body = client.text().await.unwrap();
377
378 return self.return_data(body)
379 }
380
381 /// Runs the client for a put request
382 ///
383 /// This function makes the request, applies rate limits, and returns the result
384 ///
385 /// ## Retruns:
386 ///
387 /// This function returns a UserResult enum that can be printed out
388 /// or used to get more specific data from the json data provided
389
390 #[tokio::main]
391 pub async fn put(&mut self) -> UserResult {
392 let bucket = self.bucket.clone();
393 let mut bucket = bucket.lock().unwrap();
394
395 let mut header: HeaderMap = HeaderMap::new();
396 header.insert("x-session-token", HeaderValue::from_str(&self.auth).unwrap());
397
398 while !bucket.try_acquire() {
399 time::sleep(Duration::from_millis(100)).await;
400 }
401
402 let client = ReqwestClient::new().put(&self.url).headers(header).send().await.unwrap();
403
404 let body = client.text().await.unwrap();
405
406 return self.return_data(body)
407 }
408
409 /// Runs the client for a delete request
410 ///
411 /// This function makes the request, applies rate limits, and returns the result
412 ///
413 /// ## Retruns:
414 ///
415 /// This function returns a UserResult enum that can be printed out
416 /// or used to get more specific data from the json data provided
417
418 #[tokio::main]
419 pub async fn del(&mut self) -> UserResult {
420 let bucket = self.bucket.clone();
421 let mut bucket = bucket.lock().unwrap();
422
423 let mut header: HeaderMap = HeaderMap::new();
424 header.insert("x-session-token", HeaderValue::from_str(&self.auth).unwrap());
425
426 while !bucket.try_acquire() {
427 time::sleep(Duration::from_millis(100)).await;
428 }
429
430 let client = ReqwestClient::new().delete(&self.url).headers(header).send().await.unwrap();
431
432 let body = client.text().await.unwrap();
433
434 return self.return_data(body)
435 }
436
437 /// Runs the client for a post request
438 ///
439 /// This function makes the request, applies rate limits, sends the data, and returns the result
440 ///
441 /// ## Retruns:
442 ///
443 /// This function returns a UserResult enum that can be printed out
444 /// or used to get more specific data from the json data provided
445
446 #[tokio::main]
447 pub async fn post(&mut self) -> UserResult {
448 let bucket = self.bucket.clone();
449 let mut bucket = bucket.lock().unwrap();
450
451 let mut header: HeaderMap = HeaderMap::new();
452 header.insert("x-session-token", HeaderValue::from_str(&self.auth).unwrap());
453 header.insert("Content-Type", HeaderValue::from_str("application/json").unwrap());
454
455 while !bucket.try_acquire() {
456 time::sleep(Duration::from_millis(100)).await;
457 }
458
459 let client = ReqwestClient::new().post(&self.url).headers(header).body(self.json.clone()).send().await.unwrap();
460
461 let body = client.text().await.unwrap();
462
463 return self.return_data(body)
464 }
465
466 // Literally returns the data for the main 'get, patch, etc.' functions
467 fn return_data(&mut self, body: String) -> UserResult{
468 match self.data{
469 Data::Users() => {
470 let value: User = serde_json::from_str(&body).unwrap();
471 self.cache.add(value.id.clone(), (body, "user".to_string()));
472 return UserResult::User(value);
473 },
474 Data::UsersFlags() => {
475 let value: UserFlags = serde_json::from_str(&body).unwrap();
476 self.cache.add(self.user.clone(), (body, "userflags".to_string()));
477 return UserResult::UserFlags(value);
478 },
479 Data::UsersProfile() => {
480 let value: UserProfile = serde_json::from_str(&body).unwrap();
481 self.cache.add(self.user.clone(), (body, "userprofile".to_string()));
482 return UserResult::UserProfile(value);
483 },
484 Data::UserDmChannels() => {
485 let value: Vec<Channels> = serde_json::from_str(&body).unwrap();
486 return UserResult::UserDmChannels(value);
487 },
488 Data::UserDms() => {
489 let value: UserDms = serde_json::from_str(&body).unwrap();
490 return UserResult::UserDms(value);
491 },
492 Data::UserMutuals() => {
493 let value: UserMutuals = serde_json::from_str(&body).unwrap();
494 return UserResult::UserMutuals(value);
495 }
496 Data::String() => {
497 return UserResult::String(body);
498 }
499 }
500 }
501}
502
503impl UserResult {
504
505 /// Returns a users id
506 ///
507 /// ## Returns:
508 ///
509 /// The users id as a string
510 ///
511 /// ## Panics!:
512 ///
513 /// This funtion will panic if the enum variant UserResult::User has not been called
514
515 pub fn id(&self) -> String {
516 if let UserResult::User(user_data) = self {
517 user_data.id.clone()
518 } else{
519 panic!("Cannot get id from: {:?}", self);
520 }
521 }
522
523 /// Returns a users name
524 ///
525 /// ## Returns:
526 ///
527 /// The users name as a string
528 ///
529 /// ## Panics!:
530 ///
531 /// This funtion will panic if the enum variant UserResult::User has not been called
532
533 pub fn name(&self) -> String {
534 if let UserResult::User(user_data) = self {
535 user_data.username.clone()
536 } else{
537 panic!("Cannot get name from: {:?}", self);
538 }
539 }
540
541 /// Returns a users discriminator
542 ///
543 /// ## Returns:
544 ///
545 /// The discriminator of a user as a string
546
547 pub fn discriminator(&self) -> String {
548 if let UserResult::User(user_data) = self {
549 user_data.discriminator.clone()
550 } else{
551 panic!("Cannot get discriminator from: {:?}", self);
552 }
553 }
554
555 /// Returns a users avatar
556 ///
557 /// ## Returns:
558 ///
559 /// The users avatar data in the form of a struct called image that can be parsed down to get the specific values
560 ///
561 /// ## Panics!:
562 ///
563 /// This funtion will panic if the enum variant UserResult::User has not been called
564
565 pub fn avatar(&self) -> Image{
566 if let UserResult::User(user_data) = self {
567 match user_data.avatar.clone().is_some() {
568 true => user_data.avatar.clone().unwrap(),
569 false => panic!("Avatar not found!"),
570 }
571 } else {
572 panic!("Cannot get avatar from: {:?}", self);
573 }
574 }
575
576 /// Returns the badges a users has
577 ///
578 /// Badges can be earned on Revolt for different things (will explain below)
579 ///
580 /// The API shows badges as the sum of the values of all the badges
581 ///
582 /// Badge Values:
583 ///
584 /// - 1: Developer (Develops Revolt)
585 /// - 2: Translator (Translated Revolt into another language)
586 /// - 4: Supporter (Supported Revolt with a donation)
587 /// - 8: Responsibly Disclosed Bug(s) (Disclosed a bug responibly)
588 /// - 16: Founder (Founded Revolt)
589 /// - 32: Platform Moderation (Moderates Revolt)
590 /// - 64: Active Supporter (Activlty supports Revolt)
591 /// - 128: Paw (🦊🦝)
592 /// - 256: Early Adopter (One of the first 1000 users)
593 /// - 512: ReservedRelevantJokeBadge1 (Amogus)
594 /// - 1024 ReservedRelevantJokeBadge2 (Amogus Troll Face)
595 ///
596 /// ## Returns
597 ///
598 /// A `vec<&str>` of the badges the user has earned in order from highest value to lowest value
599 ///
600 /// ## Panics!:
601 ///
602 /// This funtion will panic if the enum variant UserResult::User has not been called
603
604 pub fn badges(&self) -> Vec<&str> {
605 // Credit to FatalErrorMogus for the inital design
606 if let UserResult::User(user_data) = self {
607 let badges: Vec<(&i32, &str)> = vec![
608 (&1024, "ReservedRelevantJokeBadge1"),
609 (&512, "ReservedRelevantJokeBadge1"),
610 (&256, "EarlyAdopter"),
611 (&128, "Paw"),
612 (&64, "ActiveSupporter"),
613 (&32, "PlatformModeration"),
614 (&16, "Founder"),
615 (&8, "ResponsibleDisclosure"),
616 (&4, "Supporter"),
617 (&2, "Translator"),
618 (&1, "Developer"),
619 ];
620 let mut badge = user_data.badges;
621 let mut final_badges: Vec<&str> = Vec::new();
622 for i in badges{
623 if badge - i.0 >= 0 {
624 final_badges.push(i.1);
625 badge -= i.0;
626 };
627 }
628 final_badges
629 } else {
630 panic!("Cannot get badges from: {:?}", self) ;
631 }
632 }
633
634 /// Returns the users flags
635 ///
636 /// Flags are information about the users account status on revolt
637 ///
638 /// - 1: Suspended
639 /// - 2: Deleted
640 /// - 4: Banned
641 ///
642 /// ## Returns:
643 ///
644 /// The users flags as a usize
645 ///
646 /// ## Panics!:
647 ///
648 /// This function will panic if fetch_user_flags() was not called
649
650 pub fn flags(&self) -> usize {
651 if let UserResult::UserFlags(user_data) = self {
652 user_data.flags.clone()
653 } else {
654 panic!("Cannot get flags from: {:?}", self) ;
655 }
656 }
657
658 /// Returns a users background image
659 ///
660 /// ## Returns:
661 ///
662 /// The users background data in the form of a struct called image that can be parsed down to get the specific values
663 ///
664 /// ## Panics!:
665 ///
666 /// This funtion will panic if the enum variant UserResult::UserProfile has not been called
667
668 pub fn background(&self) -> Image{
669 if let UserResult::UserProfile(user_data) = self {
670 user_data.background.clone()
671 } else {
672 panic!("Cannot get background from: {:?}", self);
673 }
674 }
675
676 /// Returns the status text of a user
677 ///
678 /// ## Returns:
679 ///
680 /// The status text of a user as a string
681 ///
682 /// **Note:**
683 ///
684 /// This function takes both UserResult::UserProfile and UserResult::User
685 ///
686 /// When UserResult::User is called the value may be "None"
687 /// if there was no status or there was and error parsing the status
688 ///
689 /// ## Panics!:
690 ///
691 /// This function will panic if fetch_user_flags was called before
692
693 pub fn status(&self) -> String {
694 if let UserResult::UserProfile(user_data) = self {
695 user_data.content.clone()
696 }
697 else if let UserResult::User(user_data) = self {
698 match user_data.status.clone().is_some(){
699 true => user_data.status.clone().unwrap().text.unwrap_or("None".to_string()),
700 false => "None".to_string(),
701 }
702 } else {
703 panic!("Cannot get status from: {:?} \nMake sure you are not calling: \"fetch_user_flags\"", self);
704 }
705 }
706
707 #[cfg(feature="experimental")]
708 pub fn channel_types(&self) -> Vec<String> {
709 let mut types: Vec<String> = Vec::new();
710 if let UserResult::UserDmChannels(user_data) = self {
711 for channel_types in user_data {
712 types.push(channel_types.channel_type.clone());
713 }
714 types
715 }
716 else if let UserResult::UserDms(user_data) = self {
717 // Unnessisary but ok
718 types.push(user_data.channel_type.clone());
719 types
720 }
721 else {
722 panic!("Cannot get channel type(s) from: {:?}", self)
723 }
724 }
725
726 #[cfg(feature="experimental")]
727 pub fn channel_ids(&self) -> Vec<String> {
728 let mut ids: Vec<String> = Vec::new();
729 if let UserResult::UserDmChannels(user_data) = self {
730 for channel_types in user_data {
731 ids.push(channel_types.channel_id.clone());
732 }
733 ids
734 }
735 else if let UserResult::UserDms(user_data) = self {
736 // Unnessisary but ok
737 ids.push(user_data.channel_type.clone());
738 ids
739 }
740 else {
741 panic!("Cannot get channel id(s) from: {:?}", self)
742 }
743 }
744
745 /// Returns the mutual users a person has
746 ///
747 /// ## Returns:
748 ///
749 /// The mutual users of a person as a `Vec<String>`
750 ///
751 /// ## Panics!
752 ///
753 /// This function will panic if the enum varient UserMutuals was not called
754
755 pub fn mutual_users(&self) -> Vec<String> {
756 if let UserResult::UserMutuals(user_data) = self {
757 user_data.users.clone()
758 }
759 else {
760 panic!("Cannot Get mutual users from: {:?}", self)
761 }
762 }
763
764 /// Returns the mutual servers a person has
765 ///
766 /// ## Returns:
767 ///
768 /// The mutual servers of a person as a `Vec<String>`
769 ///
770 /// ## Panics!
771 ///
772 /// This function will panic if the enum varient UserMutuals was not called
773 ///
774 pub fn mutual_servers(&self) -> Vec<String> {
775 if let UserResult::UserMutuals(user_data) = self {
776 user_data.servers.clone()
777 }
778 else {
779 panic!("Cannot Get mutual servers from: {:?}", self)
780 }
781 }
782}