Add support for showing GitHub issue comments (#697)

* Add support for issue/PR comments.

* changelog

* Quote message

* Truncate messages too
This commit is contained in:
Will Hunt 2023-04-04 13:28:51 +01:00 committed by GitHub
parent 7eef384563
commit c7ca2ba94d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 35 additions and 6 deletions

1
changelog.d/697.feature Normal file
View File

@ -0,0 +1 @@
Add support for issue created notifications in Github Repo connections.

View File

@ -231,11 +231,11 @@ export class Bridge {
this.github?.onInstallationRemoved(data.data); this.github?.onInstallationRemoved(data.data);
}); });
this.bindHandlerToQueue<GitHubWebhookTypes.IssueCommentCreatedEvent, GitHubIssueConnection>( this.bindHandlerToQueue<GitHubWebhookTypes.IssueCommentCreatedEvent, GitHubIssueConnection|GitHubRepoConnection>(
"github.issue_comment.created", "github.issue_comment.created",
(data) => { (data) => {
const { repository, issue, owner } = validateRepoIssue(data); const { repository, issue, owner } = validateRepoIssue(data);
return connManager.getConnectionsForGithubIssue(owner, repository.name, issue.number).filter(c => c instanceof GitHubIssueConnection) as GitHubIssueConnection[]; return connManager.getConnectionsForGithubIssue(owner, repository.name, issue.number);
}, },
(c, data) => c.onIssueCommentCreated(data), (c, data) => c.onIssueCommentCreated(data),
); );

View File

@ -8,7 +8,7 @@ import { Connection, IConnection, IConnectionState, InstantiateConnectionOpts, P
import { GetConnectionsResponseItem } from "../provisioning/api"; import { GetConnectionsResponseItem } from "../provisioning/api";
import { IssuesOpenedEvent, IssuesReopenedEvent, IssuesEditedEvent, PullRequestOpenedEvent, IssuesClosedEvent, PullRequestClosedEvent, import { IssuesOpenedEvent, IssuesReopenedEvent, IssuesEditedEvent, PullRequestOpenedEvent, IssuesClosedEvent, PullRequestClosedEvent,
PullRequestReadyForReviewEvent, PullRequestReviewSubmittedEvent, ReleasePublishedEvent, ReleaseCreatedEvent, PullRequestReadyForReviewEvent, PullRequestReviewSubmittedEvent, ReleasePublishedEvent, ReleaseCreatedEvent,
IssuesLabeledEvent, IssuesUnlabeledEvent, WorkflowRunCompletedEvent, PushEvent, IssuesLabeledEvent, IssuesUnlabeledEvent, WorkflowRunCompletedEvent, IssueCommentCreatedEvent, PushEvent
} from "@octokit/webhooks-types"; } from "@octokit/webhooks-types";
import { MatrixMessageContent, MatrixEvent, MatrixReactionContent } from "../MatrixEvent"; import { MatrixMessageContent, MatrixEvent, MatrixReactionContent } from "../MatrixEvent";
import { MessageSenderClient } from "../MatrixSender"; import { MessageSenderClient } from "../MatrixSender";
@ -100,6 +100,8 @@ export type AllowedEventsNames =
"issue.created" | "issue.created" |
"issue.edited" | "issue.edited" |
"issue.labeled" | "issue.labeled" |
"issue.comment" |
"issue.comment.created" |
"issue" | "issue" |
"pull_request.closed" | "pull_request.closed" |
"pull_request.merged" | "pull_request.merged" |
@ -126,7 +128,9 @@ export const AllowedEvents: AllowedEventsNames[] = [
"issue.created" , "issue.created" ,
"issue.edited" , "issue.edited" ,
"issue.labeled" , "issue.labeled" ,
"issue" , "issue.comment",
"issue.comment.created",
"issue",
"pull_request.closed" , "pull_request.closed" ,
"pull_request.merged" , "pull_request.merged" ,
"pull_request.opened" , "pull_request.opened" ,
@ -321,6 +325,7 @@ const WORKFLOW_CONCLUSION_TO_NOTICE: Record<WorkflowRunCompletedEvent["workflow_
stale: "completed, but is stale 🍞" stale: "completed, but is stale 🍞"
} }
const TRUNCATE_COMMENT_SIZE = 256;
const LABELED_DEBOUNCE_MS = 5000; const LABELED_DEBOUNCE_MS = 5000;
const CREATED_GRACE_PERIOD_MS = 6000; const CREATED_GRACE_PERIOD_MS = 6000;
const DEFAULT_HOTLINK_PREFIX = "#"; const DEFAULT_HOTLINK_PREFIX = "#";
@ -358,8 +363,9 @@ export class GitHubRepoConnection extends CommandConnection<GitHubRepoConnection
static validateState(state: unknown, isExistingState = false): ConnectionValidatedState { static validateState(state: unknown, isExistingState = false): ConnectionValidatedState {
const validator = new Ajv({ allowUnionTypes: true }).compile(ConnectionStateSchema); const validator = new Ajv({ allowUnionTypes: true }).compile(ConnectionStateSchema);
if (validator(state)) { if (validator(state)) {
if (!isExistingState && state.enableHooks && !state.enableHooks.every(h => AllowedEvents.includes(h))) { const invalidHooks = !isExistingState && state.enableHooks && state.enableHooks.filter(h => !AllowedEvents.includes(h));
throw new ApiError('`enableHooks` must only contain allowed values', ErrCode.BadValue); if (invalidHooks && invalidHooks.length) {
throw new ApiError(`'enableHooks' must only contain allowed values. Found invalid values ${invalidHooks}`, ErrCode.BadValue);
} }
if (state.ignoreHooks) { if (state.ignoreHooks) {
if (!isExistingState) { if (!isExistingState) {
@ -884,6 +890,24 @@ export class GitHubRepoConnection extends CommandConnection<GitHubRepoConnection
}); });
} }
public async onIssueCommentCreated(event: IssueCommentCreatedEvent) {
if (this.hookFilter.shouldSkip('issue.comment.created', 'issue.comment', 'issue') || !this.matchesLabelFilter(event.issue)) {
return;
}
let message = `**${event.comment.user.login}** [commented](${event.issue.html_url}) on [${event.repository.full_name}#${event.issue.number}](${event.issue.html_url}) `;
message += "\n > " + event.comment.body.substring(0, TRUNCATE_COMMENT_SIZE) + (event.comment.body.length > TRUNCATE_COMMENT_SIZE ? "…" : "");
await this.intent.sendEvent(this.roomId, {
msgtype: "m.notice",
body: message,
formatted_body: md.renderInline(message),
format: "org.matrix.custom.html",
...FormatUtil.getPartialBodyForGithubIssue(event.repository, event.issue),
external_url: event.issue.html_url,
});
}
public async onIssueStateChange(event: IssuesEditedEvent|IssuesReopenedEvent|IssuesClosedEvent) { public async onIssueStateChange(event: IssuesEditedEvent|IssuesReopenedEvent|IssuesClosedEvent) {
if (this.hookFilter.shouldSkip('issue.changed', 'issue') || !this.matchesLabelFilter(event.issue)) { if (this.hookFilter.shouldSkip('issue.changed', 'issue') || !this.matchesLabelFilter(event.issue)) {
return; return;

View File

@ -128,6 +128,10 @@ const ConnectionConfiguration: FunctionComponent<ConnectionConfigurationProps<ne
<EventHookCheckbox enabledHooks={enabledHooks} parentEvent="issue" hookEventName="issue.edited" onChange={toggleEnabledHook}>Edited</EventHookCheckbox> <EventHookCheckbox enabledHooks={enabledHooks} parentEvent="issue" hookEventName="issue.edited" onChange={toggleEnabledHook}>Edited</EventHookCheckbox>
<EventHookCheckbox enabledHooks={enabledHooks} parentEvent="issue" hookEventName="issue.labeled" onChange={toggleEnabledHook}>Labeled</EventHookCheckbox> <EventHookCheckbox enabledHooks={enabledHooks} parentEvent="issue" hookEventName="issue.labeled" onChange={toggleEnabledHook}>Labeled</EventHookCheckbox>
</ul> </ul>
<EventHookCheckbox enabledHooks={enabledHooks} hookEventName="issue.comment" onChange={toggleEnabledHook}>Issue Comments</EventHookCheckbox>
<ul>
<EventHookCheckbox enabledHooks={enabledHooks} parentEvent="issue.comment" hookEventName="issue.comment.created" onChange={toggleEnabledHook}>Created</EventHookCheckbox>
</ul>
<EventHookCheckbox enabledHooks={enabledHooks} hookEventName="pull_request" onChange={toggleEnabledHook}>Pull requests</EventHookCheckbox> <EventHookCheckbox enabledHooks={enabledHooks} hookEventName="pull_request" onChange={toggleEnabledHook}>Pull requests</EventHookCheckbox>
<ul> <ul>
<EventHookCheckbox enabledHooks={enabledHooks} parentEvent="pull_request" hookEventName="pull_request.opened" onChange={toggleEnabledHook}>Opened</EventHookCheckbox> <EventHookCheckbox enabledHooks={enabledHooks} parentEvent="pull_request" hookEventName="pull_request.opened" onChange={toggleEnabledHook}>Opened</EventHookCheckbox>