2022-09-12 12:45:19 +02:00
import { ApplicationFunction } from 'probot' ;
2022-09-21 22:39:54 +02:00
import getMetaData from 'metadata-scraper' ;
2022-09-12 12:45:19 +02:00
2023-01-27 14:22:17 +01:00
import { getPlaylistIdFromUrl } from './getPlaylistIdFromUrl' ;
2022-10-09 15:02:17 +02:00
type ReviewEvent = 'REQUEST_CHANGES' | 'COMMENT' | 'APPROVE' ;
2023-01-27 14:22:17 +01:00
export const bot : ApplicationFunction = ( app ) = > {
2022-09-12 20:58:06 +02:00
app . on (
[ 'pull_request.opened' , 'pull_request.synchronize' ] ,
2023-01-27 14:22:17 +01:00
async ( { payload , octokit } ) = > {
2022-09-12 20:58:06 +02:00
const registryDirectoryPath = 'playlists/registry/' ;
const siQueryStart = '?si=' ;
2022-09-13 15:32:52 +02:00
2023-01-27 14:22:17 +01:00
const pull_number = payload . number ;
2022-09-21 22:39:54 +02:00
const workingRepo = {
2023-01-27 14:22:17 +01:00
owner : payload.repository.owner.login ,
repo : payload.repository.name
2022-09-12 20:58:06 +02:00
} ;
2022-09-14 06:53:05 +02:00
const repoAllowlist = [
{ owner : 'mackorone' , repo : 'spotify-playlist-archive' } ,
{ owner : 'maciejpedzich' , repo : 'bot-testing-ground' }
] ;
2022-09-13 15:32:52 +02:00
2023-01-27 15:39:28 +01:00
const removeRegistryPathFromFilename = ( filename : string ) = >
2022-09-12 20:58:06 +02:00
filename . replace ( registryDirectoryPath , '' ) ;
2022-09-21 22:39:54 +02:00
const upsertReview = async (
review_id : number | undefined ,
2023-01-27 15:39:28 +01:00
event : ReviewEvent ,
body : string
2022-09-21 22:39:54 +02:00
) = > {
2022-09-12 20:58:06 +02:00
if ( review_id ) {
2023-01-27 14:22:17 +01:00
await octokit . pulls . updateReview ( {
2022-09-21 22:39:54 +02:00
. . . workingRepo ,
2022-09-12 20:58:06 +02:00
pull_number ,
review_id ,
body
} ) ;
} else {
2023-01-27 14:22:17 +01:00
await octokit . pulls . createReview ( {
2022-09-21 22:39:54 +02:00
. . . workingRepo ,
2022-09-12 20:58:06 +02:00
pull_number ,
2022-09-21 22:39:54 +02:00
event ,
body
2022-09-12 20:58:06 +02:00
} ) ;
}
} ;
try {
2022-12-23 14:46:07 +01:00
const isAllowlistedRepo = repoAllowlist . find (
2022-09-14 06:53:05 +02:00
( { owner , repo } ) = >
2022-09-21 22:39:54 +02:00
workingRepo . owner === owner && workingRepo . repo === repo
2022-09-14 06:53:05 +02:00
) ;
2022-12-23 14:46:07 +01:00
if ( ! isAllowlistedRepo ) return ;
2022-09-14 06:53:05 +02:00
2023-01-27 14:22:17 +01:00
const { data : prFiles } = await octokit . pulls . listFiles ( {
2022-09-21 22:39:54 +02:00
. . . workingRepo ,
2022-09-12 20:58:06 +02:00
pull_number
} ) ;
const filesToVerify = prFiles . filter (
( { status , filename } ) = >
status === 'added' && filename . startsWith ( registryDirectoryPath )
) ;
2022-12-23 14:46:07 +01:00
if ( filesToVerify . length === 0 ) return ;
2022-09-12 20:58:06 +02:00
const playlistLookupResults = await Promise . all (
filesToVerify . map ( async ( { filename } ) = > {
2023-01-27 15:39:28 +01:00
const filenameWithoutRegistryPath = removeRegistryPathFromFilename (
filename
) . replace ( 'https:/' , 'https://' ) ;
2023-01-27 14:22:17 +01:00
2023-01-27 15:39:28 +01:00
const url = getPlaylistIdFromUrl ( filenameWithoutRegistryPath )
2023-01-27 15:48:17 +01:00
? filenameWithoutRegistryPath
2023-01-27 15:39:28 +01:00
: ` https://open.spotify.com/playlist/ ${ filenameWithoutRegistryPath } ` ;
2022-09-21 22:39:54 +02:00
const spotifyResponse = await fetch ( url ) ;
2023-01-27 14:22:17 +01:00
const expectedStatusCodes = [ 200 , 404 ] ;
2022-09-22 06:14:31 +02:00
2023-01-27 14:22:17 +01:00
if ( ! expectedStatusCodes . includes ( spotifyResponse . status ) ) {
2022-12-23 14:46:07 +01:00
throw new Error (
2023-01-27 14:22:17 +01:00
` ${ spotifyResponse . url } responded with ${ spotifyResponse . status } `
2022-12-23 14:46:07 +01:00
) ;
2023-01-27 14:22:17 +01:00
}
2022-12-23 14:46:07 +01:00
const found = spotifyResponse . status === 200 ;
2023-01-27 14:22:17 +01:00
let info = '' ;
2022-09-21 22:39:54 +02:00
if ( found ) {
const html = await spotifyResponse . text ( ) ;
const { title , description } = await getMetaData ( { html } ) ;
const playlistMeta = ( description || '' )
. split ( ' · ' )
. filter ( ( text ) = > text !== 'Playlist' ) ;
info = [ title , . . . playlistMeta ] . join ( ' · ' ) ;
}
2022-09-12 20:58:06 +02:00
return {
2023-01-27 15:39:28 +01:00
filename : filenameWithoutRegistryPath ,
2022-09-21 22:39:54 +02:00
found ,
info ,
url
2022-09-12 20:58:06 +02:00
} ;
} )
) ;
2023-01-28 12:50:18 +01:00
console . log ( playlistLookupResults ) ;
2023-01-27 15:39:28 +01:00
let successText = ` 🎉 @ ${ workingRepo . owner } can merge your pull request! 🎉 ` ;
let reviewEvent : ReviewEvent = 'APPROVE' ;
let identifiedPlaylistsText = '' ;
2022-09-21 22:39:54 +02:00
const validEntries = playlistLookupResults . filter (
( { found , filename } ) = > found && ! filename . includes ( siQueryStart )
) ;
if ( validEntries . length > 0 ) {
const playlistLinks = validEntries
. map ( ( { url , info } ) = > ` - [ ${ info } ]( ${ url } ) ` )
. join ( '\n' ) ;
2022-12-23 14:46:07 +01:00
identifiedPlaylistsText = ` ### ✅ These playlists have been indentified: \ n ${ playlistLinks } ` ;
2022-09-21 22:39:54 +02:00
}
2022-09-12 20:58:06 +02:00
2023-01-28 12:43:06 +01:00
let onlyRenameRequiredText = '' ;
const entriesToOnlyRename = playlistLookupResults . filter (
2023-01-27 15:39:28 +01:00
( { found , filename } ) = >
found &&
2023-01-28 12:43:06 +01:00
filename . includes ( siQueryStart ) &&
! getPlaylistIdFromUrl ( filename )
2023-01-27 15:39:28 +01:00
) ;
2023-01-28 12:43:06 +01:00
if ( entriesToOnlyRename . length > 0 ) {
const renameList = entriesToOnlyRename
2022-09-13 10:03:48 +02:00
. map ( ( { filename } ) = > {
2023-01-28 12:43:06 +01:00
const filenameWithoutRegistryPath =
2023-01-27 15:39:28 +01:00
removeRegistryPathFromFilename ( filename ) ;
2023-01-27 14:22:17 +01:00
2023-01-28 12:43:06 +01:00
const [ targetFilename ] =
filenameWithoutRegistryPath . split ( siQueryStart ) ;
2022-09-13 10:03:48 +02:00
2023-01-28 12:43:06 +01:00
return ` - From \` ${ filenameWithoutRegistryPath } \` to ** ${ targetFilename } ** ` ;
2022-09-13 10:03:48 +02:00
} )
. join ( '\n' ) ;
2022-09-21 22:39:54 +02:00
successText = '' ;
reviewEvent = 'REQUEST_CHANGES' ;
2023-01-28 12:43:06 +01:00
onlyRenameRequiredText = ` ### ⚠️ You have to rename these entries: \ n ${ renameList } ` ;
}
let urlEntriesToRenameText = '' ;
const urlFilenameEntries = playlistLookupResults . filter (
( { found , filename } ) = > {
found && getPlaylistIdFromUrl ( filename ) ;
}
) ;
if ( urlFilenameEntries . length > 0 ) {
successText = '' ;
reviewEvent = 'REQUEST_CHANGES' ;
urlEntriesToRenameText = ` ### ⚠️ It looks like you've tried pasting playlist URL(s) for certain entries. \ nBefore you try again, you'll have to remove the \` https: \` folder. If you don't know how to grab a playlist ID from a link, use [this tool](https://spotifyplaylistarchive.com/get-playlist-id). ` ;
2022-09-21 22:39:54 +02:00
}
2022-09-12 20:58:06 +02:00
2023-01-27 15:39:28 +01:00
let notFoundText = '' ;
const notFoundPlaylists = playlistLookupResults . filter (
( { found } ) = > ! found
) ;
2023-01-27 14:22:17 +01:00
if ( notFoundPlaylists . length > 0 ) {
const renameList = notFoundPlaylists
. map ( ( { filename } ) = > ` - ${ filename } ` )
. join ( '\n' ) ;
successText = '' ;
reviewEvent = 'REQUEST_CHANGES' ;
2023-01-28 12:43:06 +01:00
notFoundText = ` ### ❌ These entries don't point to any existing public playlists: \ n ${ renameList } ` ;
2023-01-27 14:22:17 +01:00
}
2022-09-21 22:39:54 +02:00
const reviewBody = [
identifiedPlaylistsText ,
2023-01-28 12:43:06 +01:00
onlyRenameRequiredText ,
urlEntriesToRenameText ,
2022-09-21 22:39:54 +02:00
notFoundText ,
successText
]
. filter ( Boolean )
. join ( '\n\n' ) ;
2023-01-27 15:39:28 +01:00
const { data : reviews } = await octokit . pulls . listReviews ( {
2022-09-21 22:39:54 +02:00
. . . workingRepo ,
2023-01-27 15:39:28 +01:00
pull_number
2022-09-12 20:58:06 +02:00
} ) ;
2023-01-27 15:39:28 +01:00
const [ existingReview ] = reviews ;
await upsertReview ( existingReview ? . id , reviewEvent , reviewBody ) ;
} catch ( error ) {
console . error ( error ) ;
await upsertReview (
undefined ,
'COMMENT' ,
` Something went wrong while validating new entries! @ ${ workingRepo . owner } should handle it shortly... `
) ;
2022-09-12 20:58:06 +02:00
}
}
) ;
2022-09-12 12:45:19 +02:00
} ;