diff options
author | Chocobozzz <me@florianbigard.com> | 2019-01-04 08:56:20 +0100 |
---|---|---|
committer | Chocobozzz <chocobozzz@cpy.re> | 2019-01-09 11:15:15 +0100 |
commit | f7cc67b455a12ccae9b0ea16876d166720364357 (patch) | |
tree | eac2cdbf2e92a16b3eda5d74371c82bd79ae22cb /server/lib | |
parent | dc13348070d808d0ba3feb56a435b835c2e7e791 (diff) | |
download | PeerTube-f7cc67b455a12ccae9b0ea16876d166720364357.tar.gz PeerTube-f7cc67b455a12ccae9b0ea16876d166720364357.tar.zst PeerTube-f7cc67b455a12ccae9b0ea16876d166720364357.zip |
Add new follow, mention and user registered notifs
Diffstat (limited to 'server/lib')
-rw-r--r-- | server/lib/activitypub/process/process-accept.ts | 2 | ||||
-rw-r--r-- | server/lib/activitypub/process/process-follow.ts | 11 | ||||
-rw-r--r-- | server/lib/emailer.ts | 63 | ||||
-rw-r--r-- | server/lib/job-queue/handlers/activitypub-follow.ts | 9 | ||||
-rw-r--r-- | server/lib/notifier.ts | 154 | ||||
-rw-r--r-- | server/lib/user.ts | 13 |
6 files changed, 235 insertions, 17 deletions
diff --git a/server/lib/activitypub/process/process-accept.ts b/server/lib/activitypub/process/process-accept.ts index 89bda9c32..605705ad3 100644 --- a/server/lib/activitypub/process/process-accept.ts +++ b/server/lib/activitypub/process/process-accept.ts | |||
@@ -2,6 +2,7 @@ import { ActivityAccept } from '../../../../shared/models/activitypub' | |||
2 | import { ActorModel } from '../../../models/activitypub/actor' | 2 | import { ActorModel } from '../../../models/activitypub/actor' |
3 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | 3 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' |
4 | import { addFetchOutboxJob } from '../actor' | 4 | import { addFetchOutboxJob } from '../actor' |
5 | import { Notifier } from '../../notifier' | ||
5 | 6 | ||
6 | async function processAcceptActivity (activity: ActivityAccept, targetActor: ActorModel, inboxActor?: ActorModel) { | 7 | async function processAcceptActivity (activity: ActivityAccept, targetActor: ActorModel, inboxActor?: ActorModel) { |
7 | if (inboxActor === undefined) throw new Error('Need to accept on explicit inbox.') | 8 | if (inboxActor === undefined) throw new Error('Need to accept on explicit inbox.') |
@@ -24,6 +25,7 @@ async function processAccept (actor: ActorModel, targetActor: ActorModel) { | |||
24 | if (follow.state !== 'accepted') { | 25 | if (follow.state !== 'accepted') { |
25 | follow.set('state', 'accepted') | 26 | follow.set('state', 'accepted') |
26 | await follow.save() | 27 | await follow.save() |
28 | |||
27 | await addFetchOutboxJob(targetActor) | 29 | await addFetchOutboxJob(targetActor) |
28 | } | 30 | } |
29 | } | 31 | } |
diff --git a/server/lib/activitypub/process/process-follow.ts b/server/lib/activitypub/process/process-follow.ts index 24c9085f7..a67892440 100644 --- a/server/lib/activitypub/process/process-follow.ts +++ b/server/lib/activitypub/process/process-follow.ts | |||
@@ -5,6 +5,7 @@ import { sequelizeTypescript } from '../../../initializers' | |||
5 | import { ActorModel } from '../../../models/activitypub/actor' | 5 | import { ActorModel } from '../../../models/activitypub/actor' |
6 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | 6 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' |
7 | import { sendAccept } from '../send' | 7 | import { sendAccept } from '../send' |
8 | import { Notifier } from '../../notifier' | ||
8 | 9 | ||
9 | async function processFollowActivity (activity: ActivityFollow, byActor: ActorModel) { | 10 | async function processFollowActivity (activity: ActivityFollow, byActor: ActorModel) { |
10 | const activityObject = activity.object | 11 | const activityObject = activity.object |
@@ -21,13 +22,13 @@ export { | |||
21 | // --------------------------------------------------------------------------- | 22 | // --------------------------------------------------------------------------- |
22 | 23 | ||
23 | async function processFollow (actor: ActorModel, targetActorURL: string) { | 24 | async function processFollow (actor: ActorModel, targetActorURL: string) { |
24 | await sequelizeTypescript.transaction(async t => { | 25 | const { actorFollow, created } = await sequelizeTypescript.transaction(async t => { |
25 | const targetActor = await ActorModel.loadByUrlAndPopulateAccountAndChannel(targetActorURL, t) | 26 | const targetActor = await ActorModel.loadByUrlAndPopulateAccountAndChannel(targetActorURL, t) |
26 | 27 | ||
27 | if (!targetActor) throw new Error('Unknown actor') | 28 | if (!targetActor) throw new Error('Unknown actor') |
28 | if (targetActor.isOwned() === false) throw new Error('This is not a local actor.') | 29 | if (targetActor.isOwned() === false) throw new Error('This is not a local actor.') |
29 | 30 | ||
30 | const [ actorFollow ] = await ActorFollowModel.findOrCreate({ | 31 | const [ actorFollow, created ] = await ActorFollowModel.findOrCreate({ |
31 | where: { | 32 | where: { |
32 | actorId: actor.id, | 33 | actorId: actor.id, |
33 | targetActorId: targetActor.id | 34 | targetActorId: targetActor.id |
@@ -52,8 +53,12 @@ async function processFollow (actor: ActorModel, targetActorURL: string) { | |||
52 | actorFollow.ActorFollowing = targetActor | 53 | actorFollow.ActorFollowing = targetActor |
53 | 54 | ||
54 | // Target sends to actor he accepted the follow request | 55 | // Target sends to actor he accepted the follow request |
55 | return sendAccept(actorFollow) | 56 | await sendAccept(actorFollow) |
57 | |||
58 | return { actorFollow, created } | ||
56 | }) | 59 | }) |
57 | 60 | ||
61 | if (created) Notifier.Instance.notifyOfNewFollow(actorFollow) | ||
62 | |||
58 | logger.info('Actor %s is followed by actor %s.', targetActorURL, actor.url) | 63 | logger.info('Actor %s is followed by actor %s.', targetActorURL, actor.url) |
59 | } | 64 | } |
diff --git a/server/lib/emailer.ts b/server/lib/emailer.ts index 6dc8f2adf..3429498e7 100644 --- a/server/lib/emailer.ts +++ b/server/lib/emailer.ts | |||
@@ -11,6 +11,7 @@ import { VideoCommentModel } from '../models/video/video-comment' | |||
11 | import { VideoAbuseModel } from '../models/video/video-abuse' | 11 | import { VideoAbuseModel } from '../models/video/video-abuse' |
12 | import { VideoBlacklistModel } from '../models/video/video-blacklist' | 12 | import { VideoBlacklistModel } from '../models/video/video-blacklist' |
13 | import { VideoImportModel } from '../models/video/video-import' | 13 | import { VideoImportModel } from '../models/video/video-import' |
14 | import { ActorFollowModel } from '../models/activitypub/actor-follow' | ||
14 | 15 | ||
15 | class Emailer { | 16 | class Emailer { |
16 | 17 | ||
@@ -103,6 +104,25 @@ class Emailer { | |||
103 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | 104 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) |
104 | } | 105 | } |
105 | 106 | ||
107 | addNewFollowNotification (to: string[], actorFollow: ActorFollowModel, followType: 'account' | 'channel') { | ||
108 | const followerName = actorFollow.ActorFollower.Account.getDisplayName() | ||
109 | const followingName = (actorFollow.ActorFollowing.VideoChannel || actorFollow.ActorFollowing.Account).getDisplayName() | ||
110 | |||
111 | const text = `Hi dear user,\n\n` + | ||
112 | `Your ${followType} ${followingName} has a new subscriber: ${followerName}` + | ||
113 | `\n\n` + | ||
114 | `Cheers,\n` + | ||
115 | `PeerTube.` | ||
116 | |||
117 | const emailPayload: EmailPayload = { | ||
118 | to, | ||
119 | subject: 'New follower on your channel ' + followingName, | ||
120 | text | ||
121 | } | ||
122 | |||
123 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | ||
124 | } | ||
125 | |||
106 | myVideoPublishedNotification (to: string[], video: VideoModel) { | 126 | myVideoPublishedNotification (to: string[], video: VideoModel) { |
107 | const videoUrl = CONFIG.WEBSERVER.URL + video.getWatchStaticPath() | 127 | const videoUrl = CONFIG.WEBSERVER.URL + video.getWatchStaticPath() |
108 | 128 | ||
@@ -185,7 +205,29 @@ class Emailer { | |||
185 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | 205 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) |
186 | } | 206 | } |
187 | 207 | ||
188 | async addVideoAbuseModeratorsNotification (to: string[], videoAbuse: VideoAbuseModel) { | 208 | addNewCommentMentionNotification (to: string[], comment: VideoCommentModel) { |
209 | const accountName = comment.Account.getDisplayName() | ||
210 | const video = comment.Video | ||
211 | const commentUrl = CONFIG.WEBSERVER.URL + comment.getCommentStaticPath() | ||
212 | |||
213 | const text = `Hi dear user,\n\n` + | ||
214 | `${accountName} mentioned you on video ${video.name}` + | ||
215 | `\n\n` + | ||
216 | `You can view the comment on ${commentUrl} ` + | ||
217 | `\n\n` + | ||
218 | `Cheers,\n` + | ||
219 | `PeerTube.` | ||
220 | |||
221 | const emailPayload: EmailPayload = { | ||
222 | to, | ||
223 | subject: 'Mention on video ' + video.name, | ||
224 | text | ||
225 | } | ||
226 | |||
227 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | ||
228 | } | ||
229 | |||
230 | addVideoAbuseModeratorsNotification (to: string[], videoAbuse: VideoAbuseModel) { | ||
189 | const videoUrl = CONFIG.WEBSERVER.URL + videoAbuse.Video.getWatchStaticPath() | 231 | const videoUrl = CONFIG.WEBSERVER.URL + videoAbuse.Video.getWatchStaticPath() |
190 | 232 | ||
191 | const text = `Hi,\n\n` + | 233 | const text = `Hi,\n\n` + |
@@ -202,7 +244,22 @@ class Emailer { | |||
202 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | 244 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) |
203 | } | 245 | } |
204 | 246 | ||
205 | async addVideoBlacklistNotification (to: string[], videoBlacklist: VideoBlacklistModel) { | 247 | addNewUserRegistrationNotification (to: string[], user: UserModel) { |
248 | const text = `Hi,\n\n` + | ||
249 | `User ${user.username} just registered on ${CONFIG.WEBSERVER.HOST} PeerTube instance.\n\n` + | ||
250 | `Cheers,\n` + | ||
251 | `PeerTube.` | ||
252 | |||
253 | const emailPayload: EmailPayload = { | ||
254 | to, | ||
255 | subject: '[PeerTube] New user registration on ' + CONFIG.WEBSERVER.HOST, | ||
256 | text | ||
257 | } | ||
258 | |||
259 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | ||
260 | } | ||
261 | |||
262 | addVideoBlacklistNotification (to: string[], videoBlacklist: VideoBlacklistModel) { | ||
206 | const videoName = videoBlacklist.Video.name | 263 | const videoName = videoBlacklist.Video.name |
207 | const videoUrl = CONFIG.WEBSERVER.URL + videoBlacklist.Video.getWatchStaticPath() | 264 | const videoUrl = CONFIG.WEBSERVER.URL + videoBlacklist.Video.getWatchStaticPath() |
208 | 265 | ||
@@ -224,7 +281,7 @@ class Emailer { | |||
224 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | 281 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) |
225 | } | 282 | } |
226 | 283 | ||
227 | async addVideoUnblacklistNotification (to: string[], video: VideoModel) { | 284 | addVideoUnblacklistNotification (to: string[], video: VideoModel) { |
228 | const videoUrl = CONFIG.WEBSERVER.URL + video.getWatchStaticPath() | 285 | const videoUrl = CONFIG.WEBSERVER.URL + video.getWatchStaticPath() |
229 | 286 | ||
230 | const text = 'Hi,\n\n' + | 287 | const text = 'Hi,\n\n' + |
diff --git a/server/lib/job-queue/handlers/activitypub-follow.ts b/server/lib/job-queue/handlers/activitypub-follow.ts index 36d0f237b..b4d381062 100644 --- a/server/lib/job-queue/handlers/activitypub-follow.ts +++ b/server/lib/job-queue/handlers/activitypub-follow.ts | |||
@@ -8,6 +8,7 @@ import { getOrCreateActorAndServerAndModel } from '../../activitypub/actor' | |||
8 | import { retryTransactionWrapper } from '../../../helpers/database-utils' | 8 | import { retryTransactionWrapper } from '../../../helpers/database-utils' |
9 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | 9 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' |
10 | import { ActorModel } from '../../../models/activitypub/actor' | 10 | import { ActorModel } from '../../../models/activitypub/actor' |
11 | import { Notifier } from '../../notifier' | ||
11 | 12 | ||
12 | export type ActivitypubFollowPayload = { | 13 | export type ActivitypubFollowPayload = { |
13 | followerActorId: number | 14 | followerActorId: number |
@@ -42,7 +43,7 @@ export { | |||
42 | 43 | ||
43 | // --------------------------------------------------------------------------- | 44 | // --------------------------------------------------------------------------- |
44 | 45 | ||
45 | function follow (fromActor: ActorModel, targetActor: ActorModel) { | 46 | async function follow (fromActor: ActorModel, targetActor: ActorModel) { |
46 | if (fromActor.id === targetActor.id) { | 47 | if (fromActor.id === targetActor.id) { |
47 | throw new Error('Follower is the same than target actor.') | 48 | throw new Error('Follower is the same than target actor.') |
48 | } | 49 | } |
@@ -50,7 +51,7 @@ function follow (fromActor: ActorModel, targetActor: ActorModel) { | |||
50 | // Same server, direct accept | 51 | // Same server, direct accept |
51 | const state = !fromActor.serverId && !targetActor.serverId ? 'accepted' : 'pending' | 52 | const state = !fromActor.serverId && !targetActor.serverId ? 'accepted' : 'pending' |
52 | 53 | ||
53 | return sequelizeTypescript.transaction(async t => { | 54 | const actorFollow = await sequelizeTypescript.transaction(async t => { |
54 | const [ actorFollow ] = await ActorFollowModel.findOrCreate({ | 55 | const [ actorFollow ] = await ActorFollowModel.findOrCreate({ |
55 | where: { | 56 | where: { |
56 | actorId: fromActor.id, | 57 | actorId: fromActor.id, |
@@ -68,5 +69,9 @@ function follow (fromActor: ActorModel, targetActor: ActorModel) { | |||
68 | 69 | ||
69 | // Send a notification to remote server if our follow is not already accepted | 70 | // Send a notification to remote server if our follow is not already accepted |
70 | if (actorFollow.state !== 'accepted') await sendFollow(actorFollow) | 71 | if (actorFollow.state !== 'accepted') await sendFollow(actorFollow) |
72 | |||
73 | return actorFollow | ||
71 | }) | 74 | }) |
75 | |||
76 | if (actorFollow.state === 'accepted') Notifier.Instance.notifyOfNewFollow(actorFollow) | ||
72 | } | 77 | } |
diff --git a/server/lib/notifier.ts b/server/lib/notifier.ts index 11b0937e9..2c51d7101 100644 --- a/server/lib/notifier.ts +++ b/server/lib/notifier.ts | |||
@@ -13,6 +13,8 @@ import { VideoBlacklistModel } from '../models/video/video-blacklist' | |||
13 | import * as Bluebird from 'bluebird' | 13 | import * as Bluebird from 'bluebird' |
14 | import { VideoImportModel } from '../models/video/video-import' | 14 | import { VideoImportModel } from '../models/video/video-import' |
15 | import { AccountBlocklistModel } from '../models/account/account-blocklist' | 15 | import { AccountBlocklistModel } from '../models/account/account-blocklist' |
16 | import { ActorFollowModel } from '../models/activitypub/actor-follow' | ||
17 | import { AccountModel } from '../models/account/account' | ||
16 | 18 | ||
17 | class Notifier { | 19 | class Notifier { |
18 | 20 | ||
@@ -38,7 +40,10 @@ class Notifier { | |||
38 | 40 | ||
39 | notifyOnNewComment (comment: VideoCommentModel): void { | 41 | notifyOnNewComment (comment: VideoCommentModel): void { |
40 | this.notifyVideoOwnerOfNewComment(comment) | 42 | this.notifyVideoOwnerOfNewComment(comment) |
41 | .catch(err => logger.error('Cannot notify of new comment %s.', comment.url, { err })) | 43 | .catch(err => logger.error('Cannot notify video owner of new comment %s.', comment.url, { err })) |
44 | |||
45 | this.notifyOfCommentMention(comment) | ||
46 | .catch(err => logger.error('Cannot notify mentions of comment %s.', comment.url, { err })) | ||
42 | } | 47 | } |
43 | 48 | ||
44 | notifyOnNewVideoAbuse (videoAbuse: VideoAbuseModel): void { | 49 | notifyOnNewVideoAbuse (videoAbuse: VideoAbuseModel): void { |
@@ -61,6 +66,23 @@ class Notifier { | |||
61 | .catch(err => logger.error('Cannot notify owner that its video import %s is finished.', videoImport.getTargetIdentifier(), { err })) | 66 | .catch(err => logger.error('Cannot notify owner that its video import %s is finished.', videoImport.getTargetIdentifier(), { err })) |
62 | } | 67 | } |
63 | 68 | ||
69 | notifyOnNewUserRegistration (user: UserModel): void { | ||
70 | this.notifyModeratorsOfNewUserRegistration(user) | ||
71 | .catch(err => logger.error('Cannot notify moderators of new user registration (%s).', user.username, { err })) | ||
72 | } | ||
73 | |||
74 | notifyOfNewFollow (actorFollow: ActorFollowModel): void { | ||
75 | this.notifyUserOfNewActorFollow(actorFollow) | ||
76 | .catch(err => { | ||
77 | logger.error( | ||
78 | 'Cannot notify owner of channel %s of a new follow by %s.', | ||
79 | actorFollow.ActorFollowing.VideoChannel.getDisplayName(), | ||
80 | actorFollow.ActorFollower.Account.getDisplayName(), | ||
81 | err | ||
82 | ) | ||
83 | }) | ||
84 | } | ||
85 | |||
64 | private async notifySubscribersOfNewVideo (video: VideoModel) { | 86 | private async notifySubscribersOfNewVideo (video: VideoModel) { |
65 | // List all followers that are users | 87 | // List all followers that are users |
66 | const users = await UserModel.listUserSubscribersOf(video.VideoChannel.actorId) | 88 | const users = await UserModel.listUserSubscribersOf(video.VideoChannel.actorId) |
@@ -90,6 +112,8 @@ class Notifier { | |||
90 | } | 112 | } |
91 | 113 | ||
92 | private async notifyVideoOwnerOfNewComment (comment: VideoCommentModel) { | 114 | private async notifyVideoOwnerOfNewComment (comment: VideoCommentModel) { |
115 | if (comment.Video.isOwned() === false) return | ||
116 | |||
93 | const user = await UserModel.loadByVideoId(comment.videoId) | 117 | const user = await UserModel.loadByVideoId(comment.videoId) |
94 | 118 | ||
95 | // Not our user or user comments its own video | 119 | // Not our user or user comments its own video |
@@ -122,11 +146,100 @@ class Notifier { | |||
122 | return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) | 146 | return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) |
123 | } | 147 | } |
124 | 148 | ||
125 | private async notifyModeratorsOfNewVideoAbuse (videoAbuse: VideoAbuseModel) { | 149 | private async notifyOfCommentMention (comment: VideoCommentModel) { |
126 | const users = await UserModel.listWithRight(UserRight.MANAGE_VIDEO_ABUSES) | 150 | const usernames = comment.extractMentions() |
151 | let users = await UserModel.listByUsernames(usernames) | ||
152 | |||
153 | if (comment.Video.isOwned()) { | ||
154 | const userException = await UserModel.loadByVideoId(comment.videoId) | ||
155 | users = users.filter(u => u.id !== userException.id) | ||
156 | } | ||
157 | |||
158 | // Don't notify if I mentioned myself | ||
159 | users = users.filter(u => u.Account.id !== comment.accountId) | ||
160 | |||
127 | if (users.length === 0) return | 161 | if (users.length === 0) return |
128 | 162 | ||
129 | logger.info('Notifying %s user/moderators of new video abuse %s.', users.length, videoAbuse.Video.url) | 163 | const accountMutedHash = await AccountBlocklistModel.isAccountMutedByMulti(users.map(u => u.Account.id), comment.accountId) |
164 | |||
165 | logger.info('Notifying %d users of new comment %s.', users.length, comment.url) | ||
166 | |||
167 | function settingGetter (user: UserModel) { | ||
168 | if (accountMutedHash[user.Account.id] === true) return UserNotificationSettingValue.NONE | ||
169 | |||
170 | return user.NotificationSetting.commentMention | ||
171 | } | ||
172 | |||
173 | async function notificationCreator (user: UserModel) { | ||
174 | const notification = await UserNotificationModel.create({ | ||
175 | type: UserNotificationType.COMMENT_MENTION, | ||
176 | userId: user.id, | ||
177 | commentId: comment.id | ||
178 | }) | ||
179 | notification.Comment = comment | ||
180 | |||
181 | return notification | ||
182 | } | ||
183 | |||
184 | function emailSender (emails: string[]) { | ||
185 | return Emailer.Instance.addNewCommentMentionNotification(emails, comment) | ||
186 | } | ||
187 | |||
188 | return this.notify({ users, settingGetter, notificationCreator, emailSender }) | ||
189 | } | ||
190 | |||
191 | private async notifyUserOfNewActorFollow (actorFollow: ActorFollowModel) { | ||
192 | if (actorFollow.ActorFollowing.isOwned() === false) return | ||
193 | |||
194 | // Account follows one of our account? | ||
195 | let followType: 'account' | 'channel' = 'channel' | ||
196 | let user = await UserModel.loadByChannelActorId(actorFollow.ActorFollowing.id) | ||
197 | |||
198 | // Account follows one of our channel? | ||
199 | if (!user) { | ||
200 | user = await UserModel.loadByAccountActorId(actorFollow.ActorFollowing.id) | ||
201 | followType = 'account' | ||
202 | } | ||
203 | |||
204 | if (!user) return | ||
205 | |||
206 | if (!actorFollow.ActorFollower.Account || !actorFollow.ActorFollower.Account.name) { | ||
207 | actorFollow.ActorFollower.Account = await actorFollow.ActorFollower.$get('Account') as AccountModel | ||
208 | } | ||
209 | const followerAccount = actorFollow.ActorFollower.Account | ||
210 | |||
211 | const accountMuted = await AccountBlocklistModel.isAccountMutedBy(user.Account.id, followerAccount.id) | ||
212 | if (accountMuted) return | ||
213 | |||
214 | logger.info('Notifying user %s of new follower: %s.', user.username, followerAccount.getDisplayName()) | ||
215 | |||
216 | function settingGetter (user: UserModel) { | ||
217 | return user.NotificationSetting.newFollow | ||
218 | } | ||
219 | |||
220 | async function notificationCreator (user: UserModel) { | ||
221 | const notification = await UserNotificationModel.create({ | ||
222 | type: UserNotificationType.NEW_FOLLOW, | ||
223 | userId: user.id, | ||
224 | actorFollowId: actorFollow.id | ||
225 | }) | ||
226 | notification.ActorFollow = actorFollow | ||
227 | |||
228 | return notification | ||
229 | } | ||
230 | |||
231 | function emailSender (emails: string[]) { | ||
232 | return Emailer.Instance.addNewFollowNotification(emails, actorFollow, followType) | ||
233 | } | ||
234 | |||
235 | return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) | ||
236 | } | ||
237 | |||
238 | private async notifyModeratorsOfNewVideoAbuse (videoAbuse: VideoAbuseModel) { | ||
239 | const moderators = await UserModel.listWithRight(UserRight.MANAGE_VIDEO_ABUSES) | ||
240 | if (moderators.length === 0) return | ||
241 | |||
242 | logger.info('Notifying %s user/moderators of new video abuse %s.', moderators.length, videoAbuse.Video.url) | ||
130 | 243 | ||
131 | function settingGetter (user: UserModel) { | 244 | function settingGetter (user: UserModel) { |
132 | return user.NotificationSetting.videoAbuseAsModerator | 245 | return user.NotificationSetting.videoAbuseAsModerator |
@@ -147,7 +260,7 @@ class Notifier { | |||
147 | return Emailer.Instance.addVideoAbuseModeratorsNotification(emails, videoAbuse) | 260 | return Emailer.Instance.addVideoAbuseModeratorsNotification(emails, videoAbuse) |
148 | } | 261 | } |
149 | 262 | ||
150 | return this.notify({ users, settingGetter, notificationCreator, emailSender }) | 263 | return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender }) |
151 | } | 264 | } |
152 | 265 | ||
153 | private async notifyVideoOwnerOfBlacklist (videoBlacklist: VideoBlacklistModel) { | 266 | private async notifyVideoOwnerOfBlacklist (videoBlacklist: VideoBlacklistModel) { |
@@ -264,6 +377,37 @@ class Notifier { | |||
264 | return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) | 377 | return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) |
265 | } | 378 | } |
266 | 379 | ||
380 | private async notifyModeratorsOfNewUserRegistration (registeredUser: UserModel) { | ||
381 | const moderators = await UserModel.listWithRight(UserRight.MANAGE_USERS) | ||
382 | if (moderators.length === 0) return | ||
383 | |||
384 | logger.info( | ||
385 | 'Notifying %s moderators of new user registration of %s.', | ||
386 | moderators.length, registeredUser.Account.Actor.preferredUsername | ||
387 | ) | ||
388 | |||
389 | function settingGetter (user: UserModel) { | ||
390 | return user.NotificationSetting.newUserRegistration | ||
391 | } | ||
392 | |||
393 | async function notificationCreator (user: UserModel) { | ||
394 | const notification = await UserNotificationModel.create({ | ||
395 | type: UserNotificationType.NEW_USER_REGISTRATION, | ||
396 | userId: user.id, | ||
397 | accountId: registeredUser.Account.id | ||
398 | }) | ||
399 | notification.Account = registeredUser.Account | ||
400 | |||
401 | return notification | ||
402 | } | ||
403 | |||
404 | function emailSender (emails: string[]) { | ||
405 | return Emailer.Instance.addNewUserRegistrationNotification(emails, registeredUser) | ||
406 | } | ||
407 | |||
408 | return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender }) | ||
409 | } | ||
410 | |||
267 | private async notify (options: { | 411 | private async notify (options: { |
268 | users: UserModel[], | 412 | users: UserModel[], |
269 | notificationCreator: (user: UserModel) => Promise<UserNotificationModel>, | 413 | notificationCreator: (user: UserModel) => Promise<UserNotificationModel>, |
diff --git a/server/lib/user.ts b/server/lib/user.ts index 481571828..9e24e85a0 100644 --- a/server/lib/user.ts +++ b/server/lib/user.ts | |||
@@ -10,7 +10,7 @@ import { VideoChannelModel } from '../models/video/video-channel' | |||
10 | import { FilteredModelAttributes } from 'sequelize-typescript/lib/models/Model' | 10 | import { FilteredModelAttributes } from 'sequelize-typescript/lib/models/Model' |
11 | import { ActorModel } from '../models/activitypub/actor' | 11 | import { ActorModel } from '../models/activitypub/actor' |
12 | import { UserNotificationSettingModel } from '../models/account/user-notification-setting' | 12 | import { UserNotificationSettingModel } from '../models/account/user-notification-setting' |
13 | import { UserNotificationSettingValue } from '../../shared/models/users' | 13 | import { UserNotificationSetting, UserNotificationSettingValue } from '../../shared/models/users' |
14 | 14 | ||
15 | async function createUserAccountAndChannel (userToCreate: UserModel, validateUser = true) { | 15 | async function createUserAccountAndChannel (userToCreate: UserModel, validateUser = true) { |
16 | const { user, account, videoChannel } = await sequelizeTypescript.transaction(async t => { | 16 | const { user, account, videoChannel } = await sequelizeTypescript.transaction(async t => { |
@@ -96,13 +96,18 @@ export { | |||
96 | // --------------------------------------------------------------------------- | 96 | // --------------------------------------------------------------------------- |
97 | 97 | ||
98 | function createDefaultUserNotificationSettings (user: UserModel, t: Sequelize.Transaction | undefined) { | 98 | function createDefaultUserNotificationSettings (user: UserModel, t: Sequelize.Transaction | undefined) { |
99 | return UserNotificationSettingModel.create({ | 99 | const values: UserNotificationSetting & { userId: number } = { |
100 | userId: user.id, | 100 | userId: user.id, |
101 | newVideoFromSubscription: UserNotificationSettingValue.WEB_NOTIFICATION, | 101 | newVideoFromSubscription: UserNotificationSettingValue.WEB_NOTIFICATION, |
102 | newCommentOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION, | 102 | newCommentOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION, |
103 | myVideoImportFinished: UserNotificationSettingValue.WEB_NOTIFICATION, | 103 | myVideoImportFinished: UserNotificationSettingValue.WEB_NOTIFICATION, |
104 | myVideoPublished: UserNotificationSettingValue.WEB_NOTIFICATION, | 104 | myVideoPublished: UserNotificationSettingValue.WEB_NOTIFICATION, |
105 | videoAbuseAsModerator: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL, | 105 | videoAbuseAsModerator: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL, |
106 | blacklistOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL | 106 | blacklistOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL, |
107 | }, { transaction: t }) | 107 | newUserRegistration: UserNotificationSettingValue.WEB_NOTIFICATION, |
108 | commentMention: UserNotificationSettingValue.WEB_NOTIFICATION, | ||
109 | newFollow: UserNotificationSettingValue.WEB_NOTIFICATION | ||
110 | } | ||
111 | |||
112 | return UserNotificationSettingModel.create(values, { transaction: t }) | ||
108 | } | 113 | } |