mirror of
https://github.com/matrix-org/matrix-hookshot.git
synced 2025-03-10 21:19:13 +00:00
Add support for listening to github notifications
This commit is contained in:
parent
8de70a0035
commit
a8500c107f
@ -16,6 +16,8 @@
|
||||
"lint": "tslint -p tsconfig.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@octokit/auth-app": "^2.4.2",
|
||||
"@octokit/auth-token": "^2.4.0",
|
||||
"@octokit/rest": "^16.43.1",
|
||||
"axios": "^0.19.2",
|
||||
"express": "^4.17.1",
|
||||
|
@ -7,39 +7,74 @@ import uuid from "uuid/v4";
|
||||
import qs from "querystring";
|
||||
|
||||
export const BRIDGE_ROOM_TYPE = "uk.half-shot.matrix-github.room";
|
||||
export const BRIDGE_NOTIF_TYPE = "uk.half-shot.matrix-github.notif_state";
|
||||
|
||||
export interface AdminAccountData {
|
||||
admin_user: string;
|
||||
notifications?: {
|
||||
enabled: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
export class AdminRoom {
|
||||
|
||||
private pendingOAuthState: string|null = null;
|
||||
|
||||
constructor(private roomId: string,
|
||||
public readonly userId: string,
|
||||
public readonly data: AdminAccountData,
|
||||
private botIntent: Intent,
|
||||
private tokenStore: UserTokenStore,
|
||||
private config: BridgeConfig) {
|
||||
|
||||
}
|
||||
|
||||
public get userId() {
|
||||
return this.data.admin_user;
|
||||
}
|
||||
|
||||
public get oauthState() {
|
||||
return this.pendingOAuthState;
|
||||
}
|
||||
|
||||
public get notificationsEnabled() {
|
||||
return this.data.notifications?.enabled;
|
||||
}
|
||||
|
||||
public clearOauthState() {
|
||||
this.pendingOAuthState = null;
|
||||
}
|
||||
|
||||
public async getNotifSince() {
|
||||
try {
|
||||
const { since } = await this.botIntent.underlyingClient.getRoomAccountData(BRIDGE_NOTIF_TYPE, this.roomId);
|
||||
console.log(`getNotifSince for ${this.roomId} = ${since}`);
|
||||
return since;
|
||||
} catch {
|
||||
// TODO: We should look at this error.
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
public async setNotifSince(since: number) {
|
||||
console.log(`setNotifSince for ${this.roomId} = ${since}`);
|
||||
return this.botIntent.underlyingClient.setRoomAccountData(BRIDGE_NOTIF_TYPE, this.roomId, {
|
||||
since,
|
||||
});
|
||||
}
|
||||
|
||||
public async handleCommand(command: string) {
|
||||
const cmdLower = command.toLowerCase();
|
||||
if (cmdLower.startsWith("!setpersonaltoken ")) {
|
||||
const accessToken = command.substr("!setPersonalToken ".length);
|
||||
await this.setPersonalAccessToken(accessToken);
|
||||
return;
|
||||
} else if (cmdLower.startsWith("!hastoken")) {
|
||||
await this.hasPersonalToken();
|
||||
return;
|
||||
} else if (cmdLower.startsWith("!startoauth")) {
|
||||
await this.beginOAuth();
|
||||
return;
|
||||
if (cmdLower.startsWith("setpersonaltoken ")) {
|
||||
const accessToken = command.substr("setPersonalToken ".length);
|
||||
return this.setPersonalAccessToken(accessToken);
|
||||
} else if (cmdLower.startsWith("hastoken")) {
|
||||
return this.hasPersonalToken();
|
||||
} else if (cmdLower.startsWith("startoauth")) {
|
||||
return this.beginOAuth();
|
||||
} else if (cmdLower.startsWith("notifications enable")) {
|
||||
// TODO: Check if we can do this.
|
||||
return this.setNotificationsState(true);
|
||||
} else if (cmdLower.startsWith("notifications disable")) {
|
||||
return this.setNotificationsState(false);
|
||||
}
|
||||
await this.sendNotice("Command not understood");
|
||||
}
|
||||
@ -82,9 +117,17 @@ export class AdminRoom {
|
||||
await this.sendNotice(`You should follow ${url} to link your account to the bridge`);
|
||||
}
|
||||
|
||||
private async setNotificationsState(enabled: boolean) {
|
||||
const data: AdminAccountData = await this.botIntent.underlyingClient.getRoomAccountData(BRIDGE_ROOM_TYPE, this.roomId);
|
||||
if (data.notifications?.enabled === enabled) {
|
||||
return this.sendNotice(`Notifications are already ${enabled ? "en" : "dis"}abled`);
|
||||
}
|
||||
data.notifications = { enabled };
|
||||
await this.botIntent.underlyingClient.setRoomAccountData(BRIDGE_ROOM_TYPE, this.roomId, data);
|
||||
return this.sendNotice(`${enabled ? "En" : "Dis"}abled GitHub notifcations`);
|
||||
}
|
||||
|
||||
private async sendNotice(noticeText: string) {
|
||||
return this.botIntent.sendText(this.roomId, noticeText, "m.notice");
|
||||
}
|
||||
// Initiate oauth
|
||||
// Relinquish oauth
|
||||
}
|
||||
|
@ -1,4 +1,8 @@
|
||||
import { Octokit } from '@octokit/rest';
|
||||
import { UserNotification } from './UserNotificationWatcher';
|
||||
import markdown from "markdown-it";
|
||||
|
||||
const md = new markdown();
|
||||
|
||||
export class FormatUtil {
|
||||
public static formatName(issue: Octokit.IssuesGetResponse) {
|
||||
@ -9,4 +13,30 @@ export class FormatUtil {
|
||||
public static formatTopic(issue: Octokit.IssuesGetResponse) {
|
||||
return `${issue.title} | Status: ${issue.state} | ${issue.html_url}`;
|
||||
}
|
||||
|
||||
public static formatNotification(notif: UserNotification): {plain: string, html: string} {
|
||||
let plain = `${this.getEmojiForNotifType(notif)} [${notif.subject.title}](${notif.subject.url_data.html_url})`;
|
||||
if (notif.repository) {
|
||||
plain += ` for **[${notif.repository.full_name}](${notif.repository.html_url})**`;
|
||||
}
|
||||
const commentData = notif.subject.latest_comment_url_data;
|
||||
if (commentData && commentData.body) {
|
||||
plain += `\n\n**@${commentData.user.login}**: ${commentData.body}`;
|
||||
}
|
||||
return {
|
||||
plain,
|
||||
html: md.render(plain),
|
||||
}
|
||||
}
|
||||
|
||||
private static getEmojiForNotifType(notif: UserNotification): string {
|
||||
switch(notif.subject.type) {
|
||||
case "Issue":
|
||||
return "📝";
|
||||
case "PullRequest":
|
||||
return "✋"; // What should we do about this?
|
||||
default:
|
||||
return "🔔";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import { createAppAuth } from "@octokit/auth-app";
|
||||
import markdown from "markdown-it";
|
||||
import { IBridgeRoomState, BRIDGE_STATE_TYPE } from "./BridgeState";
|
||||
import { BridgeConfig } from "./Config";
|
||||
import { IWebhookEvent, IOAuthRequest, IOAuthTokens } from "./GithubWebhooks";
|
||||
import { IWebhookEvent, IOAuthRequest, IOAuthTokens, NotificationsEnableEvent } from "./GithubWebhooks";
|
||||
import { CommentProcessor } from "./CommentProcessor";
|
||||
import { MessageQueue, createMessageQueue } from "./MessageQueue/MessageQueue";
|
||||
import { AdminRoom, BRIDGE_ROOM_TYPE } from "./AdminRoom";
|
||||
@ -15,6 +15,7 @@ import { MatrixEvent, MatrixMemberContent, MatrixMessageContent, MatrixEventCont
|
||||
import { LogWrapper } from "./LogWrapper";
|
||||
import { IMatrixSendMessage, IMatrixSendMessageResponse } from "./MatrixSender";
|
||||
import { promises as fs } from "fs";
|
||||
import { UserNotificationsEvent, UserNotification } from "./UserNotificationWatcher";
|
||||
|
||||
const md = new markdown();
|
||||
const log = new LogWrapper("GithubBridge");
|
||||
@ -80,6 +81,7 @@ export class GithubBridge {
|
||||
this.queue.subscribe("comment.*");
|
||||
this.queue.subscribe("issue.*");
|
||||
this.queue.subscribe("response.matrix.message");
|
||||
this.queue.subscribe("notifications.user.events");
|
||||
|
||||
this.queue.on<IWebhookEvent>("comment.created", async (msg) => {
|
||||
return this.onCommentCreated(msg.data);
|
||||
@ -97,6 +99,26 @@ export class GithubBridge {
|
||||
return this.onIssueStateChange(msg.data);
|
||||
});
|
||||
|
||||
this.queue.on<UserNotificationsEvent>("notifications.user.events", async (msg) => {
|
||||
const adminRoom = this.adminRooms.get(msg.data.roomId);
|
||||
if (!adminRoom) {
|
||||
log.warn("No admin room for this notif stream!");
|
||||
return;
|
||||
}
|
||||
for (const event of msg.data.events) {
|
||||
try {
|
||||
await this.handleUserNotification(msg.data.roomId, event);
|
||||
} catch (ex) {
|
||||
log.warn("Failed to handle event:", ex);
|
||||
}
|
||||
}
|
||||
try {
|
||||
await adminRoom.setNotifSince(msg.data.lastReadTs);
|
||||
} catch (ex) {
|
||||
log.error("Failed to update stream position for notifications:", ex);
|
||||
}
|
||||
});
|
||||
|
||||
this.queue.on<IOAuthRequest>("oauth.response", async (msg) => {
|
||||
const adminRoom = [...this.adminRooms.values()].find((r) => r.oauthState === msg.data.state);
|
||||
this.queue.push<boolean>({
|
||||
@ -126,9 +148,27 @@ export class GithubBridge {
|
||||
BRIDGE_ROOM_TYPE, roomId,
|
||||
);
|
||||
if (accountData.type === "admin") {
|
||||
this.adminRooms.set(roomId, new AdminRoom(
|
||||
roomId, accountData.admin_user, this.as.botIntent, this.tokenStore, this.config,
|
||||
));
|
||||
const adminRoom = new AdminRoom(
|
||||
roomId, accountData, this.as.botIntent, this.tokenStore, this.config,
|
||||
);
|
||||
this.adminRooms.set(roomId, adminRoom);
|
||||
log.info(`${roomId} is an admin room for ${adminRoom.userId}`);
|
||||
if (adminRoom.notificationsEnabled) {
|
||||
log.info(`Notifications enabled for ${adminRoom.userId}`);
|
||||
const token = await this.tokenStore.getUserToken(adminRoom.userId);
|
||||
console.log(token);
|
||||
if (token) {
|
||||
log.info(`Notifications enabled for ${adminRoom.userId} and token was found`);
|
||||
this.queue.push<NotificationsEnableEvent>({ eventName: "notifications.user.enable", sender: "GithubBridge", data: {
|
||||
user_id: adminRoom.userId,
|
||||
room_id: roomId,
|
||||
token,
|
||||
since: await adminRoom.getNotifSince(),
|
||||
}});
|
||||
} else {
|
||||
log.warn(`Notifications enabled for ${adminRoom.userId} but no token stored!`);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
} catch (ex) { /* this is an old style room */ }
|
||||
@ -189,12 +229,13 @@ export class GithubBridge {
|
||||
await this.as.botIntent.underlyingClient.leaveRoom(roomId);
|
||||
return;
|
||||
}
|
||||
const data = {admin_user: event.sender, type: "admin"};
|
||||
await this.as.botIntent.underlyingClient.setRoomAccountData(
|
||||
BRIDGE_ROOM_TYPE, roomId, {admin_user: event.sender, type: "admin"},
|
||||
BRIDGE_ROOM_TYPE, roomId, data,
|
||||
);
|
||||
this.adminRooms.set(
|
||||
roomId,
|
||||
new AdminRoom(roomId, event.sender, this.as.botIntent, this.tokenStore, this.config),
|
||||
new AdminRoom(roomId, data, this.as.botIntent, this.tokenStore, this.config),
|
||||
);
|
||||
}
|
||||
|
||||
@ -488,6 +529,17 @@ export class GithubBridge {
|
||||
this.matrixHandledEvents.add(key);
|
||||
}
|
||||
|
||||
private async handleUserNotification(roomId: string, notif: UserNotification) {
|
||||
log.info("New notification event:", notif.subject);
|
||||
const formatted = FormatUtil.formatNotification(notif);
|
||||
this.sendMatrixMessage(roomId, {
|
||||
msgtype: "m.text",
|
||||
body: formatted.plain,
|
||||
formatted_body: formatted.html,
|
||||
format: "org.matrix.custom.html",
|
||||
});
|
||||
}
|
||||
|
||||
private async sendMatrixText(roomId: string, text: string, msgtype: string = "m.text",
|
||||
sender: string|null = null): Promise<string> {
|
||||
return this.sendMatrixMessage(roomId, {
|
||||
|
@ -3,11 +3,12 @@ import { Application, default as express, Request, Response } from "express";
|
||||
import { createHmac } from "crypto";
|
||||
import { Octokit } from "@octokit/rest";
|
||||
import { EventEmitter } from "events";
|
||||
import { MessageQueue, createMessageQueue } from "./MessageQueue/MessageQueue";
|
||||
import { MessageQueue, createMessageQueue, MessageQueueMessage } from "./MessageQueue/MessageQueue";
|
||||
import { LogWrapper } from "./LogWrapper";
|
||||
import qs from "querystring";
|
||||
import { Server } from "http";
|
||||
import axios from "axios";
|
||||
import { UserNotificationWatcher } from "./UserNotificationWatcher";
|
||||
|
||||
const log = new LogWrapper("GithubWebhooks");
|
||||
|
||||
@ -35,9 +36,21 @@ export interface IOAuthTokens {
|
||||
state: string;
|
||||
}
|
||||
|
||||
export interface NotificationsEnableEvent {
|
||||
user_id: string;
|
||||
room_id: string;
|
||||
since: number;
|
||||
token: string;
|
||||
}
|
||||
|
||||
export interface NotificationsDisableEvent {
|
||||
user_id: string;
|
||||
}
|
||||
|
||||
export class GithubWebhooks extends EventEmitter {
|
||||
private expressApp: Application;
|
||||
private queue: MessageQueue;
|
||||
private userNotificationWatcher: UserNotificationWatcher;
|
||||
private server?: Server;
|
||||
constructor(private config: BridgeConfig) {
|
||||
super();
|
||||
@ -48,6 +61,14 @@ export class GithubWebhooks extends EventEmitter {
|
||||
this.expressApp.post("/", this.onPayload.bind(this));
|
||||
this.expressApp.get("/oauth", this.onGetOauth.bind(this));
|
||||
this.queue = createMessageQueue(config);
|
||||
this.userNotificationWatcher = new UserNotificationWatcher(this.queue);
|
||||
this.queue.subscribe("notifications.user.*");
|
||||
this.queue.on("notifications.user.enable", (msg: MessageQueueMessage<NotificationsEnableEvent>) => {
|
||||
this.userNotificationWatcher.addUser(msg.data);
|
||||
});
|
||||
this.queue.on("notifications.user.disable", (msg: MessageQueueMessage<NotificationsDisableEvent>) => {
|
||||
this.userNotificationWatcher.removeUser(msg.data.user_id);
|
||||
});
|
||||
}
|
||||
|
||||
public listen() {
|
||||
@ -55,6 +76,7 @@ export class GithubWebhooks extends EventEmitter {
|
||||
this.config.github.webhook.port,
|
||||
this.config.github.webhook.bindAddress,
|
||||
);
|
||||
this.userNotificationWatcher.start();
|
||||
}
|
||||
|
||||
public stop() {
|
||||
|
@ -44,6 +44,10 @@ export class LogWrapper {
|
||||
log.warn(getMessageString(messageOrObject), { module });
|
||||
},
|
||||
error: (module: string, ...messageOrObject: any[]) => {
|
||||
if (messageOrObject[0]?.error === "Room account data not found") {
|
||||
log.debug(getMessageString(messageOrObject), { module });
|
||||
return; // This is just noise :|
|
||||
}
|
||||
log.error(getMessageString(messageOrObject), { module });
|
||||
},
|
||||
debug: (module: string, ...messageOrObject: any[]) => {
|
||||
|
131
src/UserNotificationWatcher.ts
Normal file
131
src/UserNotificationWatcher.ts
Normal file
@ -0,0 +1,131 @@
|
||||
import { NotificationsEnableEvent } from "./GithubWebhooks";
|
||||
import { Octokit } from "@octokit/rest";
|
||||
import { createTokenAuth } from "@octokit/auth-token";
|
||||
import { LogWrapper } from "./LogWrapper";
|
||||
import { MessageQueue } from "./MessageQueue/MessageQueue";
|
||||
|
||||
interface UserStream {
|
||||
octoKit: Octokit,
|
||||
userId: string,
|
||||
roomId: string,
|
||||
lastReadTs: number,
|
||||
}
|
||||
|
||||
export interface UserNotificationsEvent {
|
||||
roomId: string,
|
||||
lastReadTs: number,
|
||||
events: UserNotification[],
|
||||
}
|
||||
|
||||
export interface UserNotification {
|
||||
reason: "assign"|"author"|"comment"|"invitation"|"manual"|"mention"|"review_required"|"security_alert"|"state_change"|"subscribed"|"team_mention";
|
||||
unread: boolean;
|
||||
updated_at: number;
|
||||
last_read_at: number;
|
||||
url: string;
|
||||
subject: {
|
||||
title: string;
|
||||
url: string;
|
||||
latest_comment_url: string|null;
|
||||
type: "PullRequest"|"Issue";
|
||||
url_data: any;
|
||||
latest_comment_url_data: any;
|
||||
};
|
||||
repository: Octokit.ActivityGetThreadResponseRepository;
|
||||
}
|
||||
|
||||
|
||||
const MIN_INTERVAL_MS = 45000;
|
||||
|
||||
const log = new LogWrapper("UserNotificationWatcher");
|
||||
|
||||
export class UserNotificationWatcher {
|
||||
private userStreams: Map<string, UserStream> = new Map();
|
||||
private userQueue: string[] = [];
|
||||
private shouldListen: boolean = false;
|
||||
|
||||
constructor(private queue: MessageQueue) {
|
||||
|
||||
}
|
||||
|
||||
public start() {
|
||||
this.shouldListen = true;
|
||||
this.fetchUserNotifications().catch((ex) => {
|
||||
log.error("CRITICAL ERROR when fethcing user notifications:", ex);
|
||||
})
|
||||
}
|
||||
|
||||
public async fetchUserNotifications() {
|
||||
let userId;
|
||||
while (this.shouldListen) {
|
||||
userId = this.userQueue.pop();
|
||||
if (!userId) {
|
||||
log.info(`No users queued for notifications, waiting for 5s`);
|
||||
await new Promise((res) => setTimeout(res, 5000));
|
||||
continue;
|
||||
}
|
||||
const stream = this.userStreams.get(userId);
|
||||
if (!stream) {
|
||||
log.warn("User is in the userQueue but has no stream, dropping from queue");
|
||||
continue;
|
||||
}
|
||||
const interval = MIN_INTERVAL_MS - (Date.now() - stream.lastReadTs);
|
||||
if (interval > 0) {
|
||||
log.info(`We read this users notifications ${MIN_INTERVAL_MS - interval}ms ago, waiting ${interval}ms`);
|
||||
await new Promise((res) => setTimeout(res, interval));
|
||||
}
|
||||
log.info(`Getting notifications for ${userId} ${stream.lastReadTs}`);
|
||||
try {
|
||||
const since = stream.lastReadTs !== 0 ? `?since=${new Date(stream.lastReadTs).toISOString()}`: "";
|
||||
const response = await stream.octoKit.request(`/notifications${since}`);
|
||||
stream.lastReadTs = Date.now();
|
||||
const events: UserNotification[] = await Promise.all(response.data.map(async (event: UserNotification) => {
|
||||
if (event.subject.url) {
|
||||
const res = await stream.octoKit.request(event.subject.url);
|
||||
event.subject.url_data = res.data;
|
||||
}
|
||||
if (event.subject.latest_comment_url) {
|
||||
const res = await stream.octoKit.request(event.subject.latest_comment_url);
|
||||
event.subject.latest_comment_url_data = res.data;
|
||||
}
|
||||
return event;
|
||||
}));
|
||||
this.queue.push<UserNotificationsEvent>({
|
||||
eventName: "notifications.user.events",
|
||||
data: {
|
||||
roomId: stream.roomId,
|
||||
events,
|
||||
lastReadTs: stream.lastReadTs,
|
||||
},
|
||||
sender: "GithubWebhooks",
|
||||
});
|
||||
} catch (ex) {
|
||||
log.error("An error occured getting notifications:", ex);
|
||||
}
|
||||
this.userQueue.push(userId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
removeUser(userId: string) {
|
||||
this.userStreams.delete(userId);
|
||||
log.info(`Removed ${userId} to notif queue`);
|
||||
}
|
||||
|
||||
addUser(data: NotificationsEnableEvent) {
|
||||
const clientKit = new Octokit({
|
||||
authStrategy: createTokenAuth,
|
||||
auth: data.token,
|
||||
userAgent: "matrix-github v0.0.1",
|
||||
});
|
||||
|
||||
this.userStreams.set(data.user_id, {
|
||||
octoKit: clientKit,
|
||||
userId: data.user_id,
|
||||
roomId: data.room_id,
|
||||
lastReadTs: data.since,
|
||||
});
|
||||
this.userQueue.push(data.user_id);
|
||||
log.info(`Added ${data.user_id} to notif queue`);
|
||||
}
|
||||
}
|
@ -32,13 +32,14 @@ export class UserTokenStore {
|
||||
let obj;
|
||||
try {
|
||||
obj = await this.intent.underlyingClient.getAccountData(`${ACCOUNT_DATA_TYPE}${userId}`);
|
||||
} catch (ex) {
|
||||
return null;
|
||||
}
|
||||
const encryptedTextB64 = obj.encrypted;
|
||||
const encryptedText = Buffer.from(encryptedTextB64, "base64");
|
||||
const token = privateDecrypt(this.key, encryptedText).toString("utf-8");
|
||||
this.userTokens.set(userId, token);
|
||||
return token;
|
||||
} catch (ex) {
|
||||
log.error("Failed to get token:", ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"incremental": true,
|
||||
"target": "ESNext",
|
||||
"target": "ES2019",
|
||||
"module": "commonjs",
|
||||
"allowJs": false,
|
||||
"declaration": false,
|
||||
|
119
yarn.lock
119
yarn.lock
@ -25,6 +25,19 @@
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.2"
|
||||
|
||||
"@octokit/auth-app@^2.4.2":
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/auth-app/-/auth-app-2.4.2.tgz#d6de830f2b0401b9fece6b822361e7d9f010b030"
|
||||
integrity sha512-t/kCnyCNOySTKmdEjDbG6C/mXKwsBRgbQIxhGA8JsYHDWzvOacQEvWKxWSw/4lR707ZcgPz8BpY0DHDuT6MeVA==
|
||||
dependencies:
|
||||
"@octokit/request" "^5.3.0"
|
||||
"@octokit/request-error" "^1.1.0"
|
||||
"@octokit/types" "^2.0.0"
|
||||
"@types/lru-cache" "^5.1.0"
|
||||
lru-cache "^5.1.1"
|
||||
universal-github-app-jwt "^1.0.1"
|
||||
universal-user-agent "^4.0.0"
|
||||
|
||||
"@octokit/auth-token@^2.4.0":
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.4.0.tgz#b64178975218b99e4dfe948253f0673cbbb59d9f"
|
||||
@ -69,7 +82,16 @@
|
||||
deprecation "^2.0.0"
|
||||
once "^1.4.0"
|
||||
|
||||
"@octokit/request@^5.2.0":
|
||||
"@octokit/request-error@^1.1.0":
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-1.2.1.tgz#ede0714c773f32347576c25649dc013ae6b31801"
|
||||
integrity sha512-+6yDyk1EES6WK+l3viRDElw96MvwfJxCt45GvmjDUKWjYIb3PJZQkq3i46TwGwoPD4h8NmTrENmtyA1FwbmhRA==
|
||||
dependencies:
|
||||
"@octokit/types" "^2.0.0"
|
||||
deprecation "^2.0.0"
|
||||
once "^1.4.0"
|
||||
|
||||
"@octokit/request@^5.2.0", "@octokit/request@^5.3.0":
|
||||
version "5.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.3.1.tgz#3a1ace45e6f88b1be4749c5da963b3a3b4a2f120"
|
||||
integrity sha512-5/X0AL1ZgoU32fAepTfEoggFinO3rxsMLtzhlUX+RctLrusn/CApJuGFCd0v7GMFhF+8UiCsTTfsu7Fh1HnEJg==
|
||||
@ -175,11 +197,23 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/jsonwebtoken@^8.3.3":
|
||||
version "8.3.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.3.7.tgz#ab79ad55b9435834d24cca3112f42c08eedb1a54"
|
||||
integrity sha512-B5SSifLkjB0ns7VXpOOtOUlynE78/hKcY8G8pOAhkLJZinwofIBYqz555nRj2W9iDWZqFhK5R+7NZDaRmKWAoQ==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/linkify-it@*":
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/linkify-it/-/linkify-it-2.1.0.tgz#ea3dd64c4805597311790b61e872cbd1ed2cd806"
|
||||
integrity sha512-Q7DYAOi9O/+cLLhdaSvKdaumWyHbm7HAk/bFwwyTuU0arR5yyCeW5GOoqt4tJTpDRxhpx9Q8kQL6vMpuw9hDSw==
|
||||
|
||||
"@types/lru-cache@^5.1.0":
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/lru-cache/-/lru-cache-5.1.0.tgz#57f228f2b80c046b4a1bd5cac031f81f207f4f03"
|
||||
integrity sha512-RaE0B+14ToE4l6UqdarKPnXwVDuigfFv+5j9Dze/Nqr23yyuqdNvzcZi3xB+3Agvi5R4EOgAksfv3lXX4vBt9w==
|
||||
|
||||
"@types/markdown-it@^0.0.8":
|
||||
version "0.0.8"
|
||||
resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-0.0.8.tgz#9af8704acde87fec70475369ba0413d50717bd8d"
|
||||
@ -440,6 +474,11 @@ btoa-lite@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/btoa-lite/-/btoa-lite-1.0.0.tgz#337766da15801210fdd956c22e9c6891ab9d0337"
|
||||
integrity sha1-M3dm2hWAEhD92VbCLpxokaudAzc=
|
||||
|
||||
buffer-equal-constant-time@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
|
||||
integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=
|
||||
|
||||
buffer-from@^1.0.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
|
||||
@ -770,6 +809,13 @@ ecc-jsbn@~0.1.1:
|
||||
jsbn "~0.1.0"
|
||||
safer-buffer "^2.1.0"
|
||||
|
||||
ecdsa-sig-formatter@1.0.11:
|
||||
version "1.0.11"
|
||||
resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf"
|
||||
integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==
|
||||
dependencies:
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
ee-first@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
|
||||
@ -1359,6 +1405,22 @@ json-stringify-safe@~5.0.1:
|
||||
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
|
||||
integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
|
||||
|
||||
jsonwebtoken@^8.5.1:
|
||||
version "8.5.1"
|
||||
resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d"
|
||||
integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==
|
||||
dependencies:
|
||||
jws "^3.2.2"
|
||||
lodash.includes "^4.3.0"
|
||||
lodash.isboolean "^3.0.3"
|
||||
lodash.isinteger "^4.0.4"
|
||||
lodash.isnumber "^3.0.3"
|
||||
lodash.isplainobject "^4.0.6"
|
||||
lodash.isstring "^4.0.1"
|
||||
lodash.once "^4.0.0"
|
||||
ms "^2.1.1"
|
||||
semver "^5.6.0"
|
||||
|
||||
jsprim@^1.2.2:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
|
||||
@ -1369,6 +1431,23 @@ jsprim@^1.2.2:
|
||||
json-schema "0.2.3"
|
||||
verror "1.10.0"
|
||||
|
||||
jwa@^1.4.1:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a"
|
||||
integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==
|
||||
dependencies:
|
||||
buffer-equal-constant-time "1.0.1"
|
||||
ecdsa-sig-formatter "1.0.11"
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
jws@^3.2.2:
|
||||
version "3.2.2"
|
||||
resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304"
|
||||
integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==
|
||||
dependencies:
|
||||
jwa "^1.4.1"
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
kuler@1.0.x:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/kuler/-/kuler-1.0.1.tgz#ef7c784f36c9fb6e16dd3150d152677b2b0228a6"
|
||||
@ -1423,6 +1502,26 @@ lodash.get@^4.4.2:
|
||||
resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
|
||||
integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=
|
||||
|
||||
lodash.includes@^4.3.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f"
|
||||
integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=
|
||||
|
||||
lodash.isboolean@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6"
|
||||
integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=
|
||||
|
||||
lodash.isinteger@^4.0.4:
|
||||
version "4.0.4"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343"
|
||||
integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=
|
||||
|
||||
lodash.isnumber@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc"
|
||||
integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=
|
||||
|
||||
lodash.isplainobject@^4.0.6:
|
||||
version "4.0.6"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
|
||||
@ -1438,6 +1537,11 @@ lodash.mergewith@^4.6.1:
|
||||
resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55"
|
||||
integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==
|
||||
|
||||
lodash.once@^4.0.0:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
|
||||
integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=
|
||||
|
||||
lodash.set@^4.3.2:
|
||||
version "4.3.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23"
|
||||
@ -2099,6 +2203,11 @@ semver@^5.3.0, semver@^5.5.0, semver@^5.7.0:
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b"
|
||||
integrity sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==
|
||||
|
||||
semver@^5.6.0:
|
||||
version "5.7.1"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
|
||||
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
|
||||
|
||||
send@0.17.1:
|
||||
version "0.17.1"
|
||||
resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8"
|
||||
@ -2444,6 +2553,14 @@ uc.micro@^1.0.1, uc.micro@^1.0.5:
|
||||
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"
|
||||
integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==
|
||||
|
||||
universal-github-app-jwt@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/universal-github-app-jwt/-/universal-github-app-jwt-1.0.1.tgz#d2ecc00249e02c8a244c5b4ab285de8e6bbef1ee"
|
||||
integrity sha512-neZ16w2WQbXUTuEa3V52fb+Zp84Fth3Yj7hMn0JSky9pgjM874vooDAU58TZLg2ZjxUPig1C80W1jRTaNek4Sw==
|
||||
dependencies:
|
||||
"@types/jsonwebtoken" "^8.3.3"
|
||||
jsonwebtoken "^8.5.1"
|
||||
|
||||
universal-user-agent@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-4.0.0.tgz#27da2ec87e32769619f68a14996465ea1cb9df16"
|
||||
|
Loading…
x
Reference in New Issue
Block a user