aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/lib
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2020-07-27 16:26:25 +0200
committerChocobozzz <chocobozzz@cpy.re>2020-07-31 11:35:19 +0200
commit594d3e48d8a887bbf48ce4cc594c1c36c9640fb1 (patch)
treebae28fa6215a3a3c6ccd78aea6ea7e75c500a96f /server/lib
parent94148c9028829b5576a5dcbfba2c7fb9cf6443d3 (diff)
downloadPeerTube-594d3e48d8a887bbf48ce4cc594c1c36c9640fb1.tar.gz
PeerTube-594d3e48d8a887bbf48ce4cc594c1c36c9640fb1.tar.zst
PeerTube-594d3e48d8a887bbf48ce4cc594c1c36c9640fb1.zip
Add abuse messages/states notifications
Diffstat (limited to 'server/lib')
-rw-r--r--server/lib/emailer.ts53
-rw-r--r--server/lib/emails/abuse-new-message/html.pug11
-rw-r--r--server/lib/emails/abuse-state-change/html.pug9
-rw-r--r--server/lib/notifier.ts118
-rw-r--r--server/lib/user.ts2
5 files changed, 187 insertions, 6 deletions
diff --git a/server/lib/emailer.ts b/server/lib/emailer.ts
index c6ad03328..9c49aa2f6 100644
--- a/server/lib/emailer.ts
+++ b/server/lib/emailer.ts
@@ -5,13 +5,13 @@ import { join } from 'path'
5import { VideoChannelModel } from '@server/models/video/video-channel' 5import { VideoChannelModel } from '@server/models/video/video-channel'
6import { MVideoBlacklistLightVideo, MVideoBlacklistVideo } from '@server/types/models/video/video-blacklist' 6import { MVideoBlacklistLightVideo, MVideoBlacklistVideo } from '@server/types/models/video/video-blacklist'
7import { MVideoImport, MVideoImportVideo } from '@server/types/models/video/video-import' 7import { MVideoImport, MVideoImportVideo } from '@server/types/models/video/video-import'
8import { UserAbuse, EmailPayload } from '@shared/models' 8import { AbuseState, EmailPayload, UserAbuse } from '@shared/models'
9import { SendEmailOptions } from '../../shared/models/server/emailer.model' 9import { SendEmailOptions } from '../../shared/models/server/emailer.model'
10import { isTestInstance, root } from '../helpers/core-utils' 10import { isTestInstance, root } from '../helpers/core-utils'
11import { bunyanLogger, logger } from '../helpers/logger' 11import { bunyanLogger, logger } from '../helpers/logger'
12import { CONFIG, isEmailEnabled } from '../initializers/config' 12import { CONFIG, isEmailEnabled } from '../initializers/config'
13import { WEBSERVER } from '../initializers/constants' 13import { WEBSERVER } from '../initializers/constants'
14import { MAbuseFull, MActorFollowActors, MActorFollowFull, MUser } from '../types/models' 14import { MAbuseFull, MAbuseMessage, MActorFollowActors, MActorFollowFull, MUser } from '../types/models'
15import { MCommentOwnerVideo, MVideo, MVideoAccountLight } from '../types/models/video' 15import { MCommentOwnerVideo, MVideo, MVideoAccountLight } from '../types/models/video'
16import { JobQueue } from './job-queue' 16import { JobQueue } from './job-queue'
17 17
@@ -357,6 +357,55 @@ class Emailer {
357 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) 357 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
358 } 358 }
359 359
360 addAbuseStateChangeNotification (to: string[], abuse: MAbuseFull) {
361 const text = abuse.state === AbuseState.ACCEPTED
362 ? 'Report #' + abuse.id + ' has been accepted'
363 : 'Report #' + abuse.id + ' has been rejected'
364
365 const action = {
366 text,
367 url: WEBSERVER.URL + '/my-account/abuses?search=%23' + abuse.id
368 }
369
370 const emailPayload: EmailPayload = {
371 template: 'abuse-state-change',
372 to,
373 subject: text,
374 locals: {
375 action,
376 abuseId: abuse.id,
377 isAccepted: abuse.state === AbuseState.ACCEPTED
378 }
379 }
380
381 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
382 }
383
384 addAbuseNewMessageNotification (to: string[], options: { target: 'moderator' | 'reporter', abuse: MAbuseFull, message: MAbuseMessage }) {
385 const { abuse, target, message } = options
386
387 const text = 'New message on abuse #' + abuse.id
388 const action = {
389 text,
390 url: target === 'moderator'
391 ? WEBSERVER.URL + '/admin/moderation/abuses/list?search=%23' + abuse.id
392 : WEBSERVER.URL + '/my-account/abuses?search=%23' + abuse.id
393 }
394
395 const emailPayload: EmailPayload = {
396 template: 'abuse-new-message',
397 to,
398 subject: text,
399 locals: {
400 abuseUrl: action.url,
401 messageText: message.message,
402 action
403 }
404 }
405
406 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
407 }
408
360 async addVideoAutoBlacklistModeratorsNotification (to: string[], videoBlacklist: MVideoBlacklistLightVideo) { 409 async addVideoAutoBlacklistModeratorsNotification (to: string[], videoBlacklist: MVideoBlacklistLightVideo) {
361 const VIDEO_AUTO_BLACKLIST_URL = WEBSERVER.URL + '/admin/moderation/video-auto-blacklist/list' 410 const VIDEO_AUTO_BLACKLIST_URL = WEBSERVER.URL + '/admin/moderation/video-auto-blacklist/list'
362 const videoUrl = WEBSERVER.URL + videoBlacklist.Video.getWatchStaticPath() 411 const videoUrl = WEBSERVER.URL + videoBlacklist.Video.getWatchStaticPath()
diff --git a/server/lib/emails/abuse-new-message/html.pug b/server/lib/emails/abuse-new-message/html.pug
new file mode 100644
index 000000000..a4180aba1
--- /dev/null
+++ b/server/lib/emails/abuse-new-message/html.pug
@@ -0,0 +1,11 @@
1extends ../common/greetings
2include ../common/mixins.pug
3
4block title
5 | New abuse message
6
7block content
8 p
9 | A new message was created on #[a(href=WEBSERVER.URL) abuse ##{abuseId} on #{WEBSERVER.HOST}]
10 blockquote #{messageText}
11 br(style="display: none;")
diff --git a/server/lib/emails/abuse-state-change/html.pug b/server/lib/emails/abuse-state-change/html.pug
new file mode 100644
index 000000000..a94c8521d
--- /dev/null
+++ b/server/lib/emails/abuse-state-change/html.pug
@@ -0,0 +1,9 @@
1extends ../common/greetings
2include ../common/mixins.pug
3
4block title
5 | Abuse state changed
6
7block content
8 p
9 | #[a(href=abuseUrl) Your abuse ##{abuseId} on #{WEBSERVER.HOST}] has been #{isAccepted ? 'accepted' : 'rejected'}
diff --git a/server/lib/notifier.ts b/server/lib/notifier.ts
index 8f165d2fd..5c50fcf01 100644
--- a/server/lib/notifier.ts
+++ b/server/lib/notifier.ts
@@ -1,3 +1,4 @@
1import { AbuseMessageModel } from '@server/models/abuse/abuse-message'
1import { getServerActor } from '@server/models/application/application' 2import { getServerActor } from '@server/models/application/application'
2import { ServerBlocklistModel } from '@server/models/server/server-blocklist' 3import { ServerBlocklistModel } from '@server/models/server/server-blocklist'
3import { 4import {
@@ -18,7 +19,7 @@ import { CONFIG } from '../initializers/config'
18import { AccountBlocklistModel } from '../models/account/account-blocklist' 19import { AccountBlocklistModel } from '../models/account/account-blocklist'
19import { UserModel } from '../models/account/user' 20import { UserModel } from '../models/account/user'
20import { UserNotificationModel } from '../models/account/user-notification' 21import { UserNotificationModel } from '../models/account/user-notification'
21import { MAbuseFull, MAccountServer, MActorFollowFull } from '../types/models' 22import { MAbuseFull, MAbuseMessage, MAccountServer, MActorFollowFull } from '../types/models'
22import { MCommentOwnerVideo, MVideoAccountLight, MVideoFullLight } from '../types/models/video' 23import { MCommentOwnerVideo, MVideoAccountLight, MVideoFullLight } from '../types/models/video'
23import { isBlockedByServerOrAccount } from './blocklist' 24import { isBlockedByServerOrAccount } from './blocklist'
24import { Emailer } from './emailer' 25import { Emailer } from './emailer'
@@ -129,6 +130,20 @@ class Notifier {
129 }) 130 })
130 } 131 }
131 132
133 notifyOnAbuseStateChange (abuse: MAbuseFull): void {
134 this.notifyReporterOfAbuseStateChange(abuse)
135 .catch(err => {
136 logger.error('Cannot notify reporter of abuse %d state change.', abuse.id, { err })
137 })
138 }
139
140 notifyOnAbuseMessage (abuse: MAbuseFull, message: AbuseMessageModel): void {
141 this.notifyOfNewAbuseMessage(abuse, message)
142 .catch(err => {
143 logger.error('Cannot notify on new abuse %d message.', abuse.id, { err })
144 })
145 }
146
132 private async notifySubscribersOfNewVideo (video: MVideoAccountLight) { 147 private async notifySubscribersOfNewVideo (video: MVideoAccountLight) {
133 // List all followers that are users 148 // List all followers that are users
134 const users = await UserModel.listUserSubscribersOf(video.VideoChannel.actorId) 149 const users = await UserModel.listUserSubscribersOf(video.VideoChannel.actorId)
@@ -359,9 +374,7 @@ class Notifier {
359 const moderators = await UserModel.listWithRight(UserRight.MANAGE_ABUSES) 374 const moderators = await UserModel.listWithRight(UserRight.MANAGE_ABUSES)
360 if (moderators.length === 0) return 375 if (moderators.length === 0) return
361 376
362 const url = abuseInstance.VideoAbuse?.Video?.url || 377 const url = this.getAbuseUrl(abuseInstance)
363 abuseInstance.VideoCommentAbuse?.VideoComment?.url ||
364 abuseInstance.FlaggedAccount.Actor.url
365 378
366 logger.info('Notifying %s user/moderators of new abuse %s.', moderators.length, url) 379 logger.info('Notifying %s user/moderators of new abuse %s.', moderators.length, url)
367 380
@@ -387,6 +400,97 @@ class Notifier {
387 return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender }) 400 return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender })
388 } 401 }
389 402
403 private async notifyReporterOfAbuseStateChange (abuse: MAbuseFull) {
404 // Only notify our users
405 if (abuse.ReporterAccount.isOwned() !== true) return
406
407 const url = this.getAbuseUrl(abuse)
408
409 logger.info('Notifying reporter of abuse % of state change.', url)
410
411 const reporter = await UserModel.loadByAccountActorId(abuse.ReporterAccount.actorId)
412
413 function settingGetter (user: MUserWithNotificationSetting) {
414 return user.NotificationSetting.abuseStateChange
415 }
416
417 async function notificationCreator (user: MUserWithNotificationSetting) {
418 const notification = await UserNotificationModel.create<UserNotificationModelForApi>({
419 type: UserNotificationType.ABUSE_STATE_CHANGE,
420 userId: user.id,
421 abuseId: abuse.id
422 })
423 notification.Abuse = abuse
424
425 return notification
426 }
427
428 function emailSender (emails: string[]) {
429 return Emailer.Instance.addAbuseStateChangeNotification(emails, abuse)
430 }
431
432 return this.notify({ users: [ reporter ], settingGetter, notificationCreator, emailSender })
433 }
434
435 private async notifyOfNewAbuseMessage (abuse: MAbuseFull, message: MAbuseMessage) {
436 const url = this.getAbuseUrl(abuse)
437 logger.info('Notifying reporter and moderators of new abuse message on %s.', url)
438
439 function settingGetter (user: MUserWithNotificationSetting) {
440 return user.NotificationSetting.abuseNewMessage
441 }
442
443 async function notificationCreator (user: MUserWithNotificationSetting) {
444 const notification = await UserNotificationModel.create<UserNotificationModelForApi>({
445 type: UserNotificationType.ABUSE_NEW_MESSAGE,
446 userId: user.id,
447 abuseId: abuse.id
448 })
449 notification.Abuse = abuse
450
451 return notification
452 }
453
454 function emailSenderReporter (emails: string[]) {
455 return Emailer.Instance.addAbuseNewMessageNotification(emails, { target: 'reporter', abuse, message })
456 }
457
458 function emailSenderModerators (emails: string[]) {
459 return Emailer.Instance.addAbuseNewMessageNotification(emails, { target: 'moderator', abuse, message })
460 }
461
462 async function buildReporterOptions () {
463 // Only notify our users
464 if (abuse.ReporterAccount.isOwned() !== true) return
465
466 const reporter = await UserModel.loadByAccountActorId(abuse.ReporterAccount.actorId)
467 // Don't notify my own message
468 if (reporter.Account.id === message.accountId) return
469
470 return { users: [ reporter ], settingGetter, notificationCreator, emailSender: emailSenderReporter }
471 }
472
473 async function buildModeratorsOptions () {
474 let moderators = await UserModel.listWithRight(UserRight.MANAGE_ABUSES)
475 // Don't notify my own message
476 moderators = moderators.filter(m => m.Account.id !== message.accountId)
477
478 if (moderators.length === 0) return
479
480 return { users: moderators, settingGetter, notificationCreator, emailSender: emailSenderModerators }
481 }
482
483 const [ reporterOptions, moderatorsOptions ] = await Promise.all([
484 buildReporterOptions(),
485 buildModeratorsOptions()
486 ])
487
488 return Promise.all([
489 this.notify(reporterOptions),
490 this.notify(moderatorsOptions)
491 ])
492 }
493
390 private async notifyModeratorsOfVideoAutoBlacklist (videoBlacklist: MVideoBlacklistLightVideo) { 494 private async notifyModeratorsOfVideoAutoBlacklist (videoBlacklist: MVideoBlacklistLightVideo) {
391 const moderators = await UserModel.listWithRight(UserRight.MANAGE_VIDEO_BLACKLIST) 495 const moderators = await UserModel.listWithRight(UserRight.MANAGE_VIDEO_BLACKLIST)
392 if (moderators.length === 0) return 496 if (moderators.length === 0) return
@@ -599,6 +703,12 @@ class Notifier {
599 return isBlockedByServerOrAccount(targetAccount, user?.Account) 703 return isBlockedByServerOrAccount(targetAccount, user?.Account)
600 } 704 }
601 705
706 private getAbuseUrl (abuse: MAbuseFull) {
707 return abuse.VideoAbuse?.Video?.url ||
708 abuse.VideoCommentAbuse?.VideoComment?.url ||
709 abuse.FlaggedAccount.Actor.url
710 }
711
602 static get Instance () { 712 static get Instance () {
603 return this.instance || (this.instance = new this()) 713 return this.instance || (this.instance = new this())
604 } 714 }
diff --git a/server/lib/user.ts b/server/lib/user.ts
index 6e7a738ee..aa14f0b54 100644
--- a/server/lib/user.ts
+++ b/server/lib/user.ts
@@ -141,6 +141,8 @@ function createDefaultUserNotificationSettings (user: MUserId, t: Transaction |
141 commentMention: UserNotificationSettingValue.WEB, 141 commentMention: UserNotificationSettingValue.WEB,
142 newFollow: UserNotificationSettingValue.WEB, 142 newFollow: UserNotificationSettingValue.WEB,
143 newInstanceFollower: UserNotificationSettingValue.WEB, 143 newInstanceFollower: UserNotificationSettingValue.WEB,
144 abuseNewMessage: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
145 abuseStateChange: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
144 autoInstanceFollowing: UserNotificationSettingValue.WEB 146 autoInstanceFollowing: UserNotificationSettingValue.WEB
145 } 147 }
146 148