]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/lib/activitypub/send/misc.ts
Put activity pub sends inside transactions
[github/Chocobozzz/PeerTube.git] / server / lib / activitypub / send / misc.ts
1 import { Transaction } from 'sequelize'
2 import { Activity } from '../../../../shared/models/activitypub/activity'
3 import { logger } from '../../../helpers/logger'
4 import { ACTIVITY_PUB, database as db } from '../../../initializers'
5 import { AccountInstance } from '../../../models/account/account-interface'
6 import { VideoChannelInstance } from '../../../models/index'
7 import { VideoInstance } from '../../../models/video/video-interface'
8 import {
9 activitypubHttpJobScheduler,
10 ActivityPubHttpPayload
11 } from '../../jobs/activitypub-http-job-scheduler/activitypub-http-job-scheduler'
12
13 async function forwardActivity (
14 activity: Activity,
15 t: Transaction,
16 followersException: AccountInstance[] = []
17 ) {
18 const to = activity.to || []
19 const cc = activity.cc || []
20
21 const followersUrls: string[] = []
22 for (const dest of to.concat(cc)) {
23 if (dest.endsWith('/followers')) {
24 followersUrls.push(dest)
25 }
26 }
27
28 const toAccountFollowers = await db.Account.listByFollowersUrls(followersUrls, t)
29 const uris = await computeFollowerUris(toAccountFollowers, followersException, t)
30
31 if (uris.length === 0) {
32 logger.info('0 followers for %s, no forwarding.', toAccountFollowers.map(a => a.id).join(', '))
33 return undefined
34 }
35
36 logger.debug('Creating forwarding job.', { uris })
37
38 const jobPayload: ActivityPubHttpPayload = {
39 uris,
40 body: activity
41 }
42
43 return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpBroadcastHandler', jobPayload)
44 }
45
46 async function broadcastToFollowers (
47 data: any,
48 byAccount: AccountInstance,
49 toAccountFollowers: AccountInstance[],
50 t: Transaction,
51 followersException: AccountInstance[] = []
52 ) {
53 const uris = await computeFollowerUris(toAccountFollowers, followersException, t)
54 if (uris.length === 0) {
55 logger.info('0 followers for %s, no broadcasting.', toAccountFollowers.map(a => a.id).join(', '))
56 return undefined
57 }
58
59 logger.debug('Creating broadcast job.', { uris })
60
61 const jobPayload: ActivityPubHttpPayload = {
62 uris,
63 signatureAccountId: byAccount.id,
64 body: data
65 }
66
67 return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpBroadcastHandler', jobPayload)
68 }
69
70 async function unicastTo (data: any, byAccount: AccountInstance, toAccountUrl: string, t: Transaction) {
71 logger.debug('Creating unicast job.', { uri: toAccountUrl })
72
73 const jobPayload: ActivityPubHttpPayload = {
74 uris: [ toAccountUrl ],
75 signatureAccountId: byAccount.id,
76 body: data
77 }
78
79 return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpUnicastHandler', jobPayload)
80 }
81
82 function getOriginVideoAudience (video: VideoInstance, accountsInvolvedInVideo: AccountInstance[]) {
83 return {
84 to: [ video.VideoChannel.Account.url ],
85 cc: accountsInvolvedInVideo.map(a => a.followersUrl)
86 }
87 }
88
89 function getOriginVideoChannelAudience (videoChannel: VideoChannelInstance, accountsInvolved: AccountInstance[]) {
90 return {
91 to: [ videoChannel.Account.url ],
92 cc: accountsInvolved.map(a => a.followersUrl)
93 }
94 }
95
96 function getObjectFollowersAudience (accountsInvolvedInObject: AccountInstance[]) {
97 return {
98 to: accountsInvolvedInObject.map(a => a.followersUrl),
99 cc: []
100 }
101 }
102
103 async function getAccountsInvolvedInVideo (video: VideoInstance, t: Transaction) {
104 const accountsToForwardView = await db.VideoShare.loadAccountsByShare(video.id, t)
105 accountsToForwardView.push(video.VideoChannel.Account)
106
107 return accountsToForwardView
108 }
109
110 async function getAccountsInvolvedInVideoChannel (videoChannel: VideoChannelInstance, t: Transaction) {
111 const accountsToForwardView = await db.VideoChannelShare.loadAccountsByShare(videoChannel.id, t)
112 accountsToForwardView.push(videoChannel.Account)
113
114 return accountsToForwardView
115 }
116
117 async function getAudience (accountSender: AccountInstance, t: Transaction, isPublic = true) {
118 const followerInboxUrls = await accountSender.getFollowerSharedInboxUrls(t)
119
120 // Thanks Mastodon: https://github.com/tootsuite/mastodon/blob/master/app/lib/activitypub/tag_manager.rb#L47
121 let to = []
122 let cc = []
123
124 if (isPublic) {
125 to = [ ACTIVITY_PUB.PUBLIC ]
126 cc = followerInboxUrls
127 } else { // Unlisted
128 to = followerInboxUrls
129 cc = [ ACTIVITY_PUB.PUBLIC ]
130 }
131
132 return { to, cc }
133 }
134
135 async function computeFollowerUris (toAccountFollower: AccountInstance[], followersException: AccountInstance[], t: Transaction) {
136 const toAccountFollowerIds = toAccountFollower.map(a => a.id)
137
138 const result = await db.AccountFollow.listAcceptedFollowerSharedInboxUrls(toAccountFollowerIds, t)
139 const followersSharedInboxException = followersException.map(f => f.sharedInboxUrl)
140 const uris = result.data.filter(sharedInbox => followersSharedInboxException.indexOf(sharedInbox) === -1)
141
142 return uris
143 }
144
145 // ---------------------------------------------------------------------------
146
147 export {
148 broadcastToFollowers,
149 getOriginVideoChannelAudience,
150 unicastTo,
151 getAudience,
152 getOriginVideoAudience,
153 getAccountsInvolvedInVideo,
154 getAccountsInvolvedInVideoChannel,
155 getObjectFollowersAudience,
156 forwardActivity
157 }