2018-12-26 03:36:24 -06:00
import { UserNotificationSettingValue , UserNotificationType , UserRight } from '../../shared/models/users'
import { logger } from '../helpers/logger'
import { VideoModel } from '../models/video/video'
import { Emailer } from './emailer'
import { UserNotificationModel } from '../models/account/user-notification'
import { VideoCommentModel } from '../models/video/video-comment'
import { UserModel } from '../models/account/user'
import { PeerTubeSocket } from './peertube-socket'
2019-04-11 04:33:44 -05:00
import { CONFIG } from '../initializers/config'
2018-12-26 03:36:24 -06:00
import { VideoPrivacy , VideoState } from '../../shared/models/videos'
import { VideoAbuseModel } from '../models/video/video-abuse'
import { VideoBlacklistModel } from '../models/video/video-blacklist'
import * as Bluebird from 'bluebird'
2019-01-02 09:37:43 -06:00
import { VideoImportModel } from '../models/video/video-import'
import { AccountBlocklistModel } from '../models/account/account-blocklist'
2019-01-04 01:56:20 -06:00
import { ActorFollowModel } from '../models/activitypub/actor-follow'
import { AccountModel } from '../models/account/account'
2018-12-26 03:36:24 -06:00
class Notifier {
private static instance : Notifier
private constructor ( ) { }
2019-07-23 05:04:15 -05:00
notifyOnNewVideoIfNeeded ( video : VideoModel ) : void {
2019-04-02 04:26:47 -05:00
// Only notify on public and published videos which are not blacklisted
2019-07-23 05:04:15 -05:00
if ( video . privacy !== VideoPrivacy . PUBLIC || video . state !== VideoState . PUBLISHED || video . isBlacklisted ( ) ) return
2018-12-26 03:36:24 -06:00
this . notifySubscribersOfNewVideo ( video )
. catch ( err = > logger . error ( 'Cannot notify subscribers of new video %s.' , video . url , { err } ) )
}
2019-04-02 04:26:47 -05:00
notifyOnVideoPublishedAfterTranscoding ( video : VideoModel ) : void {
// don't notify if didn't wait for transcoding or video is still blacklisted/waiting for scheduled update
if ( ! video . waitTranscoding || video . VideoBlacklist || video . ScheduleVideoUpdate ) return
2019-01-02 09:37:43 -06:00
this . notifyOwnedVideoHasBeenPublished ( video )
2019-04-02 04:26:47 -05:00
. catch ( err = > logger . error ( 'Cannot notify owner that its video %s has been published after transcoding.' , video . url , { err } ) )
}
notifyOnVideoPublishedAfterScheduledUpdate ( video : VideoModel ) : void {
// don't notify if video is still blacklisted or waiting for transcoding
if ( video . VideoBlacklist || ( video . waitTranscoding && video . state !== VideoState . PUBLISHED ) ) return
this . notifyOwnedVideoHasBeenPublished ( video )
. catch ( err = > logger . error ( 'Cannot notify owner that its video %s has been published after scheduled update.' , video . url , { err } ) )
}
notifyOnVideoPublishedAfterRemovedFromAutoBlacklist ( video : VideoModel ) : void {
// don't notify if video is still waiting for transcoding or scheduled update
if ( video . ScheduleVideoUpdate || ( video . waitTranscoding && video . state !== VideoState . PUBLISHED ) ) return
this . notifyOwnedVideoHasBeenPublished ( video )
. catch ( err = > logger . error ( 'Cannot notify owner that its video %s has been published after removed from auto-blacklist.' , video . url , { err } ) ) // tslint:disable-line:max-line-length
2019-01-02 09:37:43 -06:00
}
2018-12-26 03:36:24 -06:00
notifyOnNewComment ( comment : VideoCommentModel ) : void {
this . notifyVideoOwnerOfNewComment ( comment )
2019-01-04 01:56:20 -06:00
. catch ( err = > logger . error ( 'Cannot notify video owner of new comment %s.' , comment . url , { err } ) )
this . notifyOfCommentMention ( comment )
. catch ( err = > logger . error ( 'Cannot notify mentions of comment %s.' , comment . url , { err } ) )
2018-12-26 03:36:24 -06:00
}
notifyOnNewVideoAbuse ( videoAbuse : VideoAbuseModel ) : void {
this . notifyModeratorsOfNewVideoAbuse ( videoAbuse )
. catch ( err = > logger . error ( 'Cannot notify of new video abuse of video %s.' , videoAbuse . Video . url , { err } ) )
}
2019-04-02 04:26:47 -05:00
notifyOnVideoAutoBlacklist ( video : VideoModel ) : void {
this . notifyModeratorsOfVideoAutoBlacklist ( video )
. catch ( err = > logger . error ( 'Cannot notify of auto-blacklist of video %s.' , video . url , { err } ) )
}
2018-12-26 03:36:24 -06:00
notifyOnVideoBlacklist ( videoBlacklist : VideoBlacklistModel ) : void {
this . notifyVideoOwnerOfBlacklist ( videoBlacklist )
. catch ( err = > logger . error ( 'Cannot notify video owner of new video blacklist of %s.' , videoBlacklist . Video . url , { err } ) )
}
notifyOnVideoUnblacklist ( video : VideoModel ) : void {
this . notifyVideoOwnerOfUnblacklist ( video )
2019-04-02 04:26:47 -05:00
. catch ( err = > logger . error ( 'Cannot notify video owner of unblacklist of %s.' , video . url , { err } ) )
2018-12-26 03:36:24 -06:00
}
2019-01-02 09:37:43 -06:00
notifyOnFinishedVideoImport ( videoImport : VideoImportModel , success : boolean ) : void {
this . notifyOwnerVideoImportIsFinished ( videoImport , success )
. catch ( err = > logger . error ( 'Cannot notify owner that its video import %s is finished.' , videoImport . getTargetIdentifier ( ) , { err } ) )
}
2019-01-04 01:56:20 -06:00
notifyOnNewUserRegistration ( user : UserModel ) : void {
this . notifyModeratorsOfNewUserRegistration ( user )
. catch ( err = > logger . error ( 'Cannot notify moderators of new user registration (%s).' , user . username , { err } ) )
}
2019-04-08 10:26:01 -05:00
notifyOfNewUserFollow ( actorFollow : ActorFollowModel ) : void {
2019-01-04 01:56:20 -06:00
this . notifyUserOfNewActorFollow ( actorFollow )
. catch ( err = > {
logger . error (
'Cannot notify owner of channel %s of a new follow by %s.' ,
actorFollow . ActorFollowing . VideoChannel . getDisplayName ( ) ,
actorFollow . ActorFollower . Account . getDisplayName ( ) ,
2019-04-08 10:26:01 -05:00
{ err }
2019-01-04 01:56:20 -06:00
)
} )
}
2019-04-08 10:26:01 -05:00
notifyOfNewInstanceFollow ( actorFollow : ActorFollowModel ) : void {
this . notifyAdminsOfNewInstanceFollow ( actorFollow )
. catch ( err = > {
logger . error ( 'Cannot notify administrators of new follower %s.' , actorFollow . ActorFollower . url , { err } )
} )
}
2018-12-26 03:36:24 -06:00
private async notifySubscribersOfNewVideo ( video : VideoModel ) {
// List all followers that are users
const users = await UserModel . listUserSubscribersOf ( video . VideoChannel . actorId )
logger . info ( 'Notifying %d users of new video %s.' , users . length , video . url )
function settingGetter ( user : UserModel ) {
return user . NotificationSetting . newVideoFromSubscription
}
async function notificationCreator ( user : UserModel ) {
const notification = await UserNotificationModel . create ( {
type : UserNotificationType . NEW_VIDEO_FROM_SUBSCRIPTION ,
userId : user.id ,
videoId : video.id
} )
notification . Video = video
return notification
}
function emailSender ( emails : string [ ] ) {
return Emailer . Instance . addNewVideoFromSubscriberNotification ( emails , video )
}
return this . notify ( { users , settingGetter , notificationCreator , emailSender } )
}
private async notifyVideoOwnerOfNewComment ( comment : VideoCommentModel ) {
2019-01-04 01:56:20 -06:00
if ( comment . Video . isOwned ( ) === false ) return
2018-12-26 03:36:24 -06:00
const user = await UserModel . loadByVideoId ( comment . videoId )
// Not our user or user comments its own video
if ( ! user || comment . Account . userId === user . id ) return
2019-01-02 09:37:43 -06:00
const accountMuted = await AccountBlocklistModel . isAccountMutedBy ( user . Account . id , comment . accountId )
if ( accountMuted ) return
2018-12-26 03:36:24 -06:00
logger . info ( 'Notifying user %s of new comment %s.' , user . username , comment . url )
function settingGetter ( user : UserModel ) {
return user . NotificationSetting . newCommentOnMyVideo
}
async function notificationCreator ( user : UserModel ) {
const notification = await UserNotificationModel . create ( {
type : UserNotificationType . NEW_COMMENT_ON_MY_VIDEO ,
userId : user.id ,
commentId : comment.id
} )
notification . Comment = comment
return notification
}
function emailSender ( emails : string [ ] ) {
return Emailer . Instance . addNewCommentOnMyVideoNotification ( emails , comment )
}
return this . notify ( { users : [ user ] , settingGetter , notificationCreator , emailSender } )
}
2019-01-04 01:56:20 -06:00
private async notifyOfCommentMention ( comment : VideoCommentModel ) {
2019-02-15 08:52:18 -06:00
const extractedUsernames = comment . extractMentions ( )
logger . debug (
'Extracted %d username from comment %s.' , extractedUsernames . length , comment . url ,
{ usernames : extractedUsernames , text : comment.text }
)
2019-02-14 04:04:50 -06:00
2019-02-15 08:52:18 -06:00
let users = await UserModel . listByUsernames ( extractedUsernames )
2019-01-04 01:56:20 -06:00
if ( comment . Video . isOwned ( ) ) {
const userException = await UserModel . loadByVideoId ( comment . videoId )
users = users . filter ( u = > u . id !== userException . id )
}
// Don't notify if I mentioned myself
users = users . filter ( u = > u . Account . id !== comment . accountId )
2018-12-26 03:36:24 -06:00
if ( users . length === 0 ) return
2019-01-04 01:56:20 -06:00
const accountMutedHash = await AccountBlocklistModel . isAccountMutedByMulti ( users . map ( u = > u . Account . id ) , comment . accountId )
logger . info ( 'Notifying %d users of new comment %s.' , users . length , comment . url )
function settingGetter ( user : UserModel ) {
if ( accountMutedHash [ user . Account . id ] === true ) return UserNotificationSettingValue . NONE
return user . NotificationSetting . commentMention
}
async function notificationCreator ( user : UserModel ) {
const notification = await UserNotificationModel . create ( {
type : UserNotificationType . COMMENT_MENTION ,
userId : user.id ,
commentId : comment.id
} )
notification . Comment = comment
return notification
}
function emailSender ( emails : string [ ] ) {
return Emailer . Instance . addNewCommentMentionNotification ( emails , comment )
}
return this . notify ( { users , settingGetter , notificationCreator , emailSender } )
}
private async notifyUserOfNewActorFollow ( actorFollow : ActorFollowModel ) {
if ( actorFollow . ActorFollowing . isOwned ( ) === false ) return
// Account follows one of our account?
let followType : 'account' | 'channel' = 'channel'
let user = await UserModel . loadByChannelActorId ( actorFollow . ActorFollowing . id )
// Account follows one of our channel?
if ( ! user ) {
user = await UserModel . loadByAccountActorId ( actorFollow . ActorFollowing . id )
followType = 'account'
}
if ( ! user ) return
if ( ! actorFollow . ActorFollower . Account || ! actorFollow . ActorFollower . Account . name ) {
actorFollow . ActorFollower . Account = await actorFollow . ActorFollower . $get ( 'Account' ) as AccountModel
}
const followerAccount = actorFollow . ActorFollower . Account
const accountMuted = await AccountBlocklistModel . isAccountMutedBy ( user . Account . id , followerAccount . id )
if ( accountMuted ) return
logger . info ( 'Notifying user %s of new follower: %s.' , user . username , followerAccount . getDisplayName ( ) )
function settingGetter ( user : UserModel ) {
return user . NotificationSetting . newFollow
}
async function notificationCreator ( user : UserModel ) {
const notification = await UserNotificationModel . create ( {
type : UserNotificationType . NEW_FOLLOW ,
userId : user.id ,
actorFollowId : actorFollow.id
} )
notification . ActorFollow = actorFollow
return notification
}
function emailSender ( emails : string [ ] ) {
return Emailer . Instance . addNewFollowNotification ( emails , actorFollow , followType )
}
return this . notify ( { users : [ user ] , settingGetter , notificationCreator , emailSender } )
}
2019-04-08 10:26:01 -05:00
private async notifyAdminsOfNewInstanceFollow ( actorFollow : ActorFollowModel ) {
const admins = await UserModel . listWithRight ( UserRight . MANAGE_SERVER_FOLLOW )
logger . info ( 'Notifying %d administrators of new instance follower: %s.' , admins . length , actorFollow . ActorFollower . url )
function settingGetter ( user : UserModel ) {
return user . NotificationSetting . newInstanceFollower
}
async function notificationCreator ( user : UserModel ) {
const notification = await UserNotificationModel . create ( {
type : UserNotificationType . NEW_INSTANCE_FOLLOWER ,
userId : user.id ,
actorFollowId : actorFollow.id
} )
notification . ActorFollow = actorFollow
return notification
}
function emailSender ( emails : string [ ] ) {
return Emailer . Instance . addNewInstanceFollowerNotification ( emails , actorFollow )
}
return this . notify ( { users : admins , settingGetter , notificationCreator , emailSender } )
}
2019-01-04 01:56:20 -06:00
private async notifyModeratorsOfNewVideoAbuse ( videoAbuse : VideoAbuseModel ) {
const moderators = await UserModel . listWithRight ( UserRight . MANAGE_VIDEO_ABUSES )
if ( moderators . length === 0 ) return
logger . info ( 'Notifying %s user/moderators of new video abuse %s.' , moderators . length , videoAbuse . Video . url )
2018-12-26 03:36:24 -06:00
function settingGetter ( user : UserModel ) {
return user . NotificationSetting . videoAbuseAsModerator
}
async function notificationCreator ( user : UserModel ) {
const notification = await UserNotificationModel . create ( {
type : UserNotificationType . NEW_VIDEO_ABUSE_FOR_MODERATORS ,
userId : user.id ,
videoAbuseId : videoAbuse.id
} )
notification . VideoAbuse = videoAbuse
return notification
}
function emailSender ( emails : string [ ] ) {
return Emailer . Instance . addVideoAbuseModeratorsNotification ( emails , videoAbuse )
}
2019-01-04 01:56:20 -06:00
return this . notify ( { users : moderators , settingGetter , notificationCreator , emailSender } )
2018-12-26 03:36:24 -06:00
}
2019-04-02 04:26:47 -05:00
private async notifyModeratorsOfVideoAutoBlacklist ( video : VideoModel ) {
const moderators = await UserModel . listWithRight ( UserRight . MANAGE_VIDEO_BLACKLIST )
if ( moderators . length === 0 ) return
logger . info ( 'Notifying %s moderators of video auto-blacklist %s.' , moderators . length , video . url )
function settingGetter ( user : UserModel ) {
return user . NotificationSetting . videoAutoBlacklistAsModerator
}
async function notificationCreator ( user : UserModel ) {
const notification = await UserNotificationModel . create ( {
type : UserNotificationType . VIDEO_AUTO_BLACKLIST_FOR_MODERATORS ,
userId : user.id ,
videoId : video.id
} )
notification . Video = video
return notification
}
function emailSender ( emails : string [ ] ) {
return Emailer . Instance . addVideoAutoBlacklistModeratorsNotification ( emails , video )
}
return this . notify ( { users : moderators , settingGetter , notificationCreator , emailSender } )
}
2018-12-26 03:36:24 -06:00
private async notifyVideoOwnerOfBlacklist ( videoBlacklist : VideoBlacklistModel ) {
const user = await UserModel . loadByVideoId ( videoBlacklist . videoId )
if ( ! user ) return
logger . info ( 'Notifying user %s that its video %s has been blacklisted.' , user . username , videoBlacklist . Video . url )
function settingGetter ( user : UserModel ) {
return user . NotificationSetting . blacklistOnMyVideo
}
async function notificationCreator ( user : UserModel ) {
const notification = await UserNotificationModel . create ( {
type : UserNotificationType . BLACKLIST_ON_MY_VIDEO ,
userId : user.id ,
videoBlacklistId : videoBlacklist.id
} )
notification . VideoBlacklist = videoBlacklist
return notification
}
function emailSender ( emails : string [ ] ) {
return Emailer . Instance . addVideoBlacklistNotification ( emails , videoBlacklist )
}
return this . notify ( { users : [ user ] , settingGetter , notificationCreator , emailSender } )
}
private async notifyVideoOwnerOfUnblacklist ( video : VideoModel ) {
const user = await UserModel . loadByVideoId ( video . id )
if ( ! user ) return
logger . info ( 'Notifying user %s that its video %s has been unblacklisted.' , user . username , video . url )
function settingGetter ( user : UserModel ) {
return user . NotificationSetting . blacklistOnMyVideo
}
async function notificationCreator ( user : UserModel ) {
const notification = await UserNotificationModel . create ( {
type : UserNotificationType . UNBLACKLIST_ON_MY_VIDEO ,
userId : user.id ,
videoId : video.id
} )
notification . Video = video
return notification
}
function emailSender ( emails : string [ ] ) {
return Emailer . Instance . addVideoUnblacklistNotification ( emails , video )
}
return this . notify ( { users : [ user ] , settingGetter , notificationCreator , emailSender } )
}
2019-01-02 09:37:43 -06:00
private async notifyOwnedVideoHasBeenPublished ( video : VideoModel ) {
const user = await UserModel . loadByVideoId ( video . id )
if ( ! user ) return
logger . info ( 'Notifying user %s of the publication of its video %s.' , user . username , video . url )
function settingGetter ( user : UserModel ) {
return user . NotificationSetting . myVideoPublished
}
async function notificationCreator ( user : UserModel ) {
const notification = await UserNotificationModel . create ( {
type : UserNotificationType . MY_VIDEO_PUBLISHED ,
userId : user.id ,
videoId : video.id
} )
notification . Video = video
return notification
}
function emailSender ( emails : string [ ] ) {
return Emailer . Instance . myVideoPublishedNotification ( emails , video )
}
return this . notify ( { users : [ user ] , settingGetter , notificationCreator , emailSender } )
}
private async notifyOwnerVideoImportIsFinished ( videoImport : VideoImportModel , success : boolean ) {
const user = await UserModel . loadByVideoImportId ( videoImport . id )
if ( ! user ) return
logger . info ( 'Notifying user %s its video import %s is finished.' , user . username , videoImport . getTargetIdentifier ( ) )
function settingGetter ( user : UserModel ) {
return user . NotificationSetting . myVideoImportFinished
}
async function notificationCreator ( user : UserModel ) {
const notification = await UserNotificationModel . create ( {
type : success ? UserNotificationType.MY_VIDEO_IMPORT_SUCCESS : UserNotificationType.MY_VIDEO_IMPORT_ERROR ,
userId : user.id ,
videoImportId : videoImport.id
} )
notification . VideoImport = videoImport
return notification
}
function emailSender ( emails : string [ ] ) {
return success
? Emailer . Instance . myVideoImportSuccessNotification ( emails , videoImport )
: Emailer . Instance . myVideoImportErrorNotification ( emails , videoImport )
}
return this . notify ( { users : [ user ] , settingGetter , notificationCreator , emailSender } )
}
2019-01-04 01:56:20 -06:00
private async notifyModeratorsOfNewUserRegistration ( registeredUser : UserModel ) {
const moderators = await UserModel . listWithRight ( UserRight . MANAGE_USERS )
if ( moderators . length === 0 ) return
logger . info (
'Notifying %s moderators of new user registration of %s.' ,
moderators . length , registeredUser . Account . Actor . preferredUsername
)
function settingGetter ( user : UserModel ) {
return user . NotificationSetting . newUserRegistration
}
async function notificationCreator ( user : UserModel ) {
const notification = await UserNotificationModel . create ( {
type : UserNotificationType . NEW_USER_REGISTRATION ,
userId : user.id ,
accountId : registeredUser.Account.id
} )
notification . Account = registeredUser . Account
return notification
}
function emailSender ( emails : string [ ] ) {
return Emailer . Instance . addNewUserRegistrationNotification ( emails , registeredUser )
}
return this . notify ( { users : moderators , settingGetter , notificationCreator , emailSender } )
}
2018-12-26 03:36:24 -06:00
private async notify ( options : {
users : UserModel [ ] ,
notificationCreator : ( user : UserModel ) = > Promise < UserNotificationModel > ,
emailSender : ( emails : string [ ] ) = > Promise < any > | Bluebird < any > ,
settingGetter : ( user : UserModel ) = > UserNotificationSettingValue
} ) {
const emails : string [ ] = [ ]
for ( const user of options . users ) {
if ( this . isWebNotificationEnabled ( options . settingGetter ( user ) ) ) {
const notification = await options . notificationCreator ( user )
PeerTubeSocket . Instance . sendNotification ( user . id , notification )
}
if ( this . isEmailEnabled ( user , options . settingGetter ( user ) ) ) {
emails . push ( user . email )
}
}
if ( emails . length !== 0 ) {
await options . emailSender ( emails )
}
}
private isEmailEnabled ( user : UserModel , value : UserNotificationSettingValue ) {
2019-03-19 10:41:41 -05:00
if ( CONFIG . SIGNUP . REQUIRES_EMAIL_VERIFICATION === true && user . emailVerified === false ) return false
2018-12-26 03:36:24 -06:00
2019-01-08 04:26:41 -06:00
return value & UserNotificationSettingValue . EMAIL
2018-12-26 03:36:24 -06:00
}
private isWebNotificationEnabled ( value : UserNotificationSettingValue ) {
2019-01-08 04:26:41 -06:00
return value & UserNotificationSettingValue . WEB
2018-12-26 03:36:24 -06:00
}
static get Instance ( ) {
return this . instance || ( this . instance = new this ( ) )
}
}
// ---------------------------------------------------------------------------
export {
Notifier
}