import { ActivityAnnounce , ActivityFollow , ActivityLike , ActivityUndo , CacheFileObject } from '../../../../shared/models/activitypub' ; import { DislikeObject } from '../../../../shared/models/activitypub/objects' ; import { retryTransactionWrapper } from '../../../helpers/database-utils' ; import { logger } from '../../../helpers/logger' ; import { sequelizeTypescript } from '../../../initializers/database' ; import { AccountVideoRateModel } from '../../../models/account/account-video-rate' ; import { ActorModel } from '../../../models/actor/actor' ; import { ActorFollowModel } from '../../../models/actor/actor-follow' ; import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy' ; import { VideoShareModel } from '../../../models/video/video-share' ; import { APProcessorOptions } from '../../../types/activitypub-processor.model' ; import { MActorSignature } from '../../../types/models' ; import { forwardVideoRelatedActivity } from '../send/utils' ; import { getOrCreateAPVideo } from '../videos' ; async function processUndoActivity ( options ) { const { activity , byActor } = options ; const activityToUndo = activity . object ; if ( activityToUndo . type === 'Like' ) { return retryTransactionWrapper ( processUndoLike , byActor , activity ) ; } if ( activityToUndo . type === 'Create' ) { if ( activityToUndo . object . type === 'CacheFile' ) { return retryTransactionWrapper ( processUndoCacheFile , byActor , activity ) ; } } if ( activityToUndo . type === 'Dislike' ) { return retryTransactionWrapper ( processUndoDislike , byActor , activity ) ; } if ( activityToUndo . type === 'Follow' ) { return retryTransactionWrapper ( processUndoFollow , byActor , activityToUndo ) ; } if ( activityToUndo . type === 'Announce' ) { return retryTransactionWrapper ( processUndoAnnounce , byActor , activityToUndo ) ; } logger . warn ( 'Unknown activity object type %s -> %s when undo activity.' , activityToUndo . type , { activity : activity . id } ) ; return undefined ; } export { processUndoActivity } ; async function processUndoLike ( byActor , activity ) { const likeActivity = activity . object as ActivityLike ; const { video } = await getOrCreateAPVideo ( { videoObject : likeActivity . object } ) ; return sequelizeTypescript . transaction ( async ( t ) => { if ( ! byActor . Account ) throw new Error ( 'Unknown account ' + byActor . url ) ; const rate = await AccountVideoRateModel . loadByAccountAndVideoOrUrl ( byActor . Account . id , video . id , likeActivity . id , t ) ; if ( ! rate || rate . type !== 'like' ) throw new Error ( `Unknown like by account ${ byActor . Account . id } for video ${ video . id }.` ) ; await rate . destroy ( { transaction : t } ) ; await video . decrement ( 'likes' , { transaction : t } ) ; if ( video . isOwned ( ) ) { const exceptions = [ byActor ] ; await forwardVideoRelatedActivity ( activity , t , exceptions , video ) ; } } ) ; } async function processUndoDislike ( byActor , activity ) { const dislike = activity . object . type === 'Dislike' ? activity . object : activity . object . object as DislikeObject ; const { video } = await getOrCreateAPVideo ( { videoObject : dislike . object } ) ; return sequelizeTypescript . transaction ( async ( t ) => { if ( ! byActor . Account ) throw new Error ( 'Unknown account ' + byActor . url ) ; const rate = await AccountVideoRateModel . loadByAccountAndVideoOrUrl ( byActor . Account . id , video . id , dislike . id , t ) ; if ( ! rate || rate . type !== 'dislike' ) throw new Error ( `Unknown dislike by account ${ byActor . Account . id } for video ${ video . id }.` ) ; await rate . destroy ( { transaction : t } ) ; await video . decrement ( 'dislikes' , { transaction : t } ) ; if ( video . isOwned ( ) ) { const exceptions = [ byActor ] ; await forwardVideoRelatedActivity ( activity , t , exceptions , video ) ; } } ) ; } async function processUndoCacheFile ( byActor , activity ) { const cacheFileObject = activity . object . object as CacheFileObject ; const { video } = await getOrCreateAPVideo ( { videoObject : cacheFileObject . object } ) ; return sequelizeTypescript . transaction ( async ( t ) => { const cacheFile = await VideoRedundancyModel . loadByUrl ( cacheFileObject . id , t ) ; if ( ! cacheFile ) { logger . debug ( 'Cannot undo unknown video cache %s.' , cacheFileObject . id ) ; return ; } if ( cacheFile . actorId !== byActor . id ) throw new Error ( 'Cannot delete redundancy ' + cacheFile . url + ' of another actor.' ) ; await cacheFile . destroy ( { transaction : t } ) ; if ( video . isOwned ( ) ) { const exceptions = [ byActor ] ; await forwardVideoRelatedActivity ( activity , t , exceptions , video ) ; } } ) ; } function processUndoFollow ( follower , followActivity ) { return sequelizeTypescript . transaction ( async ( t ) => { const following = await ActorModel . loadByUrlAndPopulateAccountAndChannel ( followActivity . object , t ) ; const actorFollow = await ActorFollowModel . loadByActorAndTarget ( follower . id , following . id , t ) ; if ( ! actorFollow ) throw new Error ( `'Unknown actor follow ${ follower . id } -> ${ following . id }.` ) ; await actorFollow . destroy ( { transaction : t } ) ; return undefined ; } ) ; } function processUndoAnnounce ( byActor , announceActivity ) { return sequelizeTypescript . transaction ( async ( t ) => { const share = await VideoShareModel . loadByUrl ( announceActivity . id , t ) ; if ( ! share ) throw new Error ( `Unknown video share ${ announceActivity . id }.` ) ; if ( share . actorId !== byActor . id ) throw new Error ( `${ share . url } is not shared by ${ byActor . url }.` ) ; await share . destroy ( { transaction : t } ) ; if ( share . Video . isOwned ( ) ) { const exceptions = [ byActor ] ; await forwardVideoRelatedActivity ( announceActivity , t , exceptions , share . Video ) ; } } ) ; }