Add import finished and video published notifs
[oweals/peertube.git] / server / lib / notifier.ts
1 import { UserNotificationSettingValue, UserNotificationType, UserRight } from '../../shared/models/users'
2 import { logger } from '../helpers/logger'
3 import { VideoModel } from '../models/video/video'
4 import { Emailer } from './emailer'
5 import { UserNotificationModel } from '../models/account/user-notification'
6 import { VideoCommentModel } from '../models/video/video-comment'
7 import { UserModel } from '../models/account/user'
8 import { PeerTubeSocket } from './peertube-socket'
9 import { CONFIG } from '../initializers/constants'
10 import { VideoPrivacy, VideoState } from '../../shared/models/videos'
11 import { VideoAbuseModel } from '../models/video/video-abuse'
12 import { VideoBlacklistModel } from '../models/video/video-blacklist'
13 import * as Bluebird from 'bluebird'
14 import { VideoImportModel } from '../models/video/video-import'
15 import { AccountBlocklistModel } from '../models/account/account-blocklist'
16
17 class Notifier {
18
19   private static instance: Notifier
20
21   private constructor () {}
22
23   notifyOnNewVideo (video: VideoModel): void {
24     // Only notify on public and published videos
25     if (video.privacy !== VideoPrivacy.PUBLIC || video.state !== VideoState.PUBLISHED) return
26
27     this.notifySubscribersOfNewVideo(video)
28       .catch(err => logger.error('Cannot notify subscribers of new video %s.', video.url, { err }))
29   }
30
31   notifyOnPendingVideoPublished (video: VideoModel): void {
32     // Only notify on public videos that has been published while the user waited transcoding/scheduled update
33     if (video.waitTranscoding === false && !video.ScheduleVideoUpdate) return
34
35     this.notifyOwnedVideoHasBeenPublished(video)
36         .catch(err => logger.error('Cannot notify owner that its video %s has been published.', video.url, { err }))
37   }
38
39   notifyOnNewComment (comment: VideoCommentModel): void {
40     this.notifyVideoOwnerOfNewComment(comment)
41         .catch(err => logger.error('Cannot notify of new comment %s.', comment.url, { err }))
42   }
43
44   notifyOnNewVideoAbuse (videoAbuse: VideoAbuseModel): void {
45     this.notifyModeratorsOfNewVideoAbuse(videoAbuse)
46       .catch(err => logger.error('Cannot notify of new video abuse of video %s.', videoAbuse.Video.url, { err }))
47   }
48
49   notifyOnVideoBlacklist (videoBlacklist: VideoBlacklistModel): void {
50     this.notifyVideoOwnerOfBlacklist(videoBlacklist)
51       .catch(err => logger.error('Cannot notify video owner of new video blacklist of %s.', videoBlacklist.Video.url, { err }))
52   }
53
54   notifyOnVideoUnblacklist (video: VideoModel): void {
55     this.notifyVideoOwnerOfUnblacklist(video)
56         .catch(err => logger.error('Cannot notify video owner of new video blacklist of %s.', video.url, { err }))
57   }
58
59   notifyOnFinishedVideoImport (videoImport: VideoImportModel, success: boolean): void {
60     this.notifyOwnerVideoImportIsFinished(videoImport, success)
61       .catch(err => logger.error('Cannot notify owner that its video import %s is finished.', videoImport.getTargetIdentifier(), { err }))
62   }
63
64   private async notifySubscribersOfNewVideo (video: VideoModel) {
65     // List all followers that are users
66     const users = await UserModel.listUserSubscribersOf(video.VideoChannel.actorId)
67
68     logger.info('Notifying %d users of new video %s.', users.length, video.url)
69
70     function settingGetter (user: UserModel) {
71       return user.NotificationSetting.newVideoFromSubscription
72     }
73
74     async function notificationCreator (user: UserModel) {
75       const notification = await UserNotificationModel.create({
76         type: UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION,
77         userId: user.id,
78         videoId: video.id
79       })
80       notification.Video = video
81
82       return notification
83     }
84
85     function emailSender (emails: string[]) {
86       return Emailer.Instance.addNewVideoFromSubscriberNotification(emails, video)
87     }
88
89     return this.notify({ users, settingGetter, notificationCreator, emailSender })
90   }
91
92   private async notifyVideoOwnerOfNewComment (comment: VideoCommentModel) {
93     const user = await UserModel.loadByVideoId(comment.videoId)
94
95     // Not our user or user comments its own video
96     if (!user || comment.Account.userId === user.id) return
97
98     const accountMuted = await AccountBlocklistModel.isAccountMutedBy(user.Account.id, comment.accountId)
99     if (accountMuted) return
100
101     logger.info('Notifying user %s of new comment %s.', user.username, comment.url)
102
103     function settingGetter (user: UserModel) {
104       return user.NotificationSetting.newCommentOnMyVideo
105     }
106
107     async function notificationCreator (user: UserModel) {
108       const notification = await UserNotificationModel.create({
109         type: UserNotificationType.NEW_COMMENT_ON_MY_VIDEO,
110         userId: user.id,
111         commentId: comment.id
112       })
113       notification.Comment = comment
114
115       return notification
116     }
117
118     function emailSender (emails: string[]) {
119       return Emailer.Instance.addNewCommentOnMyVideoNotification(emails, comment)
120     }
121
122     return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender })
123   }
124
125   private async notifyModeratorsOfNewVideoAbuse (videoAbuse: VideoAbuseModel) {
126     const users = await UserModel.listWithRight(UserRight.MANAGE_VIDEO_ABUSES)
127     if (users.length === 0) return
128
129     logger.info('Notifying %s user/moderators of new video abuse %s.', users.length, videoAbuse.Video.url)
130
131     function settingGetter (user: UserModel) {
132       return user.NotificationSetting.videoAbuseAsModerator
133     }
134
135     async function notificationCreator (user: UserModel) {
136       const notification = await UserNotificationModel.create({
137         type: UserNotificationType.NEW_VIDEO_ABUSE_FOR_MODERATORS,
138         userId: user.id,
139         videoAbuseId: videoAbuse.id
140       })
141       notification.VideoAbuse = videoAbuse
142
143       return notification
144     }
145
146     function emailSender (emails: string[]) {
147       return Emailer.Instance.addVideoAbuseModeratorsNotification(emails, videoAbuse)
148     }
149
150     return this.notify({ users, settingGetter, notificationCreator, emailSender })
151   }
152
153   private async notifyVideoOwnerOfBlacklist (videoBlacklist: VideoBlacklistModel) {
154     const user = await UserModel.loadByVideoId(videoBlacklist.videoId)
155     if (!user) return
156
157     logger.info('Notifying user %s that its video %s has been blacklisted.', user.username, videoBlacklist.Video.url)
158
159     function settingGetter (user: UserModel) {
160       return user.NotificationSetting.blacklistOnMyVideo
161     }
162
163     async function notificationCreator (user: UserModel) {
164       const notification = await UserNotificationModel.create({
165         type: UserNotificationType.BLACKLIST_ON_MY_VIDEO,
166         userId: user.id,
167         videoBlacklistId: videoBlacklist.id
168       })
169       notification.VideoBlacklist = videoBlacklist
170
171       return notification
172     }
173
174     function emailSender (emails: string[]) {
175       return Emailer.Instance.addVideoBlacklistNotification(emails, videoBlacklist)
176     }
177
178     return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender })
179   }
180
181   private async notifyVideoOwnerOfUnblacklist (video: VideoModel) {
182     const user = await UserModel.loadByVideoId(video.id)
183     if (!user) return
184
185     logger.info('Notifying user %s that its video %s has been unblacklisted.', user.username, video.url)
186
187     function settingGetter (user: UserModel) {
188       return user.NotificationSetting.blacklistOnMyVideo
189     }
190
191     async function notificationCreator (user: UserModel) {
192       const notification = await UserNotificationModel.create({
193         type: UserNotificationType.UNBLACKLIST_ON_MY_VIDEO,
194         userId: user.id,
195         videoId: video.id
196       })
197       notification.Video = video
198
199       return notification
200     }
201
202     function emailSender (emails: string[]) {
203       return Emailer.Instance.addVideoUnblacklistNotification(emails, video)
204     }
205
206     return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender })
207   }
208
209   private async notifyOwnedVideoHasBeenPublished (video: VideoModel) {
210     const user = await UserModel.loadByVideoId(video.id)
211     if (!user) return
212
213     logger.info('Notifying user %s of the publication of its video %s.', user.username, video.url)
214
215     function settingGetter (user: UserModel) {
216       return user.NotificationSetting.myVideoPublished
217     }
218
219     async function notificationCreator (user: UserModel) {
220       const notification = await UserNotificationModel.create({
221         type: UserNotificationType.MY_VIDEO_PUBLISHED,
222         userId: user.id,
223         videoId: video.id
224       })
225       notification.Video = video
226
227       return notification
228     }
229
230     function emailSender (emails: string[]) {
231       return Emailer.Instance.myVideoPublishedNotification(emails, video)
232     }
233
234     return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender })
235   }
236
237   private async notifyOwnerVideoImportIsFinished (videoImport: VideoImportModel, success: boolean) {
238     const user = await UserModel.loadByVideoImportId(videoImport.id)
239     if (!user) return
240
241     logger.info('Notifying user %s its video import %s is finished.', user.username, videoImport.getTargetIdentifier())
242
243     function settingGetter (user: UserModel) {
244       return user.NotificationSetting.myVideoImportFinished
245     }
246
247     async function notificationCreator (user: UserModel) {
248       const notification = await UserNotificationModel.create({
249         type: success ? UserNotificationType.MY_VIDEO_IMPORT_SUCCESS : UserNotificationType.MY_VIDEO_IMPORT_ERROR,
250         userId: user.id,
251         videoImportId: videoImport.id
252       })
253       notification.VideoImport = videoImport
254
255       return notification
256     }
257
258     function emailSender (emails: string[]) {
259       return success
260         ? Emailer.Instance.myVideoImportSuccessNotification(emails, videoImport)
261         : Emailer.Instance.myVideoImportErrorNotification(emails, videoImport)
262     }
263
264     return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender })
265   }
266
267   private async notify (options: {
268     users: UserModel[],
269     notificationCreator: (user: UserModel) => Promise<UserNotificationModel>,
270     emailSender: (emails: string[]) => Promise<any> | Bluebird<any>,
271     settingGetter: (user: UserModel) => UserNotificationSettingValue
272   }) {
273     const emails: string[] = []
274
275     for (const user of options.users) {
276       if (this.isWebNotificationEnabled(options.settingGetter(user))) {
277         const notification = await options.notificationCreator(user)
278
279         PeerTubeSocket.Instance.sendNotification(user.id, notification)
280       }
281
282       if (this.isEmailEnabled(user, options.settingGetter(user))) {
283         emails.push(user.email)
284       }
285     }
286
287     if (emails.length !== 0) {
288       await options.emailSender(emails)
289     }
290   }
291
292   private isEmailEnabled (user: UserModel, value: UserNotificationSettingValue) {
293     if (CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION === true && user.emailVerified !== true) return false
294
295     return value === UserNotificationSettingValue.EMAIL || value === UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL
296   }
297
298   private isWebNotificationEnabled (value: UserNotificationSettingValue) {
299     return value === UserNotificationSettingValue.WEB_NOTIFICATION || value === UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL
300   }
301
302   static get Instance () {
303     return this.instance || (this.instance = new this())
304   }
305 }
306
307 // ---------------------------------------------------------------------------
308
309 export {
310   Notifier
311 }