chore(monorepo): delta, january, quark

This commit is contained in:
Paul Makles
2022-06-02 00:04:22 +01:00
parent 5d8432e267
commit 2ce610e1e7
232 changed files with 18094 additions and 554 deletions

View File

@@ -0,0 +1,380 @@
use crate::{
events::client::EventV1,
models::{
channel::{FieldsChannel, PartialChannel},
message::SystemMessage,
Channel,
},
tasks::ack::AckEvent,
Database, Error, OverrideField, Result,
};
impl Channel {
/// Get a reference to this channel's id
pub fn id(&'_ self) -> &'_ str {
match self {
Channel::DirectMessage { id, .. }
| Channel::Group { id, .. }
| Channel::SavedMessages { id, .. }
| Channel::TextChannel { id, .. }
| Channel::VoiceChannel { id, .. } => id,
}
}
/// Represent channel as its id
pub fn as_id(self) -> String {
match self {
Channel::DirectMessage { id, .. }
| Channel::Group { id, .. }
| Channel::SavedMessages { id, .. }
| Channel::TextChannel { id, .. }
| Channel::VoiceChannel { id, .. } => id,
}
}
/// Map out whether it is a direct DM
pub fn is_direct_dm(&self) -> bool {
matches!(self, Channel::DirectMessage { .. })
}
/// Create a channel
pub async fn create(&self, db: &Database) -> Result<()> {
db.insert_channel(self).await?;
let event = EventV1::ChannelCreate(self.clone());
match self {
Self::SavedMessages { user, .. } => event.private(user.clone()).await,
Self::DirectMessage { recipients, .. } | Self::Group { recipients, .. } => {
for recipient in recipients {
event.clone().private(recipient.clone()).await;
}
}
Self::TextChannel { server, .. } | Self::VoiceChannel { server, .. } => {
event.p(server.clone()).await;
}
}
Ok(())
}
/// Update channel data
pub async fn update<'a>(
&mut self,
db: &Database,
partial: PartialChannel,
remove: Vec<FieldsChannel>,
) -> Result<()> {
for field in &remove {
self.remove(field);
}
self.apply_options(partial.clone());
let id = self.id().to_string();
db.update_channel(&id, &partial, remove.clone()).await?;
EventV1::ChannelUpdate {
id: id.clone(),
data: partial,
clear: remove,
}
.p(match self {
Self::TextChannel { server, .. } | Self::VoiceChannel { server, .. } => server.clone(),
_ => id,
})
.await;
Ok(())
}
/// Delete a channel
pub async fn delete(self, db: &Database) -> Result<()> {
let id = self.id().to_string();
EventV1::ChannelDelete { id: id.clone() }.p(id).await;
db.delete_channel(&self).await
}
/// Remove a field from Channel object
pub fn remove(&mut self, field: &FieldsChannel) {
match field {
FieldsChannel::Description => match self {
Self::Group { description, .. }
| Self::TextChannel { description, .. }
| Self::VoiceChannel { description, .. } => {
description.take();
}
_ => {}
},
FieldsChannel::Icon => match self {
Self::Group { icon, .. }
| Self::TextChannel { icon, .. }
| Self::VoiceChannel { icon, .. } => {
icon.take();
}
_ => {}
},
FieldsChannel::DefaultPermissions => match self {
Self::TextChannel {
default_permissions,
..
}
| Self::VoiceChannel {
default_permissions,
..
} => {
default_permissions.take();
}
_ => {}
},
}
}
/// Apply partial channel to channel
pub fn apply_options(&mut self, partial: PartialChannel) {
// ! FIXME: maybe flatten channel object?
match self {
Self::DirectMessage { active, .. } => {
if let Some(v) = partial.active {
*active = v;
}
}
Self::Group {
name,
owner,
description,
icon,
nsfw,
permissions,
..
} => {
if let Some(v) = partial.name {
*name = v;
}
if let Some(v) = partial.owner {
*owner = v;
}
if let Some(v) = partial.description {
description.replace(v);
}
if let Some(v) = partial.icon {
icon.replace(v);
}
if let Some(v) = partial.nsfw {
*nsfw = v;
}
if let Some(v) = partial.permissions {
permissions.replace(v);
}
}
Self::TextChannel {
name,
description,
icon,
nsfw,
default_permissions,
role_permissions,
..
}
| Self::VoiceChannel {
name,
description,
icon,
nsfw,
default_permissions,
role_permissions,
..
} => {
if let Some(v) = partial.name {
*name = v;
}
if let Some(v) = partial.description {
description.replace(v);
}
if let Some(v) = partial.icon {
icon.replace(v);
}
if let Some(v) = partial.nsfw {
*nsfw = v;
}
if let Some(v) = partial.role_permissions {
*role_permissions = v;
}
if let Some(v) = partial.default_permissions {
default_permissions.replace(v);
}
}
_ => {}
}
}
/// Acknowledge a message
pub async fn ack(&self, user: &str, message: &str) -> Result<()> {
EventV1::ChannelAck {
id: self.id().to_string(),
user: user.to_string(),
message_id: message.to_string(),
}
.private(user.to_string())
.await;
crate::tasks::ack::queue(
self.id().to_string(),
user.to_string(),
AckEvent::AckMessage {
id: message.to_string(),
},
)
.await;
Ok(())
}
/// Add user to a group
pub async fn add_user_to_group(&mut self, db: &Database, user: &str, by: &str) -> Result<()> {
if let Channel::Group { recipients, .. } = self {
recipients.push(user.to_string());
}
match &self {
Channel::Group { id, .. } => {
db.add_user_to_group(id, user).await?;
EventV1::ChannelGroupJoin {
id: id.to_string(),
user: user.to_string(),
}
.p(id.to_string())
.await;
EventV1::ChannelCreate(self.clone())
.private(user.to_string())
.await;
SystemMessage::UserAdded {
id: user.to_string(),
by: by.to_string(),
}
.into_message(id.to_string())
.create(db, self, None)
.await
.ok();
Ok(())
}
_ => Err(Error::InvalidOperation),
}
}
/// Remove user from a group
pub async fn remove_user_from_group(
&self,
db: &Database,
user: &str,
by: Option<&str>,
) -> Result<()> {
match &self {
Channel::Group {
id,
owner,
recipients,
..
} => {
if user == owner {
if let Some(new_owner) = recipients.iter().find(|x| *x != user) {
db.update_channel(
id,
&PartialChannel {
owner: Some(new_owner.into()),
..Default::default()
},
vec![],
)
.await?;
} else {
db.delete_channel(self).await?;
return Ok(());
}
}
db.remove_user_from_group(id, user).await?;
EventV1::ChannelGroupLeave {
id: id.to_string(),
user: user.to_string(),
}
.p(id.to_string())
.await;
if let Some(by) = by {
SystemMessage::UserRemove {
id: user.to_string(),
by: by.to_string(),
}
} else {
SystemMessage::UserLeft {
id: user.to_string(),
}
}
.into_message(id.to_string())
.create(db, self, None)
.await
.ok();
Ok(())
}
_ => Err(Error::InvalidOperation),
}
}
/// Set role permission on a channel
pub async fn set_role_permission(
&mut self,
db: &Database,
role: &str,
permissions: OverrideField,
) -> Result<()> {
match self {
Channel::TextChannel {
id,
server,
role_permissions,
..
}
| Channel::VoiceChannel {
id,
server,
role_permissions,
..
} => {
db.set_channel_role_permission(id, role, permissions)
.await?;
role_permissions.insert(role.to_string(), permissions);
EventV1::ChannelUpdate {
id: id.clone(),
data: PartialChannel {
role_permissions: Some(role_permissions.clone()),
..Default::default()
},
clear: vec![],
}
.p(server.clone())
.await;
Ok(())
}
_ => Err(Error::InvalidOperation),
}
}
}

View File

@@ -0,0 +1,74 @@
use nanoid::nanoid;
use crate::{
models::{Channel, Invite, User},
Database, Error, Result,
};
lazy_static! {
static ref ALPHABET: [char; 54] = [
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'J', 'K', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
'e', 'f', 'g', 'h', 'j', 'k', 'm', 'n', 'p', 'q', 'r', 's', 't', 'v', 'w', 'x', 'y', 'z'
];
}
impl Invite {
/// Get the invite code for this invite
pub fn code(&'_ self) -> &'_ str {
match self {
Invite::Server { code, .. } | Invite::Group { code, .. } => code,
}
}
/// Get the ID of the user who created this invite
pub fn creator(&'_ self) -> &'_ str {
match self {
Invite::Server { creator, .. } | Invite::Group { creator, .. } => creator,
}
}
/// Create a new invite from given information
pub async fn create(db: &Database, creator: &User, target: &Channel) -> Result<Invite> {
let code = nanoid!(8, &*ALPHABET);
let invite = match &target {
Channel::Group { id, .. } => Ok(Invite::Group {
code,
creator: creator.id.clone(),
channel: id.clone(),
}),
Channel::TextChannel { id, server, .. } | Channel::VoiceChannel { id, server, .. } => {
Ok(Invite::Server {
code,
creator: creator.id.clone(),
server: server.clone(),
channel: id.clone(),
})
}
_ => Err(Error::InvalidOperation),
}?;
db.insert_invite(&invite).await?;
Ok(invite)
}
/// Resolve an invite by its ID or by a public server ID
pub async fn find(db: &Database, code: &str) -> Result<Invite> {
if let Ok(invite) = db.fetch_invite(code).await {
return Ok(invite);
} else if let Ok(server) = db.fetch_server(code).await {
if server.discoverable {
if let Some(channel) = server.channels.into_iter().next() {
return Ok(Invite::Server {
code: code.to_string(),
server: server.id,
creator: server.owner,
channel,
});
}
}
}
Err(Error::NotFound)
}
}

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,284 @@
use std::collections::HashSet;
use serde_json::json;
use ulid::Ulid;
use crate::{
events::client::EventV1,
models::{
message::{
AppendMessage, BulkMessageResponse, PartialMessage, SendableEmbed, SystemMessage,
},
Channel, Message, User,
},
presence::presence_filter_online,
tasks::ack::AckEvent,
types::{
january::{Embed, Text},
push::PushNotification,
},
Database, Result,
};
impl Message {
/// Create a message
pub async fn create_no_web_push(
&mut self,
db: &Database,
channel: &str,
is_direct_dm: bool,
) -> Result<()> {
db.insert_message(self).await?;
// Fan out events
EventV1::Message(self.clone()).p(channel.to_string()).await;
// Update last_message_id
crate::tasks::last_message_id::queue(
channel.to_string(),
self.id.to_string(),
is_direct_dm,
)
.await;
// Add mentions for affected users
if let Some(mentions) = &self.mentions {
for user in mentions {
crate::tasks::ack::queue(
channel.to_string(),
user.to_string(),
AckEvent::AddMention {
ids: vec![self.id.to_string()],
},
)
.await;
}
}
Ok(())
}
/// Create a message and Web Push events
pub async fn create(
&mut self,
db: &Database,
channel: &Channel,
sender: Option<&User>,
) -> Result<()> {
self.create_no_web_push(db, channel.id(), channel.is_direct_dm())
.await?;
// Push out Web Push notifications
crate::tasks::web_push::queue(
{
let mut target_ids = vec![];
match &channel {
Channel::DirectMessage { recipients, .. }
| Channel::Group { recipients, .. } => {
target_ids = (&recipients.iter().cloned().collect::<HashSet<String>>()
- &presence_filter_online(recipients).await)
.into_iter()
.collect::<Vec<String>>();
}
Channel::TextChannel { .. } => {
if let Some(mentions) = &self.mentions {
target_ids.append(&mut mentions.clone());
}
}
_ => {}
};
target_ids
},
json!(PushNotification::new(self.clone(), sender, channel.id())).to_string(),
)
.await;
Ok(())
}
/// Update message data
pub async fn update(&mut self, db: &Database, partial: PartialMessage) -> Result<()> {
self.apply_options(partial.clone());
db.update_message(&self.id, &partial).await?;
EventV1::MessageUpdate {
id: self.id.clone(),
channel: self.channel.clone(),
data: partial,
}
.p(self.channel.clone())
.await;
Ok(())
}
/// Append message data
pub async fn append(
db: &Database,
id: String,
channel: String,
append: AppendMessage,
) -> Result<()> {
db.append_message(&id, &append).await?;
EventV1::MessageAppend {
id,
channel: channel.to_string(),
append,
}
.p(channel)
.await;
Ok(())
}
/// Delete a message
pub async fn delete(self, db: &Database) -> Result<()> {
db.delete_message(&self.id).await?;
EventV1::MessageDelete {
id: self.id,
channel: self.channel.clone(),
}
.p(self.channel)
.await;
Ok(())
}
/// Bulk delete messages
pub async fn bulk_delete(db: &Database, channel: &str, ids: Vec<String>) -> Result<()> {
db.delete_messages(channel, ids.clone()).await?;
EventV1::BulkMessageDelete {
channel: channel.to_string(),
ids,
}
.p(channel.to_string())
.await;
Ok(())
}
}
pub trait IntoUsers {
fn get_user_ids(&self) -> Vec<String>;
}
impl IntoUsers for Message {
fn get_user_ids(&self) -> Vec<String> {
let mut ids = vec![self.author.clone()];
if let Some(msg) = &self.system {
match msg {
SystemMessage::UserAdded { id, by, .. }
| SystemMessage::UserRemove { id, by, .. } => {
ids.push(id.clone());
ids.push(by.clone());
}
SystemMessage::UserJoined { id, .. }
| SystemMessage::UserLeft { id, .. }
| SystemMessage::UserKicked { id, .. }
| SystemMessage::UserBanned { id, .. } => ids.push(id.clone()),
SystemMessage::ChannelRenamed { by, .. }
| SystemMessage::ChannelDescriptionChanged { by, .. }
| SystemMessage::ChannelIconChanged { by, .. } => ids.push(by.clone()),
_ => {}
}
}
ids
}
}
impl IntoUsers for Vec<Message> {
fn get_user_ids(&self) -> Vec<String> {
let mut ids = vec![];
for message in self {
ids.append(&mut message.get_user_ids());
}
ids
}
}
impl SystemMessage {
pub fn into_message(self, channel: String) -> Message {
Message {
id: Ulid::new().to_string(),
channel,
author: "00000000000000000000000000".to_string(),
system: Some(self),
..Default::default()
}
}
}
impl From<SystemMessage> for String {
fn from(s: SystemMessage) -> String {
match s {
SystemMessage::Text { content } => content,
SystemMessage::UserAdded { .. } => "User added to the channel.".to_string(),
SystemMessage::UserRemove { .. } => "User removed from the channel.".to_string(),
SystemMessage::UserJoined { .. } => "User joined the channel.".to_string(),
SystemMessage::UserLeft { .. } => "User left the channel.".to_string(),
SystemMessage::UserKicked { .. } => "User kicked from the channel.".to_string(),
SystemMessage::UserBanned { .. } => "User banned from the channel.".to_string(),
SystemMessage::ChannelRenamed { .. } => "Channel renamed.".to_string(),
SystemMessage::ChannelDescriptionChanged { .. } => {
"Channel description changed.".to_string()
}
SystemMessage::ChannelIconChanged { .. } => "Channel icon changed.".to_string(),
}
}
}
impl SendableEmbed {
pub async fn into_embed(self, db: &Database, message_id: String) -> Result<Embed> {
let media = if let Some(id) = self.media {
Some(
db.find_and_use_attachment(&id, "attachments", "message", &message_id)
.await?,
)
} else {
None
};
Ok(Embed::Text(Text {
icon_url: self.icon_url,
url: self.url,
title: self.title,
description: self.description,
media,
colour: self.colour,
}))
}
}
impl BulkMessageResponse {
pub async fn transform(
db: &Database,
channel: &Channel,
messages: Vec<Message>,
include_users: Option<bool>,
) -> Result<BulkMessageResponse> {
if let Some(true) = include_users {
let user_ids = messages.get_user_ids();
let users = db.fetch_users(&user_ids).await?;
Ok(match channel {
Channel::TextChannel { server, .. } | Channel::VoiceChannel { server, .. } => {
BulkMessageResponse::MessagesAndUsers {
messages,
users,
members: Some(db.fetch_members(server, &user_ids).await?),
}
}
_ => BulkMessageResponse::MessagesAndUsers {
messages,
users,
members: None,
},
})
} else {
Ok(BulkMessageResponse::JustMessages(messages))
}
}
}