feat: track activity of likes, track like content

aka emoji reactions 👀👀👀👀👀👀👀👀
This commit is contained in:
əlemi 2024-12-26 22:52:37 +01:00
parent d6756ef44d
commit 22149f7bc7
Signed by: alemi
GPG key ID: A4895B84D311642C
7 changed files with 202 additions and 22 deletions

View file

@ -33,6 +33,10 @@ pub enum Relation {
Actors, Actors,
#[sea_orm(has_many = "super::addressing::Entity")] #[sea_orm(has_many = "super::addressing::Entity")]
Addressing, Addressing,
#[sea_orm(has_many = "super::announce::Entity")]
Announces,
#[sea_orm(has_many = "super::like::Entity")]
Likes,
#[sea_orm(has_many = "super::notification::Entity")] #[sea_orm(has_many = "super::notification::Entity")]
Notifications, Notifications,
#[sea_orm( #[sea_orm(
@ -57,6 +61,18 @@ impl Related<super::addressing::Entity> for Entity {
} }
} }
impl Related<super::announce::Entity> for Entity {
fn to() -> RelationDef {
Relation::Announces.def()
}
}
impl Related<super::like::Entity> for Entity {
fn to() -> RelationDef {
Relation::Likes.def()
}
}
impl Related<super::notification::Entity> for Entity { impl Related<super::notification::Entity> for Entity {
fn to() -> RelationDef { fn to() -> RelationDef {
Relation::Notifications.def() Relation::Notifications.def()

View file

@ -8,10 +8,19 @@ pub struct Model {
pub actor: i64, pub actor: i64,
pub object: i64, pub object: i64,
pub published: ChronoDateTimeUtc, pub published: ChronoDateTimeUtc,
pub activity: Option<i64>,
} }
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation { pub enum Relation {
#[sea_orm(
belongs_to = "super::activity::Entity",
from = "Column::Activity",
to = "super::activity::Column::Internal",
on_update = "Cascade",
on_delete = "Cascade"
)]
Activities,
#[sea_orm( #[sea_orm(
belongs_to = "super::actor::Entity", belongs_to = "super::actor::Entity",
from = "Column::Actor", from = "Column::Actor",
@ -30,6 +39,12 @@ pub enum Relation {
Objects, Objects,
} }
impl Related<super::activity::Entity> for Entity {
fn to() -> RelationDef {
Relation::Activities.def()
}
}
impl Related<super::actor::Entity> for Entity { impl Related<super::actor::Entity> for Entity {
fn to() -> RelationDef { fn to() -> RelationDef {
Relation::Actors.def() Relation::Actors.def()

View file

@ -8,10 +8,20 @@ pub struct Model {
pub actor: i64, pub actor: i64,
pub object: i64, pub object: i64,
pub published: ChronoDateTimeUtc, pub published: ChronoDateTimeUtc,
pub activity: Option<i64>,
pub content: String,
} }
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation { pub enum Relation {
#[sea_orm(
belongs_to = "super::activity::Entity",
from = "Column::Activity",
to = "super::activity::Column::Internal",
on_update = "Cascade",
on_delete = "Cascade"
)]
Activities,
#[sea_orm( #[sea_orm(
belongs_to = "super::actor::Entity", belongs_to = "super::actor::Entity",
from = "Column::Actor", from = "Column::Actor",
@ -30,6 +40,12 @@ pub enum Relation {
Objects, Objects,
} }
impl Related<super::activity::Entity> for Entity {
fn to() -> RelationDef {
Relation::Activities.def()
}
}
impl Related<super::actor::Entity> for Entity { impl Related<super::actor::Entity> for Entity {
fn to() -> RelationDef { fn to() -> RelationDef {
Relation::Actors.def() Relation::Actors.def()

View file

@ -106,11 +106,26 @@ pub async fn like(ctx: &crate::Context, activity: impl apb::Activity, tx: &Datab
return Err(ProcessorError::AlreadyProcessed); return Err(ProcessorError::AlreadyProcessed);
} }
let published = activity.published().unwrap_or_else(|_|chrono::Utc::now());
let content = activity.content().unwrap_or_default();
// likes without addressing are "silent likes", process them but dont store activity or notify
let aid = if likes_local_object || !activity.addressed().is_empty() {
let mut activity_model = ctx.insert_activity(activity, tx).await?;
if likes_local_object {
activity_model.to.0.push(obj.attributed_to.clone().unwrap_or_default());
}
ctx.address(Some(&activity_model), None, tx).await?;
Some(activity_model.internal)
} else { None };
let like = crate::model::like::ActiveModel { let like = crate::model::like::ActiveModel {
internal: NotSet, internal: NotSet,
actor: Set(actor.internal), actor: Set(actor.internal),
object: Set(obj.internal), object: Set(obj.internal),
published: Set(activity.published().unwrap_or_else(|_|chrono::Utc::now())), published: Set(published),
activity: Set(aid),
content: Set(content),
}; };
crate::model::like::Entity::insert(like).exec(tx).await?; crate::model::like::Entity::insert(like).exec(tx).await?;
@ -120,19 +135,12 @@ pub async fn like(ctx: &crate::Context, activity: impl apb::Activity, tx: &Datab
.exec(tx) .exec(tx)
.await?; .await?;
// likes without addressing are "silent likes", process them but dont store activity or notify if let Some(aid) = aid {
if likes_local_object || !activity.addressed().is_empty() {
let mut activity_model = ctx.insert_activity(activity, tx).await?;
if likes_local_object {
activity_model.to.0.push(obj.attributed_to.clone().unwrap_or_default());
}
ctx.address(Some(&activity_model), None, tx).await?;
// TODO check that object author is in this like addressing!!! otherwise skip notification // TODO check that object author is in this like addressing!!! otherwise skip notification
if let Some(ref attributed_to) = obj.attributed_to { if let Some(ref attributed_to) = obj.attributed_to {
if ctx.is_local(attributed_to) { if ctx.is_local(attributed_to) {
if let Some(actor_internal) = crate::model::actor::Entity::ap_to_internal(attributed_to, tx).await? { if let Some(actor_internal) = crate::model::actor::Entity::ap_to_internal(attributed_to, tx).await? {
crate::Query::notify(activity_model.internal, actor_internal) crate::Query::notify(aid, actor_internal)
.exec(tx) .exec(tx)
.await?; .await?;
} }
@ -534,6 +542,19 @@ pub async fn announce(ctx: &crate::Context, activity: impl apb::Activity, tx: &D
let actor = ctx.fetch_user(&activity.actor().id()?, tx).await?; let actor = ctx.fetch_user(&activity.actor().id()?, tx).await?;
let published = activity.published().unwrap_or(chrono::Utc::now());
// TODO we should probably insert an activity, otherwise this won't appear on timelines!!
// or maybe go update all addressing records for this object, pushing them up
// or maybe create new addressing rows with more recent dates
// or maybe create fake objects that reference the original one
// idk!!!!
let aid = if actor.actor_type == apb::ActorType::Person || ctx.is_local(&actor.id) {
let activity_model = ctx.insert_activity(activity, tx).await?;
ctx.address(Some(&activity_model), None, tx).await?;
Some(activity_model.internal)
} else { None };
// we only care about announces produced by "Person" actors, because there's intention // we only care about announces produced by "Person" actors, because there's intention
// anything shared by groups, services or applications is automated: fetch it and be done // anything shared by groups, services or applications is automated: fetch it and be done
if actor.actor_type == apb::ActorType::Person { if actor.actor_type == apb::ActorType::Person {
@ -553,26 +574,19 @@ pub async fn announce(ctx: &crate::Context, activity: impl apb::Activity, tx: &D
internal: NotSet, internal: NotSet,
actor: Set(actor.internal), actor: Set(actor.internal),
object: Set(object.internal), object: Set(object.internal),
published: Set(activity.published().unwrap_or(chrono::Utc::now())), published: Set(published),
activity: Set(aid),
}; };
crate::model::announce::Entity::insert(share) crate::model::announce::Entity::insert(share)
.exec(tx).await?; .exec(tx).await?;
} }
// TODO we should probably insert an activity, otherwise this won't appear on timelines!! if let Some(aid) = aid {
// or maybe go update all addressing records for this object, pushing them up
// or maybe create new addressing rows with more recent dates
// or maybe create fake objects that reference the original one
// idk!!!!
if actor.actor_type == apb::ActorType::Person || ctx.is_local(&actor.id) {
let activity_model = ctx.insert_activity(activity, tx).await?;
ctx.address(Some(&activity_model), None, tx).await?;
if let Some(ref attributed_to) = object.attributed_to { if let Some(ref attributed_to) = object.attributed_to {
if ctx.is_local(attributed_to) { if ctx.is_local(attributed_to) {
if let Some(actor_internal) = crate::model::actor::Entity::ap_to_internal(attributed_to, tx).await? { if let Some(actor_internal) = crate::model::actor::Entity::ap_to_internal(attributed_to, tx).await? {
crate::Query::notify(activity_model.internal, actor_internal) crate::Query::notify(aid, actor_internal)
.exec(tx) .exec(tx)
.await?; .await?;
} }

View file

@ -21,6 +21,7 @@ mod m20240715_000001_add_quote_uri_to_objects;
mod m20240715_000002_add_actors_fields_and_aliases; mod m20240715_000002_add_actors_fields_and_aliases;
mod m20240811_000001_add_full_text_index; mod m20240811_000001_add_full_text_index;
mod m20241226_000001_add_show_likes_collection; mod m20241226_000001_add_show_likes_collection;
mod m20241226_000002_add_like_activities;
pub struct Migrator; pub struct Migrator;
@ -49,6 +50,7 @@ impl MigratorTrait for Migrator {
Box::new(m20240715_000002_add_actors_fields_and_aliases::Migration), Box::new(m20240715_000002_add_actors_fields_and_aliases::Migration),
Box::new(m20240811_000001_add_full_text_index::Migration), Box::new(m20240811_000001_add_full_text_index::Migration),
Box::new(m20241226_000001_add_show_likes_collection::Migration), Box::new(m20241226_000001_add_show_likes_collection::Migration),
Box::new(m20241226_000002_add_like_activities::Migration),
] ]
} }
} }

View file

@ -21,7 +21,8 @@ pub enum Likes {
Internal, Internal,
Actor, Actor,
Object, Object,
Activity, // DROPPED Activity, // DROPPED and ADDED BACK with migration m20241226_000002
Content, // ADDED with migration m20241226_000002
Published, Published,
} }
@ -32,6 +33,7 @@ pub enum Announces {
Internal, Internal,
Actor, Actor,
Object, Object,
Activity, // ADDED with migration m20241226_000002
Published, Published,
} }

View file

@ -0,0 +1,115 @@
use sea_orm_migration::prelude::*;
use crate::{m20240524_000001_create_actor_activity_object_tables::Activities, m20240524_000002_create_relations_likes_shares::{Announces, Likes}};
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.alter_table(
Table::alter()
.table(Likes::Table)
.add_column(ColumnDef::new(Likes::Content).string().not_null().default(""))
.add_column(ColumnDef::new(Likes::Activity).big_integer().null())
.add_foreign_key(
TableForeignKey::new()
.name("fkey-likes-activity")
.from_tbl(Likes::Table)
.from_col(Likes::Activity)
.to_tbl(Activities::Table)
.to_col(Activities::Internal)
.on_update(ForeignKeyAction::Cascade)
.on_delete(ForeignKeyAction::Cascade)
)
.to_owned()
)
.await?;
manager
.drop_index(
Index::drop()
.name("index-likes-actor-object")
.table(Likes::Table)
.to_owned()
)
.await?;
manager
.create_index(
Index::create()
.unique()
.name("index-likes-actor-object-content")
.table(Likes::Table)
.col(Likes::Actor)
.col(Likes::Object)
.col(Likes::Content)
.to_owned()
).await?;
manager
.alter_table(
Table::alter()
.table(Announces::Table)
.add_column(ColumnDef::new(Announces::Activity).big_integer().null())
.add_foreign_key(
TableForeignKey::new()
.name("fkey-announces-activity")
.from_tbl(Announces::Table)
.from_col(Announces::Activity)
.to_tbl(Activities::Table)
.to_col(Activities::Internal)
.on_update(ForeignKeyAction::Cascade)
.on_delete(ForeignKeyAction::Cascade)
)
.to_owned()
)
.await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.alter_table(
Table::alter()
.table(Likes::Table)
.drop_column(Likes::Activity)
.to_owned()
)
.await?;
manager
.drop_index(
Index::drop()
.name("index-likes-actor-object-content")
.table(Likes::Table)
.to_owned()
)
.await?;
manager
.create_index(
Index::create()
.unique()
.name("index-likes-actor-object")
.table(Likes::Table)
.col(Likes::Actor)
.col(Likes::Object)
.to_owned()
).await?;
manager
.alter_table(
Table::alter()
.table(Announces::Table)
.drop_column(Announces::Activity)
.to_owned()
)
.await?;
Ok(())
}
}