mirror of https://github.com/Chocobozzz/PeerTube
Send comment to followers and parents
parent
57a49263e4
commit
93ef8a9d02
|
@ -1,6 +1,10 @@
|
|||
@import '_variables';
|
||||
@import '_mixins';
|
||||
|
||||
form {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.avatar-and-textarea {
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
|
|
|
@ -13,8 +13,9 @@ import { VideoModel } from '../../../models/video/video'
|
|||
import { VideoAbuseModel } from '../../../models/video/video-abuse'
|
||||
import { VideoCommentModel } from '../../../models/video/video-comment'
|
||||
import { VideoFileModel } from '../../../models/video/video-file'
|
||||
import { VideoShareModel } from '../../../models/video/video-share'
|
||||
import { getOrCreateActorAndServerAndModel } from '../actor'
|
||||
import { forwardActivity } from '../send/misc'
|
||||
import { forwardActivity, getActorsInvolvedInVideo } from '../send/misc'
|
||||
import { generateThumbnailFromUrl } from '../videos'
|
||||
import { addVideoComments, addVideoShares, videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc'
|
||||
|
||||
|
@ -266,18 +267,19 @@ 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 => {
|
||||
let video = await VideoModel.loadByUrl(comment.inReplyTo, t)
|
||||
let video = await VideoModel.loadByUrlAndPopulateAccount(comment.inReplyTo, t)
|
||||
let objectToCreate
|
||||
|
||||
// This is a new thread
|
||||
if (video) {
|
||||
await VideoCommentModel.create({
|
||||
objectToCreate = {
|
||||
url: comment.id,
|
||||
text: comment.content,
|
||||
originCommentId: null,
|
||||
inReplyToComment: null,
|
||||
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)
|
||||
|
@ -285,20 +287,34 @@ function createVideoComment (byActor: ActorModel, activity: ActivityCreate) {
|
|||
video = await VideoModel.load(inReplyToComment.videoId)
|
||||
|
||||
const originCommentId = inReplyToComment.originCommentId || inReplyToComment.id
|
||||
await VideoCommentModel.create({
|
||||
objectToCreate = {
|
||||
url: comment.id,
|
||||
text: comment.content,
|
||||
originCommentId,
|
||||
inReplyToCommentId: inReplyToComment.id,
|
||||
videoId: video.id,
|
||||
accountId: byAccount.id
|
||||
}, { transaction: t })
|
||||
}
|
||||
}
|
||||
|
||||
if (video.isOwned()) {
|
||||
const options = {
|
||||
where: {
|
||||
url: objectToCreate.url
|
||||
},
|
||||
defaults: objectToCreate,
|
||||
transaction: t
|
||||
}
|
||||
const [ ,created ] = await VideoCommentModel.findOrCreate(options)
|
||||
|
||||
if (video.isOwned() && created === true) {
|
||||
// Don't resend the activity to the sender
|
||||
const exceptions = [ byActor ]
|
||||
await forwardActivity(activity, t, exceptions)
|
||||
|
||||
// Mastodon does not add our announces in audience, so we forward to them manually
|
||||
const additionalActors = await getActorsInvolvedInVideo(video, t)
|
||||
const additionalFollowerUrls = additionalActors.map(a => a.followersUrl)
|
||||
|
||||
await forwardActivity(activity, t, exceptions, additionalFollowerUrls)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -12,12 +12,13 @@ import { activitypubHttpJobScheduler, ActivityPubHttpPayload } from '../../jobs/
|
|||
async function forwardActivity (
|
||||
activity: Activity,
|
||||
t: Transaction,
|
||||
followersException: ActorModel[] = []
|
||||
followersException: ActorModel[] = [],
|
||||
additionalFollowerUrls: string[] = []
|
||||
) {
|
||||
const to = activity.to || []
|
||||
const cc = activity.cc || []
|
||||
|
||||
const followersUrls: string[] = []
|
||||
const followersUrls = additionalFollowerUrls
|
||||
for (const dest of to.concat(cc)) {
|
||||
if (dest.endsWith('/followers')) {
|
||||
followersUrls.push(dest)
|
||||
|
@ -47,13 +48,25 @@ async function broadcastToFollowers (
|
|||
byActor: ActorModel,
|
||||
toActorFollowers: ActorModel[],
|
||||
t: Transaction,
|
||||
followersException: ActorModel[] = []
|
||||
actorsException: ActorModel[] = []
|
||||
) {
|
||||
const uris = await computeFollowerUris(toActorFollowers, followersException, t)
|
||||
if (uris.length === 0) {
|
||||
logger.info('0 followers for %s, no broadcasting.', toActorFollowers.map(a => a.id).join(', '))
|
||||
return undefined
|
||||
}
|
||||
const uris = await computeFollowerUris(toActorFollowers, actorsException, t)
|
||||
return broadcastTo(uris, data, byActor, t)
|
||||
}
|
||||
|
||||
async function broadcastToActors (
|
||||
data: any,
|
||||
byActor: ActorModel,
|
||||
toActors: ActorModel[],
|
||||
t: Transaction,
|
||||
actorsException: ActorModel[] = []
|
||||
) {
|
||||
const uris = await computeUris(toActors, actorsException)
|
||||
return broadcastTo(uris, data, byActor, t)
|
||||
}
|
||||
|
||||
async function broadcastTo (uris: string[], data: any, byActor: ActorModel, t: Transaction) {
|
||||
if (uris.length === 0) return undefined
|
||||
|
||||
logger.debug('Creating broadcast job.', { uris })
|
||||
|
||||
|
@ -149,12 +162,20 @@ function audiencify (object: any, audience: ActivityAudience) {
|
|||
return Object.assign(object, audience)
|
||||
}
|
||||
|
||||
async function computeFollowerUris (toActorFollower: ActorModel[], followersException: ActorModel[], t: Transaction) {
|
||||
async function computeFollowerUris (toActorFollower: ActorModel[], actorsException: ActorModel[], t: Transaction) {
|
||||
const toActorFollowerIds = toActorFollower.map(a => a.id)
|
||||
|
||||
const result = await ActorFollowModel.listAcceptedFollowerSharedInboxUrls(toActorFollowerIds, t)
|
||||
const followersSharedInboxException = followersException.map(f => f.sharedInboxUrl)
|
||||
return result.data.filter(sharedInbox => followersSharedInboxException.indexOf(sharedInbox) === -1)
|
||||
const sharedInboxesException = actorsException.map(f => f.sharedInboxUrl)
|
||||
return result.data.filter(sharedInbox => sharedInboxesException.indexOf(sharedInbox) === -1)
|
||||
}
|
||||
|
||||
async function computeUris (toActors: ActorModel[], actorsException: ActorModel[] = []) {
|
||||
const toActorSharedInboxesSet = new Set(toActors.map(a => a.sharedInboxUrl))
|
||||
|
||||
const sharedInboxesException = actorsException.map(f => f.sharedInboxUrl)
|
||||
return Array.from(toActorSharedInboxesSet)
|
||||
.filter(sharedInbox => sharedInboxesException.indexOf(sharedInbox) === -1)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
@ -168,5 +189,7 @@ export {
|
|||
getObjectFollowersAudience,
|
||||
forwardActivity,
|
||||
audiencify,
|
||||
getOriginVideoCommentAudience
|
||||
getOriginVideoCommentAudience,
|
||||
computeUris,
|
||||
broadcastToActors
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ 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,
|
||||
audiencify, broadcastToActors, broadcastToFollowers, getActorsInvolvedInVideo, getAudience, getObjectFollowersAudience,
|
||||
getOriginVideoAudience, getOriginVideoCommentAudience,
|
||||
unicastTo
|
||||
} from './misc'
|
||||
|
@ -39,11 +39,20 @@ async function sendCreateVideoCommentToOrigin (comment: VideoCommentModel, t: Tr
|
|||
const threadParentComments = await VideoCommentModel.listThreadParentComments(comment, t)
|
||||
const commentObject = comment.toActivityPubObject(threadParentComments)
|
||||
|
||||
const actorsInvolvedInVideo = await getActorsInvolvedInVideo(comment.Video, t)
|
||||
const audience = getOriginVideoCommentAudience(comment, threadParentComments, actorsInvolvedInVideo)
|
||||
const actorsInvolvedInComment = await getActorsInvolvedInVideo(comment.Video, t)
|
||||
actorsInvolvedInComment.push(byActor)
|
||||
const audience = getOriginVideoCommentAudience(comment, threadParentComments, actorsInvolvedInComment)
|
||||
|
||||
const data = await createActivityData(comment.url, byActor, commentObject, t, audience)
|
||||
|
||||
// This was a reply, send it to the parent actors
|
||||
const actorsException = [ byActor ]
|
||||
await broadcastToActors(data, byActor, threadParentComments.map(c => c.Account.Actor), t, actorsException)
|
||||
|
||||
// Broadcast to our followers
|
||||
await broadcastToFollowers(data, byActor, [ byActor ], t)
|
||||
|
||||
// Send to origin
|
||||
return unicastTo(data, byActor, comment.Video.VideoChannel.Account.Actor.sharedInboxUrl, t)
|
||||
}
|
||||
|
||||
|
@ -52,12 +61,21 @@ async function sendCreateVideoCommentToVideoFollowers (comment: VideoCommentMode
|
|||
const threadParentComments = await VideoCommentModel.listThreadParentComments(comment, t)
|
||||
const commentObject = comment.toActivityPubObject(threadParentComments)
|
||||
|
||||
const actorsInvolvedInVideo = await getActorsInvolvedInVideo(comment.Video, t)
|
||||
const audience = getOriginVideoCommentAudience(comment, threadParentComments, actorsInvolvedInVideo)
|
||||
const actorsInvolvedInComment = await getActorsInvolvedInVideo(comment.Video, t)
|
||||
actorsInvolvedInComment.push(byActor)
|
||||
|
||||
const audience = getOriginVideoCommentAudience(comment, threadParentComments, actorsInvolvedInComment)
|
||||
const data = await createActivityData(comment.url, byActor, commentObject, t, audience)
|
||||
|
||||
const followersException = [ byActor ]
|
||||
return broadcastToFollowers(data, byActor, actorsInvolvedInVideo, t, followersException)
|
||||
// This was a reply, send it to the parent actors
|
||||
const actorsException = [ byActor ]
|
||||
await broadcastToActors(data, byActor, threadParentComments.map(c => c.Account.Actor), t, actorsException)
|
||||
|
||||
// Broadcast to our followers
|
||||
await broadcastToFollowers(data, byActor, [ byActor ], t)
|
||||
|
||||
// Send to actors involved in the comment
|
||||
return broadcastToFollowers(data, byActor, actorsInvolvedInComment, t, actorsException)
|
||||
}
|
||||
|
||||
async function sendCreateViewToOrigin (byActor: ActorModel, video: VideoModel, t: Transaction) {
|
||||
|
@ -81,8 +99,8 @@ async function sendCreateViewToVideoFollowers (byActor: ActorModel, video: Video
|
|||
|
||||
// Use the server actor to send the view
|
||||
const serverActor = await getServerActor()
|
||||
const followersException = [ byActor ]
|
||||
return broadcastToFollowers(data, serverActor, actorsToForwardView, t, followersException)
|
||||
const actorsException = [ byActor ]
|
||||
return broadcastToFollowers(data, serverActor, actorsToForwardView, t, actorsException)
|
||||
}
|
||||
|
||||
async function sendCreateDislikeToOrigin (byActor: ActorModel, video: VideoModel, t: Transaction) {
|
||||
|
@ -104,8 +122,8 @@ async function sendCreateDislikeToVideoFollowers (byActor: ActorModel, video: Vi
|
|||
const audience = getObjectFollowersAudience(actorsToForwardView)
|
||||
const data = await createActivityData(url, byActor, dislikeActivityData, t, audience)
|
||||
|
||||
const followersException = [ byActor ]
|
||||
return broadcastToFollowers(data, byActor, actorsToForwardView, t, followersException)
|
||||
const actorsException = [ byActor ]
|
||||
return broadcastToFollowers(data, byActor, actorsToForwardView, t, actorsException)
|
||||
}
|
||||
|
||||
async function createActivityData (
|
||||
|
|
|
@ -1,26 +1,15 @@
|
|||
import * as Sequelize from 'sequelize'
|
||||
import {
|
||||
AfterDestroy,
|
||||
AllowNull,
|
||||
BelongsTo,
|
||||
Column,
|
||||
CreatedAt,
|
||||
DefaultScope,
|
||||
ForeignKey,
|
||||
HasMany,
|
||||
Is,
|
||||
Model,
|
||||
Table,
|
||||
AfterDestroy, AllowNull, BelongsTo, Column, CreatedAt, DefaultScope, ForeignKey, HasMany, Model, Table,
|
||||
UpdatedAt
|
||||
} from 'sequelize-typescript'
|
||||
import { Account } from '../../../shared/models/actors'
|
||||
import { isUserUsernameValid } from '../../helpers/custom-validators/users'
|
||||
import { sendDeleteActor } from '../../lib/activitypub/send'
|
||||
import { ActorModel } from '../activitypub/actor'
|
||||
import { ApplicationModel } from '../application/application'
|
||||
import { AvatarModel } from '../avatar/avatar'
|
||||
import { ServerModel } from '../server/server'
|
||||
import { getSort, throwIfNotValid } from '../utils'
|
||||
import { getSort } from '../utils'
|
||||
import { VideoChannelModel } from '../video/video-channel'
|
||||
import { UserModel } from './user'
|
||||
|
||||
|
|
Loading…
Reference in New Issue