mirror of
https://github.com/feeddeck/feeddeck.git
synced 2026-03-10 07:32:05 -05:00
Instead of using an `import_map.json` file to define the versions for dependencies, they are now defined directly within the import. Since the `import_map.json` file should not be used anymore and instead a `deno.json` file per function should be used, we decided to define them directly with the code. The overhead compared to a `deno.json` file per function shouldn't be that large and it makes using functions in a self-hosted setup easier.
788 lines
25 KiB
TypeScript
788 lines
25 KiB
TypeScript
import { SupabaseClient } from "jsr:@supabase/supabase-js@2";
|
|
import { Redis } from "https://deno.land/x/redis@v0.32.0/mod.ts";
|
|
|
|
import { ISource } from "../models/source.ts";
|
|
import { IItem } from "../models/item.ts";
|
|
import { IProfile } from "../models/profile.ts";
|
|
import { utils } from "../utils/index.ts";
|
|
import { feedutils } from "./utils/index.ts";
|
|
|
|
export const getGithubFeed = async (
|
|
supabaseClient: SupabaseClient,
|
|
_redisClient: Redis | undefined,
|
|
profile: IProfile,
|
|
source: ISource,
|
|
_feedData: string | undefined,
|
|
): Promise<{ source: ISource; items: IItem[] }> => {
|
|
if (!source.options?.github || !source.options?.github.type) {
|
|
throw new feedutils.FeedValidationError("Invalid source options");
|
|
}
|
|
|
|
if (!profile.accountGithub?.token) {
|
|
throw new Error("GitHub token is missing");
|
|
}
|
|
const token = await utils.decrypt(profile.accountGithub.token);
|
|
|
|
if (
|
|
source.options.github.type === "notifications" ||
|
|
source.options.github.type === "repositorynotifications"
|
|
) {
|
|
/**
|
|
* With `notifications` and `repositorynotifications` type users can add
|
|
* there GitHub notifications or repository notifications as source to
|
|
* FeedDeck. The notifications are retrieved from the GitHub API via the
|
|
* list notifications endpoint.
|
|
*
|
|
* - https://docs.github.com/en/rest/activity/notifications?apiVersion=2022-11-28#list-notifications-for-the-authenticated-user
|
|
* - https://docs.github.com/en/rest/activity/notifications?apiVersion=2022-11-28#list-repository-notifications-for-the-authenticated-user
|
|
* - https://docs.github.com/en/rest/activity/notifications?apiVersion=2022-11-28#about-notification-reasons
|
|
*/
|
|
const notifications = [];
|
|
|
|
if (source.options.github.type === "notifications") {
|
|
const tmpNotifications = await request("/notifications", {
|
|
token: token,
|
|
params: {
|
|
all: "true",
|
|
participating: source.options.github.participating ? "true" : "false",
|
|
page: "1",
|
|
per_page: "50",
|
|
},
|
|
});
|
|
|
|
const user = await request("/user", {
|
|
token: token,
|
|
});
|
|
|
|
source.id = `github-${source.userId}-${source.columnId}-${source.options.github.type}-${source.options.github.participating}`;
|
|
source.title = user.login;
|
|
source.icon = user.avatar_url;
|
|
source.icon = await feedutils.uploadSourceIcon(supabaseClient, source);
|
|
notifications.push(...tmpNotifications);
|
|
} else if (
|
|
source.options.github.type === "repositorynotifications" &&
|
|
source.options.github.repository
|
|
) {
|
|
const [owner, repo] = source.options.github.repository.split("/");
|
|
const tmpNotifications = await request(
|
|
`/repos/${owner}/${repo}/notifications`,
|
|
{
|
|
token: token,
|
|
params: {
|
|
all: "true",
|
|
participating: source.options.github.participating
|
|
? "true"
|
|
: "false",
|
|
page: "1",
|
|
per_page: "50",
|
|
},
|
|
},
|
|
);
|
|
|
|
source.id = `github-${source.userId}-${source.columnId}-${source.options.github.type}--${source.options.github.participating}-${source.options.github.repository}`;
|
|
source.title = `${owner}/${repo}`;
|
|
if (
|
|
tmpNotifications.length > 0 &&
|
|
tmpNotifications[0].repository?.owner?.avatar_url
|
|
) {
|
|
source.icon = tmpNotifications[0].repository.owner.avatar_url;
|
|
source.icon = await feedutils.uploadSourceIcon(supabaseClient, source);
|
|
} else {
|
|
source.icon = `https://github.com/${owner}.png`;
|
|
source.icon = await feedutils.uploadSourceIcon(supabaseClient, source);
|
|
}
|
|
notifications.push(...tmpNotifications);
|
|
} else {
|
|
throw new feedutils.FeedValidationError("Invalid source options");
|
|
}
|
|
|
|
source.type = "github";
|
|
source.link = "https://github.com/notifications";
|
|
|
|
const items: IItem[] = [];
|
|
|
|
for (const [_, notification] of notifications.entries()) {
|
|
items.push({
|
|
id: `${source.id}-${notification.id}`,
|
|
userId: source.userId,
|
|
columnId: source.columnId,
|
|
sourceId: source.id,
|
|
title: notification.subject?.title ?? "Notification",
|
|
link: getLinkFromApiUrl(notification.subject?.url),
|
|
media: notification.repository.owner.avatar_url,
|
|
description: formatDescription(notification),
|
|
author: notification.repository?.full_name,
|
|
publishedAt: Math.floor(
|
|
new Date(notification.updated_at).getTime() / 1000,
|
|
),
|
|
});
|
|
}
|
|
|
|
return { source, items };
|
|
} else if (
|
|
source.options.github.type === "useractivities" ||
|
|
source.options.github.type === "repositoryactivities" ||
|
|
source.options.github.type === "organizationactivitiespublic" ||
|
|
source.options.github.type === "organizationactivitiesprivate"
|
|
) {
|
|
/**
|
|
* `useractivities`, `repositoryactivities`, `organizationactivitiespublic`
|
|
* and `organizationactivitiesprivate` lets a user add the user, repository
|
|
* or organization notifications as source. We are using the corresponding
|
|
* events endpoint to get the notifications and add the id, title, icon and
|
|
* link to the source. Then we are going though all the events and adding
|
|
* the actor of an event as author. The title, description and link is
|
|
* different for each notification type and generated via the `formatEvent`
|
|
* function.
|
|
*
|
|
* - https://docs.github.com/en/developers/webhooks-and-events/events/github-event-types
|
|
* - GitHubTypeUserActivities: https://docs.github.com/en/rest/activity/events?apiVersion=2022-11-28#list-events-received-by-the-authenticated-user
|
|
* - GitHubTypeRepositoryActivities: https://docs.github.com/en/rest/activity/events?apiVersion=2022-11-28#list-organization-events-for-the-authenticated-user
|
|
* - GitHubTypeOrganizationActivitiesPublic: https://docs.github.com/en/rest/activity/events?apiVersion=2022-11-28#list-public-organization-events
|
|
* - GitHubTypeOrganizationActivitiesPrivate: https://docs.github.com/en/rest/activity/events?apiVersion=2022-11-28#list-organization-events-for-the-authenticated-user
|
|
*/
|
|
const events = [];
|
|
|
|
if (
|
|
source.options.github.type === "useractivities" &&
|
|
source.options.github.user
|
|
) {
|
|
const tmpEvents = await request(
|
|
`/users/${source.options.github.user}/received_events/public`,
|
|
{
|
|
token: token,
|
|
params: {
|
|
page: "1",
|
|
per_page: "100",
|
|
},
|
|
},
|
|
);
|
|
|
|
const user = await request(`/users/${source.options.github.user}`, {
|
|
token: token,
|
|
});
|
|
|
|
source.id = `github-${source.userId}-${source.columnId}-${source.options.github.type}-${source.options.github.user}`;
|
|
source.title = source.options.github.user;
|
|
source.icon = user.avatar_url;
|
|
source.icon = await feedutils.uploadSourceIcon(supabaseClient, source);
|
|
source.link = `https://github.com/${source.options.github.user}`;
|
|
|
|
events.push(...tmpEvents);
|
|
} else if (
|
|
source.options.github.type === "repositoryactivities" &&
|
|
source.options.github.repository
|
|
) {
|
|
const [owner, repo] = source.options.github.repository.split("/");
|
|
const tmpEvents = await request(`/repos/${owner}/${repo}/events`, {
|
|
token: token,
|
|
params: {
|
|
page: "1",
|
|
per_page: "100",
|
|
},
|
|
});
|
|
|
|
const user = await request(`/users/${owner}`, {
|
|
token: token,
|
|
});
|
|
|
|
source.id = `github-${source.userId}-${source.columnId}-${source.options.github.type}-${owner}-${repo}`;
|
|
source.title = `${owner}/${repo}`;
|
|
source.icon = user.avatar_url;
|
|
source.icon = await feedutils.uploadSourceIcon(supabaseClient, source);
|
|
source.link = `https://github.com/${owner}/${repo}`;
|
|
|
|
events.push(...tmpEvents);
|
|
} else if (
|
|
source.options.github.type === "organizationactivitiespublic" &&
|
|
source.options.github.organization
|
|
) {
|
|
const tmpEvents = await request(
|
|
`/orgs/${source.options.github.organization}/events`,
|
|
{
|
|
token: token,
|
|
params: {
|
|
page: "1",
|
|
per_page: "100",
|
|
},
|
|
},
|
|
);
|
|
|
|
const user = await request(
|
|
`/users/${source.options.github.organization}`,
|
|
{
|
|
token: token,
|
|
},
|
|
);
|
|
|
|
source.id = `github-${source.userId}-${source.columnId}-${source.options.github.type}-${source.options.github.organization}`;
|
|
source.title = source.options.github.organization;
|
|
source.icon = user.avatar_url;
|
|
source.icon = await feedutils.uploadSourceIcon(supabaseClient, source);
|
|
source.link = `https://github.com/${source.options.github.organization}`;
|
|
|
|
events.push(...tmpEvents);
|
|
} else if (
|
|
source.options.github.type === "organizationactivitiesprivate" &&
|
|
source.options.github.organization
|
|
) {
|
|
const user = await request("/user", {
|
|
token: token,
|
|
});
|
|
|
|
const tmpEvents = await request(
|
|
`/users/${user.login}/events/orgs/${source.options.github.organization}`,
|
|
{
|
|
token: token,
|
|
params: {
|
|
page: "1",
|
|
per_page: "100",
|
|
},
|
|
},
|
|
);
|
|
|
|
const org = await request(
|
|
`/users/${source.options.github.organization}`,
|
|
{
|
|
token: token,
|
|
},
|
|
);
|
|
|
|
source.id = `github-${source.userId}-${source.columnId}-${source.options.github.type}-${source.options.github.organization}`;
|
|
source.title = source.options.github.organization;
|
|
source.icon = org.avatar_url;
|
|
source.icon = await feedutils.uploadSourceIcon(supabaseClient, source);
|
|
source.link = `https://github.com/${source.options.github.organization}`;
|
|
|
|
events.push(...tmpEvents);
|
|
} else {
|
|
throw new feedutils.FeedValidationError("Invalid source options");
|
|
}
|
|
|
|
source.type = "github";
|
|
|
|
const items: IItem[] = [];
|
|
|
|
for (const [_, event] of events.entries()) {
|
|
const eventDetails = formatEvent(event);
|
|
if (eventDetails) {
|
|
items.push({
|
|
id: `${source.id}-${event.id}`,
|
|
userId: source.userId,
|
|
columnId: source.columnId,
|
|
sourceId: source.id,
|
|
title: eventDetails.title ?? "",
|
|
link: eventDetails.link ?? "",
|
|
media: event.actor?.avatar_url
|
|
? event.actor?.avatar_url
|
|
: event.actor?.login
|
|
? `https://github.com/${event.actor.login}.png`
|
|
: undefined,
|
|
description: eventDetails.description,
|
|
author: event.actor?.login ? event.actor.login : undefined,
|
|
publishedAt: Math.floor(new Date(event.created_at).getTime() / 1000),
|
|
});
|
|
}
|
|
}
|
|
|
|
return { source, items };
|
|
} else if (
|
|
source.options.github.type === "searchissuesandpullrequests" &&
|
|
source.options.github.query
|
|
) {
|
|
/**
|
|
* The `searchissuesandpullrequests` let a user add a seach query as source.
|
|
* With this type it is possible to follow all issues and pull requests of
|
|
* an user, repository or organization. Since we allow a custom query we do
|
|
* not add a icon and link to the source. The id of the source is generated
|
|
* based on a hash of the query. Then we are going through all the returned
|
|
* issues and generating an item for each issue, where we are using the user
|
|
* as author (we were also thinking about using the repository, but decided
|
|
* against it).
|
|
*
|
|
* - https://docs.github.com/en/rest/search?apiVersion=2022-11-28#search-issues-and-pull-requests
|
|
*/
|
|
const issues = await request(`/search/issues`, {
|
|
token: token,
|
|
params: {
|
|
q: source.options.github.query,
|
|
sort: "created",
|
|
direction: "desc",
|
|
page: "1",
|
|
per_page: "100",
|
|
},
|
|
});
|
|
|
|
source.id = `github-${source.userId}-${source.columnId}-${source.options.github.type}-${await utils.md5(
|
|
source.options.github.query,
|
|
)}`;
|
|
source.type = "github";
|
|
source.title = source.options.github.queryName || "Search";
|
|
source.icon = undefined;
|
|
source.link = undefined;
|
|
|
|
const items: IItem[] = [];
|
|
|
|
for (const [_, issue] of issues.items.entries()) {
|
|
items.push({
|
|
id: `${source.id}-${issue.node_id}`,
|
|
userId: source.userId,
|
|
columnId: source.columnId,
|
|
sourceId: source.id,
|
|
title: issue.title,
|
|
link: issue.html_url,
|
|
media: issue?.user?.avatar_url
|
|
? issue.user.avatar_url
|
|
: issue?.user?.login
|
|
? `https://github.com/${issue.user.login}.png`
|
|
: undefined,
|
|
description: `${issue.repository_url.replace(
|
|
"https://api.github.com/repos/",
|
|
"",
|
|
)} #${issue.number}`,
|
|
author: issue.user.login,
|
|
publishedAt: Math.floor(new Date(issue.created_at).getTime() / 1000),
|
|
});
|
|
}
|
|
|
|
return { source, items };
|
|
}
|
|
|
|
throw new feedutils.FeedValidationError("Invalid source options");
|
|
};
|
|
|
|
/**
|
|
* `request` is a helper function to make a request to the GitHub API.
|
|
*/
|
|
const request = async (
|
|
url: string,
|
|
options: { token: string; params?: Record<string, string> },
|
|
) => {
|
|
const res = await utils.fetchWithTimeout(
|
|
`https://api.github.com${url}${
|
|
options.params ? `?${new URLSearchParams(options.params).toString()}` : ""
|
|
}`,
|
|
{
|
|
method: "GET",
|
|
headers: {
|
|
Accept: "application/vnd.github+json",
|
|
Authorization: `Bearer ${options.token}`,
|
|
"X-GitHub-Api-Version": "2022-11-28",
|
|
},
|
|
},
|
|
5000,
|
|
);
|
|
|
|
if (!res.ok) {
|
|
const error = await res.json();
|
|
throw new Error(error?.message ?? "Unknown error");
|
|
}
|
|
|
|
return await res.json();
|
|
};
|
|
|
|
/**
|
|
* `getLinkFromApiUrl` returns a link which can be clicked by a user based on
|
|
* the given GitHub API url. If there is no url given we return the default
|
|
* GitHub notifications link.
|
|
*/
|
|
const getLinkFromApiUrl = (url?: string): string => {
|
|
if (!url) {
|
|
return "https://github.com/notifications";
|
|
}
|
|
|
|
if (/^https:\/\/api.github.com\/repos\/.*\/.*\/pulls\/\d+$/.test(url)) {
|
|
const n = url.lastIndexOf("pulls");
|
|
url = url.slice(0, n) + url.slice(n).replace("pulls", "pull");
|
|
}
|
|
|
|
return `https://github.com/${url.replace(
|
|
"https://api.github.com/repos/",
|
|
"",
|
|
)}`;
|
|
};
|
|
|
|
/**
|
|
* `formatDescription` formats the description for a notification based on the
|
|
* notification reason.
|
|
* See: https://docs.github.com/en/rest/activity/notifications?apiVersion=2022-11-28#about-notification-reasons
|
|
*/
|
|
// deno-lint-ignore no-explicit-any
|
|
const formatDescription = (notification: any): string | undefined => {
|
|
switch (notification.reason) {
|
|
case "assign":
|
|
return "You were assigned to the issue.";
|
|
case "author":
|
|
return "You created the thread.";
|
|
case "comment":
|
|
return "You commented on the thread.";
|
|
case "ci_activity":
|
|
return "A GitHub Actions workflow run that you triggered was completed.";
|
|
case "invitation":
|
|
return "You accepted an invitation to contribute to the repository.";
|
|
case "manual":
|
|
return "You subscribed to the thread (via an issue or pull request).";
|
|
case "mention":
|
|
return "You were specifically @mentioned in the content.";
|
|
case "review_requested":
|
|
return "You, or a team you're a member of, were requested to review a pull request.";
|
|
case "security_alert":
|
|
return "GitHub discovered a security vulnerability in your repository.";
|
|
case "state_change":
|
|
return "You changed the thread state (for example, closing an issue or merging a pull request).";
|
|
case "subscribed":
|
|
return "You're watching the repository.";
|
|
case "team_mention":
|
|
return "You were on a team that was mentioned.";
|
|
default:
|
|
return undefined;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* `formatEvent` formats the given event by returning a proper title, link and
|
|
* description for a item as we save it in our database. If the event type is
|
|
* not supported or a required field is missing we return an error.
|
|
*/
|
|
const formatEvent = (
|
|
// deno-lint-ignore no-explicit-any
|
|
event: any,
|
|
):
|
|
| {
|
|
title: string | undefined;
|
|
link: string | undefined;
|
|
description: string | undefined;
|
|
}
|
|
| undefined => {
|
|
switch (event.type) {
|
|
case "CommitCommentEvent":
|
|
if (event.payload?.comment?.html_url) {
|
|
return {
|
|
title: "",
|
|
link: event.payload.comment.html_url,
|
|
description: "Added a comment to a commit",
|
|
};
|
|
}
|
|
return undefined;
|
|
|
|
case "CreateEvent":
|
|
if (event.payload?.ref_type) {
|
|
let description = "";
|
|
switch (event.payload?.ref_type) {
|
|
case "repository":
|
|
description = "Created a new repository";
|
|
break;
|
|
case "branch":
|
|
description = "Created a new branch";
|
|
break;
|
|
case "tag":
|
|
description = "Created a new tag";
|
|
break;
|
|
default:
|
|
description = "Created something";
|
|
break;
|
|
}
|
|
return {
|
|
title: "",
|
|
link: event.repo.html_url,
|
|
description: description,
|
|
};
|
|
}
|
|
return undefined;
|
|
|
|
case "DeleteEvent":
|
|
if (event.payload?.ref_type) {
|
|
let description = "";
|
|
switch (event.payload?.ref_type) {
|
|
case "repository":
|
|
description = "Deleted a repository";
|
|
break;
|
|
case "branch":
|
|
description = "Deleted a branch";
|
|
break;
|
|
case "tag":
|
|
description = "Deleted a tag";
|
|
break;
|
|
default:
|
|
description = "Deleted something";
|
|
break;
|
|
}
|
|
return {
|
|
title: "",
|
|
link: event.repo.html_url,
|
|
description: description,
|
|
};
|
|
}
|
|
return undefined;
|
|
|
|
case "ForkEvent":
|
|
if (event.payload?.forkee) {
|
|
return {
|
|
title: event.payload.forkee.name,
|
|
link: event.payload.forkee.html_url,
|
|
description: "Forked a repository",
|
|
};
|
|
}
|
|
return undefined;
|
|
|
|
case "GollumEvent":
|
|
if (event.payload?.pages && event.payload?.pages.length > 0) {
|
|
let description = "";
|
|
switch (event.payload?.pages[0].action) {
|
|
case "created":
|
|
description = "Created a new wiki page";
|
|
break;
|
|
default:
|
|
description = "Updated a wiki page";
|
|
break;
|
|
}
|
|
return {
|
|
title: event.payload.pages[0].title,
|
|
link: event.payload.pages[0].html_url,
|
|
description: description,
|
|
};
|
|
}
|
|
return undefined;
|
|
|
|
case "IssueCommentEvent":
|
|
if (event.payload?.issue && event.payload?.comment) {
|
|
return {
|
|
title: event.payload.issue.title,
|
|
link: event.payload.comment.html_url,
|
|
description: "Added a comment",
|
|
};
|
|
}
|
|
return undefined;
|
|
|
|
case "IssuesEvent":
|
|
if (event.payload?.action && event.payload?.issue) {
|
|
let description = "";
|
|
switch (event.payload?.action) {
|
|
case "assigned":
|
|
description = "Assigned";
|
|
break;
|
|
case "unassigned":
|
|
description = "Unassigned";
|
|
break;
|
|
case "review_requested":
|
|
description = "Requested a review";
|
|
break;
|
|
case "review_request_removed":
|
|
description = "Removed a requested review";
|
|
break;
|
|
case "labeled":
|
|
description = "Added a label";
|
|
break;
|
|
case "unlabeled":
|
|
description = "Removed a label";
|
|
break;
|
|
case "opened":
|
|
description = "Opened an issue";
|
|
if (event.payload.issue.pull_request) {
|
|
description = "Opened a pull request";
|
|
}
|
|
break;
|
|
case "closed":
|
|
description = "Closed an issue";
|
|
if (event.payload.issue.pull_request) {
|
|
description = "Closed a pull request";
|
|
}
|
|
break;
|
|
case "reopened":
|
|
description = "Reopened an issue";
|
|
if (event.payload.issue.pull_request) {
|
|
description = "Reopened a pull request";
|
|
}
|
|
break;
|
|
case "synchronize":
|
|
description = "Synchronized an issue";
|
|
if (event.payload.issue.pull_request) {
|
|
description = "Synchronized a pull request";
|
|
}
|
|
break;
|
|
case "edited":
|
|
description = "Edited an issue";
|
|
if (event.payload.issue.pull_request) {
|
|
description = "Edited a pull request";
|
|
}
|
|
break;
|
|
}
|
|
return {
|
|
title: event.payload.issue.title,
|
|
link: event.payload.issue.html_url,
|
|
description: description,
|
|
};
|
|
}
|
|
return undefined;
|
|
|
|
case "MemberEvent":
|
|
if (event.payload?.action && event.payload?.member) {
|
|
let description = "";
|
|
switch (event.payload?.action) {
|
|
case "added":
|
|
description = "Was added as member";
|
|
break;
|
|
}
|
|
return {
|
|
title: event.payload.member.login,
|
|
link: event.payload.member.html_url,
|
|
description: description,
|
|
};
|
|
}
|
|
return undefined;
|
|
|
|
case "PullRequestEvent":
|
|
if (event.payload?.action && event.payload?.pull_request) {
|
|
let description = "";
|
|
switch (event.payload?.action) {
|
|
case "assigned":
|
|
description = "Assigned";
|
|
break;
|
|
case "unassigned":
|
|
description = "Unassigned";
|
|
break;
|
|
case "review_requested":
|
|
description = "Requested a review";
|
|
break;
|
|
case "review_request_removed":
|
|
description = "Removed a requested review";
|
|
break;
|
|
case "labeled":
|
|
description = "Added a label";
|
|
break;
|
|
case "unlabeled":
|
|
description = "Removed a label";
|
|
break;
|
|
case "opened":
|
|
description = "Opened";
|
|
break;
|
|
case "closed":
|
|
if (
|
|
event.payload?.pull_request.merged &&
|
|
event.payload?.pull_request.merged == true
|
|
) {
|
|
description = "Merged";
|
|
break;
|
|
} else {
|
|
description = "Closed";
|
|
break;
|
|
}
|
|
case "reopened":
|
|
description = "Reopened";
|
|
break;
|
|
case "synchronize":
|
|
description = "Synchronized";
|
|
break;
|
|
case "edited":
|
|
description = "Edited";
|
|
break;
|
|
}
|
|
return {
|
|
title: event.payload.pull_request.title,
|
|
link: event.payload.pull_request.html_url,
|
|
description: description,
|
|
};
|
|
}
|
|
return undefined;
|
|
|
|
case "PullRequestReviewEvent":
|
|
if (event.payload?.pull_request && event.payload?.review) {
|
|
return {
|
|
title: event.payload.pull_request.title,
|
|
link: event.payload.review.html_url,
|
|
description: "Added a review",
|
|
};
|
|
}
|
|
return undefined;
|
|
|
|
case "PullRequestReviewCommentEvent":
|
|
if (
|
|
event.payload?.action &&
|
|
event.payload?.pull_request &&
|
|
event.payload?.comment
|
|
) {
|
|
let description = "";
|
|
switch (event.payload?.action) {
|
|
case "created":
|
|
description = "Added a review comment";
|
|
break;
|
|
case "edited":
|
|
description = "Updated a review comment";
|
|
break;
|
|
case "deleted":
|
|
description = "Deleted a review comment";
|
|
break;
|
|
}
|
|
return {
|
|
title: event.payload.pull_request.title,
|
|
link: event.payload.comment.html_url,
|
|
description: description,
|
|
};
|
|
}
|
|
return undefined;
|
|
|
|
case "PushEvent":
|
|
if (event.payload?.repository) {
|
|
return {
|
|
title: "",
|
|
link: event.payload.repository.html_url,
|
|
description: event.payload.commits
|
|
? event.payload.commits.length === 1
|
|
? `Pushed ${event.payload.commits.length} commit`
|
|
: `Pushed ${event.payload.commits.length} commits`
|
|
: "",
|
|
};
|
|
}
|
|
return undefined;
|
|
|
|
case "ReleaseEvent":
|
|
if (event.payload?.action && event.payload?.release) {
|
|
let description = "";
|
|
switch (event.payload?.action) {
|
|
case "created":
|
|
description = "Release was created";
|
|
break;
|
|
case "deleted":
|
|
description = "Release was created";
|
|
break;
|
|
case "edited":
|
|
description = "Release was updated";
|
|
break;
|
|
case "prereleased":
|
|
description = "Prerelease was created";
|
|
break;
|
|
case "published":
|
|
description = "Release was published";
|
|
break;
|
|
case "released":
|
|
description = "Release was released";
|
|
break;
|
|
case "unpublished":
|
|
description = "Release was unpublished";
|
|
break;
|
|
}
|
|
return {
|
|
title: event.payload.release.name,
|
|
link: event.payload.release.html_url,
|
|
description: description,
|
|
};
|
|
}
|
|
return undefined;
|
|
|
|
case "WatchEvent":
|
|
if (event.actor) {
|
|
return {
|
|
title: "",
|
|
link: `https://github.com/${event.actor.login}`,
|
|
description: `${event.actor.login} starred ${
|
|
event.payload?.repository?.name || "the repository"
|
|
}`,
|
|
};
|
|
}
|
|
return undefined;
|
|
|
|
default:
|
|
return undefined;
|
|
}
|
|
};
|