mirror of
https://github.com/Chocobozzz/PeerTube.git
synced 2025-10-05 19:42:24 +02:00
Implement auto tag on comments and videos
* Comments and videos can be automatically tagged using core rules or watched word lists * These tags can be used to automatically filter videos and comments * Introduce a new video comment policy where comments must be approved first * Comments may have to be approved if the user auto block them using core rules or watched word lists * Implement FEP-5624 to federate reply control policies
This commit is contained in:
parent
b3e39df59e
commit
29329d6c45
241 changed files with 8090 additions and 1399 deletions
|
@ -10,7 +10,7 @@ import { sanitizeAndCheckVideoCommentObject } from './video-comments.js'
|
|||
import { sanitizeAndCheckVideoTorrentObject } from './videos.js'
|
||||
import { isWatchActionObjectValid } from './watch-action.js'
|
||||
|
||||
function isRootActivityValid (activity: any) {
|
||||
export function isRootActivityValid (activity: any) {
|
||||
return isCollection(activity) || isActivity(activity)
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,8 @@ function isActivity (activity: any) {
|
|||
(isActivityPubUrlValid(activity.actor) || isActivityPubUrlValid(activity.actor.id))
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const activityCheckers: { [ P in ActivityType ]: (activity: Activity) => boolean } = {
|
||||
Create: isCreateActivityValid,
|
||||
Update: isUpdateActivityValid,
|
||||
|
@ -38,10 +40,12 @@ const activityCheckers: { [ P in ActivityType ]: (activity: Activity) => boolean
|
|||
Like: isLikeActivityValid,
|
||||
View: isViewActivityValid,
|
||||
Flag: isFlagActivityValid,
|
||||
Dislike: isDislikeActivityValid
|
||||
Dislike: isDislikeActivityValid,
|
||||
ApproveReply: isApproveReplyActivityValid,
|
||||
RejectReply: isRejectReplyActivityValid
|
||||
}
|
||||
|
||||
function isActivityValid (activity: any) {
|
||||
export function isActivityValid (activity: any) {
|
||||
const checker = activityCheckers[activity.type]
|
||||
// Unknown activity type
|
||||
if (!checker) return false
|
||||
|
@ -49,34 +53,34 @@ function isActivityValid (activity: any) {
|
|||
return checker(activity)
|
||||
}
|
||||
|
||||
function isFlagActivityValid (activity: any) {
|
||||
export function isFlagActivityValid (activity: any) {
|
||||
return isBaseActivityValid(activity, 'Flag') &&
|
||||
isAbuseReasonValid(activity.content) &&
|
||||
isActivityPubUrlValid(activity.object)
|
||||
}
|
||||
|
||||
function isLikeActivityValid (activity: any) {
|
||||
export function isLikeActivityValid (activity: any) {
|
||||
return isBaseActivityValid(activity, 'Like') &&
|
||||
isObjectValid(activity.object)
|
||||
}
|
||||
|
||||
function isDislikeActivityValid (activity: any) {
|
||||
export function isDislikeActivityValid (activity: any) {
|
||||
return isBaseActivityValid(activity, 'Dislike') &&
|
||||
isObjectValid(activity.object)
|
||||
}
|
||||
|
||||
function isAnnounceActivityValid (activity: any) {
|
||||
export function isAnnounceActivityValid (activity: any) {
|
||||
return isBaseActivityValid(activity, 'Announce') &&
|
||||
isObjectValid(activity.object)
|
||||
}
|
||||
|
||||
function isViewActivityValid (activity: any) {
|
||||
export function isViewActivityValid (activity: any) {
|
||||
return isBaseActivityValid(activity, 'View') &&
|
||||
isActivityPubUrlValid(activity.actor) &&
|
||||
isActivityPubUrlValid(activity.object)
|
||||
}
|
||||
|
||||
function isCreateActivityValid (activity: any) {
|
||||
export function isCreateActivityValid (activity: any) {
|
||||
return isBaseActivityValid(activity, 'Create') &&
|
||||
(
|
||||
isViewActivityValid(activity.object) ||
|
||||
|
@ -91,7 +95,7 @@ function isCreateActivityValid (activity: any) {
|
|||
)
|
||||
}
|
||||
|
||||
function isUpdateActivityValid (activity: any) {
|
||||
export function isUpdateActivityValid (activity: any) {
|
||||
return isBaseActivityValid(activity, 'Update') &&
|
||||
(
|
||||
isCacheFileObjectValid(activity.object) ||
|
||||
|
@ -101,26 +105,26 @@ function isUpdateActivityValid (activity: any) {
|
|||
)
|
||||
}
|
||||
|
||||
function isDeleteActivityValid (activity: any) {
|
||||
export function isDeleteActivityValid (activity: any) {
|
||||
// We don't really check objects
|
||||
return isBaseActivityValid(activity, 'Delete') &&
|
||||
isObjectValid(activity.object)
|
||||
}
|
||||
|
||||
function isFollowActivityValid (activity: any) {
|
||||
export function isFollowActivityValid (activity: any) {
|
||||
return isBaseActivityValid(activity, 'Follow') &&
|
||||
isObjectValid(activity.object)
|
||||
}
|
||||
|
||||
function isAcceptActivityValid (activity: any) {
|
||||
export function isAcceptActivityValid (activity: any) {
|
||||
return isBaseActivityValid(activity, 'Accept')
|
||||
}
|
||||
|
||||
function isRejectActivityValid (activity: any) {
|
||||
export function isRejectActivityValid (activity: any) {
|
||||
return isBaseActivityValid(activity, 'Reject')
|
||||
}
|
||||
|
||||
function isUndoActivityValid (activity: any) {
|
||||
export function isUndoActivityValid (activity: any) {
|
||||
return isBaseActivityValid(activity, 'Undo') &&
|
||||
(
|
||||
isFollowActivityValid(activity.object) ||
|
||||
|
@ -131,21 +135,14 @@ function isUndoActivityValid (activity: any) {
|
|||
)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
isRootActivityValid,
|
||||
isActivityValid,
|
||||
isFlagActivityValid,
|
||||
isLikeActivityValid,
|
||||
isDislikeActivityValid,
|
||||
isAnnounceActivityValid,
|
||||
isViewActivityValid,
|
||||
isCreateActivityValid,
|
||||
isUpdateActivityValid,
|
||||
isDeleteActivityValid,
|
||||
isFollowActivityValid,
|
||||
isAcceptActivityValid,
|
||||
isRejectActivityValid,
|
||||
isUndoActivityValid
|
||||
export function isApproveReplyActivityValid (activity: any) {
|
||||
return isBaseActivityValid(activity, 'ApproveReply') &&
|
||||
isActivityPubUrlValid(activity.object) &&
|
||||
isActivityPubUrlValid(activity.inReplyTo)
|
||||
}
|
||||
|
||||
export function isRejectReplyActivityValid (activity: any) {
|
||||
return isBaseActivityValid(activity, 'RejectReply') &&
|
||||
isActivityPubUrlValid(activity.object) &&
|
||||
isActivityPubUrlValid(activity.inReplyTo)
|
||||
}
|
||||
|
|
|
@ -2,8 +2,9 @@ import { hasAPPublic } from '@server/helpers/activity-pub-utils.js'
|
|||
import validator from 'validator'
|
||||
import { exists, isArray, isDateValid } from '../misc.js'
|
||||
import { isActivityPubUrlValid } from './misc.js'
|
||||
import { ActivityTombstoneObject, VideoCommentObject } from '@peertube/peertube-models'
|
||||
|
||||
function sanitizeAndCheckVideoCommentObject (comment: any) {
|
||||
function sanitizeAndCheckVideoCommentObject (comment: VideoCommentObject | ActivityTombstoneObject) {
|
||||
if (!comment) return false
|
||||
|
||||
if (!isCommentTypeValid(comment)) return false
|
||||
|
@ -23,6 +24,7 @@ function sanitizeAndCheckVideoCommentObject (comment: any) {
|
|||
isDateValid(comment.published) &&
|
||||
isActivityPubUrlValid(comment.url) &&
|
||||
isArray(comment.to) &&
|
||||
(!exists(comment.replyApproval) || isActivityPubUrlValid(comment.replyApproval)) &&
|
||||
(hasAPPublic(comment.to) || hasAPPublic(comment.cc)) // Only accept public comments
|
||||
}
|
||||
|
||||
|
|
|
@ -1,18 +1,20 @@
|
|||
import validator from 'validator'
|
||||
import {
|
||||
ActivityPubStoryboard,
|
||||
ActivityTrackerUrlObject,
|
||||
ActivityVideoFileMetadataUrlObject,
|
||||
LiveVideoLatencyMode,
|
||||
VideoCommentPolicy,
|
||||
VideoObject,
|
||||
VideoState
|
||||
} from '@peertube/peertube-models'
|
||||
import { logger } from '@server/helpers/logger.js'
|
||||
import validator from 'validator'
|
||||
import { CONSTRAINTS_FIELDS, MIMETYPES } from '../../../initializers/constants.js'
|
||||
import { peertubeTruncate } from '../../core-utils.js'
|
||||
import { isArray, isBooleanValid, isDateValid, isUUIDValid } from '../misc.js'
|
||||
import { isLiveLatencyModeValid } from '../video-lives.js'
|
||||
import {
|
||||
isVideoCommentsPolicyValid,
|
||||
isVideoDescriptionValid,
|
||||
isVideoDurationValid,
|
||||
isVideoNameValid,
|
||||
|
@ -66,12 +68,21 @@ function sanitizeAndCheckVideoTorrentObject (video: VideoObject) {
|
|||
if (!isVideoStateValid(video.state)) video.state = VideoState.PUBLISHED
|
||||
if (!isBooleanValid(video.waitTranscoding)) video.waitTranscoding = false
|
||||
if (!isBooleanValid(video.downloadEnabled)) video.downloadEnabled = true
|
||||
if (!isBooleanValid(video.commentsEnabled)) video.commentsEnabled = false
|
||||
if (!isBooleanValid(video.isLiveBroadcast)) video.isLiveBroadcast = false
|
||||
if (!isBooleanValid(video.liveSaveReplay)) video.liveSaveReplay = false
|
||||
if (!isBooleanValid(video.permanentLive)) video.permanentLive = false
|
||||
if (!isLiveLatencyModeValid(video.latencyMode)) video.latencyMode = LiveVideoLatencyMode.DEFAULT
|
||||
|
||||
if (video.commentsPolicy) {
|
||||
if (!isVideoCommentsPolicyValid(video.commentsPolicy)) {
|
||||
video.commentsPolicy = VideoCommentPolicy.DISABLED
|
||||
}
|
||||
} else if (video.commentsEnabled === true) { // Fallback to deprecated attribute
|
||||
video.commentsPolicy = VideoCommentPolicy.ENABLED
|
||||
} else {
|
||||
video.commentsPolicy = VideoCommentPolicy.DISABLED
|
||||
}
|
||||
|
||||
return isActivityPubUrlValid(video.id) &&
|
||||
isVideoNameValid(video.name) &&
|
||||
isActivityPubVideoDurationValid(video.duration) &&
|
||||
|
@ -138,12 +149,12 @@ function isAPVideoTrackerUrlObject (url: any): url is ActivityTrackerUrlObject {
|
|||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
sanitizeAndCheckVideoTorrentUpdateActivity,
|
||||
isRemoteStringIdentifierValid,
|
||||
sanitizeAndCheckVideoTorrentObject,
|
||||
isRemoteVideoUrlValid,
|
||||
isAPVideoFileUrlMetadataObject,
|
||||
isAPVideoTrackerUrlObject
|
||||
isAPVideoTrackerUrlObject,
|
||||
isRemoteStringIdentifierValid,
|
||||
isRemoteVideoUrlValid,
|
||||
sanitizeAndCheckVideoTorrentObject,
|
||||
sanitizeAndCheckVideoTorrentUpdateActivity
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import { HttpStatusCode, VideoIncludeType, VideoPrivacy, VideoPrivacyType, VideoRateType } from '@peertube/peertube-models'
|
||||
import { getVideoWithAttributes } from '@server/helpers/video.js'
|
||||
import { Request, Response, UploadFilesForCheck } from 'express'
|
||||
import { decode as magnetUriDecode } from 'magnet-uri'
|
||||
import validator from 'validator'
|
||||
import { HttpStatusCode, VideoIncludeType, VideoPrivacy, VideoPrivacyType, VideoRateType } from '@peertube/peertube-models'
|
||||
import { getVideoWithAttributes } from '@server/helpers/video.js'
|
||||
import {
|
||||
CONSTRAINTS_FIELDS,
|
||||
MIMETYPES,
|
||||
VIDEO_CATEGORIES,
|
||||
VIDEO_COMMENTS_POLICY,
|
||||
VIDEO_LICENCES,
|
||||
VIDEO_LIVE,
|
||||
VIDEO_PRIVACIES,
|
||||
|
@ -46,6 +47,10 @@ export function isVideoDescriptionValid (value: string) {
|
|||
return value === null || (exists(value) && validator.default.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.DESCRIPTION))
|
||||
}
|
||||
|
||||
export function isVideoCommentsPolicyValid (value: any) {
|
||||
return value === null || VIDEO_COMMENTS_POLICY[value] !== undefined
|
||||
}
|
||||
|
||||
export function isVideoSupportValid (value: string) {
|
||||
return value === null || (exists(value) && validator.default.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.SUPPORT))
|
||||
}
|
||||
|
|
17
server/core/helpers/custom-validators/watched-words.ts
Normal file
17
server/core/helpers/custom-validators/watched-words.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import validator from 'validator'
|
||||
import { CONSTRAINTS_FIELDS } from '../../initializers/constants.js'
|
||||
import { exists, isArray } from './misc.js'
|
||||
|
||||
export function isWatchedWordListNameValid (listName: string) {
|
||||
return exists(listName) && validator.default.isLength(listName, CONSTRAINTS_FIELDS.WATCHED_WORDS.LIST_NAME)
|
||||
}
|
||||
|
||||
export function isWatchedWordValid (word: string) {
|
||||
return exists(word) && validator.default.isLength(word, CONSTRAINTS_FIELDS.WATCHED_WORDS.WORD)
|
||||
}
|
||||
|
||||
export function areWatchedWordsValid (words: string[]) {
|
||||
return isArray(words) &&
|
||||
validator.default.isInt(words.length.toString(), CONSTRAINTS_FIELDS.WATCHED_WORDS.WORDS) &&
|
||||
words.every(word => isWatchedWordValid(word))
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue