mirror of
https://github.com/matrix-org/matrix-hookshot.git
synced 2025-03-10 21:19:13 +00:00
Land GitHub discussions first pass
This commit is contained in:
parent
68d082df19
commit
c726335be6
@ -24,14 +24,14 @@
|
||||
"dependencies": {
|
||||
"@octokit/auth-app": "^3.3.0",
|
||||
"@octokit/auth-token": "^2.4.5",
|
||||
"@octokit/rest": "^18.5.2",
|
||||
"@octokit/rest": "^18.10.0",
|
||||
"@octokit/webhooks": "^9.1.2",
|
||||
"axios": "^0.21.1",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.17.1",
|
||||
"ioredis": "^4.26.0",
|
||||
"markdown-it": "^12.0.4",
|
||||
"matrix-bot-sdk": "^0.5.17",
|
||||
"matrix-bot-sdk": "^0.5.19",
|
||||
"matrix-widget-api": "^0.1.0-beta.13",
|
||||
"micromatch": "^4.0.4",
|
||||
"mime": "^2.5.2",
|
||||
|
@ -4,19 +4,24 @@ import { UserTokenStore } from "../UserTokenStore";
|
||||
import { CommentProcessor } from "../CommentProcessor";
|
||||
import { MessageSenderClient } from "../MatrixSender";
|
||||
import { getIntentForUser } from "../IntentUtils";
|
||||
import { Discussion } from "../Github/Discussion";
|
||||
import { MatrixEvent, MatrixMessageContent } from "../MatrixEvent";
|
||||
|
||||
|
||||
import { Discussion } from "@octokit/webhooks-types";
|
||||
import emoji from "node-emoji";
|
||||
import markdown from "markdown-it";
|
||||
import { DiscussionCommentCreatedEvent } from "@octokit/webhooks-types";
|
||||
import { GithubGraphQLClient } from "../Github/GithubInstance";
|
||||
import LogWrapper from "../LogWrapper";
|
||||
export interface GitHubDiscussionConnectionState {
|
||||
owner: string;
|
||||
repo: string;
|
||||
id: number;
|
||||
internalId: string;
|
||||
discussion: number;
|
||||
category: number;
|
||||
}
|
||||
|
||||
// const log = new LogWrapper("GitHubDiscussion");
|
||||
// const md = new markdown();
|
||||
const log = new LogWrapper("GitHubDiscussion");
|
||||
const md = new markdown();
|
||||
|
||||
/**
|
||||
* Handles rooms connected to a github repo.
|
||||
@ -30,24 +35,33 @@ export class GitHubDiscussionConnection implements IConnection {
|
||||
|
||||
static readonly QueryRoomRegex = /#github_disc_(.+)_(.+)_(\d+):.*/;
|
||||
|
||||
readonly sentEvents = new Set<string>(); //TODO: Set some reasonable limits
|
||||
|
||||
public static async createDiscussionRoom(
|
||||
as: Appservice, userId: string, owner: string, repo: string, discussion: Discussion,
|
||||
tokenStore: UserTokenStore, commentProcessor: CommentProcessor, messageClient: MessageSenderClient
|
||||
as: Appservice, userId: string|null, owner: string, repo: string, discussion: Discussion,
|
||||
tokenStore: UserTokenStore, commentProcessor: CommentProcessor, messageClient: MessageSenderClient,
|
||||
) {
|
||||
const commentIntent = await getIntentForUser({
|
||||
login: discussion.author.login,
|
||||
avatarUrl: discussion.author.avatarUrl,
|
||||
login: discussion.user.login,
|
||||
avatarUrl: discussion.user.avatar_url,
|
||||
}, as);
|
||||
const state: GitHubDiscussionConnectionState = {
|
||||
owner,
|
||||
repo,
|
||||
id: discussion.id,
|
||||
internalId: discussion.node_id,
|
||||
discussion: discussion.number,
|
||||
category: discussion.category.id,
|
||||
};
|
||||
const invite = [as.botUserId];
|
||||
if (userId) {
|
||||
invite.push(userId);
|
||||
}
|
||||
const roomId = await commentIntent.underlyingClient.createRoom({
|
||||
invite: [userId, as.botUserId],
|
||||
preset: 'private_chat',
|
||||
name: `${discussion.title} (${owner}/${name})`,
|
||||
invite,
|
||||
preset: 'public_chat',
|
||||
name: `${discussion.title} (${owner}/${repo})`,
|
||||
topic: emoji.emojify(`Under ${discussion.category.emoji} ${discussion.category.name}`),
|
||||
room_alias_name: `github_disc_${owner.toLowerCase()}_${repo.toLowerCase()}_${discussion.number}`,
|
||||
initial_state: [{
|
||||
content: state,
|
||||
@ -57,11 +71,11 @@ export class GitHubDiscussionConnection implements IConnection {
|
||||
});
|
||||
await commentIntent.sendEvent(roomId, {
|
||||
msgtype: 'm.text',
|
||||
body: discussion.bodyText,
|
||||
formatted_body: discussion.bodyHTML,
|
||||
body: discussion.body,
|
||||
formatted_body: md.render(discussion.body),
|
||||
format: 'org.matrix.custom.html',
|
||||
});
|
||||
await as.botIntent.joinRoom(roomId);
|
||||
await as.botIntent.ensureJoined(roomId);
|
||||
return new GitHubDiscussionConnection(roomId, as, state, '', tokenStore, commentProcessor, messageClient);
|
||||
}
|
||||
|
||||
@ -72,6 +86,7 @@ export class GitHubDiscussionConnection implements IConnection {
|
||||
private tokenStore: UserTokenStore,
|
||||
private commentProcessor: CommentProcessor,
|
||||
private messageClient: MessageSenderClient) {
|
||||
|
||||
}
|
||||
|
||||
public isInterestedInStateEvent(eventType: string, stateKey: string) {
|
||||
@ -79,13 +94,16 @@ export class GitHubDiscussionConnection implements IConnection {
|
||||
}
|
||||
|
||||
public async onMessageEvent(ev: MatrixEvent<MatrixMessageContent>) {
|
||||
const octokit = this.tokenStore.getOctokitForUser(ev.sender);
|
||||
if (!octokit) {
|
||||
// Use Reply - Also mention user.
|
||||
const octokit = await this.tokenStore.getOctokitForUser(ev.sender);
|
||||
if (octokit === null) {
|
||||
// TODO: Use Reply - Also mention user.
|
||||
await this.as.botClient.sendNotice(this.roomId, `${ev.sender}: Cannot send comment, you are not logged into GitHub`);
|
||||
return;
|
||||
}
|
||||
|
||||
const qlClient = new GithubGraphQLClient(octokit);
|
||||
const commentId = await qlClient.addDiscussionComment(this.state.internalId, ev.content.body);
|
||||
log.info(`Sent ${commentId} for ${ev.event_id} (${ev.sender})`);
|
||||
this.sentEvents.add(commentId);
|
||||
}
|
||||
|
||||
public get discussionNumber() {
|
||||
@ -104,7 +122,16 @@ export class GitHubDiscussionConnection implements IConnection {
|
||||
return `GitHubDiscussion ${this.owner}/${this.repo}#${this.state.discussion}`;
|
||||
}
|
||||
|
||||
public onDiscussionCommentCreated() {
|
||||
this.messageClient.sendMatrixMessage()
|
||||
public onDiscussionCommentCreated(data: DiscussionCommentCreatedEvent) {
|
||||
if (this.sentEvents.has(data.comment.node_id)) {
|
||||
return;
|
||||
}
|
||||
return this.messageClient.sendMatrixMessage(this.roomId, {
|
||||
body: data.comment.body,
|
||||
formatted_body: md.render(data.comment.body),
|
||||
msgtype: 'm.text',
|
||||
external_url: data.comment.html_url,
|
||||
'uk.half-shot.matrix-github.discussion.comment_id': data.comment.id,
|
||||
});
|
||||
}
|
||||
}
|
156
src/Connections/GithubDiscussionSpace.ts
Normal file
156
src/Connections/GithubDiscussionSpace.ts
Normal file
@ -0,0 +1,156 @@
|
||||
import { IConnection } from "./IConnection";
|
||||
import { Appservice, Space } from "matrix-bot-sdk";
|
||||
import LogWrapper from "../LogWrapper";
|
||||
import { Octokit } from "@octokit/rest";
|
||||
import { ReposGetResponseData } from "../Github/Types";
|
||||
import axios from "axios";
|
||||
import { GitHubDiscussionConnection } from "./GithubDiscussion";
|
||||
|
||||
const log = new LogWrapper("GitHubDiscussionSpace");
|
||||
|
||||
export interface GitHubDiscussionSpaceConnectionState {
|
||||
owner: string;
|
||||
repo: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles rooms connected to a github repo.
|
||||
*/
|
||||
export class GitHubDiscussionSpace implements IConnection {
|
||||
static readonly CanonicalEventType = "uk.half-shot.matrix-github.discussion.space";
|
||||
|
||||
static readonly EventTypes = [
|
||||
GitHubDiscussionSpace.CanonicalEventType, // Legacy event, with an awful name.
|
||||
];
|
||||
|
||||
static readonly QueryRoomRegex = /#github_disc_(.+)_(.+):.*/;
|
||||
|
||||
static async onQueryRoom(result: RegExpExecArray, opts: {octokit: Octokit, as: Appservice}): Promise<Record<string, unknown>> {
|
||||
if (!result) {
|
||||
log.error("Invalid alias pattern");
|
||||
throw Error("Could not find issue");
|
||||
}
|
||||
|
||||
const [ owner, repo ] = result?.slice(1);
|
||||
|
||||
log.info(`Fetching ${owner}/${repo}`);
|
||||
let repoRes: ReposGetResponseData;
|
||||
try {
|
||||
// TODO: Determine if the repo has discussions?
|
||||
repoRes = (await opts.octokit.repos.get({
|
||||
owner,
|
||||
repo,
|
||||
})).data;
|
||||
if (!repoRes.owner) {
|
||||
throw Error('Repo has no owner!');
|
||||
}
|
||||
} catch (ex) {
|
||||
log.error("Failed to get repo:", ex);
|
||||
throw Error("Could not find repo");
|
||||
}
|
||||
const state: GitHubDiscussionSpaceConnectionState = {
|
||||
owner: repoRes.owner.login.toLowerCase(),
|
||||
repo: repoRes.name.toLowerCase(),
|
||||
};
|
||||
|
||||
// URL hack so we don't need to fetch the repo itself.
|
||||
let avatarUrl = undefined;
|
||||
try {
|
||||
const profile = await opts.octokit.users.getByUsername({
|
||||
username: owner,
|
||||
});
|
||||
if (profile.data.avatar_url) {
|
||||
const res = await axios.get(profile.data.avatar_url as string, {
|
||||
responseType: 'arraybuffer',
|
||||
});
|
||||
log.info(`uploading ${profile.data.avatar_url}`);
|
||||
// This does exist, but headers is silly and doesn't have content-type.
|
||||
// tslint:disable-next-line: no-any
|
||||
console.log(res.headers);
|
||||
const contentType: string = res.headers["content-type"];
|
||||
const mxcUrl = await opts.as.botClient.uploadContent(
|
||||
Buffer.from(res.data as ArrayBuffer),
|
||||
contentType,
|
||||
`avatar_${profile.data.id}.png`,
|
||||
);
|
||||
avatarUrl = {
|
||||
type: "m.room.avatar",
|
||||
state_key: "",
|
||||
content: {
|
||||
url: mxcUrl,
|
||||
},
|
||||
};
|
||||
}
|
||||
} catch (ex) {
|
||||
log.warn("Failed to get avatar for org:", ex);
|
||||
}
|
||||
|
||||
return {
|
||||
visibility: "public",
|
||||
name: `${state.owner}/${state.repo} Discussions`,
|
||||
topic: `GitHub discussion index for ${state.owner}/${state.repo}`,
|
||||
preset: 'public_chat',
|
||||
room_alias_name: `github_disc_${owner.toLowerCase()}_${repo.toLowerCase()}`,
|
||||
initial_state: [
|
||||
|
||||
{
|
||||
type: this.CanonicalEventType,
|
||||
content: state,
|
||||
state_key: `${state.owner}/${state.repo}`,
|
||||
},
|
||||
avatarUrl,
|
||||
{
|
||||
type: "m.room.history_visibility",
|
||||
state_key: "",
|
||||
content: {
|
||||
history_visibility: 'world_readable',
|
||||
},
|
||||
},
|
||||
],
|
||||
creation_content: {
|
||||
type: "m.space",
|
||||
},
|
||||
power_level_content_override: {
|
||||
ban: 100,
|
||||
events_default: 50,
|
||||
invite: 50,
|
||||
kick: 100,
|
||||
notifications: {
|
||||
room: 100,
|
||||
},
|
||||
redact: 100,
|
||||
state_default: 100,
|
||||
users_default: 0,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
get roomId() {
|
||||
return this.space.roomId;
|
||||
}
|
||||
|
||||
constructor(public readonly space: Space,
|
||||
private state: GitHubDiscussionSpaceConnectionState,
|
||||
private readonly stateKey: string) {}
|
||||
|
||||
public isInterestedInStateEvent(eventType: string, stateKey: string) {
|
||||
return GitHubDiscussionSpace.EventTypes.includes(eventType) && this.stateKey === stateKey;
|
||||
}
|
||||
|
||||
public get repo() {
|
||||
return this.state.repo.toLowerCase();
|
||||
}
|
||||
|
||||
public get owner() {
|
||||
return this.state.owner.toLowerCase();
|
||||
}
|
||||
|
||||
public toString() {
|
||||
return `GitHubDiscussionSpace ${this.owner}/${this.repo}`;
|
||||
}
|
||||
|
||||
public async onDiscussionCreated(discussion: GitHubDiscussionConnection) {
|
||||
log.info(`Adding connection to ${this.toString()}`);
|
||||
await this.space.addChildRoom(discussion.roomId);
|
||||
}
|
||||
}
|
@ -64,7 +64,9 @@ export class GitHubIssueConnection implements IConnection {
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
})).data;
|
||||
// Typing issue
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
})).data as any;
|
||||
} catch (ex) {
|
||||
log.error("Failed to get issue:", ex);
|
||||
throw Error("Could not find issue");
|
||||
@ -78,7 +80,7 @@ export class GitHubIssueConnection implements IConnection {
|
||||
username: owner,
|
||||
});
|
||||
if (profile.data.avatar_url) {
|
||||
const res = await axios.get(profile.data.avatar_url, {
|
||||
const res = await axios.get(profile.data.avatar_url as string, {
|
||||
responseType: 'arraybuffer',
|
||||
});
|
||||
log.info(`uploading ${profile.data.avatar_url}`);
|
||||
@ -239,7 +241,8 @@ export class GitHubIssueConnection implements IConnection {
|
||||
|
||||
for (const comment of comments) {
|
||||
await this.onCommentCreated({
|
||||
comment,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
comment: comment as any,
|
||||
action: "fake",
|
||||
}, false);
|
||||
this.state.comments_processed++;
|
||||
|
@ -95,7 +95,7 @@ export class GitHubRepoConnection implements IConnection {
|
||||
username: owner,
|
||||
});
|
||||
if (profile.data.avatar_url) {
|
||||
const res = await axios.get(profile.data.avatar_url, {
|
||||
const res = await axios.get(profile.data.avatar_url as string, {
|
||||
responseType: 'arraybuffer',
|
||||
});
|
||||
log.info(`uploading ${profile.data.avatar_url}`);
|
||||
|
157
src/Connections/GithubUserSpace.ts
Normal file
157
src/Connections/GithubUserSpace.ts
Normal file
@ -0,0 +1,157 @@
|
||||
import { IConnection } from "./IConnection";
|
||||
import { Appservice, Space } from "matrix-bot-sdk";
|
||||
import LogWrapper from "../LogWrapper";
|
||||
import { Octokit } from "@octokit/rest";
|
||||
import axios from "axios";
|
||||
import { GitHubDiscussionSpace } from ".";
|
||||
|
||||
const log = new LogWrapper("GitHubOwnerSpace");
|
||||
|
||||
export interface GitHubUserSpaceConnectionState {
|
||||
username: string;
|
||||
nodeId: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles rooms connected to a github repo.
|
||||
*/
|
||||
export class GitHubUserSpace implements IConnection {
|
||||
static readonly CanonicalEventType = "uk.half-shot.matrix-github.user.space";
|
||||
|
||||
static readonly EventTypes = [
|
||||
GitHubUserSpace.CanonicalEventType, // Legacy event, with an awful name.
|
||||
];
|
||||
|
||||
static readonly QueryRoomRegex = /#github_(.+):.*/;
|
||||
|
||||
static async onQueryRoom(result: RegExpExecArray, opts: {octokit: Octokit, as: Appservice}): Promise<Record<string, unknown>> {
|
||||
if (!result) {
|
||||
log.error("Invalid alias pattern");
|
||||
throw Error("Could not find issue");
|
||||
}
|
||||
|
||||
const [ username ] = result?.slice(1);
|
||||
|
||||
log.info(`Fetching ${username}`);
|
||||
let state: GitHubUserSpaceConnectionState;
|
||||
let avatarUrl: string|undefined;
|
||||
let name: string;
|
||||
try {
|
||||
// TODO: Determine if the repo has discussions?
|
||||
const userRes = (await opts.octokit.users.getByUsername({
|
||||
username,
|
||||
})).data;
|
||||
if (!userRes) {
|
||||
throw Error('User does not exist!');
|
||||
}
|
||||
name = userRes.name as string;
|
||||
state = {
|
||||
nodeId: userRes.node_id as string,
|
||||
username: userRes.login as string,
|
||||
}
|
||||
avatarUrl = userRes.avatar_url as string;
|
||||
} catch (ex) {
|
||||
log.error("Failed to get repo:", ex);
|
||||
throw Error("Could not find repo");
|
||||
}
|
||||
|
||||
let avatarState: any|undefined;
|
||||
try {
|
||||
if (avatarUrl) {
|
||||
const res = await axios.get(avatarUrl, {
|
||||
responseType: 'arraybuffer',
|
||||
});
|
||||
log.info(`uploading ${avatarUrl}`);
|
||||
// This does exist, but headers is silly and doesn't have content-type.
|
||||
const contentType: string = res.headers["content-type"];
|
||||
const mxcUrl = await opts.as.botClient.uploadContent(
|
||||
Buffer.from(res.data as ArrayBuffer),
|
||||
contentType,
|
||||
`avatar_${state.username}.png`,
|
||||
);
|
||||
avatarState = {
|
||||
type: "m.room.avatar",
|
||||
state_key: "",
|
||||
content: {
|
||||
url: mxcUrl,
|
||||
},
|
||||
};
|
||||
}
|
||||
} catch (ex) {
|
||||
log.warn("Failed to get avatar for org:", ex);
|
||||
}
|
||||
|
||||
return {
|
||||
visibility: "public",
|
||||
name: `GitHub - ${name} (${state.username.toLowerCase()})`,
|
||||
topic: `GitHub page of ${state.username.toLowerCase()}`,
|
||||
preset: 'public_chat',
|
||||
room_alias_name: `github_${state.username.toLowerCase()}`,
|
||||
initial_state: [
|
||||
|
||||
{
|
||||
type: this.CanonicalEventType,
|
||||
content: state,
|
||||
state_key: state.username.toLowerCase(),
|
||||
},
|
||||
avatarState,
|
||||
{
|
||||
type: "m.room.history_visibility",
|
||||
state_key: "",
|
||||
content: {
|
||||
history_visibility: 'world_readable',
|
||||
},
|
||||
},
|
||||
],
|
||||
creation_content: {
|
||||
type: "m.space",
|
||||
},
|
||||
power_level_content_override: {
|
||||
ban: 100,
|
||||
events_default: 50,
|
||||
invite: 50,
|
||||
kick: 100,
|
||||
notifications: {
|
||||
room: 100,
|
||||
},
|
||||
redact: 100,
|
||||
state_default: 100,
|
||||
users_default: 0,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
get roomId() {
|
||||
return this.space.roomId;
|
||||
}
|
||||
|
||||
constructor(public readonly space: Space,
|
||||
private state: GitHubUserSpaceConnectionState,
|
||||
private readonly stateKey: string) {}
|
||||
|
||||
public isInterestedInStateEvent(eventType: string, stateKey: string) {
|
||||
return GitHubUserSpace.EventTypes.includes(eventType) && this.stateKey === stateKey;
|
||||
}
|
||||
|
||||
public get owner() {
|
||||
return this.state.username.toLowerCase();
|
||||
}
|
||||
|
||||
public toString() {
|
||||
return `GitHubUserSpace ${this.owner}`;
|
||||
}
|
||||
|
||||
public async onRepoConnectionCreated(discussion: GitHubDiscussionSpace) {
|
||||
log.info(`Adding connection to ${this.toString()}`);
|
||||
await this.space.addChildRoom(discussion.roomId);
|
||||
}
|
||||
|
||||
public async ensureDiscussionInSpace(discussion: GitHubDiscussionSpace) {
|
||||
// TODO: Optimise
|
||||
const children = await this.space.getChildEntities();
|
||||
console.log("Foo", children);
|
||||
if (!children[discussion.roomId]) {
|
||||
await this.space.addChildRoom(discussion.roomId);
|
||||
}
|
||||
}
|
||||
}
|
10
src/Connections/index.ts
Normal file
10
src/Connections/index.ts
Normal file
@ -0,0 +1,10 @@
|
||||
export * from "./GithubDiscussionSpace";
|
||||
export * from "./GithubDiscussion";
|
||||
export * from "./GithubIssue";
|
||||
export * from "./GithubProject";
|
||||
export * from "./GithubRepo";
|
||||
export * from "./GithubUserSpace";
|
||||
|
||||
export * from "./GitlabIssue";
|
||||
export * from "./GitlabRepo";
|
||||
export * from "./IConnection";
|
@ -1,5 +1,5 @@
|
||||
export interface Discussion {
|
||||
id: number;
|
||||
export interface DiscussionQLResponse {
|
||||
id: string;
|
||||
number: number;
|
||||
author: {
|
||||
login: string;
|
||||
|
@ -4,7 +4,7 @@ import { Octokit } from "@octokit/rest";
|
||||
import { promises as fs } from "fs";
|
||||
import { BridgeConfigGitHub } from "../Config/Config";
|
||||
import LogWrapper from "../LogWrapper";
|
||||
import { Discussion, DiscussionQL } from "./Discussion";
|
||||
import { DiscussionQLResponse, DiscussionQL } from "./Discussion";
|
||||
|
||||
const log = new LogWrapper("GithubInstance");
|
||||
|
||||
@ -75,19 +75,19 @@ query($name: String!, $owner: String!, $number: Int!) {
|
||||
}
|
||||
}
|
||||
}`, {name, owner, number}) as any;
|
||||
return result.repository.discussion as Discussion;
|
||||
return result.repository.discussion as DiscussionQLResponse;
|
||||
}
|
||||
|
||||
public async createDiscussionComment(number: number) {
|
||||
// const result = await this.query(`
|
||||
// mutation($name: String!, $owner: String!, $number: Int!) {
|
||||
// createDiscussion(input: {}) {
|
||||
// discussion(number: $number) {
|
||||
// ${DiscussionQL}
|
||||
// }
|
||||
// }
|
||||
// }`, {name, owner, number}) as any;
|
||||
// return result.repository.discussion as Discussion;
|
||||
public async addDiscussionComment(discussionId: string, body: string): Promise<string> {
|
||||
const result = await this.query(`
|
||||
mutation addDiscussionComment($discussionId: ID!, $body: String!) {
|
||||
addDiscussionComment(input: {discussionId: $discussionId, body: $body}) {
|
||||
comment {
|
||||
id
|
||||
}
|
||||
}
|
||||
}`, {discussionId, body}) as any;
|
||||
return result.addDiscussionComment.comment.id as string;
|
||||
}
|
||||
|
||||
public async listDiscussions(owner: string, name: string) {
|
||||
|
@ -14,7 +14,8 @@ import { MemoryStorageProvider } from "./Stores/MemoryStorageProvider";
|
||||
import { NotificationProcessor } from "./NotificationsProcessor";
|
||||
import { IStorageProvider } from "./Stores/StorageProvider";
|
||||
import { retry } from "./PromiseUtil";
|
||||
import { IConnection } from "./Connections/IConnection";
|
||||
import { IConnection, GitHubDiscussionSpace, GitHubDiscussionConnection, GitHubUserSpace
|
||||
} from "./Connections";
|
||||
import { GitHubRepoConnection } from "./Connections/GithubRepo";
|
||||
import { GitHubIssueConnection } from "./Connections/GithubIssue";
|
||||
import { GitHubProjectConnection } from "./Connections/GithubProject";
|
||||
@ -25,8 +26,6 @@ import { GitLabIssueConnection } from "./Connections/GitlabIssue";
|
||||
import { GetIssueResponse, GetIssueOpts } from "./Gitlab/Types"
|
||||
import { GitLabClient } from "./Gitlab/Client";
|
||||
import { BridgeWidgetApi } from "./Widgets/BridgeWidgetApi";
|
||||
import { GitHubDiscussionConnection } from "./Connections/GithubDiscussion";
|
||||
import { Discussion } from "./Github/Discussion";
|
||||
import { ProjectsGetResponseData } from "./Github/Types";
|
||||
import { NotifFilter, NotificationFilterStateContent } from "./NotificationFilters";
|
||||
import * as GitHubWebhookTypes from "@octokit/webhooks-types";
|
||||
@ -72,6 +71,16 @@ export class GithubBridge {
|
||||
);
|
||||
}
|
||||
|
||||
if (GitHubDiscussionSpace.EventTypes.includes(state.type)) {
|
||||
if (!this.github) {
|
||||
throw Error('GitHub is not configured');
|
||||
}
|
||||
|
||||
return new GitHubDiscussionSpace(
|
||||
await this.as.botClient.getSpace(roomId), state.content, state.stateKey
|
||||
);
|
||||
}
|
||||
|
||||
if (GitHubIssueConnection.EventTypes.includes(state.type)) {
|
||||
if (!this.github) {
|
||||
throw Error('GitHub is not configured');
|
||||
@ -80,6 +89,16 @@ export class GithubBridge {
|
||||
await issue.syncIssueState();
|
||||
return issue;
|
||||
}
|
||||
|
||||
if (GitHubUserSpace.EventTypes.includes(state.type)) {
|
||||
if (!this.github) {
|
||||
throw Error('GitHub is not configured');
|
||||
}
|
||||
return new GitHubUserSpace(
|
||||
await this.as.botClient.getSpace(roomId), state.content, state.stateKey
|
||||
);
|
||||
}
|
||||
|
||||
if (GitLabRepoConnection.EventTypes.includes(state.type)) {
|
||||
if (!this.config.gitlab) {
|
||||
throw Error('GitLab is not configured');
|
||||
@ -125,13 +144,22 @@ export class GithubBridge {
|
||||
(c instanceof GitHubRepoConnection && c.org === org && c.repo === repo)) as (GitHubIssueConnection|GitLabRepoConnection)[];
|
||||
}
|
||||
|
||||
|
||||
private getConnectionsForGithubRepo(org: string, repo: string): GitHubRepoConnection[] {
|
||||
org = org.toLowerCase();
|
||||
repo = repo.toLowerCase();
|
||||
return this.connections.filter((c) => (c instanceof GitHubRepoConnection && c.org === org && c.repo === repo)) as GitHubRepoConnection[];
|
||||
}
|
||||
|
||||
private getConnectionsForGithubRepoDiscussion(owner: string, repo: string): GitHubDiscussionSpace[] {
|
||||
owner = owner.toLowerCase();
|
||||
repo = repo.toLowerCase();
|
||||
return this.connections.filter((c) => (c instanceof GitHubDiscussionSpace && c.owner === owner && c.repo === repo)) as GitHubDiscussionSpace[];
|
||||
}
|
||||
|
||||
private getConnectionForGithubUser(user: string): GitHubUserSpace {
|
||||
return this.connections.find(c => c instanceof GitHubUserSpace && c.owner === user.toLowerCase()) as GitHubUserSpace;
|
||||
}
|
||||
|
||||
|
||||
private getConnectionsForGithubDiscussion(owner: string, repo: string, discussionNumber: number) {
|
||||
owner = owner.toLowerCase();
|
||||
@ -411,6 +439,47 @@ export class GithubBridge {
|
||||
})
|
||||
});
|
||||
|
||||
this.queue.on<GitHubWebhookTypes.DiscussionCreatedEvent>("github.discussion.created", async ({data}) => {
|
||||
if (!this.github) {
|
||||
return;
|
||||
}
|
||||
const spaces = this.getConnectionsForGithubRepoDiscussion(data.repository.owner.login, data.repository.name);
|
||||
if (spaces.length === 0) {
|
||||
log.info(`Not creating discussion ${data.discussion.id} ${data.repository.owner.login}/${data.repository.name}, no target spaces`);
|
||||
// We don't want to create any discussions if we have no target spaces.
|
||||
return;
|
||||
}
|
||||
let [discussionConnection] = this.getConnectionsForGithubDiscussion(data.repository.owner.login, data.repository.name, data.discussion.id);
|
||||
if (!discussionConnection) {
|
||||
try {
|
||||
// If we don't have an existing connection for this discussion (likely), then create one.
|
||||
discussionConnection = await GitHubDiscussionConnection.createDiscussionRoom(
|
||||
this.as,
|
||||
null,
|
||||
data.repository.owner.login,
|
||||
data.repository.name,
|
||||
data.discussion,
|
||||
this.tokenStore,
|
||||
this.commentProcessor,
|
||||
this.messageClient,
|
||||
);
|
||||
this.connections.push(discussionConnection);
|
||||
} catch (ex) {
|
||||
log.error(ex);
|
||||
throw Error('Failed to create discussion room');
|
||||
}
|
||||
}
|
||||
|
||||
spaces.map(async (c) => {
|
||||
try {
|
||||
await c.onDiscussionCreated(discussionConnection);
|
||||
} catch (ex) {
|
||||
log.warn(`Failed to add discussion ${c.toString()} failed to handle comment.created:`, ex);
|
||||
}
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
// Fetch all room state
|
||||
let joinedRooms: string[]|undefined;
|
||||
while(joinedRooms === undefined) {
|
||||
@ -459,6 +528,7 @@ export class GithubBridge {
|
||||
this.connections.push(...connections);
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: Refactor this to be a connection
|
||||
try {
|
||||
const accountData = await this.as.botIntent.underlyingClient.getSafeRoomAccountData<AdminAccountData>(
|
||||
@ -485,6 +555,17 @@ export class GithubBridge {
|
||||
log.error(`Failed to setup admin room ${roomId}:`, ex);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle spaces
|
||||
for (const discussion of this.connections.filter((c) => c instanceof GitHubDiscussionSpace) as GitHubDiscussionSpace[]) {
|
||||
console.log(discussion);
|
||||
const user = this.getConnectionForGithubUser(discussion.owner);
|
||||
console.log("user:", user);
|
||||
if (user) {
|
||||
await user.ensureDiscussionInSpace(discussion);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.config.widgets) {
|
||||
await this.widgetApi.start(this.config.widgets.port);
|
||||
}
|
||||
@ -635,6 +716,22 @@ export class GithubBridge {
|
||||
}
|
||||
}
|
||||
|
||||
res = GitHubDiscussionSpace.QueryRoomRegex.exec(roomAlias);
|
||||
if (res) {
|
||||
if (!this.github) {
|
||||
throw Error("GitHub is not configured on this bridge");
|
||||
}
|
||||
try {
|
||||
return await GitHubDiscussionSpace.onQueryRoom(res, {
|
||||
octokit: this.github.octokit,
|
||||
as: this.as,
|
||||
});
|
||||
} catch (ex) {
|
||||
log.error(`Could not handle alias with GitHubRepoConnection`, ex);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
res = GitHubRepoConnection.QueryRoomRegex.exec(roomAlias);
|
||||
if (res) {
|
||||
if (!this.github) {
|
||||
@ -655,6 +752,22 @@ export class GithubBridge {
|
||||
}
|
||||
|
||||
|
||||
res = GitHubUserSpace.QueryRoomRegex.exec(roomAlias);
|
||||
if (res) {
|
||||
if (!this.github) {
|
||||
throw Error("GitHub is not configured on this bridge");
|
||||
}
|
||||
try {
|
||||
return await GitHubUserSpace.onQueryRoom(res, {
|
||||
octokit: this.github.octokit,
|
||||
as: this.as,
|
||||
});
|
||||
} catch (ex) {
|
||||
log.error(`Could not handle alias with GitHubRepoConnection`, ex);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
throw Error('No regex matching query pattern');
|
||||
}
|
||||
|
||||
@ -737,12 +850,12 @@ export class GithubBridge {
|
||||
const connection = await GitHubProjectConnection.onOpenProject(project, this.as, adminRoom.userId);
|
||||
this.connections.push(connection);
|
||||
});
|
||||
adminRoom.on("open.discussion", async (owner: string, repo: string, discussions: Discussion) => {
|
||||
const connection = await GitHubDiscussionConnection.createDiscussionRoom(
|
||||
this.as, adminRoom.userId, owner, repo, discussions, this.tokenStore, this.commentProcessor, this.messageClient,
|
||||
);
|
||||
this.connections.push(connection);
|
||||
});
|
||||
// adminRoom.on("open.discussion", async (owner: string, repo: string, discussions: Discussion) => {
|
||||
// const connection = await GitHubDiscussionConnection.createDiscussionRoom(
|
||||
// this.as, adminRoom.userId, owner, repo, discussions, this.tokenStore, this.commentProcessor, this.messageClient,
|
||||
// );
|
||||
// this.connections.push(connection);
|
||||
// });
|
||||
adminRoom.on("open.gitlab-issue", async (issueInfo: GetIssueOpts, res: GetIssueResponse, instanceName: string, instance: GitLabInstance) => {
|
||||
const [ connection ] = this.getConnectionsForGitLabIssue(instance, issueInfo.projects, issueInfo.issue);
|
||||
if (connection) {
|
||||
|
@ -45,7 +45,7 @@ export default class LogWrapper {
|
||||
log.info(getMessageString(messageOrObject), { module });
|
||||
},
|
||||
warn: (module: string, ...messageOrObject: MsgType[]) => {
|
||||
const error = messageOrObject[0].error || messageOrObject[1].body?.error;
|
||||
const error = messageOrObject[0].error || messageOrObject[1]?.body?.error;
|
||||
if (error === "Room account data not found") {
|
||||
log.debug(getMessageString(messageOrObject), { module });
|
||||
return; // This is just noise :|
|
||||
|
@ -116,7 +116,8 @@ export class GitHubWatcher extends EventEmitter implements NotificationWatcherTa
|
||||
pull_number: rawEvent.subject.url_data.number,
|
||||
owner: rawEvent.repository.owner.login,
|
||||
repo: rawEvent.repository.name,
|
||||
})).data;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
})).data as any;
|
||||
rawEvent.subject.reviews = (await this.octoKit.pulls.listReviews({
|
||||
pull_number: rawEvent.subject.url_data.number,
|
||||
owner: rawEvent.repository.owner.login,
|
||||
|
@ -63,11 +63,12 @@ export class Webhooks extends EventEmitter {
|
||||
}
|
||||
|
||||
public listen() {
|
||||
const bindAddr = this.config.webhook.bindAddress || "0.0.0.0";
|
||||
this.server = this.expressApp.listen(
|
||||
this.config.webhook.port,
|
||||
this.config.webhook.bindAddress,
|
||||
bindAddr,
|
||||
);
|
||||
log.info(`Listening on http://${this.config.webhook.bindAddress}:${this.config.webhook.port}`);
|
||||
log.info(`Listening on http://${bindAddr}:${this.config.webhook.port}`);
|
||||
}
|
||||
|
||||
public stop() {
|
||||
|
196
yarn.lock
196
yarn.lock
@ -345,14 +345,14 @@
|
||||
dependencies:
|
||||
"@octokit/types" "^6.0.3"
|
||||
|
||||
"@octokit/core@^3.2.3":
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.3.1.tgz#c6bb6ba171ad84a5f430853a98892cfe8f93d8cd"
|
||||
integrity sha512-Dc5NNQOYjgZU5S1goN6A/E500yXOfDUFRGQB8/2Tl16AcfvS3H9PudyOe3ZNE/MaVyHPIfC0htReHMJb1tMrvw==
|
||||
"@octokit/core@^3.5.1":
|
||||
version "3.5.1"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.5.1.tgz#8601ceeb1ec0e1b1b8217b960a413ed8e947809b"
|
||||
integrity sha512-omncwpLVxMP+GLpLPgeGJBF6IWJFjXDS5flY5VbppePYX9XehevbDykRH9PdCdvqt9TS5AOTiDide7h0qrkHjw==
|
||||
dependencies:
|
||||
"@octokit/auth-token" "^2.4.4"
|
||||
"@octokit/graphql" "^4.5.8"
|
||||
"@octokit/request" "^5.4.12"
|
||||
"@octokit/request" "^5.6.0"
|
||||
"@octokit/request-error" "^2.0.5"
|
||||
"@octokit/types" "^6.0.3"
|
||||
before-after-hook "^2.2.0"
|
||||
@ -392,29 +392,34 @@
|
||||
"@octokit/types" "^6.12.2"
|
||||
btoa-lite "^1.0.0"
|
||||
|
||||
"@octokit/openapi-types@^10.0.0":
|
||||
version "10.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-10.0.0.tgz#db4335de99509021f501fc4e026e6ff495fe1e62"
|
||||
integrity sha512-k1iO2zKuEjjRS1EJb4FwSLk+iF6EGp+ZV0OMRViQoWhQ1fZTk9hg1xccZII5uyYoiqcbC73MRBmT45y1vp2PPg==
|
||||
|
||||
"@octokit/openapi-types@^6.0.0":
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-6.0.0.tgz#7da8d7d5a72d3282c1a3ff9f951c8133a707480d"
|
||||
integrity sha512-CnDdK7ivHkBtJYzWzZm7gEkanA7gKH6a09Eguz7flHw//GacPJLmkHA3f3N++MJmlxD1Fl+mB7B32EEpSCwztQ==
|
||||
|
||||
"@octokit/plugin-paginate-rest@^2.6.2":
|
||||
version "2.13.3"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.13.3.tgz#f0f1792230805108762d87906fb02d573b9e070a"
|
||||
integrity sha512-46lptzM9lTeSmIBt/sVP/FLSTPGx6DCzAdSX3PfeJ3mTf4h9sGC26WpaQzMEq/Z44cOcmx8VsOhO+uEgE3cjYg==
|
||||
"@octokit/plugin-paginate-rest@^2.16.0":
|
||||
version "2.16.0"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.16.0.tgz#09dbda2e5fbca022e3cdf76b63618f7b357c9f0c"
|
||||
integrity sha512-8YYzALPMvEZ35kgy5pdYvQ22Roz+BIuEaedO575GwE2vb/ACDqQn0xQrTJR4tnZCJn7pi8+AWPVjrFDaERIyXQ==
|
||||
dependencies:
|
||||
"@octokit/types" "^6.11.0"
|
||||
"@octokit/types" "^6.26.0"
|
||||
|
||||
"@octokit/plugin-request-log@^1.0.2":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.3.tgz#70a62be213e1edc04bb8897ee48c311482f9700d"
|
||||
integrity sha512-4RFU4li238jMJAzLgAwkBAw+4Loile5haQMQr+uhFq27BmyJXcXSKvoQKqh0agsZEiUlW6iSv3FAgvmGkur7OQ==
|
||||
"@octokit/plugin-request-log@^1.0.4":
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz#5e50ed7083a613816b1e4a28aeec5fb7f1462e85"
|
||||
integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==
|
||||
|
||||
"@octokit/plugin-rest-endpoint-methods@5.0.0":
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.0.0.tgz#cf2cdeb24ea829c31688216a5b165010b61f9a98"
|
||||
integrity sha512-Jc7CLNUueIshXT+HWt6T+M0sySPjF32mSFQAK7UfAg8qGeRI6OM1GSBxDLwbXjkqy2NVdnqCedJcP1nC785JYg==
|
||||
"@octokit/plugin-rest-endpoint-methods@^5.9.0":
|
||||
version "5.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.9.0.tgz#f9a7d8411e7e4e49a65fc95b5cc23cf96bf05e1f"
|
||||
integrity sha512-Rz67pg+rEJq2Qn/qfHsMiBoP7GL5NDn8Gg0ezGznZ745Ixn1gPusZYZqCXNhICYrIZaVXmusNP0iwPdphJneqQ==
|
||||
dependencies:
|
||||
"@octokit/types" "^6.13.0"
|
||||
"@octokit/types" "^6.26.0"
|
||||
deprecation "^2.3.1"
|
||||
|
||||
"@octokit/request-error@^2.0.0", "@octokit/request-error@^2.0.2", "@octokit/request-error@^2.0.5":
|
||||
@ -426,7 +431,16 @@
|
||||
deprecation "^2.0.0"
|
||||
once "^1.4.0"
|
||||
|
||||
"@octokit/request@^5.3.0", "@octokit/request@^5.4.11", "@octokit/request@^5.4.12", "@octokit/request@^5.4.14":
|
||||
"@octokit/request-error@^2.1.0":
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.1.0.tgz#9e150357831bfc788d13a4fd4b1913d60c74d677"
|
||||
integrity sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==
|
||||
dependencies:
|
||||
"@octokit/types" "^6.0.3"
|
||||
deprecation "^2.0.0"
|
||||
once "^1.4.0"
|
||||
|
||||
"@octokit/request@^5.3.0", "@octokit/request@^5.4.11", "@octokit/request@^5.4.14":
|
||||
version "5.4.15"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.4.15.tgz#829da413dc7dd3aa5e2cdbb1c7d0ebe1f146a128"
|
||||
integrity sha512-6UnZfZzLwNhdLRreOtTkT9n57ZwulCve8q3IT/Z477vThu6snfdkBuhxnChpOKNGxcQ71ow561Qoa6uqLdPtag==
|
||||
@ -438,23 +452,42 @@
|
||||
node-fetch "^2.6.1"
|
||||
universal-user-agent "^6.0.0"
|
||||
|
||||
"@octokit/rest@^18.5.2":
|
||||
version "18.5.2"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.5.2.tgz#0369e554b7076e3749005147be94c661c7a5a74b"
|
||||
integrity sha512-Kz03XYfKS0yYdi61BkL9/aJ0pP2A/WK5vF/syhu9/kY30J8He3P68hv9GRpn8bULFx2K0A9MEErn4v3QEdbZcw==
|
||||
"@octokit/request@^5.6.0":
|
||||
version "5.6.1"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.6.1.tgz#f97aff075c37ab1d427c49082fefeef0dba2d8ce"
|
||||
integrity sha512-Ls2cfs1OfXaOKzkcxnqw5MR6drMA/zWX/LIS/p8Yjdz7QKTPQLMsB3R+OvoxE6XnXeXEE2X7xe4G4l4X0gRiKQ==
|
||||
dependencies:
|
||||
"@octokit/core" "^3.2.3"
|
||||
"@octokit/plugin-paginate-rest" "^2.6.2"
|
||||
"@octokit/plugin-request-log" "^1.0.2"
|
||||
"@octokit/plugin-rest-endpoint-methods" "5.0.0"
|
||||
"@octokit/endpoint" "^6.0.1"
|
||||
"@octokit/request-error" "^2.1.0"
|
||||
"@octokit/types" "^6.16.1"
|
||||
is-plain-object "^5.0.0"
|
||||
node-fetch "^2.6.1"
|
||||
universal-user-agent "^6.0.0"
|
||||
|
||||
"@octokit/types@^6.0.3", "@octokit/types@^6.10.0", "@octokit/types@^6.11.0", "@octokit/types@^6.12.2", "@octokit/types@^6.13.0", "@octokit/types@^6.7.1":
|
||||
"@octokit/rest@^18.10.0":
|
||||
version "18.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.10.0.tgz#8a0add9611253e0e31d3ed5b4bc941a3795a7648"
|
||||
integrity sha512-esHR5OKy38bccL/sajHqZudZCvmv4yjovMJzyXlphaUo7xykmtOdILGJ3aAm0mFHmMLmPFmDMJXf39cAjNJsrw==
|
||||
dependencies:
|
||||
"@octokit/core" "^3.5.1"
|
||||
"@octokit/plugin-paginate-rest" "^2.16.0"
|
||||
"@octokit/plugin-request-log" "^1.0.4"
|
||||
"@octokit/plugin-rest-endpoint-methods" "^5.9.0"
|
||||
|
||||
"@octokit/types@^6.0.3", "@octokit/types@^6.10.0", "@octokit/types@^6.12.2", "@octokit/types@^6.7.1":
|
||||
version "6.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.13.0.tgz#779e5b7566c8dde68f2f6273861dd2f0409480d0"
|
||||
integrity sha512-W2J9qlVIU11jMwKHUp5/rbVUeErqelCsO5vW5PKNb7wAXQVUz87Rc+imjlEvpvbH8yUb+KHmv8NEjVZdsdpyxA==
|
||||
dependencies:
|
||||
"@octokit/openapi-types" "^6.0.0"
|
||||
|
||||
"@octokit/types@^6.16.1", "@octokit/types@^6.26.0":
|
||||
version "6.26.0"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.26.0.tgz#b8af298485d064ad9424cb41520541c1bf820346"
|
||||
integrity sha512-RDxZBAFMtqs1ZPnbUu1e7ohPNfoNhTiep4fErY7tZs995BeHu369Vsh5woMIaFbllRWEZBfvTCS4hvDnMPiHrA==
|
||||
dependencies:
|
||||
"@octokit/openapi-types" "^10.0.0"
|
||||
|
||||
"@octokit/webhooks-methods@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/webhooks-methods/-/webhooks-methods-1.0.0.tgz#274799138725c89a3466e3af2026afe755f28c4b"
|
||||
@ -1156,7 +1189,7 @@ chalk@2.4.1:
|
||||
escape-string-regexp "^1.0.5"
|
||||
supports-color "^5.3.0"
|
||||
|
||||
chalk@^2.0.0, chalk@^2.4.2:
|
||||
chalk@^2.0.0:
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
|
||||
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
|
||||
@ -1262,6 +1295,11 @@ colorette@^1.2.1:
|
||||
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94"
|
||||
integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==
|
||||
|
||||
colorette@^1.2.2:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.3.0.tgz#ff45d2f0edb244069d3b772adeb04fed38d0a0af"
|
||||
integrity sha512-ecORCqbSFP7Wm8Y6lyqMJjexBQqXSF7SSeaTyGGphogUjBlFP9m9o08wy86HL2uB7fMTxtOUzLMk7ogKcxMg1w==
|
||||
|
||||
colors@^1.2.1:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78"
|
||||
@ -1537,6 +1575,11 @@ domelementtype@^2.0.1, domelementtype@^2.1.0:
|
||||
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.1.0.tgz#a851c080a6d1c3d94344aed151d99f669edf585e"
|
||||
integrity sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w==
|
||||
|
||||
domelementtype@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57"
|
||||
integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==
|
||||
|
||||
domhandler@^3.0.0:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-3.3.0.tgz#6db7ea46e4617eb15cf875df68b2b8524ce0037a"
|
||||
@ -1551,6 +1594,13 @@ domhandler@^4.0.0:
|
||||
dependencies:
|
||||
domelementtype "^2.1.0"
|
||||
|
||||
domhandler@^4.2.0:
|
||||
version "4.2.2"
|
||||
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.2.tgz#e825d721d19a86b8c201a35264e226c678ee755f"
|
||||
integrity sha512-PzE9aBMsdZO8TK4BnuJwH0QT41wgMbRzuZrHUcpYncEjmQazq8QEaBWgLG7ZyC/DAZKEgglpIA6j4Qn/HmxS3w==
|
||||
dependencies:
|
||||
domelementtype "^2.2.0"
|
||||
|
||||
domutils@^2.0.0:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.5.0.tgz#42f49cffdabb92ad243278b331fd761c1c2d3039"
|
||||
@ -1560,6 +1610,15 @@ domutils@^2.0.0:
|
||||
domelementtype "^2.0.1"
|
||||
domhandler "^4.0.0"
|
||||
|
||||
domutils@^2.5.2:
|
||||
version "2.8.0"
|
||||
resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135"
|
||||
integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==
|
||||
dependencies:
|
||||
dom-serializer "^1.0.1"
|
||||
domelementtype "^2.2.0"
|
||||
domhandler "^4.2.0"
|
||||
|
||||
draht@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/draht/-/draht-1.0.1.tgz#c4c8879923d2130dfa9c5930e956c85d62b1e975"
|
||||
@ -1670,7 +1729,7 @@ escape-html@~1.0.3:
|
||||
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
|
||||
integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=
|
||||
|
||||
escape-string-regexp@4.0.0:
|
||||
escape-string-regexp@4.0.0, escape-string-regexp@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
|
||||
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
|
||||
@ -2313,6 +2372,16 @@ htmlparser2@^4.1.0:
|
||||
domutils "^2.0.0"
|
||||
entities "^2.0.0"
|
||||
|
||||
htmlparser2@^6.0.0:
|
||||
version "6.1.0"
|
||||
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7"
|
||||
integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==
|
||||
dependencies:
|
||||
domelementtype "^2.0.1"
|
||||
domhandler "^4.0.0"
|
||||
domutils "^2.5.2"
|
||||
entities "^2.0.0"
|
||||
|
||||
http-errors@1.6.3, http-errors@~1.6.2, http-errors@~1.6.3:
|
||||
version "1.6.3"
|
||||
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d"
|
||||
@ -2733,6 +2802,11 @@ jws@^3.2.1, jws@^3.2.2:
|
||||
jwa "^1.4.1"
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
klona@^2.0.3:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.4.tgz#7bb1e3affb0cb8624547ef7e8f6708ea2e39dfc0"
|
||||
integrity sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA==
|
||||
|
||||
kuler@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3"
|
||||
@ -2892,10 +2966,10 @@ markdown-it@^12.0.4:
|
||||
mdurl "^1.0.1"
|
||||
uc.micro "^1.0.5"
|
||||
|
||||
matrix-bot-sdk@^0.5.17:
|
||||
version "0.5.17"
|
||||
resolved "https://registry.yarnpkg.com/matrix-bot-sdk/-/matrix-bot-sdk-0.5.17.tgz#ce5d1eb9e8fef5c46f0e553493798bf0cd98ea00"
|
||||
integrity sha512-6Ze0D9OmE/ssOVSn7yIbApjZIKaCrRT2H0GVxOVVSUbRnNHSR0pgfCofO18SSI9zWhzpugiKyocZxNesrppa4A==
|
||||
matrix-bot-sdk@^0.5.19:
|
||||
version "0.5.19"
|
||||
resolved "https://registry.yarnpkg.com/matrix-bot-sdk/-/matrix-bot-sdk-0.5.19.tgz#6ce13359ab53ea0af9dc3ebcbe288c5f6d9c02c6"
|
||||
integrity sha512-RIPyvQPkOVp2yTKeDgp5rcn6z/DiKdHb6E8c69K+utai8ypRGtfDRj0PGqP+1XzqC9Wb1OFrESCUB5t0ffdC9g==
|
||||
dependencies:
|
||||
"@types/express" "^4.17.7"
|
||||
chalk "^4.1.0"
|
||||
@ -2910,7 +2984,7 @@ matrix-bot-sdk@^0.5.17:
|
||||
morgan "^1.10.0"
|
||||
request "^2.88.2"
|
||||
request-promise "^4.2.6"
|
||||
sanitize-html "^1.27.2"
|
||||
sanitize-html "^2.3.2"
|
||||
|
||||
matrix-widget-api@^0.1.0-beta.13:
|
||||
version "0.1.0-beta.13"
|
||||
@ -3100,6 +3174,11 @@ nanoid@3.1.20:
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788"
|
||||
integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==
|
||||
|
||||
nanoid@^3.1.23:
|
||||
version "3.1.25"
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.25.tgz#09ca32747c0e543f0e1814b7d3793477f9c8e152"
|
||||
integrity sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==
|
||||
|
||||
natural-compare@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
|
||||
@ -3349,14 +3428,14 @@ pify@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
|
||||
integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=
|
||||
|
||||
postcss@^7.0.27:
|
||||
version "7.0.35"
|
||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.35.tgz#d2be00b998f7f211d8a276974079f2e92b970e24"
|
||||
integrity sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==
|
||||
postcss@^8.0.2:
|
||||
version "8.3.6"
|
||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.6.tgz#2730dd76a97969f37f53b9a6096197be311cc4ea"
|
||||
integrity sha512-wG1cc/JhRgdqB6WHEuyLTedf3KIRuD0hG6ldkFEZNCjRxiC+3i6kkWUUbiJQayP28iwG35cEmAbe98585BYV0A==
|
||||
dependencies:
|
||||
chalk "^2.4.2"
|
||||
source-map "^0.6.1"
|
||||
supports-color "^6.1.0"
|
||||
colorette "^1.2.2"
|
||||
nanoid "^3.1.23"
|
||||
source-map-js "^0.6.2"
|
||||
|
||||
preact@^10.5.13:
|
||||
version "10.5.13"
|
||||
@ -3643,15 +3722,18 @@ safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2, safe-buffer@~5.2.0:
|
||||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||
|
||||
sanitize-html@^1.27.2:
|
||||
version "1.27.5"
|
||||
resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-1.27.5.tgz#6c8149462adb23e360e1bb71cc0bae7f08c823c7"
|
||||
integrity sha512-M4M5iXDAUEcZKLXkmk90zSYWEtk5NH3JmojQxKxV371fnMh+x9t1rqdmXaGoyEHw3z/X/8vnFhKjGL5xFGOJ3A==
|
||||
sanitize-html@^2.3.2:
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-2.4.0.tgz#8da7524332eb210d968971621b068b53f17ab5a3"
|
||||
integrity sha512-Y1OgkUiTPMqwZNRLPERSEi39iOebn2XJLbeiGOBhaJD/yLqtLGu6GE5w7evx177LeGgSE+4p4e107LMiydOf6A==
|
||||
dependencies:
|
||||
htmlparser2 "^4.1.0"
|
||||
lodash "^4.17.15"
|
||||
deepmerge "^4.2.2"
|
||||
escape-string-regexp "^4.0.0"
|
||||
htmlparser2 "^6.0.0"
|
||||
is-plain-object "^5.0.0"
|
||||
klona "^2.0.3"
|
||||
parse-srcset "^1.0.2"
|
||||
postcss "^7.0.27"
|
||||
postcss "^8.0.2"
|
||||
|
||||
semver@^5.6.0:
|
||||
version "5.7.1"
|
||||
@ -3804,6 +3886,11 @@ snowpack@^3.2.2:
|
||||
optionalDependencies:
|
||||
fsevents "^2.2.0"
|
||||
|
||||
source-map-js@^0.6.2:
|
||||
version "0.6.2"
|
||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e"
|
||||
integrity sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==
|
||||
|
||||
source-map-support@^0.5.17, source-map-support@^0.5.19:
|
||||
version "0.5.19"
|
||||
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
|
||||
@ -3817,7 +3904,7 @@ source-map@^0.5.0:
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
|
||||
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
|
||||
|
||||
source-map@^0.6.0, source-map@^0.6.1:
|
||||
source-map@^0.6.0:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
||||
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
|
||||
@ -3992,13 +4079,6 @@ supports-color@^5.3.0:
|
||||
dependencies:
|
||||
has-flag "^3.0.0"
|
||||
|
||||
supports-color@^6.1.0:
|
||||
version "6.1.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3"
|
||||
integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==
|
||||
dependencies:
|
||||
has-flag "^3.0.0"
|
||||
|
||||
supports-color@^7.1.0:
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
|
||||
|
Loading…
x
Reference in New Issue
Block a user