1 import { Transaction } from 'sequelize'
2 import { logger } from '../../../helpers/logger'
3 import { ACTIVITY_PUB, database as db } from '../../../initializers'
4 import { AccountInstance } from '../../../models/account/account-interface'
6 activitypubHttpJobScheduler,
8 } from '../../jobs/activitypub-http-job-scheduler/activitypub-http-job-scheduler'
9 import { VideoInstance } from '../../../models/video/video-interface'
10 import { Activity } from '../../../../shared/models/activitypub/activity'
12 async function forwardActivity (
15 followersException: AccountInstance[] = []
17 const to = activity.to || []
18 const cc = activity.cc || []
20 const followersUrls: string[] = []
21 for (const dest of to.concat(cc)) {
22 if (dest.endsWith('/followers')) {
23 followersUrls.push(dest)
27 const toAccountFollowers = await db.Account.listByFollowersUrls(followersUrls)
28 const uris = await computeFollowerUris(toAccountFollowers, followersException)
30 if (uris.length === 0) {
31 logger.info('0 followers for %s, no forwarding.', toAccountFollowers.map(a => a.id).join(', '))
35 logger.debug('Creating forwarding job.', { uris })
37 const jobPayload: ActivityPubHttpPayload = {
42 return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpBroadcastHandler', jobPayload)
45 async function broadcastToFollowers (
47 byAccount: AccountInstance,
48 toAccountFollowers: AccountInstance[],
50 followersException: AccountInstance[] = []
52 const uris = await computeFollowerUris(toAccountFollowers, followersException)
53 if (uris.length === 0) {
54 logger.info('0 followers for %s, no broadcasting.', toAccountFollowers.map(a => a.id).join(', '))
58 logger.debug('Creating broadcast job.', { uris })
60 const jobPayload: ActivityPubHttpPayload = {
62 signatureAccountId: byAccount.id,
66 return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpBroadcastHandler', jobPayload)
69 async function unicastTo (data: any, byAccount: AccountInstance, toAccountUrl: string, t: Transaction) {
70 logger.debug('Creating unicast job.', { uri: toAccountUrl })
72 const jobPayload: ActivityPubHttpPayload = {
73 uris: [ toAccountUrl ],
74 signatureAccountId: byAccount.id,
78 return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpUnicastHandler', jobPayload)
81 function getOriginVideoAudience (video: VideoInstance, accountsInvolvedInVideo: AccountInstance[]) {
83 to: [ video.VideoChannel.Account.url ],
84 cc: accountsInvolvedInVideo.map(a => a.followersUrl)
88 function getVideoFollowersAudience (accountsInvolvedInVideo: AccountInstance[]) {
90 to: accountsInvolvedInVideo.map(a => a.followersUrl),
95 async function getAccountsInvolvedInVideo (video: VideoInstance) {
96 const accountsToForwardView = await db.VideoShare.loadAccountsByShare(video.id)
97 accountsToForwardView.push(video.VideoChannel.Account)
99 return accountsToForwardView
102 async function getAudience (accountSender: AccountInstance, isPublic = true) {
103 const followerInboxUrls = await accountSender.getFollowerSharedInboxUrls()
105 // Thanks Mastodon: https://github.com/tootsuite/mastodon/blob/master/app/lib/activitypub/tag_manager.rb#L47
110 to = [ ACTIVITY_PUB.PUBLIC ]
111 cc = followerInboxUrls
113 to = followerInboxUrls
114 cc = [ ACTIVITY_PUB.PUBLIC ]
120 async function computeFollowerUris (toAccountFollower: AccountInstance[], followersException: AccountInstance[]) {
121 const toAccountFollowerIds = toAccountFollower.map(a => a.id)
123 const result = await db.AccountFollow.listAcceptedFollowerSharedInboxUrls(toAccountFollowerIds)
124 const followersSharedInboxException = followersException.map(f => f.sharedInboxUrl)
125 const uris = result.data.filter(sharedInbox => followersSharedInboxException.indexOf(sharedInbox) === -1)
130 // ---------------------------------------------------------------------------
133 broadcastToFollowers,
136 getOriginVideoAudience,
137 getAccountsInvolvedInVideo,
138 getVideoFollowersAudience,