Add notification plugin hook

This commit is contained in:
Chocobozzz 2022-08-03 11:33:43 +02:00
parent 0260dc8aca
commit 785f1897a4
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
23 changed files with 61 additions and 43 deletions

View File

@ -7,12 +7,12 @@ import { MAbuseFull, MAbuseMessage, MActorFollowFull, MApplication, MPlugin } fr
import { MCommentOwnerVideo, MVideoAccountLight, MVideoFullLight } from '../../types/models/video' import { MCommentOwnerVideo, MVideoAccountLight, MVideoFullLight } from '../../types/models/video'
import { JobQueue } from '../job-queue' import { JobQueue } from '../job-queue'
import { PeerTubeSocket } from '../peertube-socket' import { PeerTubeSocket } from '../peertube-socket'
import { Hooks } from '../plugins/hooks'
import { import {
AbstractNotification, AbstractNotification,
AbuseStateChangeForReporter, AbuseStateChangeForReporter,
AutoFollowForInstance, AutoFollowForInstance,
CommentMention, CommentMention,
StudioEditionFinishedForOwner,
FollowForInstance, FollowForInstance,
FollowForUser, FollowForUser,
ImportFinishedForOwner, ImportFinishedForOwner,
@ -31,6 +31,7 @@ import {
OwnedPublicationAfterScheduleUpdate, OwnedPublicationAfterScheduleUpdate,
OwnedPublicationAfterTranscoding, OwnedPublicationAfterTranscoding,
RegistrationForModerators, RegistrationForModerators,
StudioEditionFinishedForOwner,
UnblacklistForOwner UnblacklistForOwner
} from './shared' } from './shared'
@ -222,15 +223,21 @@ class Notifier {
for (const user of users) { for (const user of users) {
const setting = object.getSetting(user) const setting = object.getSetting(user)
if (this.isWebNotificationEnabled(setting)) { const webNotificationEnabled = this.isWebNotificationEnabled(setting)
const notification = await object.createNotification(user) const emailNotificationEnabled = this.isEmailEnabled(user, setting)
const notification = object.createNotification(user)
if (webNotificationEnabled) {
await notification.save()
PeerTubeSocket.Instance.sendNotification(user.id, notification) PeerTubeSocket.Instance.sendNotification(user.id, notification)
} }
if (this.isEmailEnabled(user, setting)) { if (emailNotificationEnabled) {
toEmails.push(user.email) toEmails.push(user.email)
} }
Hooks.runAction('action:notifier.notification.created', { webNotificationEnabled, emailNotificationEnabled, user, notification })
} }
for (const to of toEmails) { for (const to of toEmails) {

View File

@ -21,8 +21,8 @@ export abstract class AbstractNewAbuseMessage extends AbstractNotification <NewA
return user.NotificationSetting.abuseNewMessage return user.NotificationSetting.abuseNewMessage
} }
async createNotification (user: MUserWithNotificationSetting) { createNotification (user: MUserWithNotificationSetting) {
const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ const notification = UserNotificationModel.build<UserNotificationModelForApi>({
type: UserNotificationType.ABUSE_NEW_MESSAGE, type: UserNotificationType.ABUSE_NEW_MESSAGE,
userId: user.id, userId: user.id,
abuseId: this.abuse.id abuseId: this.abuse.id

View File

@ -32,8 +32,8 @@ export class AbuseStateChangeForReporter extends AbstractNotification <MAbuseFul
return [ this.user ] return [ this.user ]
} }
async createNotification (user: MUserWithNotificationSetting) { createNotification (user: MUserWithNotificationSetting) {
const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ const notification = UserNotificationModel.build<UserNotificationModelForApi>({
type: UserNotificationType.ABUSE_STATE_CHANGE, type: UserNotificationType.ABUSE_STATE_CHANGE,
userId: user.id, userId: user.id,
abuseId: this.abuse.id abuseId: this.abuse.id

View File

@ -28,8 +28,8 @@ export class NewAbuseForModerators extends AbstractNotification <NewAbusePayload
return this.moderators return this.moderators
} }
async createNotification (user: MUserWithNotificationSetting) { createNotification (user: MUserWithNotificationSetting) {
const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ const notification = UserNotificationModel.build<UserNotificationModelForApi>({
type: UserNotificationType.NEW_ABUSE_FOR_MODERATORS, type: UserNotificationType.NEW_ABUSE_FOR_MODERATORS,
userId: user.id, userId: user.id,
abuseId: this.payload.abuseInstance.id abuseId: this.payload.abuseInstance.id

View File

@ -26,8 +26,8 @@ export class NewAutoBlacklistForModerators extends AbstractNotification <MVideoB
return this.moderators return this.moderators
} }
async createNotification (user: MUserWithNotificationSetting) { createNotification (user: MUserWithNotificationSetting) {
const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ const notification = UserNotificationModel.build<UserNotificationModelForApi>({
type: UserNotificationType.VIDEO_AUTO_BLACKLIST_FOR_MODERATORS, type: UserNotificationType.VIDEO_AUTO_BLACKLIST_FOR_MODERATORS,
userId: user.id, userId: user.id,
videoBlacklistId: this.payload.id videoBlacklistId: this.payload.id

View File

@ -28,8 +28,8 @@ export class NewBlacklistForOwner extends AbstractNotification <MVideoBlacklistV
return [ this.user ] return [ this.user ]
} }
async createNotification (user: MUserWithNotificationSetting) { createNotification (user: MUserWithNotificationSetting) {
const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ const notification = UserNotificationModel.build<UserNotificationModelForApi>({
type: UserNotificationType.BLACKLIST_ON_MY_VIDEO, type: UserNotificationType.BLACKLIST_ON_MY_VIDEO,
userId: user.id, userId: user.id,
videoBlacklistId: this.payload.id videoBlacklistId: this.payload.id

View File

@ -28,8 +28,8 @@ export class UnblacklistForOwner extends AbstractNotification <MVideoFullLight>
return [ this.user ] return [ this.user ]
} }
async createNotification (user: MUserWithNotificationSetting) { createNotification (user: MUserWithNotificationSetting) {
const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ const notification = UserNotificationModel.build<UserNotificationModelForApi>({
type: UserNotificationType.UNBLACKLIST_ON_MY_VIDEO, type: UserNotificationType.UNBLACKLIST_ON_MY_VIDEO,
userId: user.id, userId: user.id,
videoId: this.payload.id videoId: this.payload.id

View File

@ -71,8 +71,8 @@ export class CommentMention extends AbstractNotification <MCommentOwnerVideo, MU
return this.users return this.users
} }
async createNotification (user: MUserWithNotificationSetting) { createNotification (user: MUserWithNotificationSetting) {
const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ const notification = UserNotificationModel.build<UserNotificationModelForApi>({
type: UserNotificationType.COMMENT_MENTION, type: UserNotificationType.COMMENT_MENTION,
userId: user.id, userId: user.id,
commentId: this.payload.id commentId: this.payload.id

View File

@ -38,8 +38,8 @@ export class NewCommentForVideoOwner extends AbstractNotification <MCommentOwner
return [ this.user ] return [ this.user ]
} }
async createNotification (user: MUserWithNotificationSetting) { createNotification (user: MUserWithNotificationSetting) {
const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ const notification = UserNotificationModel.build<UserNotificationModelForApi>({
type: UserNotificationType.NEW_COMMENT_ON_MY_VIDEO, type: UserNotificationType.NEW_COMMENT_ON_MY_VIDEO,
userId: user.id, userId: user.id,
commentId: this.payload.id commentId: this.payload.id

View File

@ -13,7 +13,7 @@ export abstract class AbstractNotification <T, U = MUserWithNotificationSetting>
abstract getSetting (user: U): UserNotificationSettingValue abstract getSetting (user: U): UserNotificationSettingValue
abstract getTargetUsers (): U[] abstract getTargetUsers (): U[]
abstract createNotification (user: U): Promise<UserNotificationModelForApi> abstract createNotification (user: U): UserNotificationModelForApi
abstract createEmail (to: string): EmailPayload | Promise<EmailPayload> abstract createEmail (to: string): EmailPayload | Promise<EmailPayload>
isDisabled (): boolean | Promise<boolean> { isDisabled (): boolean | Promise<boolean> {

View File

@ -24,8 +24,8 @@ export class AutoFollowForInstance extends AbstractNotification <MActorFollowFul
return this.admins return this.admins
} }
async createNotification (user: MUserWithNotificationSetting) { createNotification (user: MUserWithNotificationSetting) {
const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ const notification = UserNotificationModel.build<UserNotificationModelForApi>({
type: UserNotificationType.AUTO_INSTANCE_FOLLOWING, type: UserNotificationType.AUTO_INSTANCE_FOLLOWING,
userId: user.id, userId: user.id,
actorFollowId: this.actorFollow.id actorFollowId: this.actorFollow.id

View File

@ -32,8 +32,8 @@ export class FollowForInstance extends AbstractNotification <MActorFollowFull> {
return this.admins return this.admins
} }
async createNotification (user: MUserWithNotificationSetting) { createNotification (user: MUserWithNotificationSetting) {
const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ const notification = UserNotificationModel.build<UserNotificationModelForApi>({
type: UserNotificationType.NEW_INSTANCE_FOLLOWER, type: UserNotificationType.NEW_INSTANCE_FOLLOWER,
userId: user.id, userId: user.id,
actorFollowId: this.actorFollow.id actorFollowId: this.actorFollow.id

View File

@ -45,8 +45,8 @@ export class FollowForUser extends AbstractNotification <MActorFollowFull> {
return [ this.user ] return [ this.user ]
} }
async createNotification (user: MUserWithNotificationSetting) { createNotification (user: MUserWithNotificationSetting) {
const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ const notification = UserNotificationModel.build<UserNotificationModelForApi>({
type: UserNotificationType.NEW_FOLLOW, type: UserNotificationType.NEW_FOLLOW,
userId: user.id, userId: user.id,
actorFollowId: this.actorFollow.id actorFollowId: this.actorFollow.id

View File

@ -30,8 +30,8 @@ export class NewPeerTubeVersionForAdmins extends AbstractNotification <NewPeerTu
return this.admins return this.admins
} }
async createNotification (user: MUserWithNotificationSetting) { createNotification (user: MUserWithNotificationSetting) {
const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ const notification = UserNotificationModel.build<UserNotificationModelForApi>({
type: UserNotificationType.NEW_PEERTUBE_VERSION, type: UserNotificationType.NEW_PEERTUBE_VERSION,
userId: user.id, userId: user.id,
applicationId: this.payload.application.id applicationId: this.payload.application.id

View File

@ -26,8 +26,8 @@ export class NewPluginVersionForAdmins extends AbstractNotification <MPlugin> {
return this.admins return this.admins
} }
async createNotification (user: MUserWithNotificationSetting) { createNotification (user: MUserWithNotificationSetting) {
const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ const notification = UserNotificationModel.build<UserNotificationModelForApi>({
type: UserNotificationType.NEW_PLUGIN_VERSION, type: UserNotificationType.NEW_PLUGIN_VERSION,
userId: user.id, userId: user.id,
pluginId: this.plugin.id pluginId: this.plugin.id

View File

@ -25,8 +25,8 @@ export class RegistrationForModerators extends AbstractNotification <MUserDefaul
return this.moderators return this.moderators
} }
async createNotification (user: MUserWithNotificationSetting) { createNotification (user: MUserWithNotificationSetting) {
const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ const notification = UserNotificationModel.build<UserNotificationModelForApi>({
type: UserNotificationType.NEW_USER_REGISTRATION, type: UserNotificationType.NEW_USER_REGISTRATION,
userId: user.id, userId: user.id,
accountId: this.payload.Account.id accountId: this.payload.Account.id

View File

@ -27,8 +27,8 @@ export abstract class AbstractOwnedVideoPublication extends AbstractNotification
return [ this.user ] return [ this.user ]
} }
async createNotification (user: MUserWithNotificationSetting) { createNotification (user: MUserWithNotificationSetting) {
const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ const notification = UserNotificationModel.build<UserNotificationModelForApi>({
type: UserNotificationType.MY_VIDEO_PUBLISHED, type: UserNotificationType.MY_VIDEO_PUBLISHED,
userId: user.id, userId: user.id,
videoId: this.payload.id videoId: this.payload.id

View File

@ -32,8 +32,8 @@ export class ImportFinishedForOwner extends AbstractNotification <ImportFinished
return [ this.user ] return [ this.user ]
} }
async createNotification (user: MUserWithNotificationSetting) { createNotification (user: MUserWithNotificationSetting) {
const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ const notification = UserNotificationModel.build<UserNotificationModelForApi>({
type: this.payload.success type: this.payload.success
? UserNotificationType.MY_VIDEO_IMPORT_SUCCESS ? UserNotificationType.MY_VIDEO_IMPORT_SUCCESS
: UserNotificationType.MY_VIDEO_IMPORT_ERROR, : UserNotificationType.MY_VIDEO_IMPORT_ERROR,

View File

@ -30,8 +30,8 @@ export class NewVideoForSubscribers extends AbstractNotification <MVideoAccountL
return this.users return this.users
} }
async createNotification (user: MUserWithNotificationSetting) { createNotification (user: MUserWithNotificationSetting) {
const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ const notification = UserNotificationModel.build<UserNotificationModelForApi>({
type: UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION, type: UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION,
userId: user.id, userId: user.id,
videoId: this.payload.id videoId: this.payload.id

View File

@ -27,8 +27,8 @@ export class StudioEditionFinishedForOwner extends AbstractNotification <MVideoF
return [ this.user ] return [ this.user ]
} }
async createNotification (user: MUserWithNotificationSetting) { createNotification (user: MUserWithNotificationSetting) {
const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ const notification = UserNotificationModel.build<UserNotificationModelForApi>({
type: UserNotificationType.MY_VIDEO_STUDIO_EDITION_FINISHED, type: UserNotificationType.MY_VIDEO_STUDIO_EDITION_FINISHED,
userId: user.id, userId: user.id,
videoId: this.payload.id videoId: this.payload.id

View File

@ -1,6 +1,7 @@
async function register ({ registerHook, registerSetting, settingsManager, storageManager, peertubeHelpers }) { async function register ({ registerHook, registerSetting, settingsManager, storageManager, peertubeHelpers }) {
const actionHooks = [ const actionHooks = [
'action:application.listening', 'action:application.listening',
'action:notifier.notification.created',
'action:api.video.updated', 'action:api.video.updated',
'action:api.video.deleted', 'action:api.video.deleted',

View File

@ -17,8 +17,8 @@ describe('Test plugin action hooks', function () {
let videoUUID: string let videoUUID: string
let threadId: number let threadId: number
function checkHook (hook: ServerHookName) { function checkHook (hook: ServerHookName, strictCount = true) {
return servers[0].servers.waitUntilLog('Run hook ' + hook) return servers[0].servers.waitUntilLog('Run hook ' + hook, 1, strictCount)
} }
before(async function () { before(async function () {
@ -225,6 +225,13 @@ describe('Test plugin action hooks', function () {
}) })
}) })
describe('Notification hook', function () {
it('Should run action:notifier.notification.created', async function () {
await checkHook('action:notifier.notification.created', false)
})
})
after(async function () { after(async function () {
await cleanupTests(servers) await cleanupTests(servers)
}) })

View File

@ -112,6 +112,9 @@ export const serverActionHookObject = {
// Fired when the application has been loaded and is listening HTTP requests // Fired when the application has been loaded and is listening HTTP requests
'action:application.listening': true, 'action:application.listening': true,
// Fired when a new notification is created
'action:notifier.notification.created': true,
// API actions hooks give access to the original express `req` and `res` parameters // API actions hooks give access to the original express `req` and `res` parameters
// Fired when a local video is updated // Fired when a local video is updated