Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
No results found
Show changes
Commits on Source (114)
Showing
with 1733 additions and 353 deletions
Rocket.toml Rocket.toml
/target /target
/target_backup
**/*.rs.bk **/*.rs.bk
.mongo .mongo
.env .env
......
{
"rust-analyzer.diagnostics.disabled": [
"unresolved-macro-call"
]
}
\ No newline at end of file
...@@ -646,12 +646,6 @@ version = "1.0.4" ...@@ -646,12 +646,6 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0"
[[package]]
name = "doc-comment"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
[[package]] [[package]]
name = "dotenv" name = "dotenv"
version = "0.15.0" version = "0.15.0"
...@@ -1068,6 +1062,8 @@ checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" ...@@ -1068,6 +1062,8 @@ checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35"
[[package]] [[package]]
name = "hive_pubsub" name = "hive_pubsub"
version = "0.4.4" version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ac4635b1e9faf304ccb33c2f0edbc615030e40d249183281fc788ef20ef5c76"
dependencies = [ dependencies = [
"futures", "futures",
"many-to-many", "many-to-many",
...@@ -1387,6 +1383,15 @@ version = "0.5.4" ...@@ -1387,6 +1383,15 @@ version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
[[package]]
name = "linkify"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1986921c3c13e81df623c66a298d4b130c061bcb98a01f5b2d3ac402b1649a7f"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "lock_api" name = "lock_api"
version = "0.3.4" version = "0.3.4"
...@@ -1594,6 +1599,15 @@ dependencies = [ ...@@ -1594,6 +1599,15 @@ dependencies = [
"rand 0.6.5", "rand 0.6.5",
] ]
[[package]]
name = "nanoid"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ffa00dec017b5b1a8b7cf5e2c008bfda1aa7e0697ac1508b491fdf2622fb4d8"
dependencies = [
"rand 0.8.3",
]
[[package]] [[package]]
name = "native-tls" name = "native-tls"
version = "0.2.7" version = "0.2.7"
...@@ -2327,8 +2341,8 @@ dependencies = [ ...@@ -2327,8 +2341,8 @@ dependencies = [
[[package]] [[package]]
name = "rauth" name = "rauth"
version = "0.2.5-alpha.0" version = "0.2.7-patch.0"
source = "git+https://gitlab.insrt.uk/insert/rauth?rev=c52758a5087cd035b0ed9c6eacc942ba5468d2ce#c52758a5087cd035b0ed9c6eacc942ba5468d2ce" source = "git+https://gitlab.insrt.uk/insert/rauth?rev=00d3c3dff51cf3242a7d4adda4c5184c97fa2a03#00d3c3dff51cf3242a7d4adda4c5184c97fa2a03"
dependencies = [ dependencies = [
"chrono", "chrono",
"handlebars", "handlebars",
...@@ -2336,7 +2350,7 @@ dependencies = [ ...@@ -2336,7 +2350,7 @@ dependencies = [
"lazy_static", "lazy_static",
"lettre", "lettre",
"mongodb", "mongodb",
"nanoid", "nanoid 0.3.0",
"regex", "regex",
"reqwest", "reqwest",
"rocket", "rocket",
...@@ -2344,7 +2358,6 @@ dependencies = [ ...@@ -2344,7 +2358,6 @@ dependencies = [
"rust-argon2", "rust-argon2",
"serde", "serde",
"serde_json", "serde_json",
"snafu",
"tokio", "tokio",
"ulid", "ulid",
"validator", "validator",
...@@ -2473,7 +2486,7 @@ dependencies = [ ...@@ -2473,7 +2486,7 @@ dependencies = [
[[package]] [[package]]
name = "revolt" name = "revolt"
version = "0.4.0-alpha.2" version = "0.0.0"
dependencies = [ dependencies = [
"async-std", "async-std",
"async-tungstenite", "async-tungstenite",
...@@ -2488,10 +2501,12 @@ dependencies = [ ...@@ -2488,10 +2501,12 @@ dependencies = [
"impl_ops", "impl_ops",
"lazy_static", "lazy_static",
"lettre", "lettre",
"linkify",
"log", "log",
"many-to-many", "many-to-many",
"md5", "md5",
"mongodb", "mongodb",
"nanoid 0.4.0",
"num_enum", "num_enum",
"once_cell", "once_cell",
"rand 0.7.3", "rand 0.7.3",
...@@ -2504,7 +2519,6 @@ dependencies = [ ...@@ -2504,7 +2519,6 @@ dependencies = [
"rocket_prometheus", "rocket_prometheus",
"serde", "serde",
"serde_json", "serde_json",
"snafu",
"time 0.2.25", "time 0.2.25",
"ulid", "ulid",
"urlencoding", "urlencoding",
...@@ -2912,27 +2926,6 @@ version = "1.6.1" ...@@ -2912,27 +2926,6 @@ version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
[[package]]
name = "snafu"
version = "0.6.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eab12d3c261b2308b0d80c26fffb58d17eba81a4be97890101f416b478c79ca7"
dependencies = [
"doc-comment",
"snafu-derive",
]
[[package]]
name = "snafu-derive"
version = "0.6.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1508efa03c362e23817f96cde18abed596a25219a8b2c66e8db33c03543d315b"
dependencies = [
"proc-macro2 1.0.24",
"quote 1.0.9",
"syn 1.0.60",
]
[[package]] [[package]]
name = "socket2" name = "socket2"
version = "0.3.19" version = "0.3.19"
......
[package] [package]
name = "revolt" name = "revolt"
version = "0.4.0-alpha.2" # To help optimise CI and Docker builds.
# Version here is left as 0.0.0, please
# adjust and run ./set_version.sh instead.
version = "0.0.0"
authors = ["Paul Makles <paulmakles@gmail.com>"] authors = ["Paul Makles <paulmakles@gmail.com>"]
edition = "2018" edition = "2018"
...@@ -13,7 +16,9 @@ log = "0.4.11" ...@@ -13,7 +16,9 @@ log = "0.4.11"
ulid = "0.4.1" ulid = "0.4.1"
rand = "0.7.3" rand = "0.7.3"
time = "0.2.16" time = "0.2.16"
nanoid = "0.4.0"
base64 = "0.13.0" base64 = "0.13.0"
linkify = "0.6.0"
dotenv = "0.15.0" dotenv = "0.15.0"
futures = "0.3.8" futures = "0.3.8"
chrono = "0.4.15" chrono = "0.4.15"
...@@ -28,7 +33,6 @@ lazy_static = "1.4.0" ...@@ -28,7 +33,6 @@ lazy_static = "1.4.0"
urlencoding = "1.1.1" urlencoding = "1.1.1"
many-to-many = "0.1.2" many-to-many = "0.1.2"
lettre = "0.10.0-alpha.1" lettre = "0.10.0-alpha.1"
snafu = { version = "0.6.9" }
reqwest = { version = "0.10.8", features = ["json"] } reqwest = { version = "0.10.8", features = ["json"] }
serde = { version = "1.0.115", features = ["derive"] } serde = { version = "1.0.115", features = ["derive"] }
validator = { version = "0.11", features = ["derive"] } validator = { version = "0.11", features = ["derive"] }
...@@ -38,7 +42,7 @@ async-std = { version = "1.8.0", features = ["tokio02", "attributes"] } ...@@ -38,7 +42,7 @@ async-std = { version = "1.8.0", features = ["tokio02", "attributes"] }
async-tungstenite = { version = "0.10.0", features = ["async-std-runtime"] } async-tungstenite = { version = "0.10.0", features = ["async-std-runtime"] }
rocket_cors = { git = "https://github.com/insertish/rocket_cors", branch = "master" } rocket_cors = { git = "https://github.com/insertish/rocket_cors", branch = "master" }
mongodb = { version = "1.1.1", features = ["tokio-runtime"], default-features = false } mongodb = { version = "1.1.1", features = ["tokio-runtime"], default-features = false }
rauth = { git = "https://gitlab.insrt.uk/insert/rauth", rev = "c52758a5087cd035b0ed9c6eacc942ba5468d2ce" } rauth = { git = "https://gitlab.insrt.uk/insert/rauth", rev = "00d3c3dff51cf3242a7d4adda4c5184c97fa2a03" }
rocket_contrib = { git = "https://github.com/SergioBenitez/Rocket", rev = "031948c1daaa146128d8a435be116476f2adde00" } rocket_contrib = { git = "https://github.com/SergioBenitez/Rocket", rev = "031948c1daaa146128d8a435be116476f2adde00" }
rocket_prometheus = { git = "https://github.com/insertish/rocket_prometheus", rev = "3d825aedb42793246c306a81fe67c5b187948983" } rocket_prometheus = { git = "https://github.com/insertish/rocket_prometheus", rev = "3d825aedb42793246c306a81fe67c5b187948983" }
rocket = { git = "https://github.com/SergioBenitez/Rocket", rev = "031948c1daaa146128d8a435be116476f2adde00", default-features = false } rocket = { git = "https://github.com/SergioBenitez/Rocket", rev = "031948c1daaa146128d8a435be116476f2adde00", default-features = false }
...@@ -6,6 +6,9 @@ WORKDIR /home/rust/src ...@@ -6,6 +6,9 @@ WORKDIR /home/rust/src
RUN USER=root cargo new --bin revolt RUN USER=root cargo new --bin revolt
WORKDIR /home/rust/src/revolt WORKDIR /home/rust/src/revolt
COPY Cargo.toml Cargo.lock ./ COPY Cargo.toml Cargo.lock ./
COPY src/bin/dummy.rs ./src/bin/dummy.rs
RUN cargo build --release --bin dummy
COPY assets/templates ./assets/templates COPY assets/templates ./assets/templates
COPY src ./src COPY src ./src
RUN cargo build --release RUN cargo build --release
......
This diff is collapsed.
...@@ -4,57 +4,58 @@ ...@@ -4,57 +4,58 @@
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Reset your password.</title> <title>Reset your password.</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<style type="text/css"> <style type="text/css">
a[x-apple-data-detectors] {color: inherit !important;} a[x-apple-data-detectors] {color: inherit !important;}
</style> </style>
</head> </head>
<body style="margin: 0; padding: 0;"> <body style="margin: 0; padding: 0;">
<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%"> <table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr> <tr>
<td style="padding: 20px 0 30px 0;"> <td style="padding: 20px 0 30px 0;">
<table align="center" border="0" cellpadding="0" cellspacing="0" width="600" style="border-collapse: collapse; border: 1px solid #cccccc;"> <table align="center" border="0" cellpadding="0" cellspacing="0" width="600" style="border-collapse: collapse; border: 1px solid #cccccc;">
<tr> <tr>
<td align="center" bgcolor="#ff4654"> <td align="center" bgcolor="#ff4654">
<img src="https://revolt.chat/header.png" alt="REVOLT logo" width="600" height="168" style="display: block;" /> <img src="https://revolt.chat/header.png" alt="Revolt logo" width="600" height="168" style="display: block;" />
</td> </td>
</tr> </tr>
<tr> <tr>
<td bgcolor="#ffffff" style="padding: 40px 30px 40px 30px;"> <td bgcolor="#ffffff" style="padding: 40px 30px 40px 30px;">
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;"> <table border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
<tr> <tr>
<td style="color: #153643; font-family: Arial, sans-serif;"> <td style="color: #153643; font-family: Arial, sans-serif;">
<h1 style="font-size: 24px; margin: 0;">Reset your password!</h1> <h1 style="font-size: 24px; margin: 0;">Reset your password!</h1>
</td> </td>
</tr> </tr>
<tr> <tr>
<td style="color: #153643; font-family: Arial, sans-serif; font-size: 16px; line-height: 24px; padding: 20px 0 0 0;"> <td style="color: #153643; font-family: Arial, sans-serif; font-size: 16px; line-height: 24px; padding: 20px 0 0 0;">
<p style="margin: 0;"> <p>
Reset your password by <a href="{{url}}">clicking here</a>. You requested a password reset, if you didn't perform this action you can safely ignore this email.
</p> </p>
<p> <p style="margin: 0;">
Or by manually navigating to the URL: {{url}} Reset your password by navigating to <a href="{{url}}">{{url}}</a>.
</p> </p>
</td> </td>
</tr> </tr>
</table> </table>
</td> </td>
</tr> </tr>
<tr> <tr>
<td bgcolor="#ff4654" style="padding: 30px 30px;"> <td bgcolor="#ff4654" style="padding: 30px 30px;">
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;"> <table border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
<tr> <tr>
<td style="color: #ffffff; font-family: Arial, sans-serif; font-size: 14px;"> <td style="color: #ffffff; font-family: Arial, sans-serif; font-size: 14px;">
<p style="margin: 0;">Sent by REVOLT. &middot; <a style="color: white;" href="https://revolt.chat">Website</a> &middot; <a style="color: white;" href="https://gitlab.insrt.uk/revolt">Source Code</a></p> <p style="margin: 0;">Sent by Revolt. &middot; Website: <a style="color: white;" href="https://revolt.chat">https://revolt.chat</a></p>
</td> <p>Revolt is a user-first chat platform built with modern web technologies.</p>
</tr> </td>
</table> </tr>
</td> </table>
</tr> </td>
</table> </tr>
</table>
</td> </td>
</tr> </tr>
</table> </table>
......
<h2>Reset your password.</h2>
<p>
You requested a password reset, if you did not perform this action you can safely ignore this email.
</p>
<p>
Reset your password here: <a href="{{url}}">{{url}}</a>
</p>
<br/>
<p>
Sent by Revolt. · Website: <a href="https://revolt.chat">https://revolt.chat</a>
</p>
<p>
Revolt is a user-first chat platform built with modern web technologies.
</p>
\ No newline at end of file
...@@ -4,57 +4,58 @@ ...@@ -4,57 +4,58 @@
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Verify your account.</title> <title>Verify your account.</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<style type="text/css"> <style type="text/css">
a[x-apple-data-detectors] {color: inherit !important;} a[x-apple-data-detectors] {color: inherit !important;}
</style> </style>
</head> </head>
<body style="margin: 0; padding: 0;"> <body style="margin: 0; padding: 0;">
<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%"> <table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr> <tr>
<td style="padding: 20px 0 30px 0;"> <td style="padding: 20px 0 30px 0;">
<table align="center" border="0" cellpadding="0" cellspacing="0" width="600" style="border-collapse: collapse; border: 1px solid #cccccc;"> <table align="center" border="0" cellpadding="0" cellspacing="0" width="600" style="border-collapse: collapse; border: 1px solid #cccccc;">
<tr> <tr>
<td align="center" bgcolor="#ff4654"> <td align="center" bgcolor="#ff4654">
<img src="https://revolt.chat/header.png" alt="REVOLT logo" width="600" height="168" style="display: block;" /> <img src="https://revolt.chat/header.png" alt="Revolt logo" width="600" height="168" style="display: block;" />
</td> </td>
</tr> </tr>
<tr> <tr>
<td bgcolor="#ffffff" style="padding: 40px 30px 40px 30px;"> <td bgcolor="#ffffff" style="padding: 40px 30px 40px 30px;">
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;"> <table border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
<tr> <tr>
<td style="color: #153643; font-family: Arial, sans-serif;"> <td style="color: #153643; font-family: Arial, sans-serif;">
<h1 style="font-size: 24px; margin: 0;">Verify your account!</h1> <h1 style="font-size: 24px; margin: 0;">You're almost there!</h1>
</td> </td>
</tr> </tr>
<tr> <tr>
<td style="color: #153643; font-family: Arial, sans-serif; font-size: 16px; line-height: 24px; padding: 20px 0 0 0;"> <td style="color: #153643; font-family: Arial, sans-serif; font-size: 16px; line-height: 24px; padding: 20px 0 0 0;">
<p style="margin: 0;"> <p>
Please verify your account by <a href="{{url}}">clicking here</a>. Verify your account to be able to log into the platform. If you didn't perform this action you can safely ignore this email.
</p> </p>
<p> <p style="margin: 0;">
Or by manually navigating to the URL: {{url}} Please verify your account by navigating to <a href="{{url}}">{{url}}</a>.
</p> </p>
</td> </td>
</tr> </tr>
</table> </table>
</td> </td>
</tr> </tr>
<tr> <tr>
<td bgcolor="#ff4654" style="padding: 30px 30px;"> <td bgcolor="#ff4654" style="padding: 30px 30px;">
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;"> <table border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
<tr> <tr>
<td style="color: #ffffff; font-family: Arial, sans-serif; font-size: 14px;"> <td style="color: #ffffff; font-family: Arial, sans-serif; font-size: 14px;">
<p style="margin: 0;">Sent by REVOLT. &middot; <a style="color: white;" href="https://revolt.chat">Website</a> &middot; <a style="color: white;" href="https://gitlab.insrt.uk/revolt">Source Code</a></p> <p style="margin: 0;">Sent by Revolt. &middot; Website: <a style="color: white;" href="https://revolt.chat">https://revolt.chat</a></p>
</td> <p>Revolt is a user-first chat platform built with modern web technologies.</p>
</tr> </td>
</table> </tr>
</td> </table>
</tr> </td>
</table> </tr>
</table>
</td> </td>
</tr> </tr>
</table> </table>
......
<h2>You're almost there!</h2>
<p>
Verify your account to be able to log into the platform.<br/>
If you did not perform this action you can safely ignore this email.
</p>
<p>
Please verify your account here: <a href="{{url}}">{{url}}</a>
</p>
<br/>
<p>
Sent by Revolt. · Website: <a href="https://revolt.chat">https://revolt.chat</a>
</p>
<p>
Revolt is a user-first chat platform built with modern web technologies.
</p>
\ No newline at end of file
#!/bin/bash
source set_version.sh
docker build -t revoltchat/server:${version} . &&
docker tag revoltchat/server:${version} revoltchat/server:latest &&
docker push revoltchat/server:${version} &&
docker push revoltchat/server:latest
#!/bin/bash
export version=0.5.1-alpha.21
echo "pub const VERSION: &str = \"${version}\";" > src/version.rs
fn main() {}
use std::collections::HashMap;
use crate::database::*; use crate::database::*;
use crate::notifications::events::ClientboundNotification; use crate::notifications::events::ClientboundNotification;
use crate::util::result::{Error, Result}; use crate::util::result::{Error, Result};
use mongodb::bson::{doc, from_document, to_document}; use futures::StreamExt;
use mongodb::bson::Bson;
use mongodb::{
bson::{doc, to_document, Document},
options::FindOptions,
};
use rocket_contrib::json::JsonValue; use rocket_contrib::json::JsonValue;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
...@@ -24,6 +31,7 @@ pub enum Channel { ...@@ -24,6 +31,7 @@ pub enum Channel {
DirectMessage { DirectMessage {
#[serde(rename = "_id")] #[serde(rename = "_id")]
id: String, id: String,
active: bool, active: bool,
recipients: Vec<String>, recipients: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
...@@ -34,12 +42,59 @@ pub enum Channel { ...@@ -34,12 +42,59 @@ pub enum Channel {
id: String, id: String,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
nonce: Option<String>, nonce: Option<String>,
name: String, name: String,
owner: String, owner: String,
description: String, #[serde(skip_serializing_if = "Option::is_none")]
description: Option<String>,
recipients: Vec<String>, recipients: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
icon: Option<File>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
last_message: Option<LastMessage>, last_message: Option<LastMessage>,
#[serde(skip_serializing_if = "Option::is_none")]
permissions: Option<i32>,
},
TextChannel {
#[serde(rename = "_id")]
id: String,
server: String,
#[serde(skip_serializing_if = "Option::is_none")]
nonce: Option<String>,
name: String,
#[serde(skip_serializing_if = "Option::is_none")]
description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
icon: Option<File>,
#[serde(skip_serializing_if = "Option::is_none")]
last_message: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
default_permissions: Option<i32>,
#[serde(default = "HashMap::new", skip_serializing_if = "HashMap::is_empty")]
role_permissions: HashMap<String, i32>
},
VoiceChannel {
#[serde(rename = "_id")]
id: String,
server: String,
#[serde(skip_serializing_if = "Option::is_none")]
nonce: Option<String>,
name: String,
#[serde(skip_serializing_if = "Option::is_none")]
description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
icon: Option<File>,
#[serde(skip_serializing_if = "Option::is_none")]
default_permissions: Option<i32>,
#[serde(default = "HashMap::new", skip_serializing_if = "HashMap::is_empty")]
role_permissions: HashMap<String, i32>
}, },
} }
...@@ -48,24 +103,19 @@ impl Channel { ...@@ -48,24 +103,19 @@ impl Channel {
match self { match self {
Channel::SavedMessages { id, .. } Channel::SavedMessages { id, .. }
| Channel::DirectMessage { id, .. } | Channel::DirectMessage { id, .. }
| Channel::Group { id, .. } => id, | Channel::Group { id, .. }
| Channel::TextChannel { id, .. }
| Channel::VoiceChannel { id, .. } => id,
} }
} }
pub fn has_messaging(&self) -> Result<()> {
pub async fn get(id: &str) -> Result<Channel> { match self {
let doc = get_collection("channels") Channel::SavedMessages { .. }
.find_one(doc! { "_id": id }, None) | Channel::DirectMessage { .. }
.await | Channel::Group { .. }
.map_err(|_| Error::DatabaseError { | Channel::TextChannel { .. } => Ok(()),
operation: "find_one", Channel::VoiceChannel { .. } => Err(Error::InvalidOperation)
with: "channel", }
})?
.ok_or_else(|| Error::UnknownChannel)?;
from_document::<Channel>(doc).map_err(|_| Error::DatabaseError {
operation: "from_document",
with: "channel",
})
} }
pub async fn publish(self) -> Result<()> { pub async fn publish(self) -> Result<()> {
...@@ -84,10 +134,7 @@ impl Channel { ...@@ -84,10 +134,7 @@ impl Channel {
})?; })?;
let channel_id = self.id().to_string(); let channel_id = self.id().to_string();
ClientboundNotification::ChannelCreate(self) ClientboundNotification::ChannelCreate(self).publish(channel_id);
.publish(channel_id)
.await
.ok();
Ok(()) Ok(())
} }
...@@ -97,17 +144,15 @@ impl Channel { ...@@ -97,17 +144,15 @@ impl Channel {
ClientboundNotification::ChannelUpdate { ClientboundNotification::ChannelUpdate {
id: id.clone(), id: id.clone(),
data, data,
clear: None,
} }
.publish(id) .publish(id);
.await
.ok();
Ok(()) Ok(())
} }
pub async fn delete(&self) -> Result<()> { pub async fn delete_associated_objects(id: Bson) -> Result<()> {
let id = self.id(); get_collection("channel_invites")
get_collection("messages")
.delete_many( .delete_many(
doc! { doc! {
"channel": id "channel": id
...@@ -115,11 +160,167 @@ impl Channel { ...@@ -115,11 +160,167 @@ impl Channel {
None, None,
) )
.await .await
.map(|_| ())
.map_err(|_| Error::DatabaseError { .map_err(|_| Error::DatabaseError {
operation: "delete_many", operation: "delete_many",
with: "messages", with: "channel_invites",
})
}
pub async fn delete_messages(id: Bson) -> Result<()> {
let messages = get_collection("messages");
// Delete any unreads.
get_collection("channel_unreads")
.delete_many(
doc! {
"_id.channel": &id
},
None,
)
.await
.map_err(|_| Error::DatabaseError {
operation: "delete_many",
with: "channel_unreads",
})?; })?;
// Check if there are any attachments we need to delete.
let message_ids = messages
.find(
doc! {
"channel": &id,
"attachment": {
"$exists": 1
}
},
FindOptions::builder().projection(doc! { "_id": 1 }).build(),
)
.await
.map_err(|_| Error::DatabaseError {
operation: "fetch_many",
with: "messages",
})?
.filter_map(async move |s| s.ok())
.collect::<Vec<Document>>()
.await
.into_iter()
.filter_map(|x| x.get_str("_id").ok().map(|x| x.to_string()))
.collect::<Vec<String>>();
// If we found any, mark them as deleted.
if message_ids.len() > 0 {
get_collection("attachments")
.update_many(
doc! {
"message_id": {
"$in": message_ids
}
},
doc! {
"$set": {
"deleted": true
}
},
None,
)
.await
.map_err(|_| Error::DatabaseError {
operation: "update_many",
with: "attachments",
})?;
}
// And then delete said messages.
messages
.delete_many(
doc! {
"channel": id
},
None,
)
.await
.map(|_| ())
.map_err(|_| Error::DatabaseError {
operation: "delete_many",
with: "messages",
})
}
pub async fn delete(&self) -> Result<()> {
let id = self.id();
// Delete any invites.
Channel::delete_associated_objects(Bson::String(id.to_string())).await?;
// Delete messages.
match &self {
Channel::VoiceChannel { .. } => {},
_ => {
Channel::delete_messages(Bson::String(id.to_string())).await?;
}
}
// Remove from server object.
match &self {
Channel::TextChannel { server, .. }
| Channel::VoiceChannel { server, .. } => {
let server = Ref::from_unchecked(server.clone()).fetch_server().await?;
let mut update = doc! {
"$pull": {
"channels": id
}
};
if let Some(sys) = &server.system_messages {
let mut unset = doc! {};
if let Some(cid) = &sys.user_joined {
if id == cid {
unset.insert("system_messages.user_joined", 1);
}
}
if let Some(cid) = &sys.user_left {
if id == cid {
unset.insert("system_messages.user_left", 1);
}
}
if let Some(cid) = &sys.user_kicked {
if id == cid {
unset.insert("system_messages.user_kicked", 1);
}
}
if let Some(cid) = &sys.user_banned {
if id == cid {
unset.insert("system_messages.user_banned", 1);
}
}
if unset.len() > 0 {
update.insert("$unset", unset);
}
}
get_collection("servers")
.update_one(
doc! {
"_id": server.id
},
update,
None,
)
.await
.map_err(|_| Error::DatabaseError {
operation: "update_one",
with: "servers",
})?;
},
_ => {}
}
// Finally, delete the channel object.
get_collection("channels") get_collection("channels")
.delete_one( .delete_one(
doc! { doc! {
...@@ -133,10 +334,13 @@ impl Channel { ...@@ -133,10 +334,13 @@ impl Channel {
with: "channel", with: "channel",
})?; })?;
ClientboundNotification::ChannelDelete { id: id.to_string() } ClientboundNotification::ChannelDelete { id: id.to_string() }.publish(id.to_string());
.publish(id.to_string())
.await if let Channel::Group { icon, .. } = self {
.ok(); if let Some(attachment) = icon {
attachment.delete().await?;
}
}
Ok(()) Ok(())
} }
......
// use serde::{Deserialize, Serialize};
/*#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct MemberCompositeKey {
pub guild: String,
pub user: String,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Member {
#[serde(rename = "_id")]
pub id: MemberCompositeKey,
pub nickname: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Invite {
pub code: String,
pub creator: String,
pub channel: String,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Ban {
pub id: String,
pub reason: String,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Guild {
#[serde(rename = "_id")]
pub id: String,
// pub nonce: String, used internally
pub name: String,
pub description: String,
pub owner: String,
pub channels: Vec<String>,
pub invites: Vec<Invite>,
pub bans: Vec<Ban>,
pub default_permissions: u32,
}*/
use mongodb::bson::doc;
use mongodb::bson::from_document;
use mongodb::bson::to_document;
use serde::{Deserialize, Serialize};
use crate::database::get_collection;
use crate::util::result::Error;
use crate::util::result::Result;
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(tag = "type")]
pub enum Invite {
Server {
#[serde(rename = "_id")]
code: String,
server: String,
creator: String,
channel: String,
},
Group {
#[serde(rename = "_id")]
code: String,
creator: String,
channel: String,
}, /* User {
code: String,
user: String
} */
}
impl Invite {
pub fn code(&self) -> &String {
match &self {
Invite::Server { code, .. } => code,
Invite::Group { code, .. } => code,
}
}
pub fn creator(&self) -> &String {
match &self {
Invite::Server { creator, .. } => creator,
Invite::Group { creator, .. } => creator,
}
}
pub async fn get(code: &str) -> Result<Invite> {
let doc = get_collection("channel_invites")
.find_one(doc! { "_id": code }, None)
.await
.map_err(|_| Error::DatabaseError {
operation: "find_one",
with: "invite",
})?
.ok_or_else(|| Error::UnknownServer)?;
from_document::<Invite>(doc).map_err(|_| Error::DatabaseError {
operation: "from_document",
with: "invite",
})
}
pub async fn save(self) -> Result<()> {
get_collection("channel_invites")
.insert_one(
to_document(&self).map_err(|_| Error::DatabaseError {
operation: "to_bson",
with: "invite",
})?,
None,
)
.await
.map_err(|_| Error::DatabaseError {
operation: "insert_one",
with: "invite",
})?;
Ok(())
}
pub async fn delete(&self) -> Result<()> {
get_collection("channel_invites")
.delete_one(
doc! {
"_id": self.code()
},
None,
)
.await
.map_err(|_| Error::DatabaseError {
operation: "delete_one",
with: "invite",
})?;
Ok(())
}
}
This diff is collapsed.
use mongodb::bson::{doc, from_document};
use serde::{Deserialize, Serialize};
use crate::database::*;
use crate::util::result::{Error, Result};
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(tag = "type")]
enum Metadata {
File,
Text,
Image { width: isize, height: isize },
Video { width: isize, height: isize },
Audio,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct File {
#[serde(rename = "_id")]
pub id: String,
tag: String,
filename: String,
metadata: Metadata,
content_type: String,
size: isize,
#[serde(skip_serializing_if = "Option::is_none")]
deleted: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
message_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
user_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
server_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
object_id: Option<String>,
}
impl File {
pub async fn find_and_use(
attachment_id: &str,
tag: &str,
parent_type: &str,
parent_id: &str,
) -> Result<File> {
let attachments = get_collection("attachments");
let key = format!("{}_id", parent_type);
if let Some(doc) = attachments
.find_one(
doc! {
"_id": attachment_id,
"tag": &tag,
key.clone(): {
"$exists": false
}
},
None,
)
.await
.map_err(|_| Error::DatabaseError {
operation: "find_one",
with: "attachment",
})?
{
let attachment = from_document::<File>(doc).map_err(|_| Error::DatabaseError {
operation: "from_document",
with: "attachment",
})?;
attachments
.update_one(
doc! {
"_id": &attachment.id
},
doc! {
"$set": {
key: &parent_id
}
},
None,
)
.await
.map_err(|_| Error::DatabaseError {
operation: "update_one",
with: "attachment",
})?;
Ok(attachment)
} else {
Err(Error::UnknownAttachment)
}
}
pub async fn delete(&self) -> Result<()> {
get_collection("attachments")
.update_one(
doc! {
"_id": &self.id
},
doc! {
"$set": {
"deleted": true
}
},
None,
)
.await
.map(|_| ())
.map_err(|_| Error::DatabaseError {
operation: "update_one",
with: "attachment",
})
}
}
use crate::util::{
result::{Error, Result},
variables::JANUARY_URL,
};
use linkify::{LinkFinder, LinkKind};
use regex::Regex;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum ImageSize {
Large,
Preview,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Image {
pub url: String,
pub width: isize,
pub height: isize,
pub size: ImageSize,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Video {
pub url: String,
pub width: isize,
pub height: isize,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum TwitchType {
Channel,
Video,
Clip,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum BandcampType {
Album,
Track,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(tag = "type")]
pub enum Special {
None,
YouTube {
id: String,
},
Twitch {
content_type: TwitchType,
id: String,
},
Spotify {
content_type: String,
id: String,
},
Soundcloud,
Bandcamp {
content_type: BandcampType,
id: String,
},
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Metadata {
#[serde(skip_serializing_if = "Option::is_none")]
url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
special: Option<Special>,
#[serde(skip_serializing_if = "Option::is_none")]
title: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
image: Option<Image>,
#[serde(skip_serializing_if = "Option::is_none")]
video: Option<Video>,
// #[serde(skip_serializing_if = "Option::is_none")]
// opengraph_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
site_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
icon_url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
color: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(tag = "type")]
pub enum Embed {
Website(Metadata),
Image(Image),
None,
}
impl Embed {
pub async fn generate(content: String) -> Result<Vec<Embed>> {
lazy_static! {
static ref RE_CODE: Regex = Regex::new("```(?:.|\n)+?```|`(?:.|\n)+?`").unwrap();
}
// Ignore code blocks.
let content = RE_CODE.replace_all(&content, "");
let content = content
// Ignore quoted lines.
.split("\n")
.map(|v| {
if let Some(c) = v.chars().next() {
if c == '>' {
return "";
}
}
v
})
.collect::<Vec<&str>>()
.join("\n");
// ! FIXME: allow multiple links
// ! FIXME: prevent generation if link is surrounded with < >
let mut finder = LinkFinder::new();
finder.kinds(&[LinkKind::Url]);
let links: Vec<_> = finder.links(&content).collect();
if links.len() == 0 {
return Err(Error::LabelMe);
}
let link = &links[0];
let client = reqwest::Client::new();
let result = client
.get(&format!("{}/embed", *JANUARY_URL))
.query(&[("url", link.as_str())])
.send()
.await;
match result {
Err(_) => return Err(Error::LabelMe),
Ok(result) => match result.status() {
reqwest::StatusCode::OK => {
let res: Embed = result.json().await.map_err(|_| Error::InvalidOperation)?;
Ok(vec![res])
}
_ => return Err(Error::LabelMe),
},
}
}
}
pub mod autumn;
pub mod january;