diff options
Diffstat (limited to 'server/lib/notifier')
35 files changed, 0 insertions, 1738 deletions
diff --git a/server/lib/notifier/index.ts b/server/lib/notifier/index.ts deleted file mode 100644 index 5bc2f5f50..000000000 --- a/server/lib/notifier/index.ts +++ /dev/null | |||
@@ -1 +0,0 @@ | |||
1 | export * from './notifier' | ||
diff --git a/server/lib/notifier/notifier.ts b/server/lib/notifier/notifier.ts deleted file mode 100644 index 920c55df0..000000000 --- a/server/lib/notifier/notifier.ts +++ /dev/null | |||
@@ -1,284 +0,0 @@ | |||
1 | import { MRegistration, MUser, MUserDefault } from '@server/types/models/user' | ||
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' | ||
10 | import { Hooks } from '../plugins/hooks' | ||
11 | import { | ||
12 | AbstractNotification, | ||
13 | AbuseStateChangeForReporter, | ||
14 | AutoFollowForInstance, | ||
15 | CommentMention, | ||
16 | DirectRegistrationForModerators, | ||
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, | ||
34 | RegistrationRequestForModerators, | ||
35 | StudioEditionFinishedForOwner, | ||
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 ], | ||
51 | directRegistration: [ DirectRegistrationForModerators ], | ||
52 | registrationRequest: [ RegistrationRequestForModerators ], | ||
53 | userFollow: [ FollowForUser ], | ||
54 | instanceFollow: [ FollowForInstance ], | ||
55 | autoInstanceFollow: [ AutoFollowForInstance ], | ||
56 | newAutoBlacklist: [ NewAutoBlacklistForModerators ], | ||
57 | abuseStateChange: [ AbuseStateChangeForReporter ], | ||
58 | newAbuseMessage: [ NewAbuseMessageForReporter, NewAbuseMessageForModerators ], | ||
59 | newPeertubeVersion: [ NewPeerTubeVersionForAdmins ], | ||
60 | newPluginVersion: [ NewPluginVersionForAdmins ], | ||
61 | videoStudioEditionFinished: [ StudioEditionFinishedForOwner ] | ||
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 | |||
143 | notifyOnNewDirectRegistration (user: MUserDefault): void { | ||
144 | const models = this.notificationModels.directRegistration | ||
145 | |||
146 | this.sendNotifications(models, user) | ||
147 | .catch(err => logger.error('Cannot notify moderators of new user registration (%s).', user.username, { err })) | ||
148 | } | ||
149 | |||
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 | |||
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 | |||
213 | notifyOfFinishedVideoStudioEdition (video: MVideoFullLight) { | ||
214 | const models = this.notificationModels.videoStudioEditionFinished | ||
215 | |||
216 | this.sendNotifications(models, video) | ||
217 | .catch(err => logger.error('Cannot notify on finished studio edition %s.', video.url, { err })) | ||
218 | } | ||
219 | |||
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 | |||
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() | ||
241 | |||
242 | PeerTubeSocket.Instance.sendNotification(user.id, notification) | ||
243 | } | ||
244 | |||
245 | if (emailNotificationEnabled) { | ||
246 | toEmails.push(user.email) | ||
247 | } | ||
248 | |||
249 | Hooks.runAction('action:notifier.notification.created', { webNotificationEnabled, emailNotificationEnabled, user, notification }) | ||
250 | } | ||
251 | |||
252 | for (const to of toEmails) { | ||
253 | const payload = await object.createEmail(to) | ||
254 | JobQueue.Instance.createJobAsync({ type: 'email', payload }) | ||
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 | } | ||
diff --git a/server/lib/notifier/shared/abuse/abstract-new-abuse-message.ts b/server/lib/notifier/shared/abuse/abstract-new-abuse-message.ts deleted file mode 100644 index 1dc1ccfc2..000000000 --- a/server/lib/notifier/shared/abuse/abstract-new-abuse-message.ts +++ /dev/null | |||
@@ -1,67 +0,0 @@ | |||
1 | import { WEBSERVER } from '@server/initializers/constants' | ||
2 | import { AccountModel } from '@server/models/account/account' | ||
3 | import { UserNotificationModel } from '@server/models/user/user-notification' | ||
4 | import { MAbuseFull, MAbuseMessage, MAccountDefault, MUserWithNotificationSetting, UserNotificationModelForApi } from '@server/types/models' | ||
5 | import { UserNotificationType } from '@shared/models' | ||
6 | import { AbstractNotification } from '../common/abstract-notification' | ||
7 | |||
8 | type NewAbuseMessagePayload = { | ||
9 | abuse: MAbuseFull | ||
10 | message: MAbuseMessage | ||
11 | } | ||
12 | |||
13 | export abstract class AbstractNewAbuseMessage extends AbstractNotification <NewAbuseMessagePayload> { | ||
14 | protected messageAccount: MAccountDefault | ||
15 | |||
16 | async loadMessageAccount () { | ||
17 | this.messageAccount = await AccountModel.load(this.message.accountId) | ||
18 | } | ||
19 | |||
20 | getSetting (user: MUserWithNotificationSetting) { | ||
21 | return user.NotificationSetting.abuseNewMessage | ||
22 | } | ||
23 | |||
24 | createNotification (user: MUserWithNotificationSetting) { | ||
25 | const notification = UserNotificationModel.build<UserNotificationModelForApi>({ | ||
26 | type: UserNotificationType.ABUSE_NEW_MESSAGE, | ||
27 | userId: user.id, | ||
28 | abuseId: this.abuse.id | ||
29 | }) | ||
30 | notification.Abuse = this.abuse | ||
31 | |||
32 | return notification | ||
33 | } | ||
34 | |||
35 | protected createEmailFor (to: string, target: 'moderator' | 'reporter') { | ||
36 | const text = 'New message on report #' + this.abuse.id | ||
37 | const abuseUrl = target === 'moderator' | ||
38 | ? WEBSERVER.URL + '/admin/moderation/abuses/list?search=%23' + this.abuse.id | ||
39 | : WEBSERVER.URL + '/my-account/abuses?search=%23' + this.abuse.id | ||
40 | |||
41 | const action = { | ||
42 | text: 'View report #' + this.abuse.id, | ||
43 | url: abuseUrl | ||
44 | } | ||
45 | |||
46 | return { | ||
47 | template: 'abuse-new-message', | ||
48 | to, | ||
49 | subject: text, | ||
50 | locals: { | ||
51 | abuseId: this.abuse.id, | ||
52 | abuseUrl: action.url, | ||
53 | messageAccountName: this.messageAccount.getDisplayName(), | ||
54 | messageText: this.message.message, | ||
55 | action | ||
56 | } | ||
57 | } | ||
58 | } | ||
59 | |||
60 | protected get abuse () { | ||
61 | return this.payload.abuse | ||
62 | } | ||
63 | |||
64 | protected get message () { | ||
65 | return this.payload.message | ||
66 | } | ||
67 | } | ||
diff --git a/server/lib/notifier/shared/abuse/abuse-state-change-for-reporter.ts b/server/lib/notifier/shared/abuse/abuse-state-change-for-reporter.ts deleted file mode 100644 index 97e896c6a..000000000 --- a/server/lib/notifier/shared/abuse/abuse-state-change-for-reporter.ts +++ /dev/null | |||
@@ -1,74 +0,0 @@ | |||
1 | import { logger } from '@server/helpers/logger' | ||
2 | import { WEBSERVER } from '@server/initializers/constants' | ||
3 | import { getAbuseTargetUrl } from '@server/lib/activitypub/url' | ||
4 | import { UserModel } from '@server/models/user/user' | ||
5 | import { UserNotificationModel } from '@server/models/user/user-notification' | ||
6 | import { MAbuseFull, MUserDefault, MUserWithNotificationSetting, UserNotificationModelForApi } from '@server/types/models' | ||
7 | import { AbuseState, UserNotificationType } from '@shared/models' | ||
8 | import { AbstractNotification } from '../common/abstract-notification' | ||
9 | |||
10 | export class AbuseStateChangeForReporter extends AbstractNotification <MAbuseFull> { | ||
11 | |||
12 | private user: MUserDefault | ||
13 | |||
14 | async prepare () { | ||
15 | const reporter = this.abuse.ReporterAccount | ||
16 | if (reporter.isOwned() !== true) return | ||
17 | |||
18 | this.user = await UserModel.loadByAccountActorId(this.abuse.ReporterAccount.actorId) | ||
19 | } | ||
20 | |||
21 | log () { | ||
22 | logger.info('Notifying reporter of abuse % of state change.', getAbuseTargetUrl(this.abuse)) | ||
23 | } | ||
24 | |||
25 | getSetting (user: MUserWithNotificationSetting) { | ||
26 | return user.NotificationSetting.abuseStateChange | ||
27 | } | ||
28 | |||
29 | getTargetUsers () { | ||
30 | if (!this.user) return [] | ||
31 | |||
32 | return [ this.user ] | ||
33 | } | ||
34 | |||
35 | createNotification (user: MUserWithNotificationSetting) { | ||
36 | const notification = UserNotificationModel.build<UserNotificationModelForApi>({ | ||
37 | type: UserNotificationType.ABUSE_STATE_CHANGE, | ||
38 | userId: user.id, | ||
39 | abuseId: this.abuse.id | ||
40 | }) | ||
41 | notification.Abuse = this.abuse | ||
42 | |||
43 | return notification | ||
44 | } | ||
45 | |||
46 | createEmail (to: string) { | ||
47 | const text = this.abuse.state === AbuseState.ACCEPTED | ||
48 | ? 'Report #' + this.abuse.id + ' has been accepted' | ||
49 | : 'Report #' + this.abuse.id + ' has been rejected' | ||
50 | |||
51 | const abuseUrl = WEBSERVER.URL + '/my-account/abuses?search=%23' + this.abuse.id | ||
52 | |||
53 | const action = { | ||
54 | text: 'View report #' + this.abuse.id, | ||
55 | url: abuseUrl | ||
56 | } | ||
57 | |||
58 | return { | ||
59 | template: 'abuse-state-change', | ||
60 | to, | ||
61 | subject: text, | ||
62 | locals: { | ||
63 | action, | ||
64 | abuseId: this.abuse.id, | ||
65 | abuseUrl, | ||
66 | isAccepted: this.abuse.state === AbuseState.ACCEPTED | ||
67 | } | ||
68 | } | ||
69 | } | ||
70 | |||
71 | private get abuse () { | ||
72 | return this.payload | ||
73 | } | ||
74 | } | ||
diff --git a/server/lib/notifier/shared/abuse/index.ts b/server/lib/notifier/shared/abuse/index.ts deleted file mode 100644 index 7b54c5591..000000000 --- a/server/lib/notifier/shared/abuse/index.ts +++ /dev/null | |||
@@ -1,4 +0,0 @@ | |||
1 | export * from './abuse-state-change-for-reporter' | ||
2 | export * from './new-abuse-for-moderators' | ||
3 | export * from './new-abuse-message-for-reporter' | ||
4 | export * from './new-abuse-message-for-moderators' | ||
diff --git a/server/lib/notifier/shared/abuse/new-abuse-for-moderators.ts b/server/lib/notifier/shared/abuse/new-abuse-for-moderators.ts deleted file mode 100644 index 7d86fb55f..000000000 --- a/server/lib/notifier/shared/abuse/new-abuse-for-moderators.ts +++ /dev/null | |||
@@ -1,119 +0,0 @@ | |||
1 | import { logger } from '@server/helpers/logger' | ||
2 | import { WEBSERVER } from '@server/initializers/constants' | ||
3 | import { getAbuseTargetUrl } from '@server/lib/activitypub/url' | ||
4 | import { UserModel } from '@server/models/user/user' | ||
5 | import { UserNotificationModel } from '@server/models/user/user-notification' | ||
6 | import { MAbuseFull, MUserDefault, MUserWithNotificationSetting, UserNotificationModelForApi } from '@server/types/models' | ||
7 | import { UserAbuse, UserNotificationType, UserRight } from '@shared/models' | ||
8 | import { AbstractNotification } from '../common/abstract-notification' | ||
9 | |||
10 | export type NewAbusePayload = { abuse: UserAbuse, abuseInstance: MAbuseFull, reporter: string } | ||
11 | |||
12 | export class NewAbuseForModerators extends AbstractNotification <NewAbusePayload> { | ||
13 | private moderators: MUserDefault[] | ||
14 | |||
15 | async prepare () { | ||
16 | this.moderators = await UserModel.listWithRight(UserRight.MANAGE_ABUSES) | ||
17 | } | ||
18 | |||
19 | log () { | ||
20 | logger.info('Notifying %s user/moderators of new abuse %s.', this.moderators.length, getAbuseTargetUrl(this.payload.abuseInstance)) | ||
21 | } | ||
22 | |||
23 | getSetting (user: MUserWithNotificationSetting) { | ||
24 | return user.NotificationSetting.abuseAsModerator | ||
25 | } | ||
26 | |||
27 | getTargetUsers () { | ||
28 | return this.moderators | ||
29 | } | ||
30 | |||
31 | createNotification (user: MUserWithNotificationSetting) { | ||
32 | const notification = UserNotificationModel.build<UserNotificationModelForApi>({ | ||
33 | type: UserNotificationType.NEW_ABUSE_FOR_MODERATORS, | ||
34 | userId: user.id, | ||
35 | abuseId: this.payload.abuseInstance.id | ||
36 | }) | ||
37 | notification.Abuse = this.payload.abuseInstance | ||
38 | |||
39 | return notification | ||
40 | } | ||
41 | |||
42 | createEmail (to: string) { | ||
43 | const abuseInstance = this.payload.abuseInstance | ||
44 | |||
45 | if (abuseInstance.VideoAbuse) return this.createVideoAbuseEmail(to) | ||
46 | if (abuseInstance.VideoCommentAbuse) return this.createCommentAbuseEmail(to) | ||
47 | |||
48 | return this.createAccountAbuseEmail(to) | ||
49 | } | ||
50 | |||
51 | private createVideoAbuseEmail (to: string) { | ||
52 | const video = this.payload.abuseInstance.VideoAbuse.Video | ||
53 | const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() | ||
54 | |||
55 | return { | ||
56 | template: 'video-abuse-new', | ||
57 | to, | ||
58 | subject: `New video abuse report from ${this.payload.reporter}`, | ||
59 | locals: { | ||
60 | videoUrl, | ||
61 | isLocal: video.remote === false, | ||
62 | videoCreatedAt: new Date(video.createdAt).toLocaleString(), | ||
63 | videoPublishedAt: new Date(video.publishedAt).toLocaleString(), | ||
64 | videoName: video.name, | ||
65 | reason: this.payload.abuse.reason, | ||
66 | videoChannel: this.payload.abuse.video.channel, | ||
67 | reporter: this.payload.reporter, | ||
68 | action: this.buildEmailAction() | ||
69 | } | ||
70 | } | ||
71 | } | ||
72 | |||
73 | private createCommentAbuseEmail (to: string) { | ||
74 | const comment = this.payload.abuseInstance.VideoCommentAbuse.VideoComment | ||
75 | const commentUrl = WEBSERVER.URL + comment.Video.getWatchStaticPath() + ';threadId=' + comment.getThreadId() | ||
76 | |||
77 | return { | ||
78 | template: 'video-comment-abuse-new', | ||
79 | to, | ||
80 | subject: `New comment abuse report from ${this.payload.reporter}`, | ||
81 | locals: { | ||
82 | commentUrl, | ||
83 | videoName: comment.Video.name, | ||
84 | isLocal: comment.isOwned(), | ||
85 | commentCreatedAt: new Date(comment.createdAt).toLocaleString(), | ||
86 | reason: this.payload.abuse.reason, | ||
87 | flaggedAccount: this.payload.abuseInstance.FlaggedAccount.getDisplayName(), | ||
88 | reporter: this.payload.reporter, | ||
89 | action: this.buildEmailAction() | ||
90 | } | ||
91 | } | ||
92 | } | ||
93 | |||
94 | private createAccountAbuseEmail (to: string) { | ||
95 | const account = this.payload.abuseInstance.FlaggedAccount | ||
96 | const accountUrl = account.getClientUrl() | ||
97 | |||
98 | return { | ||
99 | template: 'account-abuse-new', | ||
100 | to, | ||
101 | subject: `New account abuse report from ${this.payload.reporter}`, | ||
102 | locals: { | ||
103 | accountUrl, | ||
104 | accountDisplayName: account.getDisplayName(), | ||
105 | isLocal: account.isOwned(), | ||
106 | reason: this.payload.abuse.reason, | ||
107 | reporter: this.payload.reporter, | ||
108 | action: this.buildEmailAction() | ||
109 | } | ||
110 | } | ||
111 | } | ||
112 | |||
113 | private buildEmailAction () { | ||
114 | return { | ||
115 | text: 'View report #' + this.payload.abuseInstance.id, | ||
116 | url: WEBSERVER.URL + '/admin/moderation/abuses/list?search=%23' + this.payload.abuseInstance.id | ||
117 | } | ||
118 | } | ||
119 | } | ||
diff --git a/server/lib/notifier/shared/abuse/new-abuse-message-for-moderators.ts b/server/lib/notifier/shared/abuse/new-abuse-message-for-moderators.ts deleted file mode 100644 index 9d0629690..000000000 --- a/server/lib/notifier/shared/abuse/new-abuse-message-for-moderators.ts +++ /dev/null | |||
@@ -1,32 +0,0 @@ | |||
1 | import { logger } from '@server/helpers/logger' | ||
2 | import { getAbuseTargetUrl } from '@server/lib/activitypub/url' | ||
3 | import { UserModel } from '@server/models/user/user' | ||
4 | import { MUserDefault } from '@server/types/models' | ||
5 | import { UserRight } from '@shared/models' | ||
6 | import { AbstractNewAbuseMessage } from './abstract-new-abuse-message' | ||
7 | |||
8 | export class NewAbuseMessageForModerators extends AbstractNewAbuseMessage { | ||
9 | private moderators: MUserDefault[] | ||
10 | |||
11 | async prepare () { | ||
12 | this.moderators = await UserModel.listWithRight(UserRight.MANAGE_ABUSES) | ||
13 | |||
14 | // Don't notify my own message | ||
15 | this.moderators = this.moderators.filter(m => m.Account.id !== this.message.accountId) | ||
16 | if (this.moderators.length === 0) return | ||
17 | |||
18 | await this.loadMessageAccount() | ||
19 | } | ||
20 | |||
21 | log () { | ||
22 | logger.info('Notifying moderators of new abuse message on %s.', getAbuseTargetUrl(this.abuse)) | ||
23 | } | ||
24 | |||
25 | getTargetUsers () { | ||
26 | return this.moderators | ||
27 | } | ||
28 | |||
29 | createEmail (to: string) { | ||
30 | return this.createEmailFor(to, 'moderator') | ||
31 | } | ||
32 | } | ||
diff --git a/server/lib/notifier/shared/abuse/new-abuse-message-for-reporter.ts b/server/lib/notifier/shared/abuse/new-abuse-message-for-reporter.ts deleted file mode 100644 index c5bbb5447..000000000 --- a/server/lib/notifier/shared/abuse/new-abuse-message-for-reporter.ts +++ /dev/null | |||
@@ -1,36 +0,0 @@ | |||
1 | import { logger } from '@server/helpers/logger' | ||
2 | import { getAbuseTargetUrl } from '@server/lib/activitypub/url' | ||
3 | import { UserModel } from '@server/models/user/user' | ||
4 | import { MUserDefault } from '@server/types/models' | ||
5 | import { AbstractNewAbuseMessage } from './abstract-new-abuse-message' | ||
6 | |||
7 | export class NewAbuseMessageForReporter extends AbstractNewAbuseMessage { | ||
8 | private reporter: MUserDefault | ||
9 | |||
10 | async prepare () { | ||
11 | // Only notify our users | ||
12 | if (this.abuse.ReporterAccount.isOwned() !== true) return | ||
13 | |||
14 | await this.loadMessageAccount() | ||
15 | |||
16 | const reporter = await UserModel.loadByAccountActorId(this.abuse.ReporterAccount.actorId) | ||
17 | // Don't notify my own message | ||
18 | if (reporter.Account.id === this.message.accountId) return | ||
19 | |||
20 | this.reporter = reporter | ||
21 | } | ||
22 | |||
23 | log () { | ||
24 | logger.info('Notifying reporter of new abuse message on %s.', getAbuseTargetUrl(this.abuse)) | ||
25 | } | ||
26 | |||
27 | getTargetUsers () { | ||
28 | if (!this.reporter) return [] | ||
29 | |||
30 | return [ this.reporter ] | ||
31 | } | ||
32 | |||
33 | createEmail (to: string) { | ||
34 | return this.createEmailFor(to, 'reporter') | ||
35 | } | ||
36 | } | ||
diff --git a/server/lib/notifier/shared/blacklist/index.ts b/server/lib/notifier/shared/blacklist/index.ts deleted file mode 100644 index 2f98d88ae..000000000 --- a/server/lib/notifier/shared/blacklist/index.ts +++ /dev/null | |||
@@ -1,3 +0,0 @@ | |||
1 | export * from './new-auto-blacklist-for-moderators' | ||
2 | export * from './new-blacklist-for-owner' | ||
3 | export * from './unblacklist-for-owner' | ||
diff --git a/server/lib/notifier/shared/blacklist/new-auto-blacklist-for-moderators.ts b/server/lib/notifier/shared/blacklist/new-auto-blacklist-for-moderators.ts deleted file mode 100644 index ad2cc00ea..000000000 --- a/server/lib/notifier/shared/blacklist/new-auto-blacklist-for-moderators.ts +++ /dev/null | |||
@@ -1,60 +0,0 @@ | |||
1 | import { logger } from '@server/helpers/logger' | ||
2 | import { WEBSERVER } from '@server/initializers/constants' | ||
3 | import { UserModel } from '@server/models/user/user' | ||
4 | import { UserNotificationModel } from '@server/models/user/user-notification' | ||
5 | import { VideoChannelModel } from '@server/models/video/video-channel' | ||
6 | import { MUserDefault, MUserWithNotificationSetting, MVideoBlacklistLightVideo, UserNotificationModelForApi } from '@server/types/models' | ||
7 | import { UserNotificationType, UserRight } from '@shared/models' | ||
8 | import { AbstractNotification } from '../common/abstract-notification' | ||
9 | |||
10 | export class NewAutoBlacklistForModerators extends AbstractNotification <MVideoBlacklistLightVideo> { | ||
11 | private moderators: MUserDefault[] | ||
12 | |||
13 | async prepare () { | ||
14 | this.moderators = await UserModel.listWithRight(UserRight.MANAGE_VIDEO_BLACKLIST) | ||
15 | } | ||
16 | |||
17 | log () { | ||
18 | logger.info('Notifying %s moderators of video auto-blacklist %s.', this.moderators.length, this.payload.Video.url) | ||
19 | } | ||
20 | |||
21 | getSetting (user: MUserWithNotificationSetting) { | ||
22 | return user.NotificationSetting.videoAutoBlacklistAsModerator | ||
23 | } | ||
24 | |||
25 | getTargetUsers () { | ||
26 | return this.moderators | ||
27 | } | ||
28 | |||
29 | createNotification (user: MUserWithNotificationSetting) { | ||
30 | const notification = UserNotificationModel.build<UserNotificationModelForApi>({ | ||
31 | type: UserNotificationType.VIDEO_AUTO_BLACKLIST_FOR_MODERATORS, | ||
32 | userId: user.id, | ||
33 | videoBlacklistId: this.payload.id | ||
34 | }) | ||
35 | notification.VideoBlacklist = this.payload | ||
36 | |||
37 | return notification | ||
38 | } | ||
39 | |||
40 | async createEmail (to: string) { | ||
41 | const videoAutoBlacklistUrl = WEBSERVER.URL + '/admin/moderation/video-auto-blacklist/list' | ||
42 | const videoUrl = WEBSERVER.URL + this.payload.Video.getWatchStaticPath() | ||
43 | const channel = await VideoChannelModel.loadAndPopulateAccount(this.payload.Video.channelId) | ||
44 | |||
45 | return { | ||
46 | template: 'video-auto-blacklist-new', | ||
47 | to, | ||
48 | subject: 'A new video is pending moderation', | ||
49 | locals: { | ||
50 | channel: channel.toFormattedSummaryJSON(), | ||
51 | videoUrl, | ||
52 | videoName: this.payload.Video.name, | ||
53 | action: { | ||
54 | text: 'Review autoblacklist', | ||
55 | url: videoAutoBlacklistUrl | ||
56 | } | ||
57 | } | ||
58 | } | ||
59 | } | ||
60 | } | ||
diff --git a/server/lib/notifier/shared/blacklist/new-blacklist-for-owner.ts b/server/lib/notifier/shared/blacklist/new-blacklist-for-owner.ts deleted file mode 100644 index 342b69ec7..000000000 --- a/server/lib/notifier/shared/blacklist/new-blacklist-for-owner.ts +++ /dev/null | |||
@@ -1,58 +0,0 @@ | |||
1 | import { logger } from '@server/helpers/logger' | ||
2 | import { CONFIG } from '@server/initializers/config' | ||
3 | import { WEBSERVER } from '@server/initializers/constants' | ||
4 | import { UserModel } from '@server/models/user/user' | ||
5 | import { UserNotificationModel } from '@server/models/user/user-notification' | ||
6 | import { MUserDefault, MUserWithNotificationSetting, MVideoBlacklistVideo, UserNotificationModelForApi } from '@server/types/models' | ||
7 | import { UserNotificationType } from '@shared/models' | ||
8 | import { AbstractNotification } from '../common/abstract-notification' | ||
9 | |||
10 | export class NewBlacklistForOwner extends AbstractNotification <MVideoBlacklistVideo> { | ||
11 | private user: MUserDefault | ||
12 | |||
13 | async prepare () { | ||
14 | this.user = await UserModel.loadByVideoId(this.payload.videoId) | ||
15 | } | ||
16 | |||
17 | log () { | ||
18 | logger.info('Notifying user %s that its video %s has been blacklisted.', this.user.username, this.payload.Video.url) | ||
19 | } | ||
20 | |||
21 | getSetting (user: MUserWithNotificationSetting) { | ||
22 | return user.NotificationSetting.blacklistOnMyVideo | ||
23 | } | ||
24 | |||
25 | getTargetUsers () { | ||
26 | if (!this.user) return [] | ||
27 | |||
28 | return [ this.user ] | ||
29 | } | ||
30 | |||
31 | createNotification (user: MUserWithNotificationSetting) { | ||
32 | const notification = UserNotificationModel.build<UserNotificationModelForApi>({ | ||
33 | type: UserNotificationType.BLACKLIST_ON_MY_VIDEO, | ||
34 | userId: user.id, | ||
35 | videoBlacklistId: this.payload.id | ||
36 | }) | ||
37 | notification.VideoBlacklist = this.payload | ||
38 | |||
39 | return notification | ||
40 | } | ||
41 | |||
42 | createEmail (to: string) { | ||
43 | const videoName = this.payload.Video.name | ||
44 | const videoUrl = WEBSERVER.URL + this.payload.Video.getWatchStaticPath() | ||
45 | |||
46 | const reasonString = this.payload.reason ? ` for the following reason: ${this.payload.reason}` : '' | ||
47 | const blockedString = `Your video ${videoName} (${videoUrl} on ${CONFIG.INSTANCE.NAME} has been blacklisted${reasonString}.` | ||
48 | |||
49 | return { | ||
50 | to, | ||
51 | subject: `Video ${videoName} blacklisted`, | ||
52 | text: blockedString, | ||
53 | locals: { | ||
54 | title: 'Your video was blacklisted' | ||
55 | } | ||
56 | } | ||
57 | } | ||
58 | } | ||
diff --git a/server/lib/notifier/shared/blacklist/unblacklist-for-owner.ts b/server/lib/notifier/shared/blacklist/unblacklist-for-owner.ts deleted file mode 100644 index e6f90e23c..000000000 --- a/server/lib/notifier/shared/blacklist/unblacklist-for-owner.ts +++ /dev/null | |||
@@ -1,55 +0,0 @@ | |||
1 | import { logger } from '@server/helpers/logger' | ||
2 | import { CONFIG } from '@server/initializers/config' | ||
3 | import { WEBSERVER } from '@server/initializers/constants' | ||
4 | import { UserModel } from '@server/models/user/user' | ||
5 | import { UserNotificationModel } from '@server/models/user/user-notification' | ||
6 | import { MUserDefault, MUserWithNotificationSetting, MVideoFullLight, UserNotificationModelForApi } from '@server/types/models' | ||
7 | import { UserNotificationType } from '@shared/models' | ||
8 | import { AbstractNotification } from '../common/abstract-notification' | ||
9 | |||
10 | export class UnblacklistForOwner extends AbstractNotification <MVideoFullLight> { | ||
11 | private user: MUserDefault | ||
12 | |||
13 | async prepare () { | ||
14 | this.user = await UserModel.loadByVideoId(this.payload.id) | ||
15 | } | ||
16 | |||
17 | log () { | ||
18 | logger.info('Notifying user %s that its video %s has been unblacklisted.', this.user.username, this.payload.url) | ||
19 | } | ||
20 | |||
21 | getSetting (user: MUserWithNotificationSetting) { | ||
22 | return user.NotificationSetting.blacklistOnMyVideo | ||
23 | } | ||
24 | |||
25 | getTargetUsers () { | ||
26 | if (!this.user) return [] | ||
27 | |||
28 | return [ this.user ] | ||
29 | } | ||
30 | |||
31 | createNotification (user: MUserWithNotificationSetting) { | ||
32 | const notification = UserNotificationModel.build<UserNotificationModelForApi>({ | ||
33 | type: UserNotificationType.UNBLACKLIST_ON_MY_VIDEO, | ||
34 | userId: user.id, | ||
35 | videoId: this.payload.id | ||
36 | }) | ||
37 | notification.Video = this.payload | ||
38 | |||
39 | return notification | ||
40 | } | ||
41 | |||
42 | createEmail (to: string) { | ||
43 | const video = this.payload | ||
44 | const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() | ||
45 | |||
46 | return { | ||
47 | to, | ||
48 | subject: `Video ${video.name} unblacklisted`, | ||
49 | text: `Your video "${video.name}" (${videoUrl}) on ${CONFIG.INSTANCE.NAME} has been unblacklisted.`, | ||
50 | locals: { | ||
51 | title: 'Your video was unblacklisted' | ||
52 | } | ||
53 | } | ||
54 | } | ||
55 | } | ||
diff --git a/server/lib/notifier/shared/comment/comment-mention.ts b/server/lib/notifier/shared/comment/comment-mention.ts deleted file mode 100644 index 3074e97db..000000000 --- a/server/lib/notifier/shared/comment/comment-mention.ts +++ /dev/null | |||
@@ -1,111 +0,0 @@ | |||
1 | import { logger } from '@server/helpers/logger' | ||
2 | import { toSafeHtml } from '@server/helpers/markdown' | ||
3 | import { WEBSERVER } from '@server/initializers/constants' | ||
4 | import { AccountBlocklistModel } from '@server/models/account/account-blocklist' | ||
5 | import { getServerActor } from '@server/models/application/application' | ||
6 | import { ServerBlocklistModel } from '@server/models/server/server-blocklist' | ||
7 | import { UserModel } from '@server/models/user/user' | ||
8 | import { UserNotificationModel } from '@server/models/user/user-notification' | ||
9 | import { | ||
10 | MCommentOwnerVideo, | ||
11 | MUserDefault, | ||
12 | MUserNotifSettingAccount, | ||
13 | MUserWithNotificationSetting, | ||
14 | UserNotificationModelForApi | ||
15 | } from '@server/types/models' | ||
16 | import { UserNotificationSettingValue, UserNotificationType } from '@shared/models' | ||
17 | import { AbstractNotification } from '../common' | ||
18 | |||
19 | export class CommentMention extends AbstractNotification <MCommentOwnerVideo, MUserNotifSettingAccount> { | ||
20 | private users: MUserDefault[] | ||
21 | |||
22 | private serverAccountId: number | ||
23 | |||
24 | private accountMutedHash: { [ id: number ]: boolean } | ||
25 | private instanceMutedHash: { [ id: number ]: boolean } | ||
26 | |||
27 | async prepare () { | ||
28 | const extractedUsernames = this.payload.extractMentions() | ||
29 | logger.debug( | ||
30 | 'Extracted %d username from comment %s.', extractedUsernames.length, this.payload.url, | ||
31 | { usernames: extractedUsernames, text: this.payload.text } | ||
32 | ) | ||
33 | |||
34 | this.users = await UserModel.listByUsernames(extractedUsernames) | ||
35 | |||
36 | if (this.payload.Video.isOwned()) { | ||
37 | const userException = await UserModel.loadByVideoId(this.payload.videoId) | ||
38 | this.users = this.users.filter(u => u.id !== userException.id) | ||
39 | } | ||
40 | |||
41 | // Don't notify if I mentioned myself | ||
42 | this.users = this.users.filter(u => u.Account.id !== this.payload.accountId) | ||
43 | |||
44 | if (this.users.length === 0) return | ||
45 | |||
46 | this.serverAccountId = (await getServerActor()).Account.id | ||
47 | |||
48 | const sourceAccounts = this.users.map(u => u.Account.id).concat([ this.serverAccountId ]) | ||
49 | |||
50 | this.accountMutedHash = await AccountBlocklistModel.isAccountMutedByAccounts(sourceAccounts, this.payload.accountId) | ||
51 | this.instanceMutedHash = await ServerBlocklistModel.isServerMutedByAccounts(sourceAccounts, this.payload.Account.Actor.serverId) | ||
52 | } | ||
53 | |||
54 | log () { | ||
55 | logger.info('Notifying %d users of new comment %s.', this.users.length, this.payload.url) | ||
56 | } | ||
57 | |||
58 | getSetting (user: MUserNotifSettingAccount) { | ||
59 | const accountId = user.Account.id | ||
60 | if ( | ||
61 | this.accountMutedHash[accountId] === true || this.instanceMutedHash[accountId] === true || | ||
62 | this.accountMutedHash[this.serverAccountId] === true || this.instanceMutedHash[this.serverAccountId] === true | ||
63 | ) { | ||
64 | return UserNotificationSettingValue.NONE | ||
65 | } | ||
66 | |||
67 | return user.NotificationSetting.commentMention | ||
68 | } | ||
69 | |||
70 | getTargetUsers () { | ||
71 | return this.users | ||
72 | } | ||
73 | |||
74 | createNotification (user: MUserWithNotificationSetting) { | ||
75 | const notification = UserNotificationModel.build<UserNotificationModelForApi>({ | ||
76 | type: UserNotificationType.COMMENT_MENTION, | ||
77 | userId: user.id, | ||
78 | commentId: this.payload.id | ||
79 | }) | ||
80 | notification.VideoComment = this.payload | ||
81 | |||
82 | return notification | ||
83 | } | ||
84 | |||
85 | createEmail (to: string) { | ||
86 | const comment = this.payload | ||
87 | |||
88 | const accountName = comment.Account.getDisplayName() | ||
89 | const video = comment.Video | ||
90 | const videoUrl = WEBSERVER.URL + comment.Video.getWatchStaticPath() | ||
91 | const commentUrl = WEBSERVER.URL + comment.getCommentStaticPath() | ||
92 | const commentHtml = toSafeHtml(comment.text) | ||
93 | |||
94 | return { | ||
95 | template: 'video-comment-mention', | ||
96 | to, | ||
97 | subject: 'Mention on video ' + video.name, | ||
98 | locals: { | ||
99 | comment, | ||
100 | commentHtml, | ||
101 | video, | ||
102 | videoUrl, | ||
103 | accountName, | ||
104 | action: { | ||
105 | text: 'View comment', | ||
106 | url: commentUrl | ||
107 | } | ||
108 | } | ||
109 | } | ||
110 | } | ||
111 | } | ||
diff --git a/server/lib/notifier/shared/comment/index.ts b/server/lib/notifier/shared/comment/index.ts deleted file mode 100644 index ae01a9646..000000000 --- a/server/lib/notifier/shared/comment/index.ts +++ /dev/null | |||
@@ -1,2 +0,0 @@ | |||
1 | export * from './comment-mention' | ||
2 | export * from './new-comment-for-video-owner' | ||
diff --git a/server/lib/notifier/shared/comment/new-comment-for-video-owner.ts b/server/lib/notifier/shared/comment/new-comment-for-video-owner.ts deleted file mode 100644 index 4f96439a3..000000000 --- a/server/lib/notifier/shared/comment/new-comment-for-video-owner.ts +++ /dev/null | |||
@@ -1,76 +0,0 @@ | |||
1 | import { logger } from '@server/helpers/logger' | ||
2 | import { toSafeHtml } from '@server/helpers/markdown' | ||
3 | import { WEBSERVER } from '@server/initializers/constants' | ||
4 | import { isBlockedByServerOrAccount } from '@server/lib/blocklist' | ||
5 | import { UserModel } from '@server/models/user/user' | ||
6 | import { UserNotificationModel } from '@server/models/user/user-notification' | ||
7 | import { MCommentOwnerVideo, MUserDefault, MUserWithNotificationSetting, UserNotificationModelForApi } from '@server/types/models' | ||
8 | import { UserNotificationType } from '@shared/models' | ||
9 | import { AbstractNotification } from '../common/abstract-notification' | ||
10 | |||
11 | export class NewCommentForVideoOwner extends AbstractNotification <MCommentOwnerVideo> { | ||
12 | private user: MUserDefault | ||
13 | |||
14 | async prepare () { | ||
15 | this.user = await UserModel.loadByVideoId(this.payload.videoId) | ||
16 | } | ||
17 | |||
18 | log () { | ||
19 | logger.info('Notifying owner of a video %s of new comment %s.', this.user.username, this.payload.url) | ||
20 | } | ||
21 | |||
22 | isDisabled () { | ||
23 | if (this.payload.Video.isOwned() === false) return true | ||
24 | |||
25 | // Not our user or user comments its own video | ||
26 | if (!this.user || this.payload.Account.userId === this.user.id) return true | ||
27 | |||
28 | return isBlockedByServerOrAccount(this.payload.Account, this.user.Account) | ||
29 | } | ||
30 | |||
31 | getSetting (user: MUserWithNotificationSetting) { | ||
32 | return user.NotificationSetting.newCommentOnMyVideo | ||
33 | } | ||
34 | |||
35 | getTargetUsers () { | ||
36 | if (!this.user) return [] | ||
37 | |||
38 | return [ this.user ] | ||
39 | } | ||
40 | |||
41 | createNotification (user: MUserWithNotificationSetting) { | ||
42 | const notification = UserNotificationModel.build<UserNotificationModelForApi>({ | ||
43 | type: UserNotificationType.NEW_COMMENT_ON_MY_VIDEO, | ||
44 | userId: user.id, | ||
45 | commentId: this.payload.id | ||
46 | }) | ||
47 | notification.VideoComment = this.payload | ||
48 | |||
49 | return notification | ||
50 | } | ||
51 | |||
52 | createEmail (to: string) { | ||
53 | const video = this.payload.Video | ||
54 | const videoUrl = WEBSERVER.URL + this.payload.Video.getWatchStaticPath() | ||
55 | const commentUrl = WEBSERVER.URL + this.payload.getCommentStaticPath() | ||
56 | const commentHtml = toSafeHtml(this.payload.text) | ||
57 | |||
58 | return { | ||
59 | template: 'video-comment-new', | ||
60 | to, | ||
61 | subject: 'New comment on your video ' + video.name, | ||
62 | locals: { | ||
63 | accountName: this.payload.Account.getDisplayName(), | ||
64 | accountUrl: this.payload.Account.Actor.url, | ||
65 | comment: this.payload, | ||
66 | commentHtml, | ||
67 | video, | ||
68 | videoUrl, | ||
69 | action: { | ||
70 | text: 'View comment', | ||
71 | url: commentUrl | ||
72 | } | ||
73 | } | ||
74 | } | ||
75 | } | ||
76 | } | ||
diff --git a/server/lib/notifier/shared/common/abstract-notification.ts b/server/lib/notifier/shared/common/abstract-notification.ts deleted file mode 100644 index 79403611e..000000000 --- a/server/lib/notifier/shared/common/abstract-notification.ts +++ /dev/null | |||
@@ -1,23 +0,0 @@ | |||
1 | import { MUserWithNotificationSetting, UserNotificationModelForApi } from '@server/types/models' | ||
2 | import { EmailPayload, UserNotificationSettingValue } from '@shared/models' | ||
3 | |||
4 | export abstract class AbstractNotification <T, U = MUserWithNotificationSetting> { | ||
5 | |||
6 | constructor (protected readonly payload: T) { | ||
7 | |||
8 | } | ||
9 | |||
10 | abstract prepare (): Promise<void> | ||
11 | abstract log (): void | ||
12 | |||
13 | abstract getSetting (user: U): UserNotificationSettingValue | ||
14 | abstract getTargetUsers (): U[] | ||
15 | |||
16 | abstract createNotification (user: U): UserNotificationModelForApi | ||
17 | abstract createEmail (to: string): EmailPayload | Promise<EmailPayload> | ||
18 | |||
19 | isDisabled (): boolean | Promise<boolean> { | ||
20 | return false | ||
21 | } | ||
22 | |||
23 | } | ||
diff --git a/server/lib/notifier/shared/common/index.ts b/server/lib/notifier/shared/common/index.ts deleted file mode 100644 index 0b2570278..000000000 --- a/server/lib/notifier/shared/common/index.ts +++ /dev/null | |||
@@ -1 +0,0 @@ | |||
1 | export * from './abstract-notification' | ||
diff --git a/server/lib/notifier/shared/follow/auto-follow-for-instance.ts b/server/lib/notifier/shared/follow/auto-follow-for-instance.ts deleted file mode 100644 index ab9747ba8..000000000 --- a/server/lib/notifier/shared/follow/auto-follow-for-instance.ts +++ /dev/null | |||
@@ -1,51 +0,0 @@ | |||
1 | import { logger } from '@server/helpers/logger' | ||
2 | import { UserModel } from '@server/models/user/user' | ||
3 | import { UserNotificationModel } from '@server/models/user/user-notification' | ||
4 | import { MActorFollowFull, MUserDefault, MUserWithNotificationSetting, UserNotificationModelForApi } from '@server/types/models' | ||
5 | import { UserNotificationType, UserRight } from '@shared/models' | ||
6 | import { AbstractNotification } from '../common/abstract-notification' | ||
7 | |||
8 | export class AutoFollowForInstance extends AbstractNotification <MActorFollowFull> { | ||
9 | private admins: MUserDefault[] | ||
10 | |||
11 | async prepare () { | ||
12 | this.admins = await UserModel.listWithRight(UserRight.MANAGE_SERVER_FOLLOW) | ||
13 | } | ||
14 | |||
15 | log () { | ||
16 | logger.info('Notifying %d administrators of auto instance following: %s.', this.admins.length, this.actorFollow.ActorFollowing.url) | ||
17 | } | ||
18 | |||
19 | getSetting (user: MUserWithNotificationSetting) { | ||
20 | return user.NotificationSetting.autoInstanceFollowing | ||
21 | } | ||
22 | |||
23 | getTargetUsers () { | ||
24 | return this.admins | ||
25 | } | ||
26 | |||
27 | createNotification (user: MUserWithNotificationSetting) { | ||
28 | const notification = UserNotificationModel.build<UserNotificationModelForApi>({ | ||
29 | type: UserNotificationType.AUTO_INSTANCE_FOLLOWING, | ||
30 | userId: user.id, | ||
31 | actorFollowId: this.actorFollow.id | ||
32 | }) | ||
33 | notification.ActorFollow = this.actorFollow | ||
34 | |||
35 | return notification | ||
36 | } | ||
37 | |||
38 | createEmail (to: string) { | ||
39 | const instanceUrl = this.actorFollow.ActorFollowing.url | ||
40 | |||
41 | return { | ||
42 | to, | ||
43 | subject: 'Auto instance following', | ||
44 | text: `Your instance automatically followed a new instance: <a href="${instanceUrl}">${instanceUrl}</a>.` | ||
45 | } | ||
46 | } | ||
47 | |||
48 | private get actorFollow () { | ||
49 | return this.payload | ||
50 | } | ||
51 | } | ||
diff --git a/server/lib/notifier/shared/follow/follow-for-instance.ts b/server/lib/notifier/shared/follow/follow-for-instance.ts deleted file mode 100644 index 777a12ef4..000000000 --- a/server/lib/notifier/shared/follow/follow-for-instance.ts +++ /dev/null | |||
@@ -1,68 +0,0 @@ | |||
1 | import { logger } from '@server/helpers/logger' | ||
2 | import { WEBSERVER } from '@server/initializers/constants' | ||
3 | import { isBlockedByServerOrAccount } from '@server/lib/blocklist' | ||
4 | import { UserModel } from '@server/models/user/user' | ||
5 | import { UserNotificationModel } from '@server/models/user/user-notification' | ||
6 | import { MActorFollowFull, MUserDefault, MUserWithNotificationSetting, UserNotificationModelForApi } from '@server/types/models' | ||
7 | import { UserNotificationType, UserRight } from '@shared/models' | ||
8 | import { AbstractNotification } from '../common/abstract-notification' | ||
9 | |||
10 | export class FollowForInstance extends AbstractNotification <MActorFollowFull> { | ||
11 | private admins: MUserDefault[] | ||
12 | |||
13 | async prepare () { | ||
14 | this.admins = await UserModel.listWithRight(UserRight.MANAGE_SERVER_FOLLOW) | ||
15 | } | ||
16 | |||
17 | isDisabled () { | ||
18 | const follower = Object.assign(this.actorFollow.ActorFollower.Account, { Actor: this.actorFollow.ActorFollower }) | ||
19 | |||
20 | return isBlockedByServerOrAccount(follower) | ||
21 | } | ||
22 | |||
23 | log () { | ||
24 | logger.info('Notifying %d administrators of new instance follower: %s.', this.admins.length, this.actorFollow.ActorFollower.url) | ||
25 | } | ||
26 | |||
27 | getSetting (user: MUserWithNotificationSetting) { | ||
28 | return user.NotificationSetting.newInstanceFollower | ||
29 | } | ||
30 | |||
31 | getTargetUsers () { | ||
32 | return this.admins | ||
33 | } | ||
34 | |||
35 | createNotification (user: MUserWithNotificationSetting) { | ||
36 | const notification = UserNotificationModel.build<UserNotificationModelForApi>({ | ||
37 | type: UserNotificationType.NEW_INSTANCE_FOLLOWER, | ||
38 | userId: user.id, | ||
39 | actorFollowId: this.actorFollow.id | ||
40 | }) | ||
41 | notification.ActorFollow = this.actorFollow | ||
42 | |||
43 | return notification | ||
44 | } | ||
45 | |||
46 | createEmail (to: string) { | ||
47 | const awaitingApproval = this.actorFollow.state === 'pending' | ||
48 | ? ' awaiting manual approval.' | ||
49 | : '' | ||
50 | |||
51 | return { | ||
52 | to, | ||
53 | subject: 'New instance follower', | ||
54 | text: `Your instance has a new follower: ${this.actorFollow.ActorFollower.url}${awaitingApproval}.`, | ||
55 | locals: { | ||
56 | title: 'New instance follower', | ||
57 | action: { | ||
58 | text: 'Review followers', | ||
59 | url: WEBSERVER.URL + '/admin/follows/followers-list' | ||
60 | } | ||
61 | } | ||
62 | } | ||
63 | } | ||
64 | |||
65 | private get actorFollow () { | ||
66 | return this.payload | ||
67 | } | ||
68 | } | ||
diff --git a/server/lib/notifier/shared/follow/follow-for-user.ts b/server/lib/notifier/shared/follow/follow-for-user.ts deleted file mode 100644 index 697c82cdd..000000000 --- a/server/lib/notifier/shared/follow/follow-for-user.ts +++ /dev/null | |||
@@ -1,82 +0,0 @@ | |||
1 | import { logger } from '@server/helpers/logger' | ||
2 | import { isBlockedByServerOrAccount } from '@server/lib/blocklist' | ||
3 | import { UserModel } from '@server/models/user/user' | ||
4 | import { UserNotificationModel } from '@server/models/user/user-notification' | ||
5 | import { MActorFollowFull, MUserDefault, MUserWithNotificationSetting, UserNotificationModelForApi } from '@server/types/models' | ||
6 | import { UserNotificationType } from '@shared/models' | ||
7 | import { AbstractNotification } from '../common/abstract-notification' | ||
8 | |||
9 | export class FollowForUser extends AbstractNotification <MActorFollowFull> { | ||
10 | private followType: 'account' | 'channel' | ||
11 | private user: MUserDefault | ||
12 | |||
13 | async prepare () { | ||
14 | // Account follows one of our account? | ||
15 | this.followType = 'channel' | ||
16 | this.user = await UserModel.loadByChannelActorId(this.actorFollow.ActorFollowing.id) | ||
17 | |||
18 | // Account follows one of our channel? | ||
19 | if (!this.user) { | ||
20 | this.user = await UserModel.loadByAccountActorId(this.actorFollow.ActorFollowing.id) | ||
21 | this.followType = 'account' | ||
22 | } | ||
23 | } | ||
24 | |||
25 | async isDisabled () { | ||
26 | if (this.payload.ActorFollowing.isOwned() === false) return true | ||
27 | |||
28 | const followerAccount = this.actorFollow.ActorFollower.Account | ||
29 | const followerAccountWithActor = Object.assign(followerAccount, { Actor: this.actorFollow.ActorFollower }) | ||
30 | |||
31 | return isBlockedByServerOrAccount(followerAccountWithActor, this.user.Account) | ||
32 | } | ||
33 | |||
34 | log () { | ||
35 | logger.info('Notifying user %s of new follower: %s.', this.user.username, this.actorFollow.ActorFollower.Account.getDisplayName()) | ||
36 | } | ||
37 | |||
38 | getSetting (user: MUserWithNotificationSetting) { | ||
39 | return user.NotificationSetting.newFollow | ||
40 | } | ||
41 | |||
42 | getTargetUsers () { | ||
43 | if (!this.user) return [] | ||
44 | |||
45 | return [ this.user ] | ||
46 | } | ||
47 | |||
48 | createNotification (user: MUserWithNotificationSetting) { | ||
49 | const notification = UserNotificationModel.build<UserNotificationModelForApi>({ | ||
50 | type: UserNotificationType.NEW_FOLLOW, | ||
51 | userId: user.id, | ||
52 | actorFollowId: this.actorFollow.id | ||
53 | }) | ||
54 | notification.ActorFollow = this.actorFollow | ||
55 | |||
56 | return notification | ||
57 | } | ||
58 | |||
59 | createEmail (to: string) { | ||
60 | const following = this.actorFollow.ActorFollowing | ||
61 | const follower = this.actorFollow.ActorFollower | ||
62 | |||
63 | const followingName = (following.VideoChannel || following.Account).getDisplayName() | ||
64 | |||
65 | return { | ||
66 | template: 'follower-on-channel', | ||
67 | to, | ||
68 | subject: `New follower on your channel ${followingName}`, | ||
69 | locals: { | ||
70 | followerName: follower.Account.getDisplayName(), | ||
71 | followerUrl: follower.url, | ||
72 | followingName, | ||
73 | followingUrl: following.url, | ||
74 | followType: this.followType | ||
75 | } | ||
76 | } | ||
77 | } | ||
78 | |||
79 | private get actorFollow () { | ||
80 | return this.payload | ||
81 | } | ||
82 | } | ||
diff --git a/server/lib/notifier/shared/follow/index.ts b/server/lib/notifier/shared/follow/index.ts deleted file mode 100644 index 27f5289d9..000000000 --- a/server/lib/notifier/shared/follow/index.ts +++ /dev/null | |||
@@ -1,3 +0,0 @@ | |||
1 | export * from './auto-follow-for-instance' | ||
2 | export * from './follow-for-instance' | ||
3 | export * from './follow-for-user' | ||
diff --git a/server/lib/notifier/shared/index.ts b/server/lib/notifier/shared/index.ts deleted file mode 100644 index cc3ce8c7c..000000000 --- a/server/lib/notifier/shared/index.ts +++ /dev/null | |||
@@ -1,7 +0,0 @@ | |||
1 | export * from './abuse' | ||
2 | export * from './blacklist' | ||
3 | export * from './comment' | ||
4 | export * from './common' | ||
5 | export * from './follow' | ||
6 | export * from './instance' | ||
7 | export * from './video-publication' | ||
diff --git a/server/lib/notifier/shared/instance/direct-registration-for-moderators.ts b/server/lib/notifier/shared/instance/direct-registration-for-moderators.ts deleted file mode 100644 index 5044f2068..000000000 --- a/server/lib/notifier/shared/instance/direct-registration-for-moderators.ts +++ /dev/null | |||
@@ -1,49 +0,0 @@ | |||
1 | import { logger } from '@server/helpers/logger' | ||
2 | import { CONFIG } from '@server/initializers/config' | ||
3 | import { UserModel } from '@server/models/user/user' | ||
4 | import { UserNotificationModel } from '@server/models/user/user-notification' | ||
5 | import { MUserDefault, MUserWithNotificationSetting, UserNotificationModelForApi } from '@server/types/models' | ||
6 | import { UserNotificationType, UserRight } from '@shared/models' | ||
7 | import { AbstractNotification } from '../common/abstract-notification' | ||
8 | |||
9 | export class DirectRegistrationForModerators extends AbstractNotification <MUserDefault> { | ||
10 | private moderators: MUserDefault[] | ||
11 | |||
12 | async prepare () { | ||
13 | this.moderators = await UserModel.listWithRight(UserRight.MANAGE_USERS) | ||
14 | } | ||
15 | |||
16 | log () { | ||
17 | logger.info('Notifying %s moderators of new user registration of %s.', this.moderators.length, this.payload.username) | ||
18 | } | ||
19 | |||
20 | getSetting (user: MUserWithNotificationSetting) { | ||
21 | return user.NotificationSetting.newUserRegistration | ||
22 | } | ||
23 | |||
24 | getTargetUsers () { | ||
25 | return this.moderators | ||
26 | } | ||
27 | |||
28 | createNotification (user: MUserWithNotificationSetting) { | ||
29 | const notification = UserNotificationModel.build<UserNotificationModelForApi>({ | ||
30 | type: UserNotificationType.NEW_USER_REGISTRATION, | ||
31 | userId: user.id, | ||
32 | accountId: this.payload.Account.id | ||
33 | }) | ||
34 | notification.Account = this.payload.Account | ||
35 | |||
36 | return notification | ||
37 | } | ||
38 | |||
39 | createEmail (to: string) { | ||
40 | return { | ||
41 | template: 'user-registered', | ||
42 | to, | ||
43 | subject: `A new user registered on ${CONFIG.INSTANCE.NAME}: ${this.payload.username}`, | ||
44 | locals: { | ||
45 | user: this.payload | ||
46 | } | ||
47 | } | ||
48 | } | ||
49 | } | ||
diff --git a/server/lib/notifier/shared/instance/index.ts b/server/lib/notifier/shared/instance/index.ts deleted file mode 100644 index 8c75a8ee9..000000000 --- a/server/lib/notifier/shared/instance/index.ts +++ /dev/null | |||
@@ -1,4 +0,0 @@ | |||
1 | export * from './new-peertube-version-for-admins' | ||
2 | export * from './new-plugin-version-for-admins' | ||
3 | export * from './direct-registration-for-moderators' | ||
4 | export * from './registration-request-for-moderators' | ||
diff --git a/server/lib/notifier/shared/instance/new-peertube-version-for-admins.ts b/server/lib/notifier/shared/instance/new-peertube-version-for-admins.ts deleted file mode 100644 index f5646c666..000000000 --- a/server/lib/notifier/shared/instance/new-peertube-version-for-admins.ts +++ /dev/null | |||
@@ -1,54 +0,0 @@ | |||
1 | import { logger } from '@server/helpers/logger' | ||
2 | import { UserModel } from '@server/models/user/user' | ||
3 | import { UserNotificationModel } from '@server/models/user/user-notification' | ||
4 | import { MApplication, MUserDefault, MUserWithNotificationSetting, UserNotificationModelForApi } from '@server/types/models' | ||
5 | import { UserNotificationType, UserRight } from '@shared/models' | ||
6 | import { AbstractNotification } from '../common/abstract-notification' | ||
7 | |||
8 | export type NewPeerTubeVersionForAdminsPayload = { | ||
9 | application: MApplication | ||
10 | latestVersion: string | ||
11 | } | ||
12 | |||
13 | export class NewPeerTubeVersionForAdmins extends AbstractNotification <NewPeerTubeVersionForAdminsPayload> { | ||
14 | private admins: MUserDefault[] | ||
15 | |||
16 | async prepare () { | ||
17 | // Use the debug right to know who is an administrator | ||
18 | this.admins = await UserModel.listWithRight(UserRight.MANAGE_DEBUG) | ||
19 | } | ||
20 | |||
21 | log () { | ||
22 | logger.info('Notifying %s admins of new PeerTube version %s.', this.admins.length, this.payload.latestVersion) | ||
23 | } | ||
24 | |||
25 | getSetting (user: MUserWithNotificationSetting) { | ||
26 | return user.NotificationSetting.newPeerTubeVersion | ||
27 | } | ||
28 | |||
29 | getTargetUsers () { | ||
30 | return this.admins | ||
31 | } | ||
32 | |||
33 | createNotification (user: MUserWithNotificationSetting) { | ||
34 | const notification = UserNotificationModel.build<UserNotificationModelForApi>({ | ||
35 | type: UserNotificationType.NEW_PEERTUBE_VERSION, | ||
36 | userId: user.id, | ||
37 | applicationId: this.payload.application.id | ||
38 | }) | ||
39 | notification.Application = this.payload.application | ||
40 | |||
41 | return notification | ||
42 | } | ||
43 | |||
44 | createEmail (to: string) { | ||
45 | return { | ||
46 | to, | ||
47 | template: 'peertube-version-new', | ||
48 | subject: `A new PeerTube version is available: ${this.payload.latestVersion}`, | ||
49 | locals: { | ||
50 | latestVersion: this.payload.latestVersion | ||
51 | } | ||
52 | } | ||
53 | } | ||
54 | } | ||
diff --git a/server/lib/notifier/shared/instance/new-plugin-version-for-admins.ts b/server/lib/notifier/shared/instance/new-plugin-version-for-admins.ts deleted file mode 100644 index 547c6726c..000000000 --- a/server/lib/notifier/shared/instance/new-plugin-version-for-admins.ts +++ /dev/null | |||
@@ -1,58 +0,0 @@ | |||
1 | import { logger } from '@server/helpers/logger' | ||
2 | import { WEBSERVER } from '@server/initializers/constants' | ||
3 | import { UserModel } from '@server/models/user/user' | ||
4 | import { UserNotificationModel } from '@server/models/user/user-notification' | ||
5 | import { MPlugin, MUserDefault, MUserWithNotificationSetting, UserNotificationModelForApi } from '@server/types/models' | ||
6 | import { UserNotificationType, UserRight } from '@shared/models' | ||
7 | import { AbstractNotification } from '../common/abstract-notification' | ||
8 | |||
9 | export class NewPluginVersionForAdmins extends AbstractNotification <MPlugin> { | ||
10 | private admins: MUserDefault[] | ||
11 | |||
12 | async prepare () { | ||
13 | // Use the debug right to know who is an administrator | ||
14 | this.admins = await UserModel.listWithRight(UserRight.MANAGE_DEBUG) | ||
15 | } | ||
16 | |||
17 | log () { | ||
18 | logger.info('Notifying %s admins of new PeerTube version %s.', this.admins.length, this.payload.latestVersion) | ||
19 | } | ||
20 | |||
21 | getSetting (user: MUserWithNotificationSetting) { | ||
22 | return user.NotificationSetting.newPluginVersion | ||
23 | } | ||
24 | |||
25 | getTargetUsers () { | ||
26 | return this.admins | ||
27 | } | ||
28 | |||
29 | createNotification (user: MUserWithNotificationSetting) { | ||
30 | const notification = UserNotificationModel.build<UserNotificationModelForApi>({ | ||
31 | type: UserNotificationType.NEW_PLUGIN_VERSION, | ||
32 | userId: user.id, | ||
33 | pluginId: this.plugin.id | ||
34 | }) | ||
35 | notification.Plugin = this.plugin | ||
36 | |||
37 | return notification | ||
38 | } | ||
39 | |||
40 | createEmail (to: string) { | ||
41 | const pluginUrl = WEBSERVER.URL + '/admin/plugins/list-installed?pluginType=' + this.plugin.type | ||
42 | |||
43 | return { | ||
44 | to, | ||
45 | template: 'plugin-version-new', | ||
46 | subject: `A new plugin/theme version is available: ${this.plugin.name}@${this.plugin.latestVersion}`, | ||
47 | locals: { | ||
48 | pluginName: this.plugin.name, | ||
49 | latestVersion: this.plugin.latestVersion, | ||
50 | pluginUrl | ||
51 | } | ||
52 | } | ||
53 | } | ||
54 | |||
55 | private get plugin () { | ||
56 | return this.payload | ||
57 | } | ||
58 | } | ||
diff --git a/server/lib/notifier/shared/instance/registration-request-for-moderators.ts b/server/lib/notifier/shared/instance/registration-request-for-moderators.ts deleted file mode 100644 index 79920245a..000000000 --- a/server/lib/notifier/shared/instance/registration-request-for-moderators.ts +++ /dev/null | |||
@@ -1,48 +0,0 @@ | |||
1 | import { logger } from '@server/helpers/logger' | ||
2 | import { UserModel } from '@server/models/user/user' | ||
3 | import { UserNotificationModel } from '@server/models/user/user-notification' | ||
4 | import { MRegistration, MUserDefault, MUserWithNotificationSetting, UserNotificationModelForApi } from '@server/types/models' | ||
5 | import { UserNotificationType, UserRight } from '@shared/models' | ||
6 | import { AbstractNotification } from '../common/abstract-notification' | ||
7 | |||
8 | export class RegistrationRequestForModerators extends AbstractNotification <MRegistration> { | ||
9 | private moderators: MUserDefault[] | ||
10 | |||
11 | async prepare () { | ||
12 | this.moderators = await UserModel.listWithRight(UserRight.MANAGE_REGISTRATIONS) | ||
13 | } | ||
14 | |||
15 | log () { | ||
16 | logger.info('Notifying %s moderators of new user registration request of %s.', this.moderators.length, this.payload.username) | ||
17 | } | ||
18 | |||
19 | getSetting (user: MUserWithNotificationSetting) { | ||
20 | return user.NotificationSetting.newUserRegistration | ||
21 | } | ||
22 | |||
23 | getTargetUsers () { | ||
24 | return this.moderators | ||
25 | } | ||
26 | |||
27 | createNotification (user: MUserWithNotificationSetting) { | ||
28 | const notification = UserNotificationModel.build<UserNotificationModelForApi>({ | ||
29 | type: UserNotificationType.NEW_USER_REGISTRATION_REQUEST, | ||
30 | userId: user.id, | ||
31 | userRegistrationId: this.payload.id | ||
32 | }) | ||
33 | notification.UserRegistration = this.payload | ||
34 | |||
35 | return notification | ||
36 | } | ||
37 | |||
38 | createEmail (to: string) { | ||
39 | return { | ||
40 | template: 'user-registration-request', | ||
41 | to, | ||
42 | subject: `A new user wants to register: ${this.payload.username}`, | ||
43 | locals: { | ||
44 | registration: this.payload | ||
45 | } | ||
46 | } | ||
47 | } | ||
48 | } | ||
diff --git a/server/lib/notifier/shared/video-publication/abstract-owned-video-publication.ts b/server/lib/notifier/shared/video-publication/abstract-owned-video-publication.ts deleted file mode 100644 index a940cde69..000000000 --- a/server/lib/notifier/shared/video-publication/abstract-owned-video-publication.ts +++ /dev/null | |||
@@ -1,57 +0,0 @@ | |||
1 | import { logger } from '@server/helpers/logger' | ||
2 | import { WEBSERVER } from '@server/initializers/constants' | ||
3 | import { UserModel } from '@server/models/user/user' | ||
4 | import { UserNotificationModel } from '@server/models/user/user-notification' | ||
5 | import { MUserDefault, MUserWithNotificationSetting, MVideoFullLight, UserNotificationModelForApi } from '@server/types/models' | ||
6 | import { UserNotificationType } from '@shared/models' | ||
7 | import { AbstractNotification } from '../common/abstract-notification' | ||
8 | |||
9 | export abstract class AbstractOwnedVideoPublication extends AbstractNotification <MVideoFullLight> { | ||
10 | protected user: MUserDefault | ||
11 | |||
12 | async prepare () { | ||
13 | this.user = await UserModel.loadByVideoId(this.payload.id) | ||
14 | } | ||
15 | |||
16 | log () { | ||
17 | logger.info('Notifying user %s of the publication of its video %s.', this.user.username, this.payload.url) | ||
18 | } | ||
19 | |||
20 | getSetting (user: MUserWithNotificationSetting) { | ||
21 | return user.NotificationSetting.myVideoPublished | ||
22 | } | ||
23 | |||
24 | getTargetUsers () { | ||
25 | if (!this.user) return [] | ||
26 | |||
27 | return [ this.user ] | ||
28 | } | ||
29 | |||
30 | createNotification (user: MUserWithNotificationSetting) { | ||
31 | const notification = UserNotificationModel.build<UserNotificationModelForApi>({ | ||
32 | type: UserNotificationType.MY_VIDEO_PUBLISHED, | ||
33 | userId: user.id, | ||
34 | videoId: this.payload.id | ||
35 | }) | ||
36 | notification.Video = this.payload | ||
37 | |||
38 | return notification | ||
39 | } | ||
40 | |||
41 | createEmail (to: string) { | ||
42 | const videoUrl = WEBSERVER.URL + this.payload.getWatchStaticPath() | ||
43 | |||
44 | return { | ||
45 | to, | ||
46 | subject: `Your video ${this.payload.name} has been published`, | ||
47 | text: `Your video "${this.payload.name}" has been published.`, | ||
48 | locals: { | ||
49 | title: 'Your video is live', | ||
50 | action: { | ||
51 | text: 'View video', | ||
52 | url: videoUrl | ||
53 | } | ||
54 | } | ||
55 | } | ||
56 | } | ||
57 | } | ||
diff --git a/server/lib/notifier/shared/video-publication/import-finished-for-owner.ts b/server/lib/notifier/shared/video-publication/import-finished-for-owner.ts deleted file mode 100644 index 3bd64692f..000000000 --- a/server/lib/notifier/shared/video-publication/import-finished-for-owner.ts +++ /dev/null | |||
@@ -1,97 +0,0 @@ | |||
1 | import { logger } from '@server/helpers/logger' | ||
2 | import { WEBSERVER } from '@server/initializers/constants' | ||
3 | import { UserModel } from '@server/models/user/user' | ||
4 | import { UserNotificationModel } from '@server/models/user/user-notification' | ||
5 | import { MUserDefault, MUserWithNotificationSetting, MVideoImportVideo, UserNotificationModelForApi } from '@server/types/models' | ||
6 | import { UserNotificationType } from '@shared/models' | ||
7 | import { AbstractNotification } from '../common/abstract-notification' | ||
8 | |||
9 | export type ImportFinishedForOwnerPayload = { | ||
10 | videoImport: MVideoImportVideo | ||
11 | success: boolean | ||
12 | } | ||
13 | |||
14 | export class ImportFinishedForOwner extends AbstractNotification <ImportFinishedForOwnerPayload> { | ||
15 | private user: MUserDefault | ||
16 | |||
17 | async prepare () { | ||
18 | this.user = await UserModel.loadByVideoImportId(this.videoImport.id) | ||
19 | } | ||
20 | |||
21 | log () { | ||
22 | logger.info('Notifying user %s its video import %s is finished.', this.user.username, this.videoImport.getTargetIdentifier()) | ||
23 | } | ||
24 | |||
25 | getSetting (user: MUserWithNotificationSetting) { | ||
26 | return user.NotificationSetting.myVideoImportFinished | ||
27 | } | ||
28 | |||
29 | getTargetUsers () { | ||
30 | if (!this.user) return [] | ||
31 | |||
32 | return [ this.user ] | ||
33 | } | ||
34 | |||
35 | createNotification (user: MUserWithNotificationSetting) { | ||
36 | const notification = UserNotificationModel.build<UserNotificationModelForApi>({ | ||
37 | type: this.payload.success | ||
38 | ? UserNotificationType.MY_VIDEO_IMPORT_SUCCESS | ||
39 | : UserNotificationType.MY_VIDEO_IMPORT_ERROR, | ||
40 | |||
41 | userId: user.id, | ||
42 | videoImportId: this.videoImport.id | ||
43 | }) | ||
44 | notification.VideoImport = this.videoImport | ||
45 | |||
46 | return notification | ||
47 | } | ||
48 | |||
49 | createEmail (to: string) { | ||
50 | if (this.payload.success) return this.createSuccessEmail(to) | ||
51 | |||
52 | return this.createFailEmail(to) | ||
53 | } | ||
54 | |||
55 | private createSuccessEmail (to: string) { | ||
56 | const videoUrl = WEBSERVER.URL + this.videoImport.Video.getWatchStaticPath() | ||
57 | |||
58 | return { | ||
59 | to, | ||
60 | subject: `Your video import ${this.videoImport.getTargetIdentifier()} is complete`, | ||
61 | text: `Your video "${this.videoImport.getTargetIdentifier()}" just finished importing.`, | ||
62 | locals: { | ||
63 | title: 'Import complete', | ||
64 | action: { | ||
65 | text: 'View video', | ||
66 | url: videoUrl | ||
67 | } | ||
68 | } | ||
69 | } | ||
70 | } | ||
71 | |||
72 | private createFailEmail (to: string) { | ||
73 | const importUrl = WEBSERVER.URL + '/my-library/video-imports' | ||
74 | |||
75 | const text = | ||
76 | `Your video import "${this.videoImport.getTargetIdentifier()}" encountered an error.` + | ||
77 | '\n\n' + | ||
78 | `See your videos import dashboard for more information: <a href="${importUrl}">${importUrl}</a>.` | ||
79 | |||
80 | return { | ||
81 | to, | ||
82 | subject: `Your video import "${this.videoImport.getTargetIdentifier()}" encountered an error`, | ||
83 | text, | ||
84 | locals: { | ||
85 | title: 'Import failed', | ||
86 | action: { | ||
87 | text: 'Review imports', | ||
88 | url: importUrl | ||
89 | } | ||
90 | } | ||
91 | } | ||
92 | } | ||
93 | |||
94 | private get videoImport () { | ||
95 | return this.payload.videoImport | ||
96 | } | ||
97 | } | ||
diff --git a/server/lib/notifier/shared/video-publication/index.ts b/server/lib/notifier/shared/video-publication/index.ts deleted file mode 100644 index 5e92cb011..000000000 --- a/server/lib/notifier/shared/video-publication/index.ts +++ /dev/null | |||
@@ -1,6 +0,0 @@ | |||
1 | export * from './new-video-for-subscribers' | ||
2 | export * from './import-finished-for-owner' | ||
3 | export * from './owned-publication-after-auto-unblacklist' | ||
4 | export * from './owned-publication-after-schedule-update' | ||
5 | export * from './owned-publication-after-transcoding' | ||
6 | export * from './studio-edition-finished-for-owner' | ||
diff --git a/server/lib/notifier/shared/video-publication/new-video-for-subscribers.ts b/server/lib/notifier/shared/video-publication/new-video-for-subscribers.ts deleted file mode 100644 index df7a5561d..000000000 --- a/server/lib/notifier/shared/video-publication/new-video-for-subscribers.ts +++ /dev/null | |||
@@ -1,61 +0,0 @@ | |||
1 | import { logger } from '@server/helpers/logger' | ||
2 | import { WEBSERVER } from '@server/initializers/constants' | ||
3 | import { UserModel } from '@server/models/user/user' | ||
4 | import { UserNotificationModel } from '@server/models/user/user-notification' | ||
5 | import { MUserWithNotificationSetting, MVideoAccountLight, UserNotificationModelForApi } from '@server/types/models' | ||
6 | import { UserNotificationType, VideoPrivacy, VideoState } from '@shared/models' | ||
7 | import { AbstractNotification } from '../common/abstract-notification' | ||
8 | |||
9 | export class NewVideoForSubscribers extends AbstractNotification <MVideoAccountLight> { | ||
10 | private users: MUserWithNotificationSetting[] | ||
11 | |||
12 | async prepare () { | ||
13 | // List all followers that are users | ||
14 | this.users = await UserModel.listUserSubscribersOf(this.payload.VideoChannel.actorId) | ||
15 | } | ||
16 | |||
17 | log () { | ||
18 | logger.info('Notifying %d users of new video %s.', this.users.length, this.payload.url) | ||
19 | } | ||
20 | |||
21 | isDisabled () { | ||
22 | return this.payload.privacy !== VideoPrivacy.PUBLIC || this.payload.state !== VideoState.PUBLISHED || this.payload.isBlacklisted() | ||
23 | } | ||
24 | |||
25 | getSetting (user: MUserWithNotificationSetting) { | ||
26 | return user.NotificationSetting.newVideoFromSubscription | ||
27 | } | ||
28 | |||
29 | getTargetUsers () { | ||
30 | return this.users | ||
31 | } | ||
32 | |||
33 | createNotification (user: MUserWithNotificationSetting) { | ||
34 | const notification = UserNotificationModel.build<UserNotificationModelForApi>({ | ||
35 | type: UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION, | ||
36 | userId: user.id, | ||
37 | videoId: this.payload.id | ||
38 | }) | ||
39 | notification.Video = this.payload | ||
40 | |||
41 | return notification | ||
42 | } | ||
43 | |||
44 | createEmail (to: string) { | ||
45 | const channelName = this.payload.VideoChannel.getDisplayName() | ||
46 | const videoUrl = WEBSERVER.URL + this.payload.getWatchStaticPath() | ||
47 | |||
48 | return { | ||
49 | to, | ||
50 | subject: channelName + ' just published a new video', | ||
51 | text: `Your subscription ${channelName} just published a new video: "${this.payload.name}".`, | ||
52 | locals: { | ||
53 | title: 'New content ', | ||
54 | action: { | ||
55 | text: 'View video', | ||
56 | url: videoUrl | ||
57 | } | ||
58 | } | ||
59 | } | ||
60 | } | ||
61 | } | ||
diff --git a/server/lib/notifier/shared/video-publication/owned-publication-after-auto-unblacklist.ts b/server/lib/notifier/shared/video-publication/owned-publication-after-auto-unblacklist.ts deleted file mode 100644 index 27d89a5c7..000000000 --- a/server/lib/notifier/shared/video-publication/owned-publication-after-auto-unblacklist.ts +++ /dev/null | |||
@@ -1,11 +0,0 @@ | |||
1 | |||
2 | import { VideoState } from '@shared/models' | ||
3 | import { AbstractOwnedVideoPublication } from './abstract-owned-video-publication' | ||
4 | |||
5 | export class OwnedPublicationAfterAutoUnblacklist extends AbstractOwnedVideoPublication { | ||
6 | |||
7 | isDisabled () { | ||
8 | // Don't notify if video is still waiting for transcoding or scheduled update | ||
9 | return !!this.payload.ScheduleVideoUpdate || (this.payload.waitTranscoding && this.payload.state !== VideoState.PUBLISHED) | ||
10 | } | ||
11 | } | ||
diff --git a/server/lib/notifier/shared/video-publication/owned-publication-after-schedule-update.ts b/server/lib/notifier/shared/video-publication/owned-publication-after-schedule-update.ts deleted file mode 100644 index 2e253b358..000000000 --- a/server/lib/notifier/shared/video-publication/owned-publication-after-schedule-update.ts +++ /dev/null | |||
@@ -1,10 +0,0 @@ | |||
1 | import { VideoState } from '@shared/models' | ||
2 | import { AbstractOwnedVideoPublication } from './abstract-owned-video-publication' | ||
3 | |||
4 | export class OwnedPublicationAfterScheduleUpdate extends AbstractOwnedVideoPublication { | ||
5 | |||
6 | isDisabled () { | ||
7 | // Don't notify if video is still blacklisted or waiting for transcoding | ||
8 | return !!this.payload.VideoBlacklist || (this.payload.waitTranscoding && this.payload.state !== VideoState.PUBLISHED) | ||
9 | } | ||
10 | } | ||
diff --git a/server/lib/notifier/shared/video-publication/owned-publication-after-transcoding.ts b/server/lib/notifier/shared/video-publication/owned-publication-after-transcoding.ts deleted file mode 100644 index 4fab1090f..000000000 --- a/server/lib/notifier/shared/video-publication/owned-publication-after-transcoding.ts +++ /dev/null | |||
@@ -1,9 +0,0 @@ | |||
1 | import { AbstractOwnedVideoPublication } from './abstract-owned-video-publication' | ||
2 | |||
3 | export class OwnedPublicationAfterTranscoding extends AbstractOwnedVideoPublication { | ||
4 | |||
5 | isDisabled () { | ||
6 | // Don't notify if didn't wait for transcoding or video is still blacklisted/waiting for scheduled update | ||
7 | return !this.payload.waitTranscoding || !!this.payload.VideoBlacklist || !!this.payload.ScheduleVideoUpdate | ||
8 | } | ||
9 | } | ||
diff --git a/server/lib/notifier/shared/video-publication/studio-edition-finished-for-owner.ts b/server/lib/notifier/shared/video-publication/studio-edition-finished-for-owner.ts deleted file mode 100644 index f36399f05..000000000 --- a/server/lib/notifier/shared/video-publication/studio-edition-finished-for-owner.ts +++ /dev/null | |||
@@ -1,57 +0,0 @@ | |||
1 | import { logger } from '@server/helpers/logger' | ||
2 | import { WEBSERVER } from '@server/initializers/constants' | ||
3 | import { UserModel } from '@server/models/user/user' | ||
4 | import { UserNotificationModel } from '@server/models/user/user-notification' | ||
5 | import { MUserDefault, MUserWithNotificationSetting, MVideoFullLight, UserNotificationModelForApi } from '@server/types/models' | ||
6 | import { UserNotificationType } from '@shared/models' | ||
7 | import { AbstractNotification } from '../common/abstract-notification' | ||
8 | |||
9 | export class StudioEditionFinishedForOwner extends AbstractNotification <MVideoFullLight> { | ||
10 | private user: MUserDefault | ||
11 | |||
12 | async prepare () { | ||
13 | this.user = await UserModel.loadByVideoId(this.payload.id) | ||
14 | } | ||
15 | |||
16 | log () { | ||
17 | logger.info('Notifying user %s its video studio edition %s is finished.', this.user.username, this.payload.url) | ||
18 | } | ||
19 | |||
20 | getSetting (user: MUserWithNotificationSetting) { | ||
21 | return user.NotificationSetting.myVideoStudioEditionFinished | ||
22 | } | ||
23 | |||
24 | getTargetUsers () { | ||
25 | if (!this.user) return [] | ||
26 | |||
27 | return [ this.user ] | ||
28 | } | ||
29 | |||
30 | createNotification (user: MUserWithNotificationSetting) { | ||
31 | const notification = UserNotificationModel.build<UserNotificationModelForApi>({ | ||
32 | type: UserNotificationType.MY_VIDEO_STUDIO_EDITION_FINISHED, | ||
33 | userId: user.id, | ||
34 | videoId: this.payload.id | ||
35 | }) | ||
36 | notification.Video = this.payload | ||
37 | |||
38 | return notification | ||
39 | } | ||
40 | |||
41 | createEmail (to: string) { | ||
42 | const videoUrl = WEBSERVER.URL + this.payload.getWatchStaticPath() | ||
43 | |||
44 | return { | ||
45 | to, | ||
46 | subject: `Edition of your video ${this.payload.name} has finished`, | ||
47 | text: `Edition of your video ${this.payload.name} has finished.`, | ||
48 | locals: { | ||
49 | title: 'Video edition has finished', | ||
50 | action: { | ||
51 | text: 'View video', | ||
52 | url: videoUrl | ||
53 | } | ||
54 | } | ||
55 | } | ||
56 | } | ||
57 | } | ||