]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/lib/activitypub/send/utils.ts
Fix lint
[github/Chocobozzz/PeerTube.git] / server / lib / activitypub / send / utils.ts
1 import { Transaction } from 'sequelize'
2 import { Activity, ActivityAudience } from '../../../../shared/models/activitypub'
3 import { logger } from '../../../helpers/logger'
4 import { ActorModel } from '../../../models/activitypub/actor'
5 import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
6 import { JobQueue } from '../../job-queue'
7 import { getActorsInvolvedInVideo, getAudienceFromFollowersOf, getRemoteVideoAudience } from '../audience'
8 import { getServerActor } from '../../../helpers/utils'
9 import { afterCommitIfTransaction } from '../../../helpers/database-utils'
10 import { MActorWithInboxes, MActor, MActorId, MActorLight, MVideo, MVideoAccountLight } from '../../../typings/models'
11 import { ContextType } from '@server/helpers/activitypub'
12
13 async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAudience) => Activity, options: {
14 byActor: MActorLight
15 video: MVideoAccountLight
16 transaction?: Transaction
17 contextType?: ContextType
18 }) {
19 const { byActor, video, transaction, contextType } = options
20
21 const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, transaction)
22
23 // Send to origin
24 if (video.isOwned() === false) {
25 const audience = getRemoteVideoAudience(video, actorsInvolvedInVideo)
26 const activity = activityBuilder(audience)
27
28 return afterCommitIfTransaction(transaction, () => {
29 return unicastTo(activity, byActor, video.VideoChannel.Account.Actor.getSharedInbox(), contextType)
30 })
31 }
32
33 // Send to followers
34 const audience = getAudienceFromFollowersOf(actorsInvolvedInVideo)
35 const activity = activityBuilder(audience)
36
37 const actorsException = [ byActor ]
38
39 return broadcastToFollowers(activity, byActor, actorsInvolvedInVideo, transaction, actorsException, contextType)
40 }
41
42 async function forwardVideoRelatedActivity (
43 activity: Activity,
44 t: Transaction,
45 followersException: MActorWithInboxes[] = [],
46 video: MVideo
47 ) {
48 // Mastodon does not add our announces in audience, so we forward to them manually
49 const additionalActors = await getActorsInvolvedInVideo(video, t)
50 const additionalFollowerUrls = additionalActors.map(a => a.followersUrl)
51
52 return forwardActivity(activity, t, followersException, additionalFollowerUrls)
53 }
54
55 async function forwardActivity (
56 activity: Activity,
57 t: Transaction,
58 followersException: MActorWithInboxes[] = [],
59 additionalFollowerUrls: string[] = []
60 ) {
61 logger.info('Forwarding activity %s.', activity.id)
62
63 const to = activity.to || []
64 const cc = activity.cc || []
65
66 const followersUrls = additionalFollowerUrls
67 for (const dest of to.concat(cc)) {
68 if (dest.endsWith('/followers')) {
69 followersUrls.push(dest)
70 }
71 }
72
73 const toActorFollowers = await ActorModel.listByFollowersUrls(followersUrls, t)
74 const uris = await computeFollowerUris(toActorFollowers, followersException, t)
75
76 if (uris.length === 0) {
77 logger.info('0 followers for %s, no forwarding.', toActorFollowers.map(a => a.id).join(', '))
78 return undefined
79 }
80
81 logger.debug('Creating forwarding job.', { uris })
82
83 const payload = {
84 uris,
85 body: activity
86 }
87 return afterCommitIfTransaction(t, () => JobQueue.Instance.createJob({ type: 'activitypub-http-broadcast', payload }))
88 }
89
90 async function broadcastToFollowers (
91 data: any,
92 byActor: MActorId,
93 toFollowersOf: MActorId[],
94 t: Transaction,
95 actorsException: MActorWithInboxes[] = [],
96 contextType?: ContextType
97 ) {
98 const uris = await computeFollowerUris(toFollowersOf, actorsException, t)
99
100 return afterCommitIfTransaction(t, () => broadcastTo(uris, data, byActor, contextType))
101 }
102
103 async function broadcastToActors (
104 data: any,
105 byActor: MActorId,
106 toActors: MActor[],
107 t?: Transaction,
108 actorsException: MActorWithInboxes[] = [],
109 contextType?: ContextType
110 ) {
111 const uris = await computeUris(toActors, actorsException)
112 return afterCommitIfTransaction(t, () => broadcastTo(uris, data, byActor, contextType))
113 }
114
115 function broadcastTo (uris: string[], data: any, byActor: MActorId, contextType?: ContextType) {
116 if (uris.length === 0) return undefined
117
118 logger.debug('Creating broadcast job.', { uris })
119
120 const payload = {
121 uris,
122 signatureActorId: byActor.id,
123 body: data,
124 contextType
125 }
126
127 return JobQueue.Instance.createJob({ type: 'activitypub-http-broadcast', payload })
128 }
129
130 function unicastTo (data: any, byActor: MActorId, toActorUrl: string, contextType?: ContextType) {
131 logger.debug('Creating unicast job.', { uri: toActorUrl })
132
133 const payload = {
134 uri: toActorUrl,
135 signatureActorId: byActor.id,
136 body: data,
137 contextType
138 }
139
140 JobQueue.Instance.createJob({ type: 'activitypub-http-unicast', payload })
141 }
142
143 // ---------------------------------------------------------------------------
144
145 export {
146 broadcastToFollowers,
147 unicastTo,
148 forwardActivity,
149 broadcastToActors,
150 forwardVideoRelatedActivity,
151 sendVideoRelatedActivity
152 }
153
154 // ---------------------------------------------------------------------------
155
156 async function computeFollowerUris (toFollowersOf: MActorId[], actorsException: MActorWithInboxes[], t: Transaction) {
157 const toActorFollowerIds = toFollowersOf.map(a => a.id)
158
159 const result = await ActorFollowModel.listAcceptedFollowerSharedInboxUrls(toActorFollowerIds, t)
160 const sharedInboxesException = await buildSharedInboxesException(actorsException)
161
162 return result.data.filter(sharedInbox => sharedInboxesException.indexOf(sharedInbox) === -1)
163 }
164
165 async function computeUris (toActors: MActor[], actorsException: MActorWithInboxes[] = []) {
166 const serverActor = await getServerActor()
167 const targetUrls = toActors
168 .filter(a => a.id !== serverActor.id) // Don't send to ourselves
169 .map(a => a.getSharedInbox())
170
171 const toActorSharedInboxesSet = new Set(targetUrls)
172
173 const sharedInboxesException = await buildSharedInboxesException(actorsException)
174 return Array.from(toActorSharedInboxesSet)
175 .filter(sharedInbox => sharedInboxesException.indexOf(sharedInbox) === -1)
176 }
177
178 async function buildSharedInboxesException (actorsException: MActorWithInboxes[]) {
179 const serverActor = await getServerActor()
180
181 return actorsException
182 .map(f => f.getSharedInbox())
183 .concat([ serverActor.sharedInboxUrl ])
184 }