mirror of https://github.com/LemmyNet/lemmy
Remove chatserver (#2919)
* Remove chatserver * fix clippy * Remove captchas (fixes #2922) * fix prettier * fix api_common build * cargo fmtpull/2920/head
parent
ef1aa18fd2
commit
3565ad984a
@ -1,58 +0,0 @@
|
||||
use crate::{captcha_as_wav_base64, Perform};
|
||||
use actix_web::web::Data;
|
||||
use captcha::{gen, Difficulty};
|
||||
use chrono::Duration;
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
person::{CaptchaResponse, GetCaptcha, GetCaptchaResponse},
|
||||
websocket::{handlers::captcha::AddCaptcha, structs::CaptchaItem},
|
||||
};
|
||||
use lemmy_db_schema::{source::local_site::LocalSite, utils::naive_now};
|
||||
use lemmy_utils::{error::LemmyError, ConnectionId};
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl Perform for GetCaptcha {
|
||||
type Response = GetCaptchaResponse;
|
||||
|
||||
#[tracing::instrument(skip(context, _websocket_id))]
|
||||
async fn perform(
|
||||
&self,
|
||||
context: &Data<LemmyContext>,
|
||||
_websocket_id: Option<ConnectionId>,
|
||||
) -> Result<Self::Response, LemmyError> {
|
||||
let local_site = LocalSite::read(context.pool()).await?;
|
||||
|
||||
if !local_site.captcha_enabled {
|
||||
return Ok(GetCaptchaResponse { ok: None });
|
||||
}
|
||||
|
||||
let captcha = gen(match local_site.captcha_difficulty.as_str() {
|
||||
"easy" => Difficulty::Easy,
|
||||
"hard" => Difficulty::Hard,
|
||||
_ => Difficulty::Medium,
|
||||
});
|
||||
|
||||
let answer = captcha.chars_as_string();
|
||||
|
||||
let png = captcha.as_base64().expect("failed to generate captcha");
|
||||
|
||||
let uuid = uuid::Uuid::new_v4().to_string();
|
||||
|
||||
let wav = captcha_as_wav_base64(&captcha);
|
||||
|
||||
let captcha_item = CaptchaItem {
|
||||
answer,
|
||||
uuid: uuid.clone(),
|
||||
expires: naive_now() + Duration::minutes(10), // expires in 10 minutes
|
||||
};
|
||||
|
||||
// Stores the captcha item on the queue
|
||||
context.chat_server().do_send(AddCaptcha {
|
||||
captcha: captcha_item,
|
||||
});
|
||||
|
||||
Ok(GetCaptchaResponse {
|
||||
ok: Some(CaptchaResponse { png, wav, uuid }),
|
||||
})
|
||||
}
|
||||
}
|
@ -1,113 +0,0 @@
|
||||
use crate::Perform;
|
||||
use actix_web::web::Data;
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
utils::local_user_view_from_jwt,
|
||||
websocket::{
|
||||
handlers::join_rooms::{JoinCommunityRoom, JoinModRoom, JoinPostRoom, JoinUserRoom},
|
||||
structs::{
|
||||
CommunityJoin,
|
||||
CommunityJoinResponse,
|
||||
ModJoin,
|
||||
ModJoinResponse,
|
||||
PostJoin,
|
||||
PostJoinResponse,
|
||||
UserJoin,
|
||||
UserJoinResponse,
|
||||
},
|
||||
},
|
||||
};
|
||||
use lemmy_utils::{error::LemmyError, ConnectionId};
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl Perform for UserJoin {
|
||||
type Response = UserJoinResponse;
|
||||
|
||||
#[tracing::instrument(skip(context, websocket_id))]
|
||||
async fn perform(
|
||||
&self,
|
||||
context: &Data<LemmyContext>,
|
||||
websocket_id: Option<ConnectionId>,
|
||||
) -> Result<UserJoinResponse, LemmyError> {
|
||||
let data: &UserJoin = self;
|
||||
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
|
||||
|
||||
if let Some(id) = websocket_id {
|
||||
context.chat_server().do_send(JoinUserRoom {
|
||||
user_id: local_user_view.local_user.id,
|
||||
id,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(UserJoinResponse { joined: true })
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl Perform for CommunityJoin {
|
||||
type Response = CommunityJoinResponse;
|
||||
|
||||
#[tracing::instrument(skip(context, websocket_id))]
|
||||
async fn perform(
|
||||
&self,
|
||||
context: &Data<LemmyContext>,
|
||||
websocket_id: Option<ConnectionId>,
|
||||
) -> Result<CommunityJoinResponse, LemmyError> {
|
||||
let data: &CommunityJoin = self;
|
||||
|
||||
if let Some(id) = websocket_id {
|
||||
context.chat_server().do_send(JoinCommunityRoom {
|
||||
community_id: data.community_id,
|
||||
id,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(CommunityJoinResponse { joined: true })
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl Perform for ModJoin {
|
||||
type Response = ModJoinResponse;
|
||||
|
||||
#[tracing::instrument(skip(context, websocket_id))]
|
||||
async fn perform(
|
||||
&self,
|
||||
context: &Data<LemmyContext>,
|
||||
websocket_id: Option<ConnectionId>,
|
||||
) -> Result<ModJoinResponse, LemmyError> {
|
||||
let data: &ModJoin = self;
|
||||
|
||||
if let Some(id) = websocket_id {
|
||||
context.chat_server().do_send(JoinModRoom {
|
||||
community_id: data.community_id,
|
||||
id,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(ModJoinResponse { joined: true })
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl Perform for PostJoin {
|
||||
type Response = PostJoinResponse;
|
||||
|
||||
#[tracing::instrument(skip(context, websocket_id))]
|
||||
async fn perform(
|
||||
&self,
|
||||
context: &Data<LemmyContext>,
|
||||
websocket_id: Option<ConnectionId>,
|
||||
) -> Result<PostJoinResponse, LemmyError> {
|
||||
let data: &PostJoin = self;
|
||||
|
||||
if let Some(id) = websocket_id {
|
||||
context.chat_server().do_send(JoinPostRoom {
|
||||
post_id: data.post_id,
|
||||
id,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(PostJoinResponse { joined: true })
|
||||
}
|
||||
}
|
@ -0,0 +1,216 @@
|
||||
use crate::{
|
||||
comment::CommentResponse,
|
||||
community::CommunityResponse,
|
||||
context::LemmyContext,
|
||||
post::PostResponse,
|
||||
utils::{check_person_block, get_interface_language, is_mod_or_admin, send_email_to_user},
|
||||
};
|
||||
use actix_web::web::Data;
|
||||
use lemmy_db_schema::{
|
||||
newtypes::{CommentId, CommunityId, LocalUserId, PersonId, PostId},
|
||||
source::{
|
||||
actor_language::CommunityLanguage,
|
||||
comment::Comment,
|
||||
comment_reply::{CommentReply, CommentReplyInsertForm},
|
||||
person::Person,
|
||||
person_mention::{PersonMention, PersonMentionInsertForm},
|
||||
post::Post,
|
||||
},
|
||||
traits::Crud,
|
||||
};
|
||||
use lemmy_db_views::structs::{CommentView, LocalUserView, PostView};
|
||||
use lemmy_db_views_actor::structs::CommunityView;
|
||||
use lemmy_utils::{error::LemmyError, utils::mention::MentionData};
|
||||
|
||||
pub async fn build_comment_response(
|
||||
context: &Data<LemmyContext>,
|
||||
comment_id: CommentId,
|
||||
local_user_view: Option<LocalUserView>,
|
||||
form_id: Option<String>,
|
||||
recipient_ids: Vec<LocalUserId>,
|
||||
) -> Result<CommentResponse, LemmyError> {
|
||||
let person_id = local_user_view.map(|l| l.person.id);
|
||||
let comment_view = CommentView::read(context.pool(), comment_id, person_id).await?;
|
||||
Ok(CommentResponse {
|
||||
comment_view,
|
||||
recipient_ids,
|
||||
form_id,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn build_community_response(
|
||||
context: &Data<LemmyContext>,
|
||||
local_user_view: LocalUserView,
|
||||
community_id: CommunityId,
|
||||
) -> Result<CommunityResponse, LemmyError> {
|
||||
let is_mod_or_admin = is_mod_or_admin(context.pool(), local_user_view.person.id, community_id)
|
||||
.await
|
||||
.is_ok();
|
||||
let person_id = local_user_view.person.id;
|
||||
let community_view = CommunityView::read(
|
||||
context.pool(),
|
||||
community_id,
|
||||
Some(person_id),
|
||||
Some(is_mod_or_admin),
|
||||
)
|
||||
.await?;
|
||||
let discussion_languages = CommunityLanguage::read(context.pool(), community_id).await?;
|
||||
|
||||
Ok(CommunityResponse {
|
||||
community_view,
|
||||
discussion_languages,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn build_post_response(
|
||||
context: &Data<LemmyContext>,
|
||||
community_id: CommunityId,
|
||||
person_id: PersonId,
|
||||
post_id: PostId,
|
||||
) -> Result<PostResponse, LemmyError> {
|
||||
let is_mod_or_admin = is_mod_or_admin(context.pool(), person_id, community_id)
|
||||
.await
|
||||
.is_ok();
|
||||
let post_view = PostView::read(
|
||||
context.pool(),
|
||||
post_id,
|
||||
Some(person_id),
|
||||
Some(is_mod_or_admin),
|
||||
)
|
||||
.await?;
|
||||
Ok(PostResponse { post_view })
|
||||
}
|
||||
|
||||
// TODO: this function is a mess and should be split up to handle email seperately
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn send_local_notifs(
|
||||
mentions: Vec<MentionData>,
|
||||
comment: &Comment,
|
||||
person: &Person,
|
||||
post: &Post,
|
||||
do_send_email: bool,
|
||||
context: &LemmyContext,
|
||||
) -> Result<Vec<LocalUserId>, LemmyError> {
|
||||
let mut recipient_ids = Vec::new();
|
||||
let inbox_link = format!("{}/inbox", context.settings().get_protocol_and_hostname());
|
||||
|
||||
// Send the local mentions
|
||||
for mention in mentions
|
||||
.iter()
|
||||
.filter(|m| m.is_local(&context.settings().hostname) && m.name.ne(&person.name))
|
||||
.collect::<Vec<&MentionData>>()
|
||||
{
|
||||
let mention_name = mention.name.clone();
|
||||
let user_view = LocalUserView::read_from_name(context.pool(), &mention_name).await;
|
||||
if let Ok(mention_user_view) = user_view {
|
||||
// TODO
|
||||
// At some point, make it so you can't tag the parent creator either
|
||||
// This can cause two notifications, one for reply and the other for mention
|
||||
recipient_ids.push(mention_user_view.local_user.id);
|
||||
|
||||
let user_mention_form = PersonMentionInsertForm {
|
||||
recipient_id: mention_user_view.person.id,
|
||||
comment_id: comment.id,
|
||||
read: None,
|
||||
};
|
||||
|
||||
// Allow this to fail softly, since comment edits might re-update or replace it
|
||||
// Let the uniqueness handle this fail
|
||||
PersonMention::create(context.pool(), &user_mention_form)
|
||||
.await
|
||||
.ok();
|
||||
|
||||
// Send an email to those local users that have notifications on
|
||||
if do_send_email {
|
||||
let lang = get_interface_language(&mention_user_view);
|
||||
send_email_to_user(
|
||||
&mention_user_view,
|
||||
&lang.notification_mentioned_by_subject(&person.name),
|
||||
&lang.notification_mentioned_by_body(&comment.content, &inbox_link, &person.name),
|
||||
context.settings(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Send comment_reply to the parent commenter / poster
|
||||
if let Some(parent_comment_id) = comment.parent_comment_id() {
|
||||
let parent_comment = Comment::read(context.pool(), parent_comment_id).await?;
|
||||
|
||||
// Get the parent commenter local_user
|
||||
let parent_creator_id = parent_comment.creator_id;
|
||||
|
||||
// Only add to recipients if that person isn't blocked
|
||||
let creator_blocked = check_person_block(person.id, parent_creator_id, context.pool())
|
||||
.await
|
||||
.is_err();
|
||||
|
||||
// Don't send a notif to yourself
|
||||
if parent_comment.creator_id != person.id && !creator_blocked {
|
||||
let user_view = LocalUserView::read_person(context.pool(), parent_creator_id).await;
|
||||
if let Ok(parent_user_view) = user_view {
|
||||
recipient_ids.push(parent_user_view.local_user.id);
|
||||
|
||||
let comment_reply_form = CommentReplyInsertForm {
|
||||
recipient_id: parent_user_view.person.id,
|
||||
comment_id: comment.id,
|
||||
read: None,
|
||||
};
|
||||
|
||||
// Allow this to fail softly, since comment edits might re-update or replace it
|
||||
// Let the uniqueness handle this fail
|
||||
CommentReply::create(context.pool(), &comment_reply_form)
|
||||
.await
|
||||
.ok();
|
||||
|
||||
if do_send_email {
|
||||
let lang = get_interface_language(&parent_user_view);
|
||||
send_email_to_user(
|
||||
&parent_user_view,
|
||||
&lang.notification_comment_reply_subject(&person.name),
|
||||
&lang.notification_comment_reply_body(&comment.content, &inbox_link, &person.name),
|
||||
context.settings(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If there's no parent, its the post creator
|
||||
// Only add to recipients if that person isn't blocked
|
||||
let creator_blocked = check_person_block(person.id, post.creator_id, context.pool())
|
||||
.await
|
||||
.is_err();
|
||||
|
||||
if post.creator_id != person.id && !creator_blocked {
|
||||
let creator_id = post.creator_id;
|
||||
let parent_user = LocalUserView::read_person(context.pool(), creator_id).await;
|
||||
if let Ok(parent_user_view) = parent_user {
|
||||
recipient_ids.push(parent_user_view.local_user.id);
|
||||
|
||||
let comment_reply_form = CommentReplyInsertForm {
|
||||
recipient_id: parent_user_view.person.id,
|
||||
comment_id: comment.id,
|
||||
read: None,
|
||||
};
|
||||
|
||||
// Allow this to fail softly, since comment edits might re-update or replace it
|
||||
// Let the uniqueness handle this fail
|
||||
CommentReply::create(context.pool(), &comment_reply_form)
|
||||
.await
|
||||
.ok();
|
||||
|
||||
if do_send_email {
|
||||
let lang = get_interface_language(&parent_user_view);
|
||||
send_email_to_user(
|
||||
&parent_user_view,
|
||||
&lang.notification_post_reply_subject(&person.name),
|
||||
&lang.notification_post_reply_body(&comment.content, &inbox_link, &person.name),
|
||||
context.settings(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(recipient_ids)
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
use crate::websocket::{
|
||||
handlers::{SessionInfo, WsMessage},
|
||||
structs::CaptchaItem,
|
||||
};
|
||||
use actix::{Actor, Context};
|
||||
use lemmy_db_schema::newtypes::{CommunityId, LocalUserId, PostId};
|
||||
use lemmy_utils::ConnectionId;
|
||||
use rand::{rngs::StdRng, SeedableRng};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
pub struct ChatServer {
|
||||
/// A map from generated random ID to session addr
|
||||
pub sessions: HashMap<ConnectionId, SessionInfo>,
|
||||
|
||||
/// A map from post_id to set of connectionIDs
|
||||
pub post_rooms: HashMap<PostId, HashSet<ConnectionId>>,
|
||||
|
||||
/// A map from community to set of connectionIDs
|
||||
pub community_rooms: HashMap<CommunityId, HashSet<ConnectionId>>,
|
||||
|
||||
pub mod_rooms: HashMap<CommunityId, HashSet<ConnectionId>>,
|
||||
|
||||
/// A map from user id to its connection ID for joined users. Remember a user can have multiple
|
||||
/// sessions (IE clients)
|
||||
pub(super) user_rooms: HashMap<LocalUserId, HashSet<ConnectionId>>,
|
||||
|
||||
pub(super) rng: StdRng,
|
||||
|
||||
/// A list of the current captchas
|
||||
pub(super) captchas: Vec<CaptchaItem>,
|
||||
}
|
||||
|
||||
/// `ChatServer` is an actor. It maintains list of connection client session.
|
||||
/// And manages available rooms. Peers send messages to other peers in same
|
||||
/// room through `ChatServer`.
|
||||
impl ChatServer {
|
||||
pub fn new() -> ChatServer {
|
||||
ChatServer {
|
||||
sessions: Default::default(),
|
||||
post_rooms: Default::default(),
|
||||
community_rooms: Default::default(),
|
||||
mod_rooms: Default::default(),
|
||||
user_rooms: Default::default(),
|
||||
rng: StdRng::from_entropy(),
|
||||
captchas: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_message(
|
||||
&self,
|
||||
connections: &HashSet<ConnectionId>,
|
||||
message: &str,
|
||||
exclude_connection: Option<ConnectionId>,
|
||||
) {
|
||||
for id in connections
|
||||
.iter()
|
||||
.filter(|c| Some(*c) != exclude_connection.as_ref())
|
||||
{
|
||||
if let Some(session) = self.sessions.get(id) {
|
||||
session.addr.do_send(WsMessage(message.to_owned()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ChatServer {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// Make actor from `ChatServer`
|
||||
impl Actor for ChatServer {
|
||||
/// We are going to use simple Context, we just need ability to communicate
|
||||
/// with other actors.
|
||||
type Context = Context<Self>;
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
use crate::websocket::{chat_server::ChatServer, structs::CaptchaItem};
|
||||
use actix::{Context, Handler, Message};
|
||||
use lemmy_db_schema::utils::naive_now;
|
||||
|
||||
/// Adding a Captcha
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct AddCaptcha {
|
||||
pub captcha: CaptchaItem,
|
||||
}
|
||||
|
||||
impl Handler<AddCaptcha> for ChatServer {
|
||||
type Result = ();
|
||||
|
||||
fn handle(&mut self, msg: AddCaptcha, _: &mut Context<Self>) -> Self::Result {
|
||||
self.captchas.push(msg.captcha);
|
||||
}
|
||||
}
|
||||
|
||||
/// Checking a Captcha
|
||||
#[derive(Message)]
|
||||
#[rtype(bool)]
|
||||
pub struct CheckCaptcha {
|
||||
pub uuid: String,
|
||||
pub answer: String,
|
||||
}
|
||||
|
||||
impl Handler<CheckCaptcha> for ChatServer {
|
||||
type Result = bool;
|
||||
|
||||
fn handle(&mut self, msg: CheckCaptcha, _: &mut Context<Self>) -> Self::Result {
|
||||
// Remove all the ones that are past the expire time
|
||||
self.captchas.retain(|x| x.expires.gt(&naive_now()));
|
||||
|
||||
let check = self
|
||||
.captchas
|
||||
.iter()
|
||||
.any(|r| r.uuid == msg.uuid && r.answer.to_lowercase() == msg.answer.to_lowercase());
|
||||
|
||||
// Remove this uuid so it can't be re-checked (Checks only work once)
|
||||
self.captchas.retain(|x| x.uuid != msg.uuid);
|
||||
|
||||
check
|
||||
}
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
use crate::websocket::{
|
||||
chat_server::ChatServer,
|
||||
handlers::{SessionInfo, WsMessage},
|
||||
};
|
||||
use actix::{Context, Handler, Message, Recipient};
|
||||
use lemmy_utils::ConnectionId;
|
||||
use rand::Rng;
|
||||
|
||||
/// New chat session is created
|
||||
#[derive(Message)]
|
||||
#[rtype(ConnectionId)]
|
||||
pub struct Connect {
|
||||
pub addr: Recipient<WsMessage>,
|
||||
}
|
||||
|
||||
/// Handler for Connect message.
|
||||
///
|
||||
/// Register new session and assign unique id to this session
|
||||
impl Handler<Connect> for ChatServer {
|
||||
type Result = ConnectionId;
|
||||
|
||||
fn handle(&mut self, msg: Connect, _: &mut Context<Self>) -> Self::Result {
|
||||
// register session with random id
|
||||
let id = self.rng.gen::<usize>();
|
||||
let session = SessionInfo { addr: msg.addr };
|
||||
self.sessions.insert(id, session);
|
||||
|
||||
// send id back
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
/// Session is disconnected
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct Disconnect {
|
||||
pub id: ConnectionId,
|
||||
}
|
||||
|
||||
/// Handler for Disconnect message.
|
||||
impl Handler<Disconnect> for ChatServer {
|
||||
type Result = ();
|
||||
|
||||
fn handle(&mut self, msg: Disconnect, _: &mut Context<Self>) -> Self::Result {
|
||||
// remove address
|
||||
if self.sessions.remove(&msg.id).is_some() {
|
||||
// remove session from all rooms
|
||||
for sessions in self.user_rooms.values_mut() {
|
||||
sessions.remove(&msg.id);
|
||||
}
|
||||
for sessions in self.post_rooms.values_mut() {
|
||||
sessions.remove(&msg.id);
|
||||
}
|
||||
for sessions in self.community_rooms.values_mut() {
|
||||
sessions.remove(&msg.id);
|
||||
}
|
||||
for sessions in self.mod_rooms.values_mut() {
|
||||
sessions.remove(&msg.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,120 +0,0 @@
|
||||
use crate::websocket::chat_server::ChatServer;
|
||||
use actix::{Context, Handler, Message};
|
||||
use lemmy_db_schema::newtypes::{CommunityId, LocalUserId, PostId};
|
||||
use lemmy_utils::ConnectionId;
|
||||
use std::collections::HashSet;
|
||||
|
||||
/// Joining a Post room
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct JoinPostRoom {
|
||||
pub post_id: PostId,
|
||||
pub id: ConnectionId,
|
||||
}
|
||||
|
||||
impl Handler<JoinPostRoom> for ChatServer {
|
||||
type Result = ();
|
||||
|
||||
fn handle(&mut self, msg: JoinPostRoom, _: &mut Context<Self>) -> Self::Result {
|
||||
// remove session from all rooms
|
||||
for sessions in self.post_rooms.values_mut() {
|
||||
sessions.remove(&msg.id);
|
||||
}
|
||||
|
||||
// Also leave all communities
|
||||
// This avoids double messages
|
||||
// TODO found a bug, whereby community messages like
|
||||
// delete and remove aren't sent, because
|
||||
// you left the community room
|
||||
for sessions in self.community_rooms.values_mut() {
|
||||
sessions.remove(&msg.id);
|
||||
}
|
||||
|
||||
self
|
||||
.post_rooms
|
||||
.entry(msg.post_id)
|
||||
.or_insert_with(HashSet::new)
|
||||
.insert(msg.id);
|
||||
}
|
||||
}
|
||||
|
||||
/// Joining a Community Room
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct JoinCommunityRoom {
|
||||
pub community_id: CommunityId,
|
||||
pub id: ConnectionId,
|
||||
}
|
||||
|
||||
impl Handler<JoinCommunityRoom> for ChatServer {
|
||||
type Result = ();
|
||||
|
||||
fn handle(&mut self, msg: JoinCommunityRoom, _: &mut Context<Self>) -> Self::Result {
|
||||
// remove session from all rooms
|
||||
for sessions in self.community_rooms.values_mut() {
|
||||
sessions.remove(&msg.id);
|
||||
}
|
||||
|
||||
// Also leave all post rooms
|
||||
// This avoids double messages
|
||||
for sessions in self.post_rooms.values_mut() {
|
||||
sessions.remove(&msg.id);
|
||||
}
|
||||
|
||||
self
|
||||
.community_rooms
|
||||
.entry(msg.community_id)
|
||||
.or_insert_with(HashSet::new)
|
||||
.insert(msg.id);
|
||||
}
|
||||
}
|
||||
|
||||
/// Joining a Mod room
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct JoinModRoom {
|
||||
pub community_id: CommunityId,
|
||||
pub id: ConnectionId,
|
||||
}
|
||||
|
||||
impl Handler<JoinModRoom> for ChatServer {
|
||||
type Result = ();
|
||||
|
||||
fn handle(&mut self, msg: JoinModRoom, _: &mut Context<Self>) -> Self::Result {
|
||||
// remove session from all rooms
|
||||
for sessions in self.mod_rooms.values_mut() {
|
||||
sessions.remove(&msg.id);
|
||||
}
|
||||
|
||||
self
|
||||
.mod_rooms
|
||||
.entry(msg.community_id)
|
||||
.or_insert_with(HashSet::new)
|
||||
.insert(msg.id);
|
||||
}
|
||||
}
|
||||
|
||||
/// Joining a User room
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct JoinUserRoom {
|
||||
pub user_id: LocalUserId,
|
||||
pub id: ConnectionId,
|
||||
}
|
||||
|
||||
impl Handler<JoinUserRoom> for ChatServer {
|
||||
type Result = ();
|
||||
|
||||
fn handle(&mut self, msg: JoinUserRoom, _: &mut Context<Self>) -> Self::Result {
|
||||
// remove session from all rooms
|
||||
for sessions in self.user_rooms.values_mut() {
|
||||
sessions.remove(&msg.id);
|
||||
}
|
||||
|
||||
self
|
||||
.user_rooms
|
||||
.entry(msg.user_id)
|
||||
.or_insert_with(HashSet::new)
|
||||
.insert(msg.id);
|
||||
}
|
||||
}
|
@ -1,130 +0,0 @@
|
||||
use crate::websocket::chat_server::ChatServer;
|
||||
use actix::{Context, Handler, Message};
|
||||
use lemmy_db_schema::newtypes::{CommunityId, LocalUserId, PostId};
|
||||
use lemmy_utils::ConnectionId;
|
||||
use std::collections::HashSet;
|
||||
|
||||
/// Sending a post room message
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct SendPostRoomMessage {
|
||||
pub post_id: PostId,
|
||||
pub message: String,
|
||||
pub websocket_id: Option<ConnectionId>,
|
||||
}
|
||||
|
||||
impl Handler<SendPostRoomMessage> for ChatServer {
|
||||
type Result = ();
|
||||
|
||||
fn handle(&mut self, msg: SendPostRoomMessage, _: &mut Context<Self>) -> Self::Result {
|
||||
let room_connections = self.post_rooms.get(&msg.post_id);
|
||||
if let Some(connections) = room_connections {
|
||||
self.send_message(connections, &msg.message, msg.websocket_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sending a community room message
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct SendCommunityRoomMessage {
|
||||
pub community_id: CommunityId,
|
||||
pub message: String,
|
||||
pub websocket_id: Option<ConnectionId>,
|
||||
}
|
||||
|
||||
impl Handler<SendCommunityRoomMessage> for ChatServer {
|
||||
type Result = ();
|
||||
|
||||
fn handle(&mut self, msg: SendCommunityRoomMessage, _: &mut Context<Self>) -> Self::Result {
|
||||
let room_connections = self.community_rooms.get(&msg.community_id);
|
||||
if let Some(connections) = room_connections {
|
||||
self.send_message(connections, &msg.message, msg.websocket_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sending a mod room message
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct SendModRoomMessage {
|
||||
pub community_id: CommunityId,
|
||||
pub message: String,
|
||||
pub websocket_id: Option<ConnectionId>,
|
||||
}
|
||||
|
||||
impl Handler<SendModRoomMessage> for ChatServer {
|
||||
type Result = ();
|
||||
|
||||
fn handle(&mut self, msg: SendModRoomMessage, _: &mut Context<Self>) -> Self::Result {
|
||||
let room_connections = self.community_rooms.get(&msg.community_id);
|
||||
if let Some(connections) = room_connections {
|
||||
self.send_message(connections, &msg.message, msg.websocket_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sending a user room message
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct SendUserRoomMessage {
|
||||
pub recipient_id: LocalUserId,
|
||||
pub message: String,
|
||||
pub websocket_id: Option<ConnectionId>,
|
||||
}
|
||||
|
||||
impl Handler<SendUserRoomMessage> for ChatServer {
|
||||
type Result = ();
|
||||
|
||||
fn handle(&mut self, msg: SendUserRoomMessage, _: &mut Context<Self>) -> Self::Result {
|
||||
let room_connections = self.user_rooms.get(&msg.recipient_id);
|
||||
if let Some(connections) = room_connections {
|
||||
self.send_message(connections, &msg.message, msg.websocket_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sending a message to every session
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct SendAllMessage {
|
||||
pub message: String,
|
||||
pub websocket_id: Option<ConnectionId>,
|
||||
}
|
||||
|
||||
impl Handler<SendAllMessage> for ChatServer {
|
||||
type Result = ();
|
||||
|
||||
fn handle(&mut self, msg: SendAllMessage, _: &mut Context<Self>) -> Self::Result {
|
||||
let connections: HashSet<ConnectionId> = self.sessions.keys().cloned().collect();
|
||||
self.send_message(&connections, &msg.message, msg.websocket_id);
|
||||
}
|
||||
}
|
||||
|
||||
///// Send websocket message in all sessions which joined a specific room.
|
||||
/////
|
||||
///// `message` - The json message body to send
|
||||
///// `room` - Connection IDs which should receive the message
|
||||
///// `exclude_connection` - Dont send to user who initiated the api call, as that
|
||||
///// would result in duplicate notification
|
||||
//async fn send_message_in_room(
|
||||
// &self,
|
||||
// message: &str,
|
||||
// room: Option<HashSet<ConnectionId>>,
|
||||
// exclude_connection: Option<ConnectionId>,
|
||||
//) -> Result<(), LemmyError> {
|
||||
// let mut session = self.inner()?.sessions.clone();
|
||||
// if let Some(room) = room {
|
||||
// // Note, this will ignore any errors, such as closed connections
|
||||
// join_all(
|
||||
// room
|
||||
// .into_iter()
|
||||
// .filter(|c| Some(c) != exclude_connection.as_ref())
|
||||
// .filter_map(|c| session.remove(&c))
|
||||
// .map(|mut s: Session| async move { s.text(message).await }),
|
||||
// )
|
||||
// .await;
|
||||
// }
|
||||
// Ok(())
|
||||
//}
|
||||
//}
|
@ -1,18 +0,0 @@
|
||||
use actix::{Message, Recipient};
|
||||
|
||||
pub mod captcha;
|
||||
pub mod connect;
|
||||
pub mod join_rooms;
|
||||
pub mod messages;
|
||||
pub mod online_users;
|
||||
|
||||
/// A string message sent to a websocket session
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct WsMessage(pub String);
|
||||
|
||||
// TODO move this?
|
||||
pub struct SessionInfo {
|
||||
pub addr: Recipient<WsMessage>,
|
||||
// pub ip: IpAddr
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
use crate::websocket::chat_server::ChatServer;
|
||||
use actix::{Context, Handler, Message};
|
||||
use lemmy_db_schema::newtypes::{CommunityId, PostId};
|
||||
|
||||
/// Getting the number of online connections
|
||||
#[derive(Message)]
|
||||
#[rtype(usize)]
|
||||
pub struct GetUsersOnline;
|
||||
|
||||
/// Handler for Disconnect message.
|
||||
impl Handler<GetUsersOnline> for ChatServer {
|
||||
type Result = usize;
|
||||
|
||||
fn handle(&mut self, _msg: GetUsersOnline, _: &mut Context<Self>) -> Self::Result {
|
||||
self.sessions.len()
|
||||
}
|
||||
}
|
||||
|
||||
/// Getting the number of post users online
|
||||
#[derive(Message)]
|
||||
#[rtype(usize)]
|
||||
pub struct GetPostUsersOnline {
|
||||
pub post_id: PostId,
|
||||
}
|
||||
|
||||
/// Handler for Disconnect message.
|
||||
impl Handler<GetPostUsersOnline> for ChatServer {
|
||||
type Result = usize;
|
||||
|
||||
fn handle(&mut self, msg: GetPostUsersOnline, _: &mut Context<Self>) -> Self::Result {
|
||||
self
|
||||
.post_rooms
|
||||
.get(&msg.post_id)
|
||||
.map_or(1, std::collections::HashSet::len)
|
||||
}
|
||||
}
|
||||
|
||||
/// Getting the number of post users online
|
||||
#[derive(Message)]
|
||||
#[rtype(usize)]
|
||||
pub struct GetCommunityUsersOnline {
|
||||
pub community_id: CommunityId,
|
||||
}
|
||||
|
||||
/// Handler for Disconnect message.
|
||||
impl Handler<GetCommunityUsersOnline> for ChatServer {
|
||||
type Result = usize;
|
||||
|
||||
fn handle(&mut self, msg: GetCommunityUsersOnline, _: &mut Context<Self>) -> Self::Result {
|
||||
self
|
||||
.community_rooms
|
||||
.get(&msg.community_id)
|
||||
.map_or(1, std::collections::HashSet::len)
|
||||
}
|
||||
}
|
@ -1,146 +0,0 @@
|
||||
use actix::{Message, Recipient};
|
||||
use lemmy_utils::error::LemmyError;
|
||||
use serde::Serialize;
|
||||
|
||||
pub mod chat_server;
|
||||
pub mod handlers;
|
||||
pub mod send;
|
||||
pub mod structs;
|
||||
|
||||
/// A string message sent to a websocket session
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct WsMessage(pub String);
|
||||
|
||||
pub struct SessionInfo {
|
||||
pub addr: Recipient<WsMessage>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct WebsocketResponse<T> {
|
||||
op: String,
|
||||
data: T,
|
||||
}
|
||||
|
||||
pub fn serialize_websocket_message<Response, OP>(
|
||||
op: &OP,
|
||||
data: &Response,
|
||||
) -> Result<String, LemmyError>
|
||||
where
|
||||
Response: Serialize,
|
||||
OP: ToString,
|
||||
{
|
||||
let response = WebsocketResponse {
|
||||
op: op.to_string(),
|
||||
data,
|
||||
};
|
||||
Ok(serde_json::to_string(&response)?)
|
||||
}
|
||||
|
||||
#[derive(EnumString, Display, Debug, Clone)]
|
||||
pub enum UserOperation {
|
||||
Login,
|
||||
GetCaptcha,
|
||||
SaveComment,
|
||||
CreateCommentLike,
|
||||
DistinguishComment,
|
||||
CreateCommentReport,
|
||||
ResolveCommentReport,
|
||||
ListCommentReports,
|
||||
CreatePostLike,
|
||||
LockPost,
|
||||
FeaturePost,
|
||||
MarkPostAsRead,
|
||||
SavePost,
|
||||
CreatePostReport,
|
||||
ResolvePostReport,
|
||||
ListPostReports,
|
||||
GetReportCount,
|
||||
GetUnreadCount,
|
||||
VerifyEmail,
|
||||
FollowCommunity,
|
||||
GetReplies,
|
||||
GetPersonMentions,
|
||||
MarkPersonMentionAsRead,
|
||||
MarkCommentReplyAsRead,
|
||||
GetModlog,
|
||||
BanFromCommunity,
|
||||
AddModToCommunity,
|
||||
AddAdmin,
|
||||
GetUnreadRegistrationApplicationCount,
|
||||
ListRegistrationApplications,
|
||||
ApproveRegistrationApplication,
|
||||
BanPerson,
|
||||
GetBannedPersons,
|
||||
MarkAllAsRead,
|
||||
SaveUserSettings,
|
||||
TransferCommunity,
|
||||
LeaveAdmin,
|
||||
PasswordReset,
|
||||
PasswordChange,
|
||||
MarkPrivateMessageAsRead,
|
||||
CreatePrivateMessageReport,
|
||||
ResolvePrivateMessageReport,
|
||||
ListPrivateMessageReports,
|
||||
UserJoin,
|
||||
PostJoin,
|
||||
CommunityJoin,
|
||||
ModJoin,
|
||||
ChangePassword,
|
||||
GetSiteMetadata,
|
||||
BlockCommunity,
|
||||
BlockPerson,
|
||||
PurgePerson,
|
||||
PurgeCommunity,
|
||||
PurgePost,
|
||||
PurgeComment,
|
||||
GetFederatedInstances,
|
||||
}
|
||||
|
||||
#[derive(EnumString, Display, Debug, Clone)]
|
||||
pub enum UserOperationCrud {
|
||||
// Site
|
||||
CreateSite,
|
||||
GetSite,
|
||||
EditSite,
|
||||
// Community
|
||||
CreateCommunity,
|
||||
ListCommunities,
|
||||
EditCommunity,
|
||||
DeleteCommunity,
|
||||
RemoveCommunity,
|
||||
// Post
|
||||
CreatePost,
|
||||
GetPost,
|
||||
EditPost,
|
||||
DeletePost,
|
||||
RemovePost,
|
||||
// Comment
|
||||
CreateComment,
|
||||
GetComment,
|
||||
EditComment,
|
||||
DeleteComment,
|
||||
RemoveComment,
|
||||
// User
|
||||
Register,
|
||||
DeleteAccount,
|
||||
// Private Message
|
||||
CreatePrivateMessage,
|
||||
GetPrivateMessages,
|
||||
EditPrivateMessage,
|
||||
DeletePrivateMessage,
|
||||
//Emojis
|
||||
CreateCustomEmoji,
|
||||
EditCustomEmoji,
|
||||
DeleteCustomEmoji,
|
||||
}
|
||||
|
||||
#[derive(EnumString, Display, Debug, Clone)]
|
||||
pub enum UserOperationApub {
|
||||
GetPosts,
|
||||
GetCommunity,
|
||||
GetComments,
|
||||
GetPersonDetails,
|
||||
Search,
|
||||
ResolveObject,
|
||||
}
|
@ -1,405 +0,0 @@
|
||||
use super::{
|
||||
handlers::messages::{
|
||||
SendAllMessage,
|
||||
SendCommunityRoomMessage,
|
||||
SendModRoomMessage,
|
||||
SendPostRoomMessage,
|
||||
SendUserRoomMessage,
|
||||
},
|
||||
serialize_websocket_message,
|
||||
};
|
||||
use crate::{
|
||||
comment::CommentResponse,
|
||||
community::CommunityResponse,
|
||||
context::LemmyContext,
|
||||
post::PostResponse,
|
||||
private_message::PrivateMessageResponse,
|
||||
utils::{check_person_block, get_interface_language, send_email_to_user},
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
newtypes::{CommentId, CommunityId, LocalUserId, PersonId, PostId, PrivateMessageId},
|
||||
source::{
|
||||
actor_language::CommunityLanguage,
|
||||
comment::Comment,
|
||||
comment_reply::{CommentReply, CommentReplyInsertForm},
|
||||
person::Person,
|
||||
person_mention::{PersonMention, PersonMentionInsertForm},
|
||||
post::Post,
|
||||
},
|
||||
traits::Crud,
|
||||
SubscribedType,
|
||||
};
|
||||
use lemmy_db_views::structs::{CommentView, LocalUserView, PostView, PrivateMessageView};
|
||||
use lemmy_db_views_actor::structs::CommunityView;
|
||||
use lemmy_utils::{error::LemmyError, utils::mention::MentionData, ConnectionId};
|
||||
use serde::Serialize;
|
||||
|
||||
impl LemmyContext {
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn send_post_ws_message<OP>(
|
||||
&self,
|
||||
op: &OP,
|
||||
post_id: PostId,
|
||||
websocket_id: Option<ConnectionId>,
|
||||
person_id: Option<PersonId>,
|
||||
) -> Result<PostResponse, LemmyError>
|
||||
where
|
||||
OP: ToString,
|
||||
{
|
||||
let post_view = PostView::read(self.pool(), post_id, person_id, Some(true)).await?;
|
||||
|
||||
let res = PostResponse { post_view };
|
||||
|
||||
// Send it to the post room
|
||||
// Don't send my data with it
|
||||
let mut post_sent = res.clone();
|
||||
post_sent.post_view.my_vote = None;
|
||||
let message = serialize_websocket_message(op, &post_sent)?;
|
||||
|
||||
self.chat_server().do_send(SendPostRoomMessage {
|
||||
post_id,
|
||||
message: message.clone(),
|
||||
websocket_id,
|
||||
});
|
||||
|
||||
// Send it to /c/all and that community
|
||||
self.chat_server().do_send(SendCommunityRoomMessage {
|
||||
community_id: CommunityId(0),
|
||||
message: message.clone(),
|
||||
websocket_id,
|
||||
});
|
||||
|
||||
self.chat_server().do_send(SendCommunityRoomMessage {
|
||||
community_id: post_sent.post_view.community.id,
|
||||
message,
|
||||
websocket_id,
|
||||
});
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
// TODO: in many call sites in apub crate, we are setting an empty vec for recipient_ids,
|
||||
// we should get the actual recipient actors from somewhere
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn send_comment_ws_message_simple<OP>(
|
||||
&self,
|
||||
op: &OP,
|
||||
comment_id: CommentId,
|
||||
) -> Result<CommentResponse, LemmyError>
|
||||
where
|
||||
OP: ToString,
|
||||
{
|
||||
self
|
||||
.send_comment_ws_message(op, comment_id, None, None, None, vec![])
|
||||
.await
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn send_comment_ws_message<OP>(
|
||||
&self,
|
||||
op: &OP,
|
||||
comment_id: CommentId,
|
||||
websocket_id: Option<ConnectionId>,
|
||||
form_id: Option<String>,
|
||||
person_id: Option<PersonId>,
|
||||
recipient_ids: Vec<LocalUserId>,
|
||||
) -> Result<CommentResponse, LemmyError>
|
||||
where
|
||||
OP: ToString,
|
||||
{
|
||||
let view = CommentView::read(self.pool(), comment_id, person_id).await?;
|
||||
|
||||
let mut res = CommentResponse {
|
||||
comment_view: view,
|
||||
recipient_ids,
|
||||
form_id,
|
||||
};
|
||||
|
||||
// Strip out my specific user info
|
||||
let mut sent_recipient_comment = res.clone();
|
||||
sent_recipient_comment.form_id = None;
|
||||
sent_recipient_comment.comment_view.my_vote = None;
|
||||
let recipient_message = serialize_websocket_message(op, &sent_recipient_comment)?;
|
||||
|
||||
// Send it to the recipient(s) including the mentioned users
|
||||
for recipient_id in &sent_recipient_comment.recipient_ids {
|
||||
self.chat_server().do_send(SendUserRoomMessage {
|
||||
recipient_id: *recipient_id,
|
||||
message: recipient_message.clone(),
|
||||
websocket_id,
|
||||
});
|
||||
}
|
||||
|
||||
// Remove the recipients here to separate mentions / user messages from post or community comments
|
||||
let mut sent_post_comment = sent_recipient_comment;
|
||||
sent_post_comment.recipient_ids = Vec::new();
|
||||
let post_message = serialize_websocket_message(op, &sent_post_comment)?;
|
||||
|
||||
// Send it to the post room
|
||||
self.chat_server().do_send(SendPostRoomMessage {
|
||||
post_id: sent_post_comment.comment_view.post.id,
|
||||
message: post_message.clone(),
|
||||
websocket_id,
|
||||
});
|
||||
|
||||
// Send it to the community too
|
||||
self.chat_server().do_send(SendCommunityRoomMessage {
|
||||
community_id: sent_post_comment.comment_view.community.id,
|
||||
message: post_message,
|
||||
websocket_id,
|
||||
});
|
||||
// TODO should I send it to all? Seems excessive
|
||||
// self
|
||||
// .send_community_room_message(
|
||||
// user_operation,
|
||||
// &comment_post_sent,
|
||||
// CommunityId(0),
|
||||
// websocket_id,
|
||||
// )
|
||||
// .await?;
|
||||
|
||||
// No need to return recipients
|
||||
res.recipient_ids = Vec::new();
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn send_community_ws_message<OP>(
|
||||
&self,
|
||||
op: &OP,
|
||||
community_id: CommunityId,
|
||||
websocket_id: Option<ConnectionId>,
|
||||
person_id: Option<PersonId>,
|
||||
) -> Result<CommunityResponse, LemmyError>
|
||||
where
|
||||
OP: ToString,
|
||||
{
|
||||
let community_view =
|
||||
CommunityView::read(self.pool(), community_id, person_id, Some(true)).await?;
|
||||
let discussion_languages = CommunityLanguage::read(self.pool(), community_id).await?;
|
||||
|
||||
let mut res = CommunityResponse {
|
||||
community_view,
|
||||
discussion_languages,
|
||||
};
|
||||
|
||||
// Strip out the person id and subscribed when sending to others
|
||||
res.community_view.subscribed = SubscribedType::NotSubscribed;
|
||||
let message = serialize_websocket_message(op, &res)?;
|
||||
|
||||
self.chat_server().do_send(SendCommunityRoomMessage {
|
||||
community_id: res.community_view.community.id,
|
||||
message,
|
||||
websocket_id,
|
||||
});
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn send_pm_ws_message<OP>(
|
||||
&self,
|
||||
op: &OP,
|
||||
private_message_id: PrivateMessageId,
|
||||
websocket_id: Option<ConnectionId>,
|
||||
) -> Result<PrivateMessageResponse, LemmyError>
|
||||
where
|
||||
OP: ToString,
|
||||
{
|
||||
let view = PrivateMessageView::read(self.pool(), private_message_id).await?;
|
||||
|
||||
let res = PrivateMessageResponse {
|
||||
private_message_view: view,
|
||||
};
|
||||
|
||||
// Send notifications to the local recipient, if one exists
|
||||
if res.private_message_view.recipient.local {
|
||||
let recipient_id = res.private_message_view.recipient.id;
|
||||
let local_recipient = LocalUserView::read_person(self.pool(), recipient_id).await?;
|
||||
|
||||
let message = serialize_websocket_message(op, &res)?;
|
||||
|
||||
self.chat_server().do_send(SendUserRoomMessage {
|
||||
recipient_id: local_recipient.local_user.id,
|
||||
message,
|
||||
websocket_id,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn send_local_notifs(
|
||||
&self,
|
||||
mentions: Vec<MentionData>,
|
||||
comment: &Comment,
|
||||
person: &Person,
|
||||
post: &Post,
|
||||
do_send_email: bool,
|
||||
) -> Result<Vec<LocalUserId>, LemmyError> {
|
||||
let mut recipient_ids = Vec::new();
|
||||
let inbox_link = format!("{}/inbox", self.settings().get_protocol_and_hostname());
|
||||
|
||||
// Send the local mentions
|
||||
for mention in mentions
|
||||
.iter()
|
||||
.filter(|m| m.is_local(&self.settings().hostname) && m.name.ne(&person.name))
|
||||
.collect::<Vec<&MentionData>>()
|
||||
{
|
||||
let mention_name = mention.name.clone();
|
||||
let user_view = LocalUserView::read_from_name(self.pool(), &mention_name).await;
|
||||
if let Ok(mention_user_view) = user_view {
|
||||
// TODO
|
||||
// At some point, make it so you can't tag the parent creator either
|
||||
// This can cause two notifications, one for reply and the other for mention
|
||||
recipient_ids.push(mention_user_view.local_user.id);
|
||||
|
||||
let user_mention_form = PersonMentionInsertForm {
|
||||
recipient_id: mention_user_view.person.id,
|
||||
comment_id: comment.id,
|
||||
read: None,
|
||||
};
|
||||
|
||||
// Allow this to fail softly, since comment edits might re-update or replace it
|
||||
// Let the uniqueness handle this fail
|
||||
PersonMention::create(self.pool(), &user_mention_form)
|
||||
.await
|
||||
.ok();
|
||||
|
||||
// Send an email to those local users that have notifications on
|
||||
if do_send_email {
|
||||
let lang = get_interface_language(&mention_user_view);
|
||||
send_email_to_user(
|
||||
&mention_user_view,
|
||||
&lang.notification_mentioned_by_subject(&person.name),
|
||||
&lang.notification_mentioned_by_body(&comment.content, &inbox_link, &person.name),
|
||||
self.settings(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Send comment_reply to the parent commenter / poster
|
||||
if let Some(parent_comment_id) = comment.parent_comment_id() {
|
||||
let parent_comment = Comment::read(self.pool(), parent_comment_id).await?;
|
||||
|
||||
// Get the parent commenter local_user
|
||||
let parent_creator_id = parent_comment.creator_id;
|
||||
|
||||
// Only add to recipients if that person isn't blocked
|
||||
let creator_blocked = check_person_block(person.id, parent_creator_id, self.pool())
|
||||
.await
|
||||
.is_err();
|
||||
|
||||
// Don't send a notif to yourself
|
||||
if parent_comment.creator_id != person.id && !creator_blocked {
|
||||
let user_view = LocalUserView::read_person(self.pool(), parent_creator_id).await;
|
||||
if let Ok(parent_user_view) = user_view {
|
||||
recipient_ids.push(parent_user_view.local_user.id);
|
||||
|
||||
let comment_reply_form = CommentReplyInsertForm {
|
||||
recipient_id: parent_user_view.person.id,
|
||||
comment_id: comment.id,
|
||||
read: None,
|
||||
};
|
||||
|
||||
// Allow this to fail softly, since comment edits might re-update or replace it
|
||||
// Let the uniqueness handle this fail
|
||||
CommentReply::create(self.pool(), &comment_reply_form)
|
||||
.await
|
||||
.ok();
|
||||
|
||||
if do_send_email {
|
||||
let lang = get_interface_language(&parent_user_view);
|
||||
send_email_to_user(
|
||||
&parent_user_view,
|
||||
&lang.notification_comment_reply_subject(&person.name),
|
||||
&lang.notification_comment_reply_body(&comment.content, &inbox_link, &person.name),
|
||||
self.settings(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If there's no parent, its the post creator
|
||||
// Only add to recipients if that person isn't blocked
|
||||
let creator_blocked = check_person_block(person.id, post.creator_id, self.pool())
|
||||
.await
|
||||
.is_err();
|
||||
|
||||
if post.creator_id != person.id && !creator_blocked {
|
||||
let creator_id = post.creator_id;
|
||||
let parent_user = LocalUserView::read_person(self.pool(), creator_id).await;
|
||||
if let Ok(parent_user_view) = parent_user {
|
||||
recipient_ids.push(parent_user_view.local_user.id);
|
||||
|
||||
let comment_reply_form = CommentReplyInsertForm {
|
||||
recipient_id: parent_user_view.person.id,
|
||||
comment_id: comment.id,
|
||||
read: None,
|
||||
};
|
||||
|
||||
// Allow this to fail softly, since comment edits might re-update or replace it
|
||||
// Let the uniqueness handle this fail
|
||||
CommentReply::create(self.pool(), &comment_reply_form)
|
||||
.await
|
||||
.ok();
|
||||
|
||||
if do_send_email {
|
||||
let lang = get_interface_language(&parent_user_view);
|
||||
send_email_to_user(
|
||||
&parent_user_view,
|
||||
&lang.notification_post_reply_subject(&person.name),
|
||||
&lang.notification_post_reply_body(&comment.content, &inbox_link, &person.name),
|
||||
self.settings(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(recipient_ids)
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub fn send_all_ws_message<Data, OP>(
|
||||
&self,
|
||||
op: &OP,
|
||||
data: Data,
|
||||
websocket_id: Option<ConnectionId>,
|
||||
) -> Result<(), LemmyError>
|
||||
where
|
||||
Data: Serialize,
|
||||
OP: ToString,
|
||||
{
|
||||
let message = serialize_websocket_message(op, &data)?;
|
||||
self.chat_server().do_send(SendAllMessage {
|
||||
message,
|
||||
websocket_id,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub fn send_mod_ws_message<Data, OP>(
|
||||
&self,
|
||||
op: &OP,
|
||||
data: Data,
|
||||
community_id: CommunityId,
|
||||
websocket_id: Option<ConnectionId>,
|
||||
) -> Result<(), LemmyError>
|
||||
where
|
||||
Data: Serialize,
|
||||
OP: ToString,
|
||||
{
|
||||
let message = serialize_websocket_message(op, &data)?;
|
||||
self.chat_server().do_send(SendModRoomMessage {
|
||||
community_id,
|
||||
message,
|
||||
websocket_id,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
use crate::sensitive::Sensitive;
|
||||
use lemmy_db_schema::newtypes::{CommunityId, PostId};
|
||||
use serde::{Deserialize, Serialize};
|
||||
#[cfg(feature = "full")]
|
||||
use ts_rs::TS;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(TS))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// Join a user room.
|
||||
pub struct UserJoin {
|
||||
pub auth: Sensitive<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(TS))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// The join response.
|
||||
pub struct UserJoinResponse {
|
||||
pub joined: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(TS))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// Join a community room.
|
||||
pub struct CommunityJoin {
|
||||
pub community_id: CommunityId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(TS))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// The join response.
|
||||
pub struct CommunityJoinResponse {
|
||||
pub joined: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(TS))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// Join a mod room.
|
||||
pub struct ModJoin {
|
||||
pub community_id: CommunityId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(TS))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// The join response.
|
||||
pub struct ModJoinResponse {
|
||||
pub joined: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(TS))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// Join a post room.
|
||||
pub struct PostJoin {
|
||||
pub post_id: PostId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(TS))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// The join response.
|
||||
pub struct PostJoinResponse {
|
||||
pub joined: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CaptchaItem {
|
||||
pub uuid: String,
|
||||
pub answer: String,
|
||||
pub expires: chrono::NaiveDateTime,
|
||||
}
|
@ -1,40 +1,26 @@
|
||||
use crate::PerformCrud;
|
||||
use actix_web::web::Data;
|
||||
use lemmy_api_common::{
|
||||
build_response::build_comment_response,
|
||||
comment::{CommentResponse, GetComment},
|
||||
context::LemmyContext,
|
||||
utils::{check_private_instance, local_user_view_from_jwt_opt},
|
||||
};
|
||||
use lemmy_db_schema::source::local_site::LocalSite;
|
||||
use lemmy_db_views::structs::CommentView;
|
||||
use lemmy_utils::{error::LemmyError, ConnectionId};
|
||||
use lemmy_utils::error::LemmyError;
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl PerformCrud for GetComment {
|
||||
type Response = CommentResponse;
|
||||
|
||||
#[tracing::instrument(skip(context, _websocket_id))]
|
||||
async fn perform(
|
||||
&self,
|
||||
context: &Data<LemmyContext>,
|
||||
_websocket_id: Option<ConnectionId>,
|
||||
) -> Result<Self::Response, LemmyError> {
|
||||
#[tracing::instrument(skip(context))]
|
||||
async fn perform(&self, context: &Data<LemmyContext>) -> Result<Self::Response, LemmyError> {
|
||||
let data = self;
|
||||
let local_user_view = local_user_view_from_jwt_opt(data.auth.as_ref(), context).await;
|
||||
let local_site = LocalSite::read(context.pool()).await?;
|
||||
|
||||
check_private_instance(&local_user_view, &local_site)?;
|
||||
|
||||
let person_id = local_user_view.map(|u| u.person.id);
|
||||
let id = data.id;
|
||||
let comment_view = CommentView::read(context.pool(), id, person_id)
|
||||
.await
|
||||
.map_err(|e| LemmyError::from_error_message(e, "couldnt_find_comment"))?;
|
||||
|
||||
Ok(Self::Response {
|
||||
comment_view,
|
||||
form_id: None,
|
||||
recipient_ids: Vec::new(),
|
||||
})
|
||||
build_comment_response(context, data.id, local_user_view, None, vec![]).await
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue