]>
Commit | Line | Data |
---|---|---|
e364e31e | 1 | import { MRegistration, MUser, MUserDefault } from '@server/types/models/user' |
d26836cd C |
2 | import { MVideoBlacklistLightVideo, MVideoBlacklistVideo } from '@server/types/models/video/video-blacklist' |
3 | import { UserNotificationSettingValue } from '../../../shared/models/users' | |
4 | import { logger } from '../../helpers/logger' | |
5 | import { CONFIG } from '../../initializers/config' | |
6 | import { MAbuseFull, MAbuseMessage, MActorFollowFull, MApplication, MPlugin } from '../../types/models' | |
7 | import { MCommentOwnerVideo, MVideoAccountLight, MVideoFullLight } from '../../types/models/video' | |
8 | import { JobQueue } from '../job-queue' | |
9 | import { PeerTubeSocket } from '../peertube-socket' | |
785f1897 | 10 | import { Hooks } from '../plugins/hooks' |
d26836cd C |
11 | import { |
12 | AbstractNotification, | |
13 | AbuseStateChangeForReporter, | |
14 | AutoFollowForInstance, | |
15 | CommentMention, | |
e364e31e | 16 | DirectRegistrationForModerators, |
d26836cd C |
17 | FollowForInstance, |
18 | FollowForUser, | |
19 | ImportFinishedForOwner, | |
20 | ImportFinishedForOwnerPayload, | |
21 | NewAbuseForModerators, | |
22 | NewAbuseMessageForModerators, | |
23 | NewAbuseMessageForReporter, | |
24 | NewAbusePayload, | |
25 | NewAutoBlacklistForModerators, | |
26 | NewBlacklistForOwner, | |
27 | NewCommentForVideoOwner, | |
28 | NewPeerTubeVersionForAdmins, | |
29 | NewPluginVersionForAdmins, | |
30 | NewVideoForSubscribers, | |
31 | OwnedPublicationAfterAutoUnblacklist, | |
32 | OwnedPublicationAfterScheduleUpdate, | |
33 | OwnedPublicationAfterTranscoding, | |
e364e31e | 34 | RegistrationRequestForModerators, |
785f1897 | 35 | StudioEditionFinishedForOwner, |
d26836cd C |
36 | UnblacklistForOwner |
37 | } from './shared' | |
38 | ||
39 | class Notifier { | |
40 | ||
41 | private readonly notificationModels = { | |
42 | newVideo: [ NewVideoForSubscribers ], | |
43 | publicationAfterTranscoding: [ OwnedPublicationAfterTranscoding ], | |
44 | publicationAfterScheduleUpdate: [ OwnedPublicationAfterScheduleUpdate ], | |
45 | publicationAfterAutoUnblacklist: [ OwnedPublicationAfterAutoUnblacklist ], | |
46 | newComment: [ CommentMention, NewCommentForVideoOwner ], | |
47 | newAbuse: [ NewAbuseForModerators ], | |
48 | newBlacklist: [ NewBlacklistForOwner ], | |
49 | unblacklist: [ UnblacklistForOwner ], | |
50 | importFinished: [ ImportFinishedForOwner ], | |
e364e31e C |
51 | directRegistration: [ DirectRegistrationForModerators ], |
52 | registrationRequest: [ RegistrationRequestForModerators ], | |
d26836cd C |
53 | userFollow: [ FollowForUser ], |
54 | instanceFollow: [ FollowForInstance ], | |
55 | autoInstanceFollow: [ AutoFollowForInstance ], | |
56 | newAutoBlacklist: [ NewAutoBlacklistForModerators ], | |
57 | abuseStateChange: [ AbuseStateChangeForReporter ], | |
58 | newAbuseMessage: [ NewAbuseMessageForReporter, NewAbuseMessageForModerators ], | |
59 | newPeertubeVersion: [ NewPeerTubeVersionForAdmins ], | |
1808a1f8 | 60 | newPluginVersion: [ NewPluginVersionForAdmins ], |
92e66e04 | 61 | videoStudioEditionFinished: [ StudioEditionFinishedForOwner ] |
d26836cd C |
62 | } |
63 | ||
64 | private static instance: Notifier | |
65 | ||
66 | private constructor () { | |
67 | } | |
68 | ||
69 | notifyOnNewVideoIfNeeded (video: MVideoAccountLight): void { | |
70 | const models = this.notificationModels.newVideo | |
71 | ||
72 | this.sendNotifications(models, video) | |
73 | .catch(err => logger.error('Cannot notify subscribers of new video %s.', video.url, { err })) | |
74 | } | |
75 | ||
76 | notifyOnVideoPublishedAfterTranscoding (video: MVideoFullLight): void { | |
77 | const models = this.notificationModels.publicationAfterTranscoding | |
78 | ||
79 | this.sendNotifications(models, video) | |
80 | .catch(err => logger.error('Cannot notify owner that its video %s has been published after transcoding.', video.url, { err })) | |
81 | } | |
82 | ||
83 | notifyOnVideoPublishedAfterScheduledUpdate (video: MVideoFullLight): void { | |
84 | const models = this.notificationModels.publicationAfterScheduleUpdate | |
85 | ||
86 | this.sendNotifications(models, video) | |
87 | .catch(err => logger.error('Cannot notify owner that its video %s has been published after scheduled update.', video.url, { err })) | |
88 | } | |
89 | ||
90 | notifyOnVideoPublishedAfterRemovedFromAutoBlacklist (video: MVideoFullLight): void { | |
91 | const models = this.notificationModels.publicationAfterAutoUnblacklist | |
92 | ||
93 | this.sendNotifications(models, video) | |
94 | .catch(err => { | |
95 | logger.error('Cannot notify owner that its video %s has been published after removed from auto-blacklist.', video.url, { err }) | |
96 | }) | |
97 | } | |
98 | ||
99 | notifyOnNewComment (comment: MCommentOwnerVideo): void { | |
100 | const models = this.notificationModels.newComment | |
101 | ||
102 | this.sendNotifications(models, comment) | |
103 | .catch(err => logger.error('Cannot notify of new comment.', comment.url, { err })) | |
104 | } | |
105 | ||
106 | notifyOnNewAbuse (payload: NewAbusePayload): void { | |
107 | const models = this.notificationModels.newAbuse | |
108 | ||
109 | this.sendNotifications(models, payload) | |
110 | .catch(err => logger.error('Cannot notify of new abuse %d.', payload.abuseInstance.id, { err })) | |
111 | } | |
112 | ||
113 | notifyOnVideoAutoBlacklist (videoBlacklist: MVideoBlacklistLightVideo): void { | |
114 | const models = this.notificationModels.newAutoBlacklist | |
115 | ||
116 | this.sendNotifications(models, videoBlacklist) | |
117 | .catch(err => logger.error('Cannot notify of auto-blacklist of video %s.', videoBlacklist.Video.url, { err })) | |
118 | } | |
119 | ||
120 | notifyOnVideoBlacklist (videoBlacklist: MVideoBlacklistVideo): void { | |
121 | const models = this.notificationModels.newBlacklist | |
122 | ||
123 | this.sendNotifications(models, videoBlacklist) | |
124 | .catch(err => logger.error('Cannot notify video owner of new video blacklist of %s.', videoBlacklist.Video.url, { err })) | |
125 | } | |
126 | ||
127 | notifyOnVideoUnblacklist (video: MVideoFullLight): void { | |
128 | const models = this.notificationModels.unblacklist | |
129 | ||
130 | this.sendNotifications(models, video) | |
131 | .catch(err => logger.error('Cannot notify video owner of unblacklist of %s.', video.url, { err })) | |
132 | } | |
133 | ||
134 | notifyOnFinishedVideoImport (payload: ImportFinishedForOwnerPayload): void { | |
135 | const models = this.notificationModels.importFinished | |
136 | ||
137 | this.sendNotifications(models, payload) | |
138 | .catch(err => { | |
139 | logger.error('Cannot notify owner that its video import %s is finished.', payload.videoImport.getTargetIdentifier(), { err }) | |
140 | }) | |
141 | } | |
142 | ||
e364e31e C |
143 | notifyOnNewDirectRegistration (user: MUserDefault): void { |
144 | const models = this.notificationModels.directRegistration | |
d26836cd C |
145 | |
146 | this.sendNotifications(models, user) | |
147 | .catch(err => logger.error('Cannot notify moderators of new user registration (%s).', user.username, { err })) | |
148 | } | |
149 | ||
e364e31e C |
150 | notifyOnNewRegistrationRequest (registration: MRegistration): void { |
151 | const models = this.notificationModels.registrationRequest | |
152 | ||
153 | this.sendNotifications(models, registration) | |
154 | .catch(err => logger.error('Cannot notify moderators of new registration request (%s).', registration.username, { err })) | |
155 | } | |
156 | ||
d26836cd C |
157 | notifyOfNewUserFollow (actorFollow: MActorFollowFull): void { |
158 | const models = this.notificationModels.userFollow | |
159 | ||
160 | this.sendNotifications(models, actorFollow) | |
161 | .catch(err => { | |
162 | logger.error( | |
163 | 'Cannot notify owner of channel %s of a new follow by %s.', | |
164 | actorFollow.ActorFollowing.VideoChannel.getDisplayName(), | |
165 | actorFollow.ActorFollower.Account.getDisplayName(), | |
166 | { err } | |
167 | ) | |
168 | }) | |
169 | } | |
170 | ||
171 | notifyOfNewInstanceFollow (actorFollow: MActorFollowFull): void { | |
172 | const models = this.notificationModels.instanceFollow | |
173 | ||
174 | this.sendNotifications(models, actorFollow) | |
175 | .catch(err => logger.error('Cannot notify administrators of new follower %s.', actorFollow.ActorFollower.url, { err })) | |
176 | } | |
177 | ||
178 | notifyOfAutoInstanceFollowing (actorFollow: MActorFollowFull): void { | |
179 | const models = this.notificationModels.autoInstanceFollow | |
180 | ||
181 | this.sendNotifications(models, actorFollow) | |
182 | .catch(err => logger.error('Cannot notify administrators of auto instance following %s.', actorFollow.ActorFollowing.url, { err })) | |
183 | } | |
184 | ||
185 | notifyOnAbuseStateChange (abuse: MAbuseFull): void { | |
186 | const models = this.notificationModels.abuseStateChange | |
187 | ||
188 | this.sendNotifications(models, abuse) | |
189 | .catch(err => logger.error('Cannot notify of abuse %d state change.', abuse.id, { err })) | |
190 | } | |
191 | ||
192 | notifyOnAbuseMessage (abuse: MAbuseFull, message: MAbuseMessage): void { | |
193 | const models = this.notificationModels.newAbuseMessage | |
194 | ||
195 | this.sendNotifications(models, { abuse, message }) | |
196 | .catch(err => logger.error('Cannot notify on new abuse %d message.', abuse.id, { err })) | |
197 | } | |
198 | ||
199 | notifyOfNewPeerTubeVersion (application: MApplication, latestVersion: string) { | |
200 | const models = this.notificationModels.newPeertubeVersion | |
201 | ||
202 | this.sendNotifications(models, { application, latestVersion }) | |
203 | .catch(err => logger.error('Cannot notify on new PeerTubeb version %s.', latestVersion, { err })) | |
204 | } | |
205 | ||
206 | notifyOfNewPluginVersion (plugin: MPlugin) { | |
207 | const models = this.notificationModels.newPluginVersion | |
208 | ||
209 | this.sendNotifications(models, plugin) | |
210 | .catch(err => logger.error('Cannot notify on new plugin version %s.', plugin.name, { err })) | |
211 | } | |
212 | ||
92e66e04 C |
213 | notifyOfFinishedVideoStudioEdition (video: MVideoFullLight) { |
214 | const models = this.notificationModels.videoStudioEditionFinished | |
1808a1f8 C |
215 | |
216 | this.sendNotifications(models, video) | |
92e66e04 | 217 | .catch(err => logger.error('Cannot notify on finished studio edition %s.', video.url, { err })) |
1808a1f8 C |
218 | } |
219 | ||
d26836cd C |
220 | private async notify <T> (object: AbstractNotification<T>) { |
221 | await object.prepare() | |
222 | ||
223 | const users = object.getTargetUsers() | |
224 | ||
225 | if (users.length === 0) return | |
226 | if (await object.isDisabled()) return | |
227 | ||
228 | object.log() | |
229 | ||
230 | const toEmails: string[] = [] | |
231 | ||
232 | for (const user of users) { | |
233 | const setting = object.getSetting(user) | |
234 | ||
785f1897 C |
235 | const webNotificationEnabled = this.isWebNotificationEnabled(setting) |
236 | const emailNotificationEnabled = this.isEmailEnabled(user, setting) | |
237 | const notification = object.createNotification(user) | |
238 | ||
239 | if (webNotificationEnabled) { | |
240 | await notification.save() | |
d26836cd C |
241 | |
242 | PeerTubeSocket.Instance.sendNotification(user.id, notification) | |
243 | } | |
244 | ||
785f1897 | 245 | if (emailNotificationEnabled) { |
d26836cd C |
246 | toEmails.push(user.email) |
247 | } | |
785f1897 C |
248 | |
249 | Hooks.runAction('action:notifier.notification.created', { webNotificationEnabled, emailNotificationEnabled, user, notification }) | |
d26836cd C |
250 | } |
251 | ||
252 | for (const to of toEmails) { | |
253 | const payload = await object.createEmail(to) | |
bd911b54 | 254 | JobQueue.Instance.createJobAsync({ type: 'email', payload }) |
d26836cd C |
255 | } |
256 | } | |
257 | ||
258 | private isEmailEnabled (user: MUser, value: UserNotificationSettingValue) { | |
259 | if (CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION === true && user.emailVerified === false) return false | |
260 | ||
261 | return value & UserNotificationSettingValue.EMAIL | |
262 | } | |
263 | ||
264 | private isWebNotificationEnabled (value: UserNotificationSettingValue) { | |
265 | return value & UserNotificationSettingValue.WEB | |
266 | } | |
267 | ||
268 | private async sendNotifications <T> (models: (new (payload: T) => AbstractNotification<T>)[], payload: T) { | |
269 | for (const model of models) { | |
270 | // eslint-disable-next-line new-cap | |
271 | await this.notify(new model(payload)) | |
272 | } | |
273 | } | |
274 | ||
275 | static get Instance () { | |
276 | return this.instance || (this.instance = new this()) | |
277 | } | |
278 | } | |
279 | ||
280 | // --------------------------------------------------------------------------- | |
281 | ||
282 | export { | |
283 | Notifier | |
284 | } |