From ea44f375f5d3da06ca0aebfe871b9f924a26ec29 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Wed, 27 Dec 2017 10:39:31 +0100 Subject: [PATCH] Send video comment comments to followers/origin --- server/controllers/api/videos/comment.ts | 4 +- .../lib/activitypub/process/process-create.ts | 36 ++++++---- server/lib/activitypub/send/send-create.ts | 71 ++++++++++++------- server/lib/video-comment.ts | 26 ++++--- server/models/video/video-comment.ts | 32 ++++++++- 5 files changed, 118 insertions(+), 51 deletions(-) diff --git a/server/controllers/api/videos/comment.ts b/server/controllers/api/videos/comment.ts index ac64f0154..e9dbb6d1b 100644 --- a/server/controllers/api/videos/comment.ts +++ b/server/controllers/api/videos/comment.ts @@ -78,7 +78,7 @@ function addVideoCommentThread (req: express.Request, res: express.Response) { return sequelizeTypescript.transaction(async t => { return createVideoComment({ text: videoCommentInfo.text, - inReplyToCommentId: null, + inReplyToComment: null, video: res.locals.video, accountId: res.locals.oauth.token.User.Account.id }, t) @@ -106,7 +106,7 @@ function addVideoCommentReply (req: express.Request, res: express.Response, next return sequelizeTypescript.transaction(async t => { return createVideoComment({ text: videoCommentInfo.text, - inReplyToCommentId: res.locals.videoComment.id, + inReplyToComment: res.locals.videoComment, video: res.locals.video, accountId: res.locals.oauth.token.User.Account.id }, t) diff --git a/server/lib/activitypub/process/process-create.ts b/server/lib/activitypub/process/process-create.ts index 6c2ee97eb..628942a58 100644 --- a/server/lib/activitypub/process/process-create.ts +++ b/server/lib/activitypub/process/process-create.ts @@ -257,11 +257,11 @@ function createVideoComment (byActor: ActorModel, activity: ActivityCreate) { if (!byAccount) throw new Error('Cannot create video comment with the non account actor ' + byActor.url) return sequelizeTypescript.transaction(async t => { - const video = await VideoModel.loadByUrl(comment.inReplyTo, t) + let video = await VideoModel.loadByUrl(comment.inReplyTo, t) // This is a new thread if (video) { - return VideoCommentModel.create({ + await VideoCommentModel.create({ url: comment.id, text: comment.content, originCommentId: null, @@ -269,19 +269,27 @@ function createVideoComment (byActor: ActorModel, activity: ActivityCreate) { videoId: video.id, accountId: byAccount.id }, { transaction: t }) + } else { + const inReplyToComment = await VideoCommentModel.loadByUrl(comment.inReplyTo, t) + if (!inReplyToComment) throw new Error('Unknown replied comment ' + comment.inReplyTo) + + video = await VideoModel.load(inReplyToComment.videoId) + + const originCommentId = inReplyToComment.originCommentId || inReplyToComment.id + await VideoCommentModel.create({ + url: comment.id, + text: comment.content, + originCommentId, + inReplyToCommentId: inReplyToComment.id, + videoId: video.id, + accountId: byAccount.id + }, { transaction: t }) } - const inReplyToComment = await VideoCommentModel.loadByUrl(comment.inReplyTo, t) - if (!inReplyToComment) throw new Error('Unknown replied comment ' + comment.inReplyTo) - - const originCommentId = inReplyToComment.originCommentId || inReplyToComment.id - return VideoCommentModel.create({ - url: comment.id, - text: comment.content, - originCommentId, - inReplyToCommentId: inReplyToComment.id, - videoId: inReplyToComment.videoId, - accountId: byAccount.id - }, { transaction: t }) + if (video.isOwned()) { + // Don't resend the activity to the sender + const exceptions = [ byActor ] + await forwardActivity(activity, t, exceptions) + } }) } diff --git a/server/lib/activitypub/send/send-create.ts b/server/lib/activitypub/send/send-create.ts index 27b03c45f..ca50460be 100644 --- a/server/lib/activitypub/send/send-create.ts +++ b/server/lib/activitypub/send/send-create.ts @@ -5,14 +5,10 @@ import { getServerActor } from '../../../helpers' import { ActorModel } from '../../../models/activitypub/actor' import { VideoModel } from '../../../models/video/video' import { VideoAbuseModel } from '../../../models/video/video-abuse' +import { VideoCommentModel } from '../../../models/video/video-comment' import { getVideoAbuseActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoViewActivityPubUrl } from '../url' import { - audiencify, - broadcastToFollowers, - getActorsInvolvedInVideo, - getAudience, - getObjectFollowersAudience, - getOriginVideoAudience, + audiencify, broadcastToFollowers, getActorsInvolvedInVideo, getAudience, getObjectFollowersAudience, getOriginVideoAudience, unicastTo } from './misc' @@ -37,24 +33,49 @@ async function sendVideoAbuse (byActor: ActorModel, videoAbuse: VideoAbuseModel, return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl, t) } +async function sendCreateVideoCommentToOrigin (comment: VideoCommentModel, t: Transaction) { + const byActor = comment.Account.Actor + + const actorsInvolvedInVideo = await getActorsInvolvedInVideo(comment.Video, t) + const audience = getOriginVideoAudience(comment.Video, actorsInvolvedInVideo) + + const commentObject = comment.toActivityPubObject() + const data = await createActivityData(comment.url, byActor, commentObject, t, audience) + + return unicastTo(data, byActor, comment.Video.VideoChannel.Account.Actor.sharedInboxUrl, t) +} + +async function sendCreateVideoCommentToVideoFollowers (comment: VideoCommentModel, t: Transaction) { + const byActor = comment.Account.Actor + + const actorsToForwardView = await getActorsInvolvedInVideo(comment.Video, t) + const audience = getObjectFollowersAudience(actorsToForwardView) + + const commentObject = comment.toActivityPubObject() + const data = await createActivityData(comment.url, byActor, commentObject, t, audience) + + const followersException = [ byActor ] + return broadcastToFollowers(data, byActor, actorsToForwardView, t, followersException) +} + async function sendCreateViewToOrigin (byActor: ActorModel, video: VideoModel, t: Transaction) { const url = getVideoViewActivityPubUrl(byActor, video) - const viewActivity = createViewActivityData(byActor, video) + const viewActivityData = createViewActivityData(byActor, video) const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t) const audience = getOriginVideoAudience(video, actorsInvolvedInVideo) - const data = await createActivityData(url, byActor, viewActivity, t, audience) + const data = await createActivityData(url, byActor, viewActivityData, t, audience) return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl, t) } async function sendCreateViewToVideoFollowers (byActor: ActorModel, video: VideoModel, t: Transaction) { const url = getVideoViewActivityPubUrl(byActor, video) - const viewActivity = createViewActivityData(byActor, video) + const viewActivityData = createViewActivityData(byActor, video) const actorsToForwardView = await getActorsInvolvedInVideo(video, t) const audience = getObjectFollowersAudience(actorsToForwardView) - const data = await createActivityData(url, byActor, viewActivity, t, audience) + const data = await createActivityData(url, byActor, viewActivityData, t, audience) // Use the server actor to send the view const serverActor = await getServerActor() @@ -64,22 +85,22 @@ async function sendCreateViewToVideoFollowers (byActor: ActorModel, video: Video async function sendCreateDislikeToOrigin (byActor: ActorModel, video: VideoModel, t: Transaction) { const url = getVideoDislikeActivityPubUrl(byActor, video) - const dislikeActivity = createDislikeActivityData(byActor, video) + const dislikeActivityData = createDislikeActivityData(byActor, video) const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t) const audience = getOriginVideoAudience(video, actorsInvolvedInVideo) - const data = await createActivityData(url, byActor, dislikeActivity, t, audience) + const data = await createActivityData(url, byActor, dislikeActivityData, t, audience) return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl, t) } async function sendCreateDislikeToVideoFollowers (byActor: ActorModel, video: VideoModel, t: Transaction) { const url = getVideoDislikeActivityPubUrl(byActor, video) - const dislikeActivity = createDislikeActivityData(byActor, video) + const dislikeActivityData = createDislikeActivityData(byActor, video) const actorsToForwardView = await getActorsInvolvedInVideo(video, t) const audience = getObjectFollowersAudience(actorsToForwardView) - const data = await createActivityData(url, byActor, dislikeActivity, t, audience) + const data = await createActivityData(url, byActor, dislikeActivityData, t, audience) const followersException = [ byActor ] return broadcastToFollowers(data, byActor, actorsToForwardView, t, followersException) @@ -112,6 +133,14 @@ function createDislikeActivityData (byActor: ActorModel, video: VideoModel) { } } +function createViewActivityData (byActor: ActorModel, video: VideoModel) { + return { + type: 'View', + actor: byActor.url, + object: video.url + } +} + // --------------------------------------------------------------------------- export { @@ -122,15 +151,7 @@ export { sendCreateViewToVideoFollowers, sendCreateDislikeToOrigin, sendCreateDislikeToVideoFollowers, - createDislikeActivityData -} - -// --------------------------------------------------------------------------- - -function createViewActivityData (byActor: ActorModel, video: VideoModel) { - return { - type: 'View', - actor: byActor.url, - object: video.url - } + createDislikeActivityData, + sendCreateVideoCommentToOrigin, + sendCreateVideoCommentToVideoFollowers } diff --git a/server/lib/video-comment.ts b/server/lib/video-comment.ts index e3fe26e35..ef6a8f097 100644 --- a/server/lib/video-comment.ts +++ b/server/lib/video-comment.ts @@ -3,27 +3,25 @@ import { ResultList } from '../../shared/models' import { VideoCommentThreadTree } from '../../shared/models/videos/video-comment.model' import { VideoModel } from '../models/video/video' import { VideoCommentModel } from '../models/video/video-comment' -import { getVideoCommentActivityPubUrl } from './activitypub' +import { getVideoCommentActivityPubUrl, sendVideoRateChangeToFollowers } from './activitypub' +import { sendCreateVideoCommentToOrigin, sendCreateVideoCommentToVideoFollowers } from './activitypub/send' async function createVideoComment (obj: { text: string, - inReplyToCommentId: number, + inReplyToComment: VideoCommentModel, video: VideoModel accountId: number }, t: Sequelize.Transaction) { let originCommentId: number = null - if (obj.inReplyToCommentId) { - const repliedComment = await VideoCommentModel.loadById(obj.inReplyToCommentId) - if (!repliedComment) throw new Error('Unknown replied comment.') - - originCommentId = repliedComment.originCommentId || repliedComment.id + if (obj.inReplyToComment) { + originCommentId = obj.inReplyToComment.originCommentId || obj.inReplyToComment.id } const comment = await VideoCommentModel.create({ text: obj.text, originCommentId, - inReplyToCommentId: obj.inReplyToCommentId, + inReplyToCommentId: obj.inReplyToComment.id, videoId: obj.video.id, accountId: obj.accountId, url: 'fake url' @@ -31,7 +29,17 @@ async function createVideoComment (obj: { comment.set('url', getVideoCommentActivityPubUrl(obj.video, comment)) - return comment.save({ transaction: t }) + const savedComment = await comment.save({ transaction: t }) + savedComment.InReplyToVideoComment = obj.inReplyToComment + savedComment.Video = obj.video + + if (savedComment.Video.isOwned()) { + await sendCreateVideoCommentToVideoFollowers(savedComment, t) + } else { + await sendCreateVideoCommentToOrigin(savedComment, t) + } + + return savedComment } function buildFormattedCommentTree (resultList: ResultList): VideoCommentThreadTree { diff --git a/server/models/video/video-comment.ts b/server/models/video/video-comment.ts index 8e84bfc06..25cd6d563 100644 --- a/server/models/video/video-comment.ts +++ b/server/models/video/video-comment.ts @@ -3,6 +3,7 @@ import { AfterDestroy, AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, IFindOptions, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' +import { VideoCommentObject } from '../../../shared/models/activitypub/objects/video-comment-object' import { VideoComment } from '../../../shared/models/videos/video-comment.model' import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub' import { CONSTRAINTS_FIELDS } from '../../initializers' @@ -11,7 +12,8 @@ import { getSort, throwIfNotValid } from '../utils' import { VideoModel } from './video' enum ScopeNames { - WITH_ACCOUNT = 'WITH_ACCOUNT' + WITH_ACCOUNT = 'WITH_ACCOUNT', + WITH_IN_REPLY_TO = 'WITH_IN_REPLY_TO' } @Scopes({ @@ -19,6 +21,14 @@ enum ScopeNames { include: [ () => AccountModel ] + }, + [ScopeNames.WITH_IN_REPLY_TO]: { + include: [ + { + model: () => VideoCommentModel, + as: 'InReplyTo' + } + ] } }) @Table({ @@ -68,6 +78,7 @@ export class VideoCommentModel extends Model { foreignKey: { allowNull: true }, + as: 'InReplyTo', onDelete: 'CASCADE' }) InReplyToVideoComment: VideoCommentModel @@ -180,4 +191,23 @@ export class VideoCommentModel extends Model { } } as VideoComment } + + toActivityPubObject (): VideoCommentObject { + let inReplyTo: string + // New thread, so in AS we reply to the video + if (this.inReplyToCommentId === null) { + inReplyTo = this.Video.url + } else { + inReplyTo = this.InReplyToVideoComment.url + } + + return { + type: 'Note' as 'Note', + id: this.url, + content: this.text, + inReplyTo, + published: this.createdAt.toISOString(), + url: this.url + } + } }