]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - server/lib/activitypub/send/misc.ts
Improve AP validation for Notes
[github/Chocobozzz/PeerTube.git] / server / lib / activitypub / send / misc.ts
index 2dc8d3d5915a7bfde2dc37fffb71464ebd59fca4..b2d968c9cc4c6124651e44ed6d4c835c558f2baf 100644 (file)
@@ -1,22 +1,24 @@
 import { Transaction } from 'sequelize'
 import { Activity, ActivityAudience } from '../../../../shared/models/activitypub'
-import { logger } from '../../../helpers'
+import { logger } from '../../../helpers/logger'
 import { ACTIVITY_PUB } from '../../../initializers'
 import { ActorModel } from '../../../models/activitypub/actor'
 import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
 import { VideoModel } from '../../../models/video/video'
+import { VideoCommentModel } from '../../../models/video/video-comment'
 import { VideoShareModel } from '../../../models/video/video-share'
-import { activitypubHttpJobScheduler, ActivityPubHttpPayload } from '../../jobs/activitypub-http-job-scheduler'
+import { JobQueue } from '../../job-queue'
 
 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)
@@ -33,12 +35,11 @@ async function forwardActivity (
 
   logger.debug('Creating forwarding job.', { uris })
 
-  const jobPayload: ActivityPubHttpPayload = {
+  const payload = {
     uris,
     body: activity
   }
-
-  return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpBroadcastHandler', jobPayload)
+  return JobQueue.Instance.createJob({ type: 'activitypub-http-broadcast', payload })
 }
 
 async function broadcastToFollowers (
@@ -46,35 +47,46 @@ 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)
+}
+
+async function broadcastToActors (
+  data: any,
+  byActor: ActorModel,
+  toActors: ActorModel[],
+  actorsException: ActorModel[] = []
+) {
+  const uris = await computeUris(toActors, actorsException)
+  return broadcastTo(uris, data, byActor)
+}
+
+async function broadcastTo (uris: string[], data: any, byActor: ActorModel) {
+  if (uris.length === 0) return undefined
 
   logger.debug('Creating broadcast job.', { uris })
 
-  const jobPayload: ActivityPubHttpPayload = {
+  const payload = {
     uris,
     signatureActorId: byActor.id,
     body: data
   }
 
-  return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpBroadcastHandler', jobPayload)
+  return JobQueue.Instance.createJob({ type: 'activitypub-http-broadcast', payload })
 }
 
-async function unicastTo (data: any, byActor: ActorModel, toActorUrl: string, t: Transaction) {
+async function unicastTo (data: any, byActor: ActorModel, toActorUrl: string) {
   logger.debug('Creating unicast job.', { uri: toActorUrl })
 
-  const jobPayload: ActivityPubHttpPayload = {
-    uris: [ toActorUrl ],
+  const payload = {
+    uri: toActorUrl,
     signatureActorId: byActor.id,
     body: data
   }
 
-  return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpUnicastHandler', jobPayload)
+  return JobQueue.Instance.createJob({ type: 'activitypub-http-unicast', payload })
 }
 
 function getOriginVideoAudience (video: VideoModel, actorsInvolvedInVideo: ActorModel[]) {
@@ -84,23 +96,55 @@ function getOriginVideoAudience (video: VideoModel, actorsInvolvedInVideo: Actor
   }
 }
 
+function getVideoCommentAudience (
+  videoComment: VideoCommentModel,
+  threadParentComments: VideoCommentModel[],
+  actorsInvolvedInVideo: ActorModel[],
+  isOrigin = false
+) {
+  const to = [ ACTIVITY_PUB.PUBLIC ]
+  const cc = [ ]
+
+  // Owner of the video we comment
+  if (isOrigin === false) {
+    cc.push(videoComment.Video.VideoChannel.Account.Actor.url)
+  }
+
+  // Followers of the poster
+  cc.push(videoComment.Account.Actor.followersUrl)
+
+  // Send to actors we reply to
+  for (const parentComment of threadParentComments) {
+    cc.push(parentComment.Account.Actor.url)
+  }
+
+  return {
+    to,
+    cc: cc.concat(actorsInvolvedInVideo.map(a => a.followersUrl))
+  }
+}
+
 function getObjectFollowersAudience (actorsInvolvedInObject: ActorModel[]) {
   return {
-    to: actorsInvolvedInObject.map(a => a.followersUrl),
+    to: [ ACTIVITY_PUB.PUBLIC ].concat(actorsInvolvedInObject.map(a => a.followersUrl)),
     cc: []
   }
 }
 
 async function getActorsInvolvedInVideo (video: VideoModel, t: Transaction) {
-  const actorsToForwardView = await VideoShareModel.loadActorsByShare(video.id, t)
-  actorsToForwardView.push(video.VideoChannel.Account.Actor)
+  const actors = await VideoShareModel.loadActorsByShare(video.id, t)
+  actors.push(video.VideoChannel.Account.Actor)
 
-  return actorsToForwardView
+  return actors
 }
 
 async function getAudience (actorSender: ActorModel, t: Transaction, isPublic = true) {
   const followerInboxUrls = await actorSender.getFollowerSharedInboxUrls(t)
 
+  return buildAudience(followerInboxUrls, isPublic)
+}
+
+function buildAudience (followerInboxUrls: string[], isPublic = true) {
   // Thanks Mastodon: https://github.com/tootsuite/mastodon/blob/master/app/lib/activitypub/tag_manager.rb#L47
   let to = []
   let cc = []
@@ -109,23 +153,31 @@ async function getAudience (actorSender: ActorModel, t: Transaction, isPublic =
     to = [ ACTIVITY_PUB.PUBLIC ]
     cc = followerInboxUrls
   } else { // Unlisted
-    to = followerInboxUrls
-    cc = [ ACTIVITY_PUB.PUBLIC ]
+    to = [ ]
+    cc = [ ]
   }
 
   return { to, cc }
 }
 
-function audiencify (object: any, audience: ActivityAudience) {
+function audiencify <T> (object: T, 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 || f.inboxUrl)
+  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 || a.inboxUrl))
+
+  const sharedInboxesException = actorsException.map(f => f.sharedInboxUrl || f.inboxUrl)
+  return Array.from(toActorSharedInboxesSet)
+    .filter(sharedInbox => sharedInboxesException.indexOf(sharedInbox) === -1)
 }
 
 // ---------------------------------------------------------------------------
@@ -133,10 +185,14 @@ async function computeFollowerUris (toActorFollower: ActorModel[], followersExce
 export {
   broadcastToFollowers,
   unicastTo,
+  buildAudience,
   getAudience,
   getOriginVideoAudience,
   getActorsInvolvedInVideo,
   getObjectFollowersAudience,
   forwardActivity,
-  audiencify
+  audiencify,
+  getVideoCommentAudience,
+  computeUris,
+  broadcastToActors
 }