playlist-entry-validator/bot.ts

180 lines
5.4 KiB
TypeScript
Raw Normal View History

2022-09-12 12:45:19 +02:00
import { ApplicationFunction } from 'probot';
import getMetaData from 'metadata-scraper';
2022-09-12 12:45:19 +02:00
type ReviewEvent = 'REQUEST_CHANGES' | 'COMMENT' | 'APPROVE';
2022-09-13 10:03:48 +02:00
const bot: ApplicationFunction = (app) => {
app.on(
['pull_request.opened', 'pull_request.synchronize'],
async (context) => {
const registryDirectoryPath = 'playlists/registry/';
const siQueryStart = '?si=';
const pull_number = context.payload.number;
const workingRepo = {
2022-09-13 10:03:48 +02:00
owner: context.payload.repository.owner.login,
repo: context.payload.repository.name
};
const repoAllowlist = [
{ owner: 'mackorone', repo: 'spotify-playlist-archive' },
{ owner: 'maciejpedzich', repo: 'bot-testing-ground' }
];
const removePathFromFilename = (filename: string) =>
filename.replace(registryDirectoryPath, '');
const upsertReview = async (
review_id: number | undefined,
body: string,
2022-10-01 13:14:35 +02:00
event: ReviewEvent
) => {
if (review_id) {
await context.octokit.pulls.updateReview({
...workingRepo,
pull_number,
review_id,
event,
body
});
} else {
await context.octokit.pulls.createReview({
...workingRepo,
pull_number,
event,
body
});
}
};
try {
const allowlistedRepo = repoAllowlist.find(
({ owner, repo }) =>
workingRepo.owner === owner && workingRepo.repo === repo
);
if (!allowlistedRepo) return;
const { data: prFiles } = await context.octokit.pulls.listFiles({
...workingRepo,
pull_number
});
const filesToVerify = prFiles.filter(
({ status, filename }) =>
status === 'added' && filename.startsWith(registryDirectoryPath)
);
const playlistLookupResults = await Promise.all(
filesToVerify.map(async ({ filename }) => {
const filenameWithoutPath = removePathFromFilename(filename);
const url = `https://open.spotify.com/playlist/${filenameWithoutPath}`;
const spotifyResponse = await fetch(url);
const found = spotifyResponse.status === 200;
let info: string | null = null;
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(' · ');
}
return {
filename: removePathFromFilename(filename),
found,
info,
url
};
})
);
const validEntries = playlistLookupResults.filter(
({ found, filename }) => found && !filename.includes(siQueryStart)
);
const entriesWithSiQuery = playlistLookupResults.filter(
({ found, filename }) => found && filename.includes(siQueryStart)
);
const notFoundPlaylists = playlistLookupResults.filter(
({ found }) => !found
);
const { data: priorReviews } = await context.octokit.pulls.listReviews({
...workingRepo,
pull_number
});
const [existingReview] = priorReviews;
let identifiedPlaylistsText = '';
let renameRequiredText = '';
let notFoundText = '';
let successText = `🎉 @${workingRepo.owner} can merge your pull request! 🎉`;
2022-10-01 13:14:35 +02:00
let reviewEvent: ReviewEvent = 'APPROVE';
if (validEntries.length > 0) {
const playlistLinks = validEntries
.map(({ url, info }) => `- [${info}](${url})`)
.join('\n');
identifiedPlaylistsText = `### ✅ These playlists were indentified:\n${playlistLinks}`;
}
if (notFoundPlaylists.length > 0) {
const renameList = notFoundPlaylists
.map(({ filename }) => `- ${filename}`)
.join('\n');
successText = '';
reviewEvent = 'REQUEST_CHANGES';
notFoundText = `### ❌ Playlists for these entries were not found:\n${renameList}`;
}
2022-09-13 10:03:48 +02:00
if (entriesWithSiQuery.length > 0) {
const renameList = entriesWithSiQuery
2022-09-13 10:03:48 +02:00
.map(({ filename }) => {
const filenameWithoutPath = removePathFromFilename(filename);
const [targetFilename] = filenameWithoutPath.split(siQueryStart);
return `- From ${filenameWithoutPath} to **${targetFilename}**`;
2022-09-13 10:03:48 +02:00
})
.join('\n');
successText = '';
reviewEvent = 'REQUEST_CHANGES';
renameRequiredText = `### ⚠️ These entries have to be renamed:\n${renameList}`;
}
const reviewBody = [
identifiedPlaylistsText,
renameRequiredText,
notFoundText,
successText
]
.filter(Boolean)
.join('\n\n');
await upsertReview(existingReview?.id, reviewBody, reviewEvent);
} catch (error) {
console.error(error);
await context.octokit.pulls.createReview({
...workingRepo,
pull_number,
event: 'COMMENT',
body: `Something went wrong while validating new entries! @${workingRepo.owner} should handle it shortly...`
});
}
}
);
2022-09-12 12:45:19 +02:00
};
2022-09-13 10:03:48 +02:00
export = bot;