diff options
Diffstat (limited to 'server/lib/notifier.ts')
-rw-r--r-- | server/lib/notifier.ts | 796 |
1 files changed, 0 insertions, 796 deletions
diff --git a/server/lib/notifier.ts b/server/lib/notifier.ts deleted file mode 100644 index 1f9ff16df..000000000 --- a/server/lib/notifier.ts +++ /dev/null | |||
@@ -1,796 +0,0 @@ | |||
1 | import { AccountModel } from '@server/models/account/account' | ||
2 | import { getServerActor } from '@server/models/application/application' | ||
3 | import { ServerBlocklistModel } from '@server/models/server/server-blocklist' | ||
4 | import { | ||
5 | MUser, | ||
6 | MUserAccount, | ||
7 | MUserDefault, | ||
8 | MUserNotifSettingAccount, | ||
9 | MUserWithNotificationSetting, | ||
10 | UserNotificationModelForApi | ||
11 | } from '@server/types/models/user' | ||
12 | import { MVideoBlacklistLightVideo, MVideoBlacklistVideo } from '@server/types/models/video/video-blacklist' | ||
13 | import { MVideoImportVideo } from '@server/types/models/video/video-import' | ||
14 | import { UserAbuse } from '@shared/models' | ||
15 | import { UserNotificationSettingValue, UserNotificationType, UserRight } from '../../shared/models/users' | ||
16 | import { VideoPrivacy, VideoState } from '../../shared/models/videos' | ||
17 | import { logger } from '../helpers/logger' | ||
18 | import { CONFIG } from '../initializers/config' | ||
19 | import { AccountBlocklistModel } from '../models/account/account-blocklist' | ||
20 | import { UserModel } from '../models/user/user' | ||
21 | import { UserNotificationModel } from '../models/user/user-notification' | ||
22 | import { MAbuseFull, MAbuseMessage, MAccountServer, MActorFollowFull, MApplication, MPlugin } from '../types/models' | ||
23 | import { MCommentOwnerVideo, MVideoAccountLight, MVideoFullLight } from '../types/models/video' | ||
24 | import { isBlockedByServerOrAccount } from './blocklist' | ||
25 | import { Emailer } from './emailer' | ||
26 | import { PeerTubeSocket } from './peertube-socket' | ||
27 | |||
28 | class Notifier { | ||
29 | |||
30 | private static instance: Notifier | ||
31 | |||
32 | private constructor () { | ||
33 | } | ||
34 | |||
35 | notifyOnNewVideoIfNeeded (video: MVideoAccountLight): void { | ||
36 | // Only notify on public and published videos which are not blacklisted | ||
37 | if (video.privacy !== VideoPrivacy.PUBLIC || video.state !== VideoState.PUBLISHED || video.isBlacklisted()) return | ||
38 | |||
39 | this.notifySubscribersOfNewVideo(video) | ||
40 | .catch(err => logger.error('Cannot notify subscribers of new video %s.', video.url, { err })) | ||
41 | } | ||
42 | |||
43 | notifyOnVideoPublishedAfterTranscoding (video: MVideoFullLight): void { | ||
44 | // don't notify if didn't wait for transcoding or video is still blacklisted/waiting for scheduled update | ||
45 | if (!video.waitTranscoding || video.VideoBlacklist || video.ScheduleVideoUpdate) return | ||
46 | |||
47 | this.notifyOwnedVideoHasBeenPublished(video) | ||
48 | .catch(err => logger.error('Cannot notify owner that its video %s has been published after transcoding.', video.url, { err })) | ||
49 | } | ||
50 | |||
51 | notifyOnVideoPublishedAfterScheduledUpdate (video: MVideoFullLight): void { | ||
52 | // don't notify if video is still blacklisted or waiting for transcoding | ||
53 | if (video.VideoBlacklist || (video.waitTranscoding && video.state !== VideoState.PUBLISHED)) return | ||
54 | |||
55 | this.notifyOwnedVideoHasBeenPublished(video) | ||
56 | .catch(err => logger.error('Cannot notify owner that its video %s has been published after scheduled update.', video.url, { err })) | ||
57 | } | ||
58 | |||
59 | notifyOnVideoPublishedAfterRemovedFromAutoBlacklist (video: MVideoFullLight): void { | ||
60 | // don't notify if video is still waiting for transcoding or scheduled update | ||
61 | if (video.ScheduleVideoUpdate || (video.waitTranscoding && video.state !== VideoState.PUBLISHED)) return | ||
62 | |||
63 | this.notifyOwnedVideoHasBeenPublished(video) | ||
64 | .catch(err => { | ||
65 | logger.error('Cannot notify owner that its video %s has been published after removed from auto-blacklist.', video.url, { err }) | ||
66 | }) | ||
67 | } | ||
68 | |||
69 | notifyOnNewComment (comment: MCommentOwnerVideo): void { | ||
70 | this.notifyVideoOwnerOfNewComment(comment) | ||
71 | .catch(err => logger.error('Cannot notify video owner of new comment %s.', comment.url, { err })) | ||
72 | |||
73 | this.notifyOfCommentMention(comment) | ||
74 | .catch(err => logger.error('Cannot notify mentions of comment %s.', comment.url, { err })) | ||
75 | } | ||
76 | |||
77 | notifyOnNewAbuse (parameters: { abuse: UserAbuse, abuseInstance: MAbuseFull, reporter: string }): void { | ||
78 | this.notifyModeratorsOfNewAbuse(parameters) | ||
79 | .catch(err => logger.error('Cannot notify of new abuse %d.', parameters.abuseInstance.id, { err })) | ||
80 | } | ||
81 | |||
82 | notifyOnVideoAutoBlacklist (videoBlacklist: MVideoBlacklistLightVideo): void { | ||
83 | this.notifyModeratorsOfVideoAutoBlacklist(videoBlacklist) | ||
84 | .catch(err => logger.error('Cannot notify of auto-blacklist of video %s.', videoBlacklist.Video.url, { err })) | ||
85 | } | ||
86 | |||
87 | notifyOnVideoBlacklist (videoBlacklist: MVideoBlacklistVideo): void { | ||
88 | this.notifyVideoOwnerOfBlacklist(videoBlacklist) | ||
89 | .catch(err => logger.error('Cannot notify video owner of new video blacklist of %s.', videoBlacklist.Video.url, { err })) | ||
90 | } | ||
91 | |||
92 | notifyOnVideoUnblacklist (video: MVideoFullLight): void { | ||
93 | this.notifyVideoOwnerOfUnblacklist(video) | ||
94 | .catch(err => logger.error('Cannot notify video owner of unblacklist of %s.', video.url, { err })) | ||
95 | } | ||
96 | |||
97 | notifyOnFinishedVideoImport (videoImport: MVideoImportVideo, success: boolean): void { | ||
98 | this.notifyOwnerVideoImportIsFinished(videoImport, success) | ||
99 | .catch(err => logger.error('Cannot notify owner that its video import %s is finished.', videoImport.getTargetIdentifier(), { err })) | ||
100 | } | ||
101 | |||
102 | notifyOnNewUserRegistration (user: MUserDefault): void { | ||
103 | this.notifyModeratorsOfNewUserRegistration(user) | ||
104 | .catch(err => logger.error('Cannot notify moderators of new user registration (%s).', user.username, { err })) | ||
105 | } | ||
106 | |||
107 | notifyOfNewUserFollow (actorFollow: MActorFollowFull): void { | ||
108 | this.notifyUserOfNewActorFollow(actorFollow) | ||
109 | .catch(err => { | ||
110 | logger.error( | ||
111 | 'Cannot notify owner of channel %s of a new follow by %s.', | ||
112 | actorFollow.ActorFollowing.VideoChannel.getDisplayName(), | ||
113 | actorFollow.ActorFollower.Account.getDisplayName(), | ||
114 | { err } | ||
115 | ) | ||
116 | }) | ||
117 | } | ||
118 | |||
119 | notifyOfNewInstanceFollow (actorFollow: MActorFollowFull): void { | ||
120 | this.notifyAdminsOfNewInstanceFollow(actorFollow) | ||
121 | .catch(err => { | ||
122 | logger.error('Cannot notify administrators of new follower %s.', actorFollow.ActorFollower.url, { err }) | ||
123 | }) | ||
124 | } | ||
125 | |||
126 | notifyOfAutoInstanceFollowing (actorFollow: MActorFollowFull): void { | ||
127 | this.notifyAdminsOfAutoInstanceFollowing(actorFollow) | ||
128 | .catch(err => { | ||
129 | logger.error('Cannot notify administrators of auto instance following %s.', actorFollow.ActorFollowing.url, { err }) | ||
130 | }) | ||
131 | } | ||
132 | |||
133 | notifyOnAbuseStateChange (abuse: MAbuseFull): void { | ||
134 | this.notifyReporterOfAbuseStateChange(abuse) | ||
135 | .catch(err => { | ||
136 | logger.error('Cannot notify reporter of abuse %d state change.', abuse.id, { err }) | ||
137 | }) | ||
138 | } | ||
139 | |||
140 | notifyOnAbuseMessage (abuse: MAbuseFull, message: MAbuseMessage): void { | ||
141 | this.notifyOfNewAbuseMessage(abuse, message) | ||
142 | .catch(err => { | ||
143 | logger.error('Cannot notify on new abuse %d message.', abuse.id, { err }) | ||
144 | }) | ||
145 | } | ||
146 | |||
147 | notifyOfNewPeerTubeVersion (application: MApplication, latestVersion: string) { | ||
148 | this.notifyAdminsOfNewPeerTubeVersion(application, latestVersion) | ||
149 | .catch(err => { | ||
150 | logger.error('Cannot notify on new PeerTubeb version %s.', latestVersion, { err }) | ||
151 | }) | ||
152 | } | ||
153 | |||
154 | notifyOfNewPluginVersion (plugin: MPlugin) { | ||
155 | this.notifyAdminsOfNewPluginVersion(plugin) | ||
156 | .catch(err => { | ||
157 | logger.error('Cannot notify on new plugin version %s.', plugin.name, { err }) | ||
158 | }) | ||
159 | } | ||
160 | |||
161 | private async notifySubscribersOfNewVideo (video: MVideoAccountLight) { | ||
162 | // List all followers that are users | ||
163 | const users = await UserModel.listUserSubscribersOf(video.VideoChannel.actorId) | ||
164 | |||
165 | logger.info('Notifying %d users of new video %s.', users.length, video.url) | ||
166 | |||
167 | function settingGetter (user: MUserWithNotificationSetting) { | ||
168 | return user.NotificationSetting.newVideoFromSubscription | ||
169 | } | ||
170 | |||
171 | async function notificationCreator (user: MUserWithNotificationSetting) { | ||
172 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ | ||
173 | type: UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION, | ||
174 | userId: user.id, | ||
175 | videoId: video.id | ||
176 | }) | ||
177 | notification.Video = video | ||
178 | |||
179 | return notification | ||
180 | } | ||
181 | |||
182 | function emailSender (emails: string[]) { | ||
183 | return Emailer.Instance.addNewVideoFromSubscriberNotification(emails, video) | ||
184 | } | ||
185 | |||
186 | return this.notify({ users, settingGetter, notificationCreator, emailSender }) | ||
187 | } | ||
188 | |||
189 | private async notifyVideoOwnerOfNewComment (comment: MCommentOwnerVideo) { | ||
190 | if (comment.Video.isOwned() === false) return | ||
191 | |||
192 | const user = await UserModel.loadByVideoId(comment.videoId) | ||
193 | |||
194 | // Not our user or user comments its own video | ||
195 | if (!user || comment.Account.userId === user.id) return | ||
196 | |||
197 | if (await this.isBlockedByServerOrUser(comment.Account, user)) return | ||
198 | |||
199 | logger.info('Notifying user %s of new comment %s.', user.username, comment.url) | ||
200 | |||
201 | function settingGetter (user: MUserWithNotificationSetting) { | ||
202 | return user.NotificationSetting.newCommentOnMyVideo | ||
203 | } | ||
204 | |||
205 | async function notificationCreator (user: MUserWithNotificationSetting) { | ||
206 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ | ||
207 | type: UserNotificationType.NEW_COMMENT_ON_MY_VIDEO, | ||
208 | userId: user.id, | ||
209 | commentId: comment.id | ||
210 | }) | ||
211 | notification.Comment = comment | ||
212 | |||
213 | return notification | ||
214 | } | ||
215 | |||
216 | function emailSender (emails: string[]) { | ||
217 | return Emailer.Instance.addNewCommentOnMyVideoNotification(emails, comment) | ||
218 | } | ||
219 | |||
220 | return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) | ||
221 | } | ||
222 | |||
223 | private async notifyOfCommentMention (comment: MCommentOwnerVideo) { | ||
224 | const extractedUsernames = comment.extractMentions() | ||
225 | logger.debug( | ||
226 | 'Extracted %d username from comment %s.', extractedUsernames.length, comment.url, | ||
227 | { usernames: extractedUsernames, text: comment.text } | ||
228 | ) | ||
229 | |||
230 | let users = await UserModel.listByUsernames(extractedUsernames) | ||
231 | |||
232 | if (comment.Video.isOwned()) { | ||
233 | const userException = await UserModel.loadByVideoId(comment.videoId) | ||
234 | users = users.filter(u => u.id !== userException.id) | ||
235 | } | ||
236 | |||
237 | // Don't notify if I mentioned myself | ||
238 | users = users.filter(u => u.Account.id !== comment.accountId) | ||
239 | |||
240 | if (users.length === 0) return | ||
241 | |||
242 | const serverAccountId = (await getServerActor()).Account.id | ||
243 | const sourceAccounts = users.map(u => u.Account.id).concat([ serverAccountId ]) | ||
244 | |||
245 | const accountMutedHash = await AccountBlocklistModel.isAccountMutedByMulti(sourceAccounts, comment.accountId) | ||
246 | const instanceMutedHash = await ServerBlocklistModel.isServerMutedByMulti(sourceAccounts, comment.Account.Actor.serverId) | ||
247 | |||
248 | logger.info('Notifying %d users of new comment %s.', users.length, comment.url) | ||
249 | |||
250 | function settingGetter (user: MUserNotifSettingAccount) { | ||
251 | const accountId = user.Account.id | ||
252 | if ( | ||
253 | accountMutedHash[accountId] === true || instanceMutedHash[accountId] === true || | ||
254 | accountMutedHash[serverAccountId] === true || instanceMutedHash[serverAccountId] === true | ||
255 | ) { | ||
256 | return UserNotificationSettingValue.NONE | ||
257 | } | ||
258 | |||
259 | return user.NotificationSetting.commentMention | ||
260 | } | ||
261 | |||
262 | async function notificationCreator (user: MUserNotifSettingAccount) { | ||
263 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ | ||
264 | type: UserNotificationType.COMMENT_MENTION, | ||
265 | userId: user.id, | ||
266 | commentId: comment.id | ||
267 | }) | ||
268 | notification.Comment = comment | ||
269 | |||
270 | return notification | ||
271 | } | ||
272 | |||
273 | function emailSender (emails: string[]) { | ||
274 | return Emailer.Instance.addNewCommentMentionNotification(emails, comment) | ||
275 | } | ||
276 | |||
277 | return this.notify({ users, settingGetter, notificationCreator, emailSender }) | ||
278 | } | ||
279 | |||
280 | private async notifyUserOfNewActorFollow (actorFollow: MActorFollowFull) { | ||
281 | if (actorFollow.ActorFollowing.isOwned() === false) return | ||
282 | |||
283 | // Account follows one of our account? | ||
284 | let followType: 'account' | 'channel' = 'channel' | ||
285 | let user = await UserModel.loadByChannelActorId(actorFollow.ActorFollowing.id) | ||
286 | |||
287 | // Account follows one of our channel? | ||
288 | if (!user) { | ||
289 | user = await UserModel.loadByAccountActorId(actorFollow.ActorFollowing.id) | ||
290 | followType = 'account' | ||
291 | } | ||
292 | |||
293 | if (!user) return | ||
294 | |||
295 | const followerAccount = actorFollow.ActorFollower.Account | ||
296 | const followerAccountWithActor = Object.assign(followerAccount, { Actor: actorFollow.ActorFollower }) | ||
297 | |||
298 | if (await this.isBlockedByServerOrUser(followerAccountWithActor, user)) return | ||
299 | |||
300 | logger.info('Notifying user %s of new follower: %s.', user.username, followerAccount.getDisplayName()) | ||
301 | |||
302 | function settingGetter (user: MUserWithNotificationSetting) { | ||
303 | return user.NotificationSetting.newFollow | ||
304 | } | ||
305 | |||
306 | async function notificationCreator (user: MUserWithNotificationSetting) { | ||
307 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ | ||
308 | type: UserNotificationType.NEW_FOLLOW, | ||
309 | userId: user.id, | ||
310 | actorFollowId: actorFollow.id | ||
311 | }) | ||
312 | notification.ActorFollow = actorFollow | ||
313 | |||
314 | return notification | ||
315 | } | ||
316 | |||
317 | function emailSender (emails: string[]) { | ||
318 | return Emailer.Instance.addNewFollowNotification(emails, actorFollow, followType) | ||
319 | } | ||
320 | |||
321 | return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) | ||
322 | } | ||
323 | |||
324 | private async notifyAdminsOfNewInstanceFollow (actorFollow: MActorFollowFull) { | ||
325 | const admins = await UserModel.listWithRight(UserRight.MANAGE_SERVER_FOLLOW) | ||
326 | |||
327 | const follower = Object.assign(actorFollow.ActorFollower.Account, { Actor: actorFollow.ActorFollower }) | ||
328 | if (await this.isBlockedByServerOrUser(follower)) return | ||
329 | |||
330 | logger.info('Notifying %d administrators of new instance follower: %s.', admins.length, actorFollow.ActorFollower.url) | ||
331 | |||
332 | function settingGetter (user: MUserWithNotificationSetting) { | ||
333 | return user.NotificationSetting.newInstanceFollower | ||
334 | } | ||
335 | |||
336 | async function notificationCreator (user: MUserWithNotificationSetting) { | ||
337 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ | ||
338 | type: UserNotificationType.NEW_INSTANCE_FOLLOWER, | ||
339 | userId: user.id, | ||
340 | actorFollowId: actorFollow.id | ||
341 | }) | ||
342 | notification.ActorFollow = actorFollow | ||
343 | |||
344 | return notification | ||
345 | } | ||
346 | |||
347 | function emailSender (emails: string[]) { | ||
348 | return Emailer.Instance.addNewInstanceFollowerNotification(emails, actorFollow) | ||
349 | } | ||
350 | |||
351 | return this.notify({ users: admins, settingGetter, notificationCreator, emailSender }) | ||
352 | } | ||
353 | |||
354 | private async notifyAdminsOfAutoInstanceFollowing (actorFollow: MActorFollowFull) { | ||
355 | const admins = await UserModel.listWithRight(UserRight.MANAGE_SERVER_FOLLOW) | ||
356 | |||
357 | logger.info('Notifying %d administrators of auto instance following: %s.', admins.length, actorFollow.ActorFollowing.url) | ||
358 | |||
359 | function settingGetter (user: MUserWithNotificationSetting) { | ||
360 | return user.NotificationSetting.autoInstanceFollowing | ||
361 | } | ||
362 | |||
363 | async function notificationCreator (user: MUserWithNotificationSetting) { | ||
364 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ | ||
365 | type: UserNotificationType.AUTO_INSTANCE_FOLLOWING, | ||
366 | userId: user.id, | ||
367 | actorFollowId: actorFollow.id | ||
368 | }) | ||
369 | notification.ActorFollow = actorFollow | ||
370 | |||
371 | return notification | ||
372 | } | ||
373 | |||
374 | function emailSender (emails: string[]) { | ||
375 | return Emailer.Instance.addAutoInstanceFollowingNotification(emails, actorFollow) | ||
376 | } | ||
377 | |||
378 | return this.notify({ users: admins, settingGetter, notificationCreator, emailSender }) | ||
379 | } | ||
380 | |||
381 | private async notifyModeratorsOfNewAbuse (parameters: { | ||
382 | abuse: UserAbuse | ||
383 | abuseInstance: MAbuseFull | ||
384 | reporter: string | ||
385 | }) { | ||
386 | const { abuse, abuseInstance } = parameters | ||
387 | |||
388 | const moderators = await UserModel.listWithRight(UserRight.MANAGE_ABUSES) | ||
389 | if (moderators.length === 0) return | ||
390 | |||
391 | const url = this.getAbuseUrl(abuseInstance) | ||
392 | |||
393 | logger.info('Notifying %s user/moderators of new abuse %s.', moderators.length, url) | ||
394 | |||
395 | function settingGetter (user: MUserWithNotificationSetting) { | ||
396 | return user.NotificationSetting.abuseAsModerator | ||
397 | } | ||
398 | |||
399 | async function notificationCreator (user: MUserWithNotificationSetting) { | ||
400 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ | ||
401 | type: UserNotificationType.NEW_ABUSE_FOR_MODERATORS, | ||
402 | userId: user.id, | ||
403 | abuseId: abuse.id | ||
404 | }) | ||
405 | notification.Abuse = abuseInstance | ||
406 | |||
407 | return notification | ||
408 | } | ||
409 | |||
410 | function emailSender (emails: string[]) { | ||
411 | return Emailer.Instance.addAbuseModeratorsNotification(emails, parameters) | ||
412 | } | ||
413 | |||
414 | return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender }) | ||
415 | } | ||
416 | |||
417 | private async notifyReporterOfAbuseStateChange (abuse: MAbuseFull) { | ||
418 | // Only notify our users | ||
419 | if (abuse.ReporterAccount.isOwned() !== true) return | ||
420 | |||
421 | const url = this.getAbuseUrl(abuse) | ||
422 | |||
423 | logger.info('Notifying reporter of abuse % of state change.', url) | ||
424 | |||
425 | const reporter = await UserModel.loadByAccountActorId(abuse.ReporterAccount.actorId) | ||
426 | |||
427 | function settingGetter (user: MUserWithNotificationSetting) { | ||
428 | return user.NotificationSetting.abuseStateChange | ||
429 | } | ||
430 | |||
431 | async function notificationCreator (user: MUserWithNotificationSetting) { | ||
432 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ | ||
433 | type: UserNotificationType.ABUSE_STATE_CHANGE, | ||
434 | userId: user.id, | ||
435 | abuseId: abuse.id | ||
436 | }) | ||
437 | notification.Abuse = abuse | ||
438 | |||
439 | return notification | ||
440 | } | ||
441 | |||
442 | function emailSender (emails: string[]) { | ||
443 | return Emailer.Instance.addAbuseStateChangeNotification(emails, abuse) | ||
444 | } | ||
445 | |||
446 | return this.notify({ users: [ reporter ], settingGetter, notificationCreator, emailSender }) | ||
447 | } | ||
448 | |||
449 | private async notifyOfNewAbuseMessage (abuse: MAbuseFull, message: MAbuseMessage) { | ||
450 | const url = this.getAbuseUrl(abuse) | ||
451 | logger.info('Notifying reporter and moderators of new abuse message on %s.', url) | ||
452 | |||
453 | const accountMessage = await AccountModel.load(message.accountId) | ||
454 | |||
455 | function settingGetter (user: MUserWithNotificationSetting) { | ||
456 | return user.NotificationSetting.abuseNewMessage | ||
457 | } | ||
458 | |||
459 | async function notificationCreator (user: MUserWithNotificationSetting) { | ||
460 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ | ||
461 | type: UserNotificationType.ABUSE_NEW_MESSAGE, | ||
462 | userId: user.id, | ||
463 | abuseId: abuse.id | ||
464 | }) | ||
465 | notification.Abuse = abuse | ||
466 | |||
467 | return notification | ||
468 | } | ||
469 | |||
470 | function emailSenderReporter (emails: string[]) { | ||
471 | return Emailer.Instance.addAbuseNewMessageNotification(emails, { target: 'reporter', abuse, message, accountMessage }) | ||
472 | } | ||
473 | |||
474 | function emailSenderModerators (emails: string[]) { | ||
475 | return Emailer.Instance.addAbuseNewMessageNotification(emails, { target: 'moderator', abuse, message, accountMessage }) | ||
476 | } | ||
477 | |||
478 | async function buildReporterOptions () { | ||
479 | // Only notify our users | ||
480 | if (abuse.ReporterAccount.isOwned() !== true) return undefined | ||
481 | |||
482 | const reporter = await UserModel.loadByAccountActorId(abuse.ReporterAccount.actorId) | ||
483 | // Don't notify my own message | ||
484 | if (reporter.Account.id === message.accountId) return undefined | ||
485 | |||
486 | return { users: [ reporter ], settingGetter, notificationCreator, emailSender: emailSenderReporter } | ||
487 | } | ||
488 | |||
489 | async function buildModeratorsOptions () { | ||
490 | let moderators = await UserModel.listWithRight(UserRight.MANAGE_ABUSES) | ||
491 | // Don't notify my own message | ||
492 | moderators = moderators.filter(m => m.Account.id !== message.accountId) | ||
493 | |||
494 | if (moderators.length === 0) return undefined | ||
495 | |||
496 | return { users: moderators, settingGetter, notificationCreator, emailSender: emailSenderModerators } | ||
497 | } | ||
498 | |||
499 | const options = await Promise.all([ | ||
500 | buildReporterOptions(), | ||
501 | buildModeratorsOptions() | ||
502 | ]) | ||
503 | |||
504 | return Promise.all( | ||
505 | options | ||
506 | .filter(opt => !!opt) | ||
507 | .map(opt => this.notify(opt)) | ||
508 | ) | ||
509 | } | ||
510 | |||
511 | private async notifyModeratorsOfVideoAutoBlacklist (videoBlacklist: MVideoBlacklistLightVideo) { | ||
512 | const moderators = await UserModel.listWithRight(UserRight.MANAGE_VIDEO_BLACKLIST) | ||
513 | if (moderators.length === 0) return | ||
514 | |||
515 | logger.info('Notifying %s moderators of video auto-blacklist %s.', moderators.length, videoBlacklist.Video.url) | ||
516 | |||
517 | function settingGetter (user: MUserWithNotificationSetting) { | ||
518 | return user.NotificationSetting.videoAutoBlacklistAsModerator | ||
519 | } | ||
520 | |||
521 | async function notificationCreator (user: MUserWithNotificationSetting) { | ||
522 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ | ||
523 | type: UserNotificationType.VIDEO_AUTO_BLACKLIST_FOR_MODERATORS, | ||
524 | userId: user.id, | ||
525 | videoBlacklistId: videoBlacklist.id | ||
526 | }) | ||
527 | notification.VideoBlacklist = videoBlacklist | ||
528 | |||
529 | return notification | ||
530 | } | ||
531 | |||
532 | function emailSender (emails: string[]) { | ||
533 | return Emailer.Instance.addVideoAutoBlacklistModeratorsNotification(emails, videoBlacklist) | ||
534 | } | ||
535 | |||
536 | return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender }) | ||
537 | } | ||
538 | |||
539 | private async notifyVideoOwnerOfBlacklist (videoBlacklist: MVideoBlacklistVideo) { | ||
540 | const user = await UserModel.loadByVideoId(videoBlacklist.videoId) | ||
541 | if (!user) return | ||
542 | |||
543 | logger.info('Notifying user %s that its video %s has been blacklisted.', user.username, videoBlacklist.Video.url) | ||
544 | |||
545 | function settingGetter (user: MUserWithNotificationSetting) { | ||
546 | return user.NotificationSetting.blacklistOnMyVideo | ||
547 | } | ||
548 | |||
549 | async function notificationCreator (user: MUserWithNotificationSetting) { | ||
550 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ | ||
551 | type: UserNotificationType.BLACKLIST_ON_MY_VIDEO, | ||
552 | userId: user.id, | ||
553 | videoBlacklistId: videoBlacklist.id | ||
554 | }) | ||
555 | notification.VideoBlacklist = videoBlacklist | ||
556 | |||
557 | return notification | ||
558 | } | ||
559 | |||
560 | function emailSender (emails: string[]) { | ||
561 | return Emailer.Instance.addVideoBlacklistNotification(emails, videoBlacklist) | ||
562 | } | ||
563 | |||
564 | return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) | ||
565 | } | ||
566 | |||
567 | private async notifyVideoOwnerOfUnblacklist (video: MVideoFullLight) { | ||
568 | const user = await UserModel.loadByVideoId(video.id) | ||
569 | if (!user) return | ||
570 | |||
571 | logger.info('Notifying user %s that its video %s has been unblacklisted.', user.username, video.url) | ||
572 | |||
573 | function settingGetter (user: MUserWithNotificationSetting) { | ||
574 | return user.NotificationSetting.blacklistOnMyVideo | ||
575 | } | ||
576 | |||
577 | async function notificationCreator (user: MUserWithNotificationSetting) { | ||
578 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ | ||
579 | type: UserNotificationType.UNBLACKLIST_ON_MY_VIDEO, | ||
580 | userId: user.id, | ||
581 | videoId: video.id | ||
582 | }) | ||
583 | notification.Video = video | ||
584 | |||
585 | return notification | ||
586 | } | ||
587 | |||
588 | function emailSender (emails: string[]) { | ||
589 | return Emailer.Instance.addVideoUnblacklistNotification(emails, video) | ||
590 | } | ||
591 | |||
592 | return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) | ||
593 | } | ||
594 | |||
595 | private async notifyOwnedVideoHasBeenPublished (video: MVideoFullLight) { | ||
596 | const user = await UserModel.loadByVideoId(video.id) | ||
597 | if (!user) return | ||
598 | |||
599 | logger.info('Notifying user %s of the publication of its video %s.', user.username, video.url) | ||
600 | |||
601 | function settingGetter (user: MUserWithNotificationSetting) { | ||
602 | return user.NotificationSetting.myVideoPublished | ||
603 | } | ||
604 | |||
605 | async function notificationCreator (user: MUserWithNotificationSetting) { | ||
606 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ | ||
607 | type: UserNotificationType.MY_VIDEO_PUBLISHED, | ||
608 | userId: user.id, | ||
609 | videoId: video.id | ||
610 | }) | ||
611 | notification.Video = video | ||
612 | |||
613 | return notification | ||
614 | } | ||
615 | |||
616 | function emailSender (emails: string[]) { | ||
617 | return Emailer.Instance.myVideoPublishedNotification(emails, video) | ||
618 | } | ||
619 | |||
620 | return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) | ||
621 | } | ||
622 | |||
623 | private async notifyOwnerVideoImportIsFinished (videoImport: MVideoImportVideo, success: boolean) { | ||
624 | const user = await UserModel.loadByVideoImportId(videoImport.id) | ||
625 | if (!user) return | ||
626 | |||
627 | logger.info('Notifying user %s its video import %s is finished.', user.username, videoImport.getTargetIdentifier()) | ||
628 | |||
629 | function settingGetter (user: MUserWithNotificationSetting) { | ||
630 | return user.NotificationSetting.myVideoImportFinished | ||
631 | } | ||
632 | |||
633 | async function notificationCreator (user: MUserWithNotificationSetting) { | ||
634 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ | ||
635 | type: success ? UserNotificationType.MY_VIDEO_IMPORT_SUCCESS : UserNotificationType.MY_VIDEO_IMPORT_ERROR, | ||
636 | userId: user.id, | ||
637 | videoImportId: videoImport.id | ||
638 | }) | ||
639 | notification.VideoImport = videoImport | ||
640 | |||
641 | return notification | ||
642 | } | ||
643 | |||
644 | function emailSender (emails: string[]) { | ||
645 | return success | ||
646 | ? Emailer.Instance.myVideoImportSuccessNotification(emails, videoImport) | ||
647 | : Emailer.Instance.myVideoImportErrorNotification(emails, videoImport) | ||
648 | } | ||
649 | |||
650 | return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) | ||
651 | } | ||
652 | |||
653 | private async notifyModeratorsOfNewUserRegistration (registeredUser: MUserDefault) { | ||
654 | const moderators = await UserModel.listWithRight(UserRight.MANAGE_USERS) | ||
655 | if (moderators.length === 0) return | ||
656 | |||
657 | logger.info( | ||
658 | 'Notifying %s moderators of new user registration of %s.', | ||
659 | moderators.length, registeredUser.username | ||
660 | ) | ||
661 | |||
662 | function settingGetter (user: MUserWithNotificationSetting) { | ||
663 | return user.NotificationSetting.newUserRegistration | ||
664 | } | ||
665 | |||
666 | async function notificationCreator (user: MUserWithNotificationSetting) { | ||
667 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ | ||
668 | type: UserNotificationType.NEW_USER_REGISTRATION, | ||
669 | userId: user.id, | ||
670 | accountId: registeredUser.Account.id | ||
671 | }) | ||
672 | notification.Account = registeredUser.Account | ||
673 | |||
674 | return notification | ||
675 | } | ||
676 | |||
677 | function emailSender (emails: string[]) { | ||
678 | return Emailer.Instance.addNewUserRegistrationNotification(emails, registeredUser) | ||
679 | } | ||
680 | |||
681 | return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender }) | ||
682 | } | ||
683 | |||
684 | private async notifyAdminsOfNewPeerTubeVersion (application: MApplication, latestVersion: string) { | ||
685 | // Use the debug right to know who is an administrator | ||
686 | const admins = await UserModel.listWithRight(UserRight.MANAGE_DEBUG) | ||
687 | if (admins.length === 0) return | ||
688 | |||
689 | logger.info('Notifying %s admins of new PeerTube version %s.', admins.length, latestVersion) | ||
690 | |||
691 | function settingGetter (user: MUserWithNotificationSetting) { | ||
692 | return user.NotificationSetting.newPeerTubeVersion | ||
693 | } | ||
694 | |||
695 | async function notificationCreator (user: MUserWithNotificationSetting) { | ||
696 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ | ||
697 | type: UserNotificationType.NEW_PEERTUBE_VERSION, | ||
698 | userId: user.id, | ||
699 | applicationId: application.id | ||
700 | }) | ||
701 | notification.Application = application | ||
702 | |||
703 | return notification | ||
704 | } | ||
705 | |||
706 | function emailSender (emails: string[]) { | ||
707 | return Emailer.Instance.addNewPeerTubeVersionNotification(emails, latestVersion) | ||
708 | } | ||
709 | |||
710 | return this.notify({ users: admins, settingGetter, notificationCreator, emailSender }) | ||
711 | } | ||
712 | |||
713 | private async notifyAdminsOfNewPluginVersion (plugin: MPlugin) { | ||
714 | // Use the debug right to know who is an administrator | ||
715 | const admins = await UserModel.listWithRight(UserRight.MANAGE_DEBUG) | ||
716 | if (admins.length === 0) return | ||
717 | |||
718 | logger.info('Notifying %s admins of new plugin version %s@%s.', admins.length, plugin.name, plugin.latestVersion) | ||
719 | |||
720 | function settingGetter (user: MUserWithNotificationSetting) { | ||
721 | return user.NotificationSetting.newPluginVersion | ||
722 | } | ||
723 | |||
724 | async function notificationCreator (user: MUserWithNotificationSetting) { | ||
725 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ | ||
726 | type: UserNotificationType.NEW_PLUGIN_VERSION, | ||
727 | userId: user.id, | ||
728 | pluginId: plugin.id | ||
729 | }) | ||
730 | notification.Plugin = plugin | ||
731 | |||
732 | return notification | ||
733 | } | ||
734 | |||
735 | function emailSender (emails: string[]) { | ||
736 | return Emailer.Instance.addNewPlugionVersionNotification(emails, plugin) | ||
737 | } | ||
738 | |||
739 | return this.notify({ users: admins, settingGetter, notificationCreator, emailSender }) | ||
740 | } | ||
741 | |||
742 | private async notify<T extends MUserWithNotificationSetting> (options: { | ||
743 | users: T[] | ||
744 | notificationCreator: (user: T) => Promise<UserNotificationModelForApi> | ||
745 | emailSender: (emails: string[]) => void | ||
746 | settingGetter: (user: T) => UserNotificationSettingValue | ||
747 | }) { | ||
748 | const emails: string[] = [] | ||
749 | |||
750 | for (const user of options.users) { | ||
751 | if (this.isWebNotificationEnabled(options.settingGetter(user))) { | ||
752 | const notification = await options.notificationCreator(user) | ||
753 | |||
754 | PeerTubeSocket.Instance.sendNotification(user.id, notification) | ||
755 | } | ||
756 | |||
757 | if (this.isEmailEnabled(user, options.settingGetter(user))) { | ||
758 | emails.push(user.email) | ||
759 | } | ||
760 | } | ||
761 | |||
762 | if (emails.length !== 0) { | ||
763 | options.emailSender(emails) | ||
764 | } | ||
765 | } | ||
766 | |||
767 | private isEmailEnabled (user: MUser, value: UserNotificationSettingValue) { | ||
768 | if (CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION === true && user.emailVerified === false) return false | ||
769 | |||
770 | return value & UserNotificationSettingValue.EMAIL | ||
771 | } | ||
772 | |||
773 | private isWebNotificationEnabled (value: UserNotificationSettingValue) { | ||
774 | return value & UserNotificationSettingValue.WEB | ||
775 | } | ||
776 | |||
777 | private isBlockedByServerOrUser (targetAccount: MAccountServer, user?: MUserAccount) { | ||
778 | return isBlockedByServerOrAccount(targetAccount, user?.Account) | ||
779 | } | ||
780 | |||
781 | private getAbuseUrl (abuse: MAbuseFull) { | ||
782 | return abuse.VideoAbuse?.Video?.url || | ||
783 | abuse.VideoCommentAbuse?.VideoComment?.url || | ||
784 | abuse.FlaggedAccount.Actor.url | ||
785 | } | ||
786 | |||
787 | static get Instance () { | ||
788 | return this.instance || (this.instance = new this()) | ||
789 | } | ||
790 | } | ||
791 | |||
792 | // --------------------------------------------------------------------------- | ||
793 | |||
794 | export { | ||
795 | Notifier | ||
796 | } | ||