A bunch of new features for GitHub Repo

This commit is contained in:
Will Hunt 2021-09-08 01:21:59 +01:00
parent 144b7b840f
commit 433c906c7b
8 changed files with 328 additions and 59 deletions

View File

@ -49,7 +49,7 @@ export interface AdminAccountData {
}
export class AdminRoom extends EventEmitter {
public static helpMessage: MatrixMessageContent;
public static helpMessage: () => MatrixMessageContent;
private widgetAccessToken = `abcdef`;
static botCommands: BotCommands;
@ -149,7 +149,7 @@ export class AdminRoom extends EventEmitter {
@botCommand("help", "This help text")
public async helpCommand() {
return this.botIntent.sendEvent(this.roomId, AdminRoom.helpMessage);
return this.botIntent.sendEvent(this.roomId, AdminRoom.helpMessage());
}
@botCommand("github setpersonaltoken", "Set your personal access token for GitHub", ['accessToken'])

View File

@ -24,7 +24,7 @@ export type BotCommands = {[prefix: string]: {
includeUserId: boolean,
}};
export function compileBotCommands(prototype: Record<string, BotCommandFunction>): {helpMessage: MatrixMessageContent, botCommands: BotCommands} {
export function compileBotCommands(prototype: Record<string, BotCommandFunction>): {helpMessage: (cmdPrefix?: string) => MatrixMessageContent, botCommands: BotCommands} {
let content = "Commands:\n";
const botCommands: BotCommands = {};
Object.getOwnPropertyNames(prototype).forEach(propetyKey => {
@ -32,7 +32,7 @@ export function compileBotCommands(prototype: Record<string, BotCommandFunction>
if (b) {
const requiredArgs = b.requiredArgs.join(" ");
const optionalArgs = b.optionalArgs.map((arg: string) => `[${arg}]`).join(" ");
content += ` - \`${b.prefix}\` ${requiredArgs} ${optionalArgs} - ${b.help}\n`;
content += ` - \`££PREFIX££${b.prefix}\` ${requiredArgs} ${optionalArgs} - ${b.help}\n`;
// We know that this is safe.
botCommands[b.prefix as string] = {
fn: prototype[propetyKey],
@ -43,17 +43,23 @@ export function compileBotCommands(prototype: Record<string, BotCommandFunction>
}
});
return {
helpMessage: {
helpMessage: (cmdPrefix?: string) => ({
msgtype: "m.notice",
body: content,
formatted_body: md.render(content),
formatted_body: md.render(content).replace(/££PREFIX££/g, cmdPrefix || ""),
format: "org.matrix.custom.html"
},
}),
botCommands,
}
}
export async function handleCommand(userId: string, command: string, botCommands: BotCommands, obj: unknown): Promise<{error?: string, handled?: boolean}> {
export async function handleCommand(userId: string, command: string, botCommands: BotCommands, obj: unknown, prefix?: string): Promise<{error?: string, handled?: boolean, humanError?: string}> {
if (prefix) {
if (!command.startsWith(prefix)) {
return {handled: false};
}
command = command.substring(prefix.length);
}
const parts = stringArgv(command);
for (let i = parts.length; i > 0; i--) {
const prefix = parts.slice(0, i).join(" ").toLowerCase();
@ -71,7 +77,7 @@ export async function handleCommand(userId: string, command: string, botCommands
await botCommands[prefix].fn.apply(obj, args);
return {handled: true};
} catch (ex) {
return {handled: true, error: ex.message};
return {handled: true, error: ex.message, humanError: ex.humanError};
}
}
}

View File

@ -146,11 +146,11 @@ export class GitHubIssueConnection implements IConnection {
}
public get org() {
return this.state.org;
return this.state.org.toLowerCase();
}
public get repo() {
return this.state.repo;
return this.state.repo.toLowerCase();
}
public async onIssueCommentCreated(event: IssueCommentCreatedEvent) {
@ -316,7 +316,7 @@ export class GitHubIssueConnection implements IConnection {
}
}
public onIssueStateChange() {
public onIssueStateChange(data?: any) {
return this.syncIssueState();
}

View File

@ -12,8 +12,9 @@ import { FormatUtil } from "../FormatUtil";
import axios from "axios";
import { BotCommands, handleCommand, botCommand, compileBotCommands } from "../BotCommands";
import { ReposGetResponseData } from "../Github/Types";
import { IssuesOpenedEvent, IssuesEditedEvent } from "@octokit/webhooks-types";
import { IssuesOpenedEvent, IssuesEditedEvent, PullRequestOpenedEvent, PullRequestClosedEvent, PullRequestReadyForReviewEvent, PullRequestReviewSubmittedEvent, ReleaseCreatedEvent } from "@octokit/webhooks-types";
import emoji from "node-emoji";
import { NotLoggedInError } from "../errors";
const log = new LogWrapper("GitHubRepoConnection");
const md = new markdown();
@ -28,6 +29,8 @@ interface IQueryRoomOpts {
export interface GitHubRepoConnectionState {
org: string;
repo: string;
ignoreHooks?: string[],
commandPrefix?: string;
}
const GITHUB_REACTION_CONTENT: {[emoji: string]: string} = {
@ -140,7 +143,7 @@ export class GitHubRepoConnection implements IConnection {
};
}
static helpMessage: MatrixMessageContent;
static helpMessage: (cmdPrefix: string) => MatrixMessageContent;
static botCommands: BotCommands;
constructor(public readonly roomId: string,
@ -158,20 +161,31 @@ export class GitHubRepoConnection implements IConnection {
return this.state.repo.toLowerCase();
}
private get commandPrefix() {
return (this.state.commandPrefix || "gh") + " ";
}
public isInterestedInStateEvent() {
return false;
}
public async onMessageEvent(ev: MatrixEvent<MatrixMessageContent>) {
const { error, handled } = await handleCommand(ev.sender, ev.content.body, GitHubRepoConnection.botCommands, this);
const { error, handled, humanError } = await handleCommand(ev.sender, ev.content.body, GitHubRepoConnection.botCommands, this, this.commandPrefix);
if (!handled) {
// Not for us.
return;
}
if (error) {
await this.as.botClient.sendEvent(this.roomId, "m.reaction", {
"m.relates_to": {
rel_type: "m.annotation",
event_id: ev.event_id,
key: "⛔",
}
});
await this.as.botIntent.sendEvent(this.roomId,{
msgtype: "m.notice",
body: "Failed to handle command",
body: humanError ? `Failed to handle command: ${humanError}` : "Failed to handle command",
});
return;
}
@ -184,12 +198,17 @@ export class GitHubRepoConnection implements IConnection {
});
}
@botCommand("gh create", "Create an issue for this repo", ["title"], ["description", "labels"], true)
@botCommand("help", "This help text")
public async helpCommand() {
return this.as.botIntent.sendEvent(this.roomId, GitHubRepoConnection.helpMessage(this.commandPrefix));
}
@botCommand("create", "Create an issue for this repo", ["title"], ["description", "labels"], true)
// @ts-ignore
private async onCreateIssue(userId: string, title: string, description?: string, labels?: string) {
const octokit = await this.tokenStore.getOctokitForUser(userId);
if (!octokit) {
return this.as.botIntent.sendText(this.roomId, "You must login to create an issue", "m.notice");
throw new NotLoggedInError();
}
const labelsNames = labels?.split(",");
const res = await octokit.issues.create({
@ -209,7 +228,7 @@ export class GitHubRepoConnection implements IConnection {
});
}
@botCommand("gh assign", "Assign an issue to a user", ["number", "...users"], [], true)
@botCommand("assign", "Assign an issue to a user", ["number", "...users"], [], true)
// @ts-ignore
private async onAssign(userId: string, number: string, ...users: string[]) {
const octokit = await this.tokenStore.getOctokitForUser(userId);
@ -229,7 +248,7 @@ export class GitHubRepoConnection implements IConnection {
});
}
@botCommand("gh close", "Close an issue", ["number"], ["comment"], true)
@botCommand("close", "Close an issue", ["number"], ["comment"], true)
// @ts-ignore
private async onClose(userId: string, number: string, comment?: string) {
const octokit = await this.tokenStore.getOctokitForUser(userId);
@ -255,6 +274,9 @@ export class GitHubRepoConnection implements IConnection {
}
public async onIssueCreated(event: IssuesOpenedEvent) {
if (this.shouldSkipHook('issue.created', 'issue')) {
return;
}
log.info(`onIssueCreated ${this.roomId} ${this.org}/${this.repo} #${event.issue?.number}`);
if (!event.issue) {
throw Error('No issue content!');
@ -262,20 +284,13 @@ export class GitHubRepoConnection implements IConnection {
if (!event.repository) {
throw Error('No repository content!');
}
const orgRepoName = event.issue.repository_url.substr("https://api.github.com/repos/".length);
const orgRepoName = event.repository.full_name;
const content = emoji.emojify(`${event.issue.user?.login} created new issue [${orgRepoName}#${event.issue.number}](${event.issue.html_url}): "${event.issue.title}"`);
const labelsHtml = (event.issue.labels || []).map((label: {color?: string|null, name?: string, description?: string|null}|string) =>
typeof(label) === "string" ?
`<span>${label}</span>` :
`<span title="${label.description}" data-mx-color="#CCCCCC" data-mx-bg-color="#${label.color}">${label.name}</span>`
).join(" ") || "";
const labels = (event.issue?.labels || []).map((label: {name?: string}|string) =>
typeof(label) === "string" ? label : label.name
).join(", ") || "";
const { labelsHtml, labelsStr } = FormatUtil.formatLabels(event.issue.labels);
await this.as.botIntent.sendEvent(this.roomId, {
msgtype: "m.notice",
body: content + (labels.length > 0 ? ` with labels ${labels}`: ""),
body: content + (labelsStr.length > 0 ? ` with labels ${labelsStr}`: ""),
formatted_body: md.renderInline(content) + (labelsHtml.length > 0 ? ` with labels ${labelsHtml}`: ""),
format: "org.matrix.custom.html",
// TODO: Fix types.
@ -284,6 +299,9 @@ export class GitHubRepoConnection implements IConnection {
}
public async onIssueStateChange(event: IssuesEditedEvent) {
if (this.shouldSkipHook('issue.changed', 'issue')) {
return;
}
log.info(`onIssueStateChange ${this.roomId} ${this.org}/${this.repo} #${event.issue?.number}`);
if (!event.issue) {
throw Error('No issue content!');
@ -291,18 +309,158 @@ export class GitHubRepoConnection implements IConnection {
if (!event.repository) {
throw Error('No repository content!');
}
if (event.issue.state === "closed" && event.sender) {
const orgRepoName = event.issue.repository_url.substr("https://api.github.com/repos/".length);
const content = `**@${event.sender.login}** closed issue [${orgRepoName}#${event.issue.number}](${event.issue.html_url}): "${emoji.emojify(event.issue.title)}"`;
await this.as.botIntent.sendEvent(this.roomId, {
msgtype: "m.notice",
body: content,
formatted_body: md.renderInline(content),
format: "org.matrix.custom.html",
// TODO: Fix types
...FormatUtil.getPartialBodyForIssue(event.repository, event.issue as any),
});
const state = event.issue.state === 'open' ? 'reopened' : 'closed';
const orgRepoName = event.repository.full_name;
const content = `**${event.sender.login}** ${state} issue [${orgRepoName}#${event.issue.number}](${event.issue.html_url}): "${emoji.emojify(event.issue.title)}"`;
await this.as.botIntent.sendEvent(this.roomId, {
msgtype: "m.notice",
body: content,
formatted_body: md.renderInline(content),
format: "org.matrix.custom.html",
// TODO: Fix types
...FormatUtil.getPartialBodyForIssue(event.repository, event.issue as any),
});
}
public async onIssueEdited(event: IssuesEditedEvent) {
if (this.shouldSkipHook('issue.edited', 'issue')) {
return;
}
if (!event.issue) {
throw Error('No issue content!');
}
log.info(`onIssueEdited ${this.roomId} ${this.org}/${this.repo} #${event.issue.number}`);
const orgRepoName = event.repository.full_name;
const content = `**${event.sender.login}** edited issue [${orgRepoName}#${event.issue.number}](${event.issue.html_url}): "${emoji.emojify(event.issue.title)}"`;
await this.as.botIntent.sendEvent(this.roomId, {
msgtype: "m.notice",
body: content,
formatted_body: md.renderInline(content),
format: "org.matrix.custom.html",
// TODO: Fix types
...FormatUtil.getPartialBodyForIssue(event.repository, event.issue as any),
});
}
public async onPROpened(event: PullRequestOpenedEvent) {
if (this.shouldSkipHook('pull_request.opened', 'pull_request')) {
return;
}
log.info(`onPROpened ${this.roomId} ${this.org}/${this.repo} #${event.pull_request.number}`);
if (!event.pull_request) {
throw Error('No pull_request content!');
}
if (!event.repository) {
throw Error('No repository content!');
}
const orgRepoName = event.repository.full_name;
const verb = event.pull_request.draft ? 'drafted' : 'opened';
const content = emoji.emojify(`**${event.pull_request.user?.login}** ${verb} a new PR [${orgRepoName}#${event.pull_request.number}](${event.pull_request.html_url}): "${event.pull_request.title}"`);
const { labelsHtml, labelsStr } = FormatUtil.formatLabels(event.pull_request.labels);
await this.as.botIntent.sendEvent(this.roomId, {
msgtype: "m.notice",
body: content + (labelsStr.length > 0 ? ` with labels ${labelsStr}`: ""),
formatted_body: md.renderInline(content) + (labelsHtml.length > 0 ? ` with labels ${labelsHtml}`: ""),
format: "org.matrix.custom.html",
// TODO: Fix types.
...FormatUtil.getPartialBodyForIssue(event.repository, event.pull_request as any),
});
}
public async onPRReadyForReview(event: PullRequestReadyForReviewEvent) {
if (this.shouldSkipHook('pull_request.ready_for_review', 'pull_request')) {
return;
}
log.info(`onPRReadyForReview ${this.roomId} ${this.org}/${this.repo} #${event.pull_request.number}`);
if (!event.pull_request) {
throw Error('No pull_request content!');
}
if (!event.repository) {
throw Error('No repository content!');
}
const orgRepoName = event.repository.full_name;
const content = emoji.emojify(`**${event.pull_request.user?.login}** has marked [${orgRepoName}#${event.pull_request.number}](${event.pull_request.html_url}) as ready to review (${event.pull_request.title})`);
await this.as.botIntent.sendEvent(this.roomId, {
msgtype: "m.notice",
body: content,
formatted_body: md.renderInline(content),
format: "org.matrix.custom.html",
// TODO: Fix types.
...FormatUtil.getPartialBodyForIssue(event.repository, event.pull_request as any),
});
}
public async onPRReviewed(event: PullRequestReviewSubmittedEvent) {
if (this.shouldSkipHook('pull_request.reviewed', 'pull_request')) {
return;
}
log.info(`onPRReadyForReview ${this.roomId} ${this.org}/${this.repo} #${event.pull_request.number}`);
if (!event.pull_request) {
throw Error('No pull_request content!');
}
if (!event.repository) {
throw Error('No repository content!');
}
const orgRepoName = event.repository.full_name;
const emojiForReview = {'approved': '✅', 'commented': '🗨️', 'changes_requested': '🔴'}[event.review.state.toLowerCase()];
if (!emojiForReview) {
// We don't recongnise this state, run away!
return;
}
const content = emoji.emojify(`**${event.review.user?.login}** ${emojiForReview} ${event.review.state.toLowerCase()} [${orgRepoName}#${event.pull_request.number}](${event.pull_request.html_url}) (${event.pull_request.title})`);
await this.as.botIntent.sendEvent(this.roomId, {
msgtype: "m.notice",
body: content,
formatted_body: md.renderInline(content),
format: "org.matrix.custom.html",
// TODO: Fix types.
...FormatUtil.getPartialBodyForIssue(event.repository, event.pull_request as any),
});
}
public async onPRClosed(event: PullRequestClosedEvent) {
if (this.shouldSkipHook('pull_request.closed', 'pull_request')) {
return;
}
log.info(`onPRClosed ${this.roomId} ${this.org}/${this.repo} #${event.pull_request.number}`);
if (!event.pull_request) {
throw Error('No pull_request content!');
}
if (!event.repository) {
throw Error('No repository content!');
}
const orgRepoName = event.repository.full_name;
const verb = event.pull_request.merged ? 'merged' : 'closed';
const content = emoji.emojify(`**${event.pull_request.user?.login}** ${verb} PR [${orgRepoName}#${event.pull_request.number}](${event.pull_request.html_url}): "${event.pull_request.title}"`);
await this.as.botIntent.sendEvent(this.roomId, {
msgtype: "m.notice",
body: content,
formatted_body: md.renderInline(content),
format: "org.matrix.custom.html",
// TODO: Fix types.
...FormatUtil.getPartialBodyForIssue(event.repository, event.pull_request as any),
});
}
public async onReleaseCreated(event: ReleaseCreatedEvent) {
if (this.shouldSkipHook('release', 'release.created')) {
return;
}
log.info(`onReleaseCreated ${this.roomId} ${this.org}/${this.repo} #${event.release.tag_name}`);
if (!event.release) {
throw Error('No release content!');
}
if (!event.repository) {
throw Error('No repository content!');
}
const orgRepoName = event.repository.full_name;
const content = `🪄 **${event.release.author?.login}** released [${event.release.name}]${event.release.html_url} for ${orgRepoName}`;
await this.as.botIntent.sendEvent(this.roomId, {
msgtype: "m.notice",
body: content,
formatted_body: md.renderInline(content),
format: "org.matrix.custom.html",
});
}
public async onEvent(evt: MatrixEvent<unknown>) {
@ -316,6 +474,7 @@ export class GitHubRepoConnection implements IConnection {
const ev = await this.as.botClient.getEvent(this.roomId, event_id);
const issueContent = ev.content["uk.half-shot.matrix-github.issue"];
if (!issueContent) {
log.debug('Reaction to event did not pertain to a issue');
return; // Not our event.
}
@ -335,27 +494,37 @@ export class GitHubRepoConnection implements IConnection {
]
}
});
} else if (action && action[1] === "close") {
} else if (action && action === "close") {
await octokit.issues.update({
state: "closed",
owner: this.org,
repo: this.repo,
issue_number: ev.number,
issue_number: issueContent.number,
});
} else if (action && action[1] === "open") {
} else if (action && action === "open") {
await octokit.issues.update({
state: "open",
owner: this.org,
repo: this.repo,
issue_number: ev.number,
issue_number: issueContent.number,
});
}
return;
}
}
public toString() {
return `GitHubRepo`;
return `GitHubRepo ${this.org}/${this.repo}`;
}
private shouldSkipHook(...hookName: string[]) {
if (this.state.ignoreHooks) {
for (const name of hookName) {
if (this.state.ignoreHooks?.includes(name)) {
return true;
}
}
}
return false;
}
}

View File

@ -29,7 +29,6 @@ export class GitLabRepoConnection implements IConnection {
GitLabRepoConnection.CanonicalEventType, // Legacy event, with an awful name.
];
static helpMessage: MatrixMessageContent;
static botCommands: BotCommands;
constructor(public readonly roomId: string,
@ -131,5 +130,4 @@ export class GitLabRepoConnection implements IConnection {
// Typescript doesn't understand Prototypes very well yet.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const res = compileBotCommands(GitLabRepoConnection.prototype as any);
GitLabRepoConnection.helpMessage = res.helpMessage;
GitLabRepoConnection.botCommands = res.botCommands;

View File

@ -79,4 +79,19 @@ export class FormatUtil {
}
return f;
}
public static formatLabels(labels: Array<{color?: string|null, name?: string, description?: string|null}|string> = []) {
const labelsHtml = labels.map((label: {color?: string|null, name?: string, description?: string|null}|string) =>
typeof(label) === "string" ?
`<span>${label}</span>` :
`<span title="${label.description}" data-mx-color="#CCCCCC" data-mx-bg-color="#${label.color}">${label.name}</span>`
).join(" ") || "";
const labelsStr = labels.map((label: {name?: string}|string) =>
typeof(label) === "string" ? label : label.name
).join(", ") || "";
return {
labelsStr,
labelsHtml,
}
}
}

View File

@ -141,6 +141,8 @@ export class GithubBridge {
}
private getConnectionsForGithubIssue(org: string, repo: string, issueNumber: number): (GitHubIssueConnection|GitLabRepoConnection)[] {
org = org.toLowerCase();
repo = repo.toLowerCase();
return this.connections.filter((c) => (c instanceof GitHubIssueConnection && c.org === org && c.repo === repo && c.issueNumber === issueNumber) ||
(c instanceof GitHubRepoConnection && c.org === org && c.repo === repo)) as (GitHubIssueConnection|GitLabRepoConnection)[];
}
@ -313,7 +315,7 @@ export class GithubBridge {
if (c instanceof GitHubIssueConnection)
await c.onIssueCommentCreated(data);
} catch (ex) {
log.warn(`Connection ${c.toString()} failed to handle comment.created:`, ex);
log.warn(`Connection ${c.toString()} failed to handle github.issue_comment.created:`, ex);
}
})
});
@ -325,7 +327,7 @@ export class GithubBridge {
try {
await c.onIssueCreated(data);
} catch (ex) {
log.warn(`Connection ${c.toString()} failed to handle comment.created:`, ex);
log.warn(`Connection ${c.toString()} failed to handle github.issues.opened:`, ex);
}
})
});
@ -339,7 +341,7 @@ export class GithubBridge {
if (c instanceof GitHubIssueConnection /* || c instanceof GitHubRepoConnection*/)
await c.onIssueEdited(data);
} catch (ex) {
log.warn(`Connection ${c.toString()} failed to handle comment.created:`, ex);
log.warn(`Connection ${c.toString()} failed to handle github.issues.edited:`, ex);
}
})
});
@ -350,9 +352,9 @@ export class GithubBridge {
connections.map(async (c) => {
try {
if (c instanceof GitHubIssueConnection || c instanceof GitHubRepoConnection)
await c.onIssueStateChange();
await c.onIssueStateChange(data);
} catch (ex) {
log.warn(`Connection ${c.toString()} failed to handle comment.created:`, ex);
log.warn(`Connection ${c.toString()} failed to handle github.issues.closed:`, ex);
}
})
});
@ -363,9 +365,76 @@ export class GithubBridge {
connections.map(async (c) => {
try {
if (c instanceof GitHubIssueConnection || c instanceof GitHubRepoConnection)
await c.onIssueStateChange();
await c.onIssueStateChange(data);
} catch (ex) {
log.warn(`Connection ${c.toString()} failed to handle comment.created:`, ex);
log.warn(`Connection ${c.toString()} failed to handle github.issues.reopened:`, ex);
}
})
});
this.queue.on<GitHubWebhookTypes.IssuesEditedEvent>("github.issues.edited", async ({ data }) => {
const { repository, issue, owner } = validateRepoIssue(data);
const connections = this.getConnectionsForGithubRepo(owner, repository.name);
connections.map(async (c) => {
try {
await c.onIssueEdited(data);
} catch (ex) {
log.warn(`Connection ${c.toString()} failed to handle github.issues.edited:`, ex);
}
})
});
this.queue.on<GitHubWebhookTypes.PullRequestOpenedEvent>("github.pull_request.opened", async ({ data }) => {
const connections = this.getConnectionsForGithubRepo(data.repository.owner.login, data.repository.name);
connections.map(async (c) => {
try {
await c.onPROpened(data);
} catch (ex) {
log.warn(`Connection ${c.toString()} failed to handle github.pull_request.opened:`, ex);
}
})
});
this.queue.on<GitHubWebhookTypes.PullRequestClosedEvent>("github.pull_request.closed", async ({ data }) => {
const connections = this.getConnectionsForGithubRepo(data.repository.owner.login, data.repository.name);
connections.map(async (c) => {
try {
await c.onPRClosed(data);
} catch (ex) {
log.warn(`Connection ${c.toString()} failed to handle github.pull_request.closed:`, ex);
}
})
});
this.queue.on<GitHubWebhookTypes.PullRequestReadyForReviewEvent>("github.pull_request.ready_for_review", async ({ data }) => {
const connections = this.getConnectionsForGithubRepo(data.repository.owner.login, data.repository.name);
connections.map(async (c) => {
try {
await c.onPRReadyForReview(data);
} catch (ex) {
log.warn(`Connection ${c.toString()} failed to handle github.pull_request.closed:`, ex);
}
})
});
this.queue.on<GitHubWebhookTypes.PullRequestReviewSubmittedEvent>("github.pull_request_review.submitted", async ({ data }) => {
const connections = this.getConnectionsForGithubRepo(data.repository.owner.login, data.repository.name);
connections.map(async (c) => {
try {
await c.onPRReviewed(data);
} catch (ex) {
log.warn(`Connection ${c.toString()} failed to handle github.pull_request.closed:`, ex);
}
})
});
this.queue.on<GitHubWebhookTypes.ReleaseCreatedEvent>("github.release.created", async ({ data }) => {
const connections = this.getConnectionsForGithubRepo(data.repository.owner.login, data.repository.name);
connections.map(async (c) => {
try {
await c.onReleaseCreated(data);
} catch (ex) {
log.warn(`Connection ${c.toString()} failed to handle github.pull_request.closed:`, ex);
}
})
});
@ -614,6 +683,10 @@ export class GithubBridge {
/* We ignore messages from our users */
return;
}
if (Date.now() - event.origin_server_ts > 30000) {
/* We ignore old messages too */
return;
}
log.info(`Got message roomId=${roomId} type=${event.type} from=${event.sender}`);
console.log(event);
log.debug("Content:", JSON.stringify(event));
@ -699,15 +772,22 @@ export class GithubBridge {
this.connections.push(connection);
}
}
return null;
return;
}
if (event.sender === this.as.botUserId) {
// It's us
return;
}
// Alas, it's just an event.
return this.connections.filter((c) => c.roomId === roomId).map((c) => c.onEvent ? c.onEvent(event) : undefined);
for (const connection of this.connections.filter((c) => c.roomId === roomId)) {
try {
if (connection.onEvent) {
await connection.onEvent(event);
}
} catch (ex) {
log.warn(`Connection ${connection.toString()} failed to handle event:`, ex);
}
}
}
private async onQueryRoom(roomAlias: string) {

View File

@ -96,6 +96,7 @@ export class Webhooks extends EventEmitter {
private async onGitHubPayload({id, name, payload}: EmitterWebhookEvent) {
log.info(`Got GitHub webhook event ${id} ${name}`);
console.log(payload);
log.debug("Payload:", payload);
const action = (payload as unknown as {action: string|undefined}).action;
try {
await this.queue.push({