1#![allow(deprecated)]
2use std::{borrow::Cow, collections::HashMap};
3
4use revolt_config::config;
5use revolt_models::v0::{self, MessageAuthor};
6use revolt_permissions::OverrideField;
7use revolt_result::Result;
8use serde::{Deserialize, Serialize};
9use ulid::Ulid;
10
11use crate::{
12 events::client::EventV1, Database, File, PartialServer, Server, SystemMessage, User, AMQP,
13};
14
15#[cfg(feature = "mongodb")]
16use crate::IntoDocumentPath;
17
18auto_derived!(
19 #[serde(tag = "channel_type")]
20 pub enum Channel {
21 SavedMessages {
23 #[serde(rename = "_id")]
25 id: String,
26 user: String,
28 },
29 DirectMessage {
31 #[serde(rename = "_id")]
33 id: String,
34
35 active: bool,
37 recipients: Vec<String>,
39 #[serde(skip_serializing_if = "Option::is_none")]
41 last_message_id: Option<String>,
42 },
43 Group {
45 #[serde(rename = "_id")]
47 id: String,
48
49 name: String,
51 owner: String,
53 #[serde(skip_serializing_if = "Option::is_none")]
55 description: Option<String>,
56 recipients: Vec<String>,
58
59 #[serde(skip_serializing_if = "Option::is_none")]
61 icon: Option<File>,
62 #[serde(skip_serializing_if = "Option::is_none")]
64 last_message_id: Option<String>,
65
66 #[serde(skip_serializing_if = "Option::is_none")]
69 permissions: Option<i64>,
70
71 #[serde(skip_serializing_if = "crate::if_false", default)]
73 nsfw: bool,
74 },
75 TextChannel {
77 #[serde(rename = "_id")]
79 id: String,
80 server: String,
82
83 name: String,
85 #[serde(skip_serializing_if = "Option::is_none")]
87 description: Option<String>,
88
89 #[serde(skip_serializing_if = "Option::is_none")]
91 icon: Option<File>,
92 #[serde(skip_serializing_if = "Option::is_none")]
94 last_message_id: Option<String>,
95
96 #[serde(skip_serializing_if = "Option::is_none")]
98 default_permissions: Option<OverrideField>,
99 #[serde(
101 default = "HashMap::<String, OverrideField>::new",
102 skip_serializing_if = "HashMap::<String, OverrideField>::is_empty"
103 )]
104 role_permissions: HashMap<String, OverrideField>,
105
106 #[serde(skip_serializing_if = "crate::if_false", default)]
108 nsfw: bool,
109
110 #[serde(skip_serializing_if = "Option::is_none")]
112 voice: Option<VoiceInformation>,
113 },
114 }
115
116 #[derive(Default)]
117 pub struct VoiceInformation {
118 #[serde(skip_serializing_if = "Option::is_none")]
120 pub max_users: Option<usize>,
121 }
122);
123
124auto_derived!(
125 #[derive(Default)]
126 pub struct PartialChannel {
127 #[serde(skip_serializing_if = "Option::is_none")]
128 pub name: Option<String>,
129 #[serde(skip_serializing_if = "Option::is_none")]
130 pub owner: Option<String>,
131 #[serde(skip_serializing_if = "Option::is_none")]
132 pub description: Option<String>,
133 #[serde(skip_serializing_if = "Option::is_none")]
134 pub icon: Option<File>,
135 #[serde(skip_serializing_if = "Option::is_none")]
136 pub nsfw: Option<bool>,
137 #[serde(skip_serializing_if = "Option::is_none")]
138 pub active: Option<bool>,
139 #[serde(skip_serializing_if = "Option::is_none")]
140 pub permissions: Option<i64>,
141 #[serde(skip_serializing_if = "Option::is_none")]
142 pub role_permissions: Option<HashMap<String, OverrideField>>,
143 #[serde(skip_serializing_if = "Option::is_none")]
144 pub default_permissions: Option<OverrideField>,
145 #[serde(skip_serializing_if = "Option::is_none")]
146 pub last_message_id: Option<String>,
147 #[serde(skip_serializing_if = "Option::is_none")]
148 pub voice: Option<VoiceInformation>,
149 }
150
151 pub enum FieldsChannel {
153 Description,
154 Icon,
155 DefaultPermissions,
156 Voice,
157 }
158);
159
160#[allow(clippy::disallowed_methods)]
161impl Channel {
162 pub async fn create_server_channel(
184 db: &Database,
185 server: &mut Server,
186 data: v0::DataCreateServerChannel,
187 update_server: bool,
188 ) -> Result<Channel> {
189 let config = config().await;
190 if server.channels.len() > config.features.limits.global.server_channels {
191 return Err(create_error!(TooManyChannels {
192 max: config.features.limits.global.server_channels,
193 }));
194 };
195
196 let id = ulid::Ulid::new().to_string();
197 let channel = match data.channel_type {
198 v0::LegacyServerChannelType::Text => Channel::TextChannel {
199 id: id.clone(),
200 server: server.id.to_owned(),
201 name: data.name,
202 description: data.description,
203 icon: None,
204 last_message_id: None,
205 default_permissions: None,
206 role_permissions: HashMap::new(),
207 nsfw: data.nsfw.unwrap_or(false),
208 voice: data.voice.map(|voice| voice.into()),
209 },
210 v0::LegacyServerChannelType::Voice => Channel::TextChannel {
211 id: id.clone(),
212 server: server.id.to_owned(),
213 name: data.name,
214 description: data.description,
215 icon: None,
216 last_message_id: None,
217 default_permissions: None,
218 role_permissions: HashMap::new(),
219 nsfw: data.nsfw.unwrap_or(false),
220 voice: Some(data.voice.unwrap_or_default().into()),
221 },
222 };
223
224 db.insert_channel(&channel).await?;
225
226 if update_server {
227 server
228 .update(
229 db,
230 PartialServer {
231 channels: Some([server.channels.clone(), [id].into()].concat()),
232 ..Default::default()
233 },
234 vec![],
235 )
236 .await?;
237
238 EventV1::ChannelCreate(channel.clone().into())
239 .p(server.id.clone())
240 .await;
241 }
242
243 Ok(channel)
244 }
245
246 pub async fn create_group(
248 db: &Database,
249 mut data: v0::DataCreateGroup,
250 owner_id: String,
251 ) -> Result<Channel> {
252 data.users.insert(owner_id.to_string());
253
254 let config = config().await;
255 if data.users.len() > config.features.limits.global.group_size {
256 return Err(create_error!(GroupTooLarge {
257 max: config.features.limits.global.group_size,
258 }));
259 }
260
261 let id = ulid::Ulid::new().to_string();
262
263 let icon = if let Some(icon_id) = data.icon {
264 Some(File::use_channel_icon(db, &icon_id, &id, &owner_id).await?)
265 } else {
266 None
267 };
268
269 let recipients = data.users.into_iter().collect::<Vec<String>>();
270 let channel = Channel::Group {
271 id,
272
273 name: data.name,
274 owner: owner_id,
275 description: data.description,
276 recipients: recipients.clone(),
277
278 icon,
279 last_message_id: None,
280
281 permissions: None,
282
283 nsfw: data.nsfw.unwrap_or(false),
284 };
285
286 db.insert_channel(&channel).await?;
287
288 let event = EventV1::ChannelCreate(channel.clone().into());
289 for recipient in recipients {
290 event.clone().private(recipient).await;
291 }
292
293 Ok(channel)
294 }
295
296 pub async fn create_dm(db: &Database, user_a: &User, user_b: &User) -> Result<Channel> {
298 if let Ok(channel) = db.find_direct_message_channel(&user_a.id, &user_b.id).await {
300 Ok(channel)
301 } else {
302 let channel = if user_a.id == user_b.id {
303 Channel::SavedMessages {
305 id: Ulid::new().to_string(),
306 user: user_a.id.to_string(),
307 }
308 } else {
309 Channel::DirectMessage {
311 id: Ulid::new().to_string(),
312 active: true, recipients: vec![user_a.id.clone(), user_b.id.clone()],
314 last_message_id: None,
315 }
316 };
317
318 db.insert_channel(&channel).await?;
319
320 if let Channel::DirectMessage { .. } = &channel {
321 let event = EventV1::ChannelCreate(channel.clone().into());
322 event.clone().private(user_a.id.clone()).await;
323 event.private(user_b.id.clone()).await;
324 };
325
326 Ok(channel)
327 }
328 }
329
330 pub async fn add_user_to_group(
332 &mut self,
333 db: &Database,
334 amqp: &AMQP,
335 user: &User,
336 by_id: &str,
337 ) -> Result<()> {
338 if let Channel::Group { recipients, .. } = self {
339 if recipients.contains(&String::from(&user.id)) {
340 return Err(create_error!(AlreadyInGroup));
341 }
342
343 let config = config().await;
344 if recipients.len() >= config.features.limits.global.group_size {
345 return Err(create_error!(GroupTooLarge {
346 max: config.features.limits.global.group_size
347 }));
348 }
349
350 recipients.push(String::from(&user.id));
351 }
352
353 match &self {
354 Channel::Group { id, .. } => {
355 db.add_user_to_group(id, &user.id).await?;
356
357 EventV1::ChannelGroupJoin {
358 id: id.to_string(),
359 user: user.id.to_string(),
360 }
361 .p(id.to_string())
362 .await;
363
364 SystemMessage::UserAdded {
365 id: user.id.to_string(),
366 by: by_id.to_string(),
367 }
368 .into_message(id.to_string())
369 .send(
370 db,
371 Some(amqp),
372 MessageAuthor::System {
373 username: &user.username,
374 avatar: user.avatar.as_ref().map(|file| file.id.as_ref()),
375 },
376 None,
377 None,
378 self,
379 false,
380 )
381 .await
382 .ok();
383
384 EventV1::ChannelCreate(self.clone().into())
385 .private(user.id.to_string())
386 .await;
387
388 Ok(())
389 }
390 _ => Err(create_error!(InvalidOperation)),
391 }
392 }
393
394 pub fn is_direct_dm(&self) -> bool {
396 matches!(self, Channel::DirectMessage { .. })
397 }
398
399 pub fn contains_user(&self, user_id: &str) -> bool {
401 match self {
402 Channel::Group { recipients, .. } => recipients.contains(&String::from(user_id)),
403 _ => false,
404 }
405 }
406
407 pub fn users(&self) -> Result<Vec<String>> {
409 match self {
410 Channel::Group { recipients, .. } => Ok(recipients.to_owned()),
411 _ => Err(create_error!(NotFound)),
412 }
413 }
414
415 pub fn id(&self) -> &str {
417 match self {
418 Channel::DirectMessage { id, .. }
419 | Channel::Group { id, .. }
420 | Channel::SavedMessages { id, .. }
421 | Channel::TextChannel { id, .. } => id,
422 }
423 }
424
425 pub fn server(&self) -> Option<&str> {
427 match self {
428 Channel::TextChannel { server, .. } => Some(server),
429 _ => None,
430 }
431 }
432
433 pub fn voice(&self) -> Option<Cow<VoiceInformation>> {
435 match self {
436 Self::DirectMessage { .. } | Self::Group { .. } => {
437 Some(Cow::Owned(VoiceInformation::default()))
438 }
439 Self::TextChannel {
440 voice: Some(voice), ..
441 } => Some(Cow::Borrowed(voice)),
442 _ => None,
443 }
444 }
445
446 pub async fn set_role_permission(
448 &mut self,
449 db: &Database,
450 role_id: &str,
451 permissions: OverrideField,
452 ) -> Result<()> {
453 match self {
454 Channel::TextChannel {
455 id,
456 server,
457 role_permissions,
458 ..
459 } => {
460 db.set_channel_role_permission(id, role_id, permissions)
461 .await?;
462
463 role_permissions.insert(role_id.to_string(), permissions);
464
465 EventV1::ChannelUpdate {
466 id: id.clone(),
467 data: PartialChannel {
468 role_permissions: Some(role_permissions.clone()),
469 ..Default::default()
470 }
471 .into(),
472 clear: vec![],
473 }
474 .p(server.clone())
475 .await;
476
477 Ok(())
478 }
479 _ => Err(create_error!(InvalidOperation)),
480 }
481 }
482
483 pub async fn update(
485 &mut self,
486 db: &Database,
487 partial: PartialChannel,
488 remove: Vec<FieldsChannel>,
489 ) -> Result<()> {
490 for field in &remove {
491 self.remove_field(field);
492 }
493
494 self.apply_options(partial.clone());
495
496 let id = self.id().to_string();
497 db.update_channel(&id, &partial, remove.clone()).await?;
498
499 EventV1::ChannelUpdate {
500 id: id.clone(),
501 data: partial.into(),
502 clear: remove.into_iter().map(|v| v.into()).collect(),
503 }
504 .p(match self {
505 Self::TextChannel { server, .. } => server.clone(),
506 _ => id,
507 })
508 .await;
509
510 Ok(())
511 }
512
513 pub fn remove_field(&mut self, field: &FieldsChannel) {
515 match field {
516 FieldsChannel::Description => match self {
517 Self::Group { description, .. } | Self::TextChannel { description, .. } => {
518 description.take();
519 }
520 _ => {}
521 },
522 FieldsChannel::Icon => match self {
523 Self::Group { icon, .. } | Self::TextChannel { icon, .. } => {
524 icon.take();
525 }
526 _ => {}
527 },
528 FieldsChannel::DefaultPermissions => match self {
529 Self::TextChannel {
530 default_permissions,
531 ..
532 } => {
533 default_permissions.take();
534 }
535 _ => {}
536 },
537 FieldsChannel::Voice => match self {
538 Self::TextChannel { voice, .. } => {
539 voice.take();
540 }
541 _ => {}
542 },
543 }
544 }
545
546 pub fn remove_fields(&mut self, partial: Vec<FieldsChannel>) {
548 for field in partial {
549 self.remove_field(&field)
550 }
551 }
552
553 #[allow(deprecated)]
555 pub fn apply_options(&mut self, partial: PartialChannel) {
556 match self {
557 Self::SavedMessages { .. } => {}
558 Self::DirectMessage { active, .. } => {
559 if let Some(v) = partial.active {
560 *active = v;
561 }
562 }
563 Self::Group {
564 name,
565 owner,
566 description,
567 icon,
568 nsfw,
569 permissions,
570 ..
571 } => {
572 if let Some(v) = partial.name {
573 *name = v;
574 }
575
576 if let Some(v) = partial.owner {
577 *owner = v;
578 }
579
580 if let Some(v) = partial.description {
581 description.replace(v);
582 }
583
584 if let Some(v) = partial.icon {
585 icon.replace(v);
586 }
587
588 if let Some(v) = partial.nsfw {
589 *nsfw = v;
590 }
591
592 if let Some(v) = partial.permissions {
593 permissions.replace(v);
594 }
595 }
596 Self::TextChannel {
597 name,
598 description,
599 icon,
600 nsfw,
601 default_permissions,
602 role_permissions,
603 voice,
604 ..
605 } => {
606 if let Some(v) = partial.name {
607 *name = v;
608 }
609
610 if let Some(v) = partial.description {
611 description.replace(v);
612 }
613
614 if let Some(v) = partial.icon {
615 icon.replace(v);
616 }
617
618 if let Some(v) = partial.nsfw {
619 *nsfw = v;
620 }
621
622 if let Some(v) = partial.role_permissions {
623 *role_permissions = v;
624 }
625
626 if let Some(v) = partial.default_permissions {
627 default_permissions.replace(v);
628 }
629
630 if let Some(v) = partial.voice {
631 voice.replace(v);
632 }
633 }
634 }
635 }
636
637 pub async fn ack(&self, user: &str, message: &str) -> Result<()> {
639 EventV1::ChannelAck {
640 id: self.id().to_string(),
641 user: user.to_string(),
642 message_id: message.to_string(),
643 }
644 .private(user.to_string())
645 .await;
646
647 #[cfg(feature = "tasks")]
648 crate::tasks::ack::queue_ack(
649 self.id().to_string(),
650 user.to_string(),
651 crate::tasks::ack::AckEvent::AckMessage {
652 id: message.to_string(),
653 },
654 )
655 .await;
656
657 Ok(())
658 }
659
660 pub async fn remove_user_from_group(
662 &self,
663 db: &Database,
664 amqp: &AMQP,
665 user: &User,
666 by_id: Option<&str>,
667 silent: bool,
668 ) -> Result<()> {
669 match &self {
670 Channel::Group {
671 id,
672 name,
673 owner,
674 recipients,
675 ..
676 } => {
677 if &user.id == owner {
678 if let Some(new_owner) = recipients.iter().find(|x| *x != &user.id) {
679 db.update_channel(
680 id,
681 &PartialChannel {
682 owner: Some(new_owner.into()),
683 ..Default::default()
684 },
685 vec![],
686 )
687 .await?;
688
689 SystemMessage::ChannelOwnershipChanged {
690 from: owner.to_string(),
691 to: new_owner.to_string(),
692 }
693 .into_message(id.to_string())
694 .send(
695 db,
696 Some(amqp),
697 MessageAuthor::System {
698 username: name,
699 avatar: None,
700 },
701 None,
702 None,
703 self,
704 false,
705 )
706 .await
707 .ok();
708 } else {
709 return self.delete(db).await;
710 }
711 }
712
713 db.remove_user_from_group(id, &user.id).await?;
714
715 EventV1::ChannelGroupLeave {
716 id: id.to_string(),
717 user: user.id.to_string(),
718 }
719 .p(id.to_string())
720 .await;
721
722 if !silent {
723 if let Some(by) = by_id {
724 SystemMessage::UserRemove {
725 id: user.id.to_string(),
726 by: by.to_string(),
727 }
728 } else {
729 SystemMessage::UserLeft {
730 id: user.id.to_string(),
731 }
732 }
733 .into_message(id.to_string())
734 .send(
735 db,
736 Some(amqp),
737 MessageAuthor::System {
738 username: &user.username,
739 avatar: user.avatar.as_ref().map(|file| file.id.as_ref()),
740 },
741 None,
742 None,
743 self,
744 false,
745 )
746 .await
747 .ok();
748 }
749
750 Ok(())
751 }
752
753 _ => Err(create_error!(InvalidOperation)),
754 }
755 }
756
757 pub async fn delete(&self, db: &Database) -> Result<()> {
759 let id = self.id().to_string();
760 EventV1::ChannelDelete { id: id.clone() }.p(id).await;
761 db.delete_channel(self).await
765 }
766}
767
768#[cfg(feature = "mongodb")]
769impl IntoDocumentPath for FieldsChannel {
770 fn as_path(&self) -> Option<&'static str> {
771 Some(match self {
772 FieldsChannel::Description => "description",
773 FieldsChannel::Icon => "icon",
774 FieldsChannel::DefaultPermissions => "default_permissions",
775 FieldsChannel::Voice => "voice",
776 })
777 }
778}
779
780#[cfg(test)]
781mod tests {
782 use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
783
784 use crate::{fixture, util::permissions::DatabasePermissionQuery};
785
786 #[async_std::test]
787 async fn permissions_group_channel() {
788 database_test!(|db| async move {
789 fixture!(db, "group_with_members",
790 owner user 0
791 member1 user 1
792 member2 user 2
793 channel channel 3);
794
795 let mut query = DatabasePermissionQuery::new(&db, &owner).channel(&channel);
796 assert!(calculate_channel_permissions(&mut query)
797 .await
798 .has_channel_permission(ChannelPermission::SendMessage));
799
800 let mut query = DatabasePermissionQuery::new(&db, &member1).channel(&channel);
801 assert!(calculate_channel_permissions(&mut query)
802 .await
803 .has_channel_permission(ChannelPermission::SendMessage));
804
805 let mut query = DatabasePermissionQuery::new(&db, &member2).channel(&channel);
806 assert!(!calculate_channel_permissions(&mut query)
807 .await
808 .has_channel_permission(ChannelPermission::SendMessage));
809 });
810 }
811
812 #[async_std::test]
813 async fn permissions_text_channel() {
814 database_test!(|db| async move {
815 fixture!(db, "server_with_roles",
816 owner user 0
817 moderator user 1
818 user user 2
819 channel channel 3);
820
821 let mut query = DatabasePermissionQuery::new(&db, &owner).channel(&channel);
822 assert!(calculate_channel_permissions(&mut query)
823 .await
824 .has_channel_permission(ChannelPermission::SendMessage));
825
826 let mut query = DatabasePermissionQuery::new(&db, &moderator).channel(&channel);
827 assert!(calculate_channel_permissions(&mut query)
828 .await
829 .has_channel_permission(ChannelPermission::SendMessage));
830
831 let mut query = DatabasePermissionQuery::new(&db, &user).channel(&channel);
832 assert!(!calculate_channel_permissions(&mut query)
833 .await
834 .has_channel_permission(ChannelPermission::SendMessage));
835 });
836 }
837}