From 1a794e2d7e678c7da210de60ca8955055e287a3f Mon Sep 17 00:00:00 2001 From: Kata Date: Fri, 29 Mar 2019 13:59:57 -0500 Subject: [PATCH] add tests; resync files, forgot where i was. --- Dockerfile | 4 +- Roleypoly.js | 21 +- babel.config.js | 5 +- flow-typed/npm/chokidar_vx.x.x.js | 52 + flow-typed/npm/eris_vx.x.x.js | 2603 ++++++++++++++--- flow-typed/npm/koa_v2.x.x.js | 38 +- package.json | 37 +- rpc/_security.js | 148 +- rpc/index.js | 32 +- rpc/rpc_test.js | 13 +- rpc/servers.js | 15 +- rpc/user.js | 15 +- services/discord.js | 7 + services/discord.old.js | 346 +++ services/discord/botFetcher.js | 17 + services/discord/restFetcher.js | 36 + services/discord/types.js | 14 + services/presentation.js | 5 +- ui/components/role/Role.test.js | 77 + .../role/__snapshots__/Role.test.js.snap | 25 + .../role/__snapshots__/demo.test.js.snap | 3 + ui/components/role/demo.test.js | 18 + ui/components/role/index.js | 2 +- .../__test__/__snapshots__/media.test.js.snap | 19 + ui/kit/__test__/media.test.js | 16 + ui/pages/_internal/_server.js | 2 +- ui/setupTests.js | 4 + ui/stores/roles.js | 3 +- util/rpcrepl.js | 81 + yarn.lock | 530 +++- 30 files changed, 3654 insertions(+), 534 deletions(-) create mode 100644 flow-typed/npm/chokidar_vx.x.x.js create mode 100644 services/discord.old.js create mode 100644 services/discord/botFetcher.js create mode 100644 services/discord/restFetcher.js create mode 100644 services/discord/types.js create mode 100644 ui/components/role/Role.test.js create mode 100644 ui/components/role/__snapshots__/Role.test.js.snap create mode 100644 ui/components/role/__snapshots__/demo.test.js.snap create mode 100644 ui/components/role/demo.test.js create mode 100644 ui/kit/__test__/__snapshots__/media.test.js.snap create mode 100644 ui/kit/__test__/media.test.js create mode 100644 ui/setupTests.js create mode 100644 util/rpcrepl.js diff --git a/Dockerfile b/Dockerfile index 1808301..d22d11c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:10 AS builder +FROM node:11 AS builder WORKDIR /src COPY . /src # we double yarn here to strip off dev-only packages that are needed at build time. @@ -6,7 +6,7 @@ RUN yarn --frozen-lockfile &&\ yarn build &&\ yarn --prod --frozen-lockfile -FROM mhart/alpine-node:10 +FROM mhart/alpine-node:11 ENV NODE_ENV production WORKDIR /dist COPY --from=builder /src /dist diff --git a/Roleypoly.js b/Roleypoly.js index 13295ff..3c8a518 100644 --- a/Roleypoly.js +++ b/Roleypoly.js @@ -2,7 +2,7 @@ import Sequelize from 'sequelize' import Next from 'next' import betterRouter from 'koa-better-router' -import EventEmitter from 'events' +import type EventEmitter from 'events' import fs from 'fs' import logger from './logger' import ServerService from './services/server' @@ -28,6 +28,9 @@ export type Router = { put: HTTPHandler, middleware: () => any } + +export type RouteHook = (router: Router) => void + export type AppContext = { config: { appUrl: string, @@ -59,6 +62,8 @@ class Roleypoly { __initialized: Promise __apiWatcher: EventEmitter __rpcWatcher: EventEmitter + + __routeHooks: Set = new Set() constructor (io: SocketIO, app: KoaApp) { this.io = io this.__app = app @@ -121,12 +126,24 @@ class Roleypoly { this.ctx.RPC = new RPCServer(this) } + addRouteHook (hook: RouteHook) { + this.__routeHooks.add(hook) + } + + hookServiceRoutes (router: Router) { + for (let h of this.__routeHooks) { + h(router) + } + } + async loadRoutes (forceClear: boolean = false) { await this.ctx.ui.prepare() this.router = betterRouter().loadMethods() fetchApis(this.router, this.ctx, { forceClear }) - this.ctx.RPC.hookRoutes(this.router) + // this.ctx.RPC.hookRoutes(this.router) + + this.hookServiceRoutes(this.router) // after routing, add the * for ui handler this.router.get('*', async ctx => { diff --git a/babel.config.js b/babel.config.js index 320b715..072859f 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,6 +1,7 @@ module.exports = { babelrcRoots: [ - '.' + '.', + './ui' ], - ignore: [ './ui', './node_modules', './flow-typed' ] + ignore: [ './node_modules', './flow-typed' ] } diff --git a/flow-typed/npm/chokidar_vx.x.x.js b/flow-typed/npm/chokidar_vx.x.x.js new file mode 100644 index 0000000..d5c2484 --- /dev/null +++ b/flow-typed/npm/chokidar_vx.x.x.js @@ -0,0 +1,52 @@ +// flow-typed signature: d2dd6b4e6831834e8554fcd055fcf1ab +// flow-typed version: <>/chokidar_v2.1.2/flow_v0.95.1 + +/** + * This is an autogenerated libdef stub for: + * + * 'chokidar' + * + * Fill this stub out by replacing all the `any` types. + * + * Once filled out, we encourage you to share your work with the + * community by sending a pull request to: + * https://github.com/flowtype/flow-typed + */ + +declare module 'chokidar' { + import type { FSWatcher } from 'fs' + import type EventEmitter from 'events' + declare type BufferEncoding = "ascii" | "utf8" | "utf16le" | "ucs2" | "base64" | "latin1" | "binary" | "hex" + declare type Chokidar = { + watch: (filename: string, options?: { encoding?: BufferEncoding; persistent?: boolean; recursive?: boolean; } | BufferEncoding, listener?: (event: string, filename: string) => void) => EventEmitter + }; + + declare module.exports: Chokidar; +} + +/** + * We include stubs for each file inside this npm package in case you need to + * require those files directly. Feel free to delete any files that aren't + * needed. + */ +declare module 'chokidar/lib/fsevents-handler' { + declare module.exports: any; +} + +declare module 'chokidar/lib/nodefs-handler' { + declare module.exports: any; +} + +// Filename aliases +declare module 'chokidar/index' { + declare module.exports: $Exports<'chokidar'>; +} +declare module 'chokidar/index.js' { + declare module.exports: $Exports<'chokidar'>; +} +declare module 'chokidar/lib/fsevents-handler.js' { + declare module.exports: $Exports<'chokidar/lib/fsevents-handler'>; +} +declare module 'chokidar/lib/nodefs-handler.js' { + declare module.exports: $Exports<'chokidar/lib/nodefs-handler'>; +} diff --git a/flow-typed/npm/eris_vx.x.x.js b/flow-typed/npm/eris_vx.x.x.js index 18853c1..8bac3a3 100644 --- a/flow-typed/npm/eris_vx.x.x.js +++ b/flow-typed/npm/eris_vx.x.x.js @@ -1,388 +1,2229 @@ -// flow-typed signature: 432d6a3fa21c7cbfcaf3bc516b214fac -// flow-typed version: <>/eris_v^0.9.0/flow_v0.95.1 - +// @flow /** - * This is an autogenerated libdef stub for: - * - * 'eris' - * - * Fill this stub out by replacing all the `any` types. - * - * Once filled out, we encourage you to share your work with the - * community by sending a pull request to: - * https://github.com/flowtype/flow-typed + * Flowtype definitions for eris + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.8.0 + * Author: [Katalina T.](http://twitter.com/kayteh) + * Repo: http://github.com/kayteh/roleypoly */ declare module 'eris' { - declare module.exports: any; -} + import type EventEmitter from 'events' -/** - * We include stubs for each file inside this npm package in case you need to - * require those files directly. Feel free to delete any files that aren't - * needed. - */ -declare module 'eris/lib/Client' { - declare module.exports: any; -} + import type { Readable as ReadableStream } from 'stream' -declare module 'eris/lib/command/Command' { - declare module.exports: any; -} - -declare module 'eris/lib/command/CommandClient' { - declare module.exports: any; -} - -declare module 'eris/lib/Constants' { - declare module.exports: any; -} - -declare module 'eris/lib/errors/DiscordHTTPError' { - declare module.exports: any; -} - -declare module 'eris/lib/errors/DiscordRESTError' { - declare module.exports: any; -} - -declare module 'eris/lib/gateway/Shard' { - declare module.exports: any; -} - -declare module 'eris/lib/gateway/ShardManager' { - declare module.exports: any; -} - -declare module 'eris/lib/rest/Endpoints' { - declare module.exports: any; -} - -declare module 'eris/lib/rest/RequestHandler' { - declare module.exports: any; -} - -declare module 'eris/lib/structures/Base' { - declare module.exports: any; -} - -declare module 'eris/lib/structures/Call' { - declare module.exports: any; -} - -declare module 'eris/lib/structures/CategoryChannel' { - declare module.exports: any; -} - -declare module 'eris/lib/structures/Channel' { - declare module.exports: any; -} - -declare module 'eris/lib/structures/ExtendedUser' { - declare module.exports: any; -} - -declare module 'eris/lib/structures/GroupChannel' { - declare module.exports: any; -} - -declare module 'eris/lib/structures/Guild' { - declare module.exports: any; -} - -declare module 'eris/lib/structures/GuildAuditLogEntry' { - declare module.exports: any; -} - -declare module 'eris/lib/structures/GuildChannel' { - declare module.exports: any; -} - -declare module 'eris/lib/structures/GuildIntegration' { - declare module.exports: any; -} - -declare module 'eris/lib/structures/Invite' { - declare module.exports: any; -} - -declare module 'eris/lib/structures/Member' { - declare module.exports: any; -} - -declare module 'eris/lib/structures/Message' { - declare module.exports: any; -} - -declare module 'eris/lib/structures/Permission' { - declare module.exports: any; -} - -declare module 'eris/lib/structures/PermissionOverwrite' { - declare module.exports: any; -} - -declare module 'eris/lib/structures/PrivateChannel' { - declare module.exports: any; -} - -declare module 'eris/lib/structures/Relationship' { - declare module.exports: any; -} - -declare module 'eris/lib/structures/Role' { - declare module.exports: any; -} - -declare module 'eris/lib/structures/TextChannel' { - declare module.exports: any; -} - -declare module 'eris/lib/structures/UnavailableGuild' { - declare module.exports: any; -} - -declare module 'eris/lib/structures/User' { - declare module.exports: any; -} - -declare module 'eris/lib/structures/VoiceChannel' { - declare module.exports: any; -} - -declare module 'eris/lib/structures/VoiceState' { - declare module.exports: any; -} - -declare module 'eris/lib/util/Bucket' { - declare module.exports: any; -} - -declare module 'eris/lib/util/Collection' { - declare module.exports: any; -} - -declare module 'eris/lib/util/MultipartData' { - declare module.exports: any; -} - -declare module 'eris/lib/util/SequentialBucket' { - declare module.exports: any; -} - -declare module 'eris/lib/voice/Piper' { - declare module.exports: any; -} - -declare module 'eris/lib/voice/SharedStream' { - declare module.exports: any; -} - -declare module 'eris/lib/voice/streams/BaseTransformer' { - declare module.exports: any; -} - -declare module 'eris/lib/voice/streams/DCAOpusTransformer' { - declare module.exports: any; -} - -declare module 'eris/lib/voice/streams/FFmpegDuplex' { - declare module.exports: any; -} - -declare module 'eris/lib/voice/streams/FFmpegOggTransformer' { - declare module.exports: any; -} - -declare module 'eris/lib/voice/streams/FFmpegPCMTransformer' { - declare module.exports: any; -} - -declare module 'eris/lib/voice/streams/OggOpusTransformer' { - declare module.exports: any; -} - -declare module 'eris/lib/voice/streams/PCMOpusTransformer' { - declare module.exports: any; -} - -declare module 'eris/lib/voice/streams/VolumeTransformer' { - declare module.exports: any; -} - -declare module 'eris/lib/voice/streams/WebmOpusTransformer' { - declare module.exports: any; -} - -declare module 'eris/lib/voice/VoiceConnection' { - declare module.exports: any; -} - -declare module 'eris/lib/voice/VoiceConnectionManager' { - declare module.exports: any; -} - -declare module 'eris/lib/voice/VoiceDataStream' { - declare module.exports: any; -} - -// Filename aliases -declare module 'eris/index' { - declare module.exports: $Exports<'eris'>; -} -declare module 'eris/index.js' { - declare module.exports: $Exports<'eris'>; -} -declare module 'eris/lib/Client.js' { - declare module.exports: $Exports<'eris/lib/Client'>; -} -declare module 'eris/lib/command/Command.js' { - declare module.exports: $Exports<'eris/lib/command/Command'>; -} -declare module 'eris/lib/command/CommandClient.js' { - declare module.exports: $Exports<'eris/lib/command/CommandClient'>; -} -declare module 'eris/lib/Constants.js' { - declare module.exports: $Exports<'eris/lib/Constants'>; -} -declare module 'eris/lib/errors/DiscordHTTPError.js' { - declare module.exports: $Exports<'eris/lib/errors/DiscordHTTPError'>; -} -declare module 'eris/lib/errors/DiscordRESTError.js' { - declare module.exports: $Exports<'eris/lib/errors/DiscordRESTError'>; -} -declare module 'eris/lib/gateway/Shard.js' { - declare module.exports: $Exports<'eris/lib/gateway/Shard'>; -} -declare module 'eris/lib/gateway/ShardManager.js' { - declare module.exports: $Exports<'eris/lib/gateway/ShardManager'>; -} -declare module 'eris/lib/rest/Endpoints.js' { - declare module.exports: $Exports<'eris/lib/rest/Endpoints'>; -} -declare module 'eris/lib/rest/RequestHandler.js' { - declare module.exports: $Exports<'eris/lib/rest/RequestHandler'>; -} -declare module 'eris/lib/structures/Base.js' { - declare module.exports: $Exports<'eris/lib/structures/Base'>; -} -declare module 'eris/lib/structures/Call.js' { - declare module.exports: $Exports<'eris/lib/structures/Call'>; -} -declare module 'eris/lib/structures/CategoryChannel.js' { - declare module.exports: $Exports<'eris/lib/structures/CategoryChannel'>; -} -declare module 'eris/lib/structures/Channel.js' { - declare module.exports: $Exports<'eris/lib/structures/Channel'>; -} -declare module 'eris/lib/structures/ExtendedUser.js' { - declare module.exports: $Exports<'eris/lib/structures/ExtendedUser'>; -} -declare module 'eris/lib/structures/GroupChannel.js' { - declare module.exports: $Exports<'eris/lib/structures/GroupChannel'>; -} -declare module 'eris/lib/structures/Guild.js' { - declare module.exports: $Exports<'eris/lib/structures/Guild'>; -} -declare module 'eris/lib/structures/GuildAuditLogEntry.js' { - declare module.exports: $Exports<'eris/lib/structures/GuildAuditLogEntry'>; -} -declare module 'eris/lib/structures/GuildChannel.js' { - declare module.exports: $Exports<'eris/lib/structures/GuildChannel'>; -} -declare module 'eris/lib/structures/GuildIntegration.js' { - declare module.exports: $Exports<'eris/lib/structures/GuildIntegration'>; -} -declare module 'eris/lib/structures/Invite.js' { - declare module.exports: $Exports<'eris/lib/structures/Invite'>; -} -declare module 'eris/lib/structures/Member.js' { - declare module.exports: $Exports<'eris/lib/structures/Member'>; -} -declare module 'eris/lib/structures/Message.js' { - declare module.exports: $Exports<'eris/lib/structures/Message'>; -} -declare module 'eris/lib/structures/Permission.js' { - declare module.exports: $Exports<'eris/lib/structures/Permission'>; -} -declare module 'eris/lib/structures/PermissionOverwrite.js' { - declare module.exports: $Exports<'eris/lib/structures/PermissionOverwrite'>; -} -declare module 'eris/lib/structures/PrivateChannel.js' { - declare module.exports: $Exports<'eris/lib/structures/PrivateChannel'>; -} -declare module 'eris/lib/structures/Relationship.js' { - declare module.exports: $Exports<'eris/lib/structures/Relationship'>; -} -declare module 'eris/lib/structures/Role.js' { - declare module.exports: $Exports<'eris/lib/structures/Role'>; -} -declare module 'eris/lib/structures/TextChannel.js' { - declare module.exports: $Exports<'eris/lib/structures/TextChannel'>; -} -declare module 'eris/lib/structures/UnavailableGuild.js' { - declare module.exports: $Exports<'eris/lib/structures/UnavailableGuild'>; -} -declare module 'eris/lib/structures/User.js' { - declare module.exports: $Exports<'eris/lib/structures/User'>; -} -declare module 'eris/lib/structures/VoiceChannel.js' { - declare module.exports: $Exports<'eris/lib/structures/VoiceChannel'>; -} -declare module 'eris/lib/structures/VoiceState.js' { - declare module.exports: $Exports<'eris/lib/structures/VoiceState'>; -} -declare module 'eris/lib/util/Bucket.js' { - declare module.exports: $Exports<'eris/lib/util/Bucket'>; -} -declare module 'eris/lib/util/Collection.js' { - declare module.exports: $Exports<'eris/lib/util/Collection'>; -} -declare module 'eris/lib/util/MultipartData.js' { - declare module.exports: $Exports<'eris/lib/util/MultipartData'>; -} -declare module 'eris/lib/util/SequentialBucket.js' { - declare module.exports: $Exports<'eris/lib/util/SequentialBucket'>; -} -declare module 'eris/lib/voice/Piper.js' { - declare module.exports: $Exports<'eris/lib/voice/Piper'>; -} -declare module 'eris/lib/voice/SharedStream.js' { - declare module.exports: $Exports<'eris/lib/voice/SharedStream'>; -} -declare module 'eris/lib/voice/streams/BaseTransformer.js' { - declare module.exports: $Exports<'eris/lib/voice/streams/BaseTransformer'>; -} -declare module 'eris/lib/voice/streams/DCAOpusTransformer.js' { - declare module.exports: $Exports<'eris/lib/voice/streams/DCAOpusTransformer'>; -} -declare module 'eris/lib/voice/streams/FFmpegDuplex.js' { - declare module.exports: $Exports<'eris/lib/voice/streams/FFmpegDuplex'>; -} -declare module 'eris/lib/voice/streams/FFmpegOggTransformer.js' { - declare module.exports: $Exports<'eris/lib/voice/streams/FFmpegOggTransformer'>; -} -declare module 'eris/lib/voice/streams/FFmpegPCMTransformer.js' { - declare module.exports: $Exports<'eris/lib/voice/streams/FFmpegPCMTransformer'>; -} -declare module 'eris/lib/voice/streams/OggOpusTransformer.js' { - declare module.exports: $Exports<'eris/lib/voice/streams/OggOpusTransformer'>; -} -declare module 'eris/lib/voice/streams/PCMOpusTransformer.js' { - declare module.exports: $Exports<'eris/lib/voice/streams/PCMOpusTransformer'>; -} -declare module 'eris/lib/voice/streams/VolumeTransformer.js' { - declare module.exports: $Exports<'eris/lib/voice/streams/VolumeTransformer'>; -} -declare module 'eris/lib/voice/streams/WebmOpusTransformer.js' { - declare module.exports: $Exports<'eris/lib/voice/streams/WebmOpusTransformer'>; -} -declare module 'eris/lib/voice/VoiceConnection.js' { - declare module.exports: $Exports<'eris/lib/voice/VoiceConnection'>; -} -declare module 'eris/lib/voice/VoiceConnectionManager.js' { - declare module.exports: $Exports<'eris/lib/voice/VoiceConnectionManager'>; -} -declare module 'eris/lib/voice/VoiceDataStream.js' { - declare module.exports: $Exports<'eris/lib/voice/VoiceDataStream'>; + declare interface JSONCache { + [s: string]: any; + } + declare interface SimpleJSON { + toJSON(simple?: boolean): JSONCache; + } + declare interface NestedJSON { + toJSON(arg?: any, cache?: Array): JSONCache; + } + declare type TextableChannel = TextChannel | PrivateChannel | GroupChannel; + declare type AnyChannel = + | TextChannel + | VoiceChannel + | CategoryChannel + | PrivateChannel + | GroupChannel; + declare type AnyGuildChannel = TextChannel | VoiceChannel | CategoryChannel; + declare interface CreateInviteOptions { + maxAge?: number; + maxUses?: number; + temporary?: boolean; + } + declare interface Invitable { + getInvites(): Promise; + createInvite( + options?: CreateInviteOptions, + reason?: string + ): Promise; + } + declare interface Textable { + lastMessageID: string; + messages: Collection; + sendTyping(): Promise; + getMessage(messageID: string): Promise; + getMessages( + limit?: number, + before?: string, + after?: string, + around?: string + ): Promise; + getPins(): Promise; + createMessage( + content: MessageContent, + file?: MessageFile + ): Promise; + editMessage(messageID: string, content: MessageContent): Promise; + pinMessage(messageID: string): Promise; + unpinMessage(messageID: string): Promise; + getMessageReaction( + messageID: string, + reaction: string, + limit?: number, + before?: string, + after?: string + ): Promise; + addMessageReaction( + messageID: string, + reaction: string, + userID?: string + ): Promise; + removeMessageReaction( + messageID: string, + reaction: string, + userID?: string + ): Promise; + removeMessageReactions(messageID: string): Promise; + deleteMessage(messageID: string, reason?: string): Promise; + unsendMessage(messageID: string): Promise; + } + declare interface OldCall { + participants: string[]; + endedTimestamp?: number; + ringing: string[]; + region: string; + unavailable: boolean; + } + declare interface OldChannel { + name: string; + position: string; + topic?: string; + bitrate?: number; + permissionOverwrites: Collection; + } + declare type FriendSuggestionReasons = Array<{ + type: number, + platform_type: string, + name: string + }>; + declare interface MemberPartial { + id: string; + user: User; + } + declare interface OldPresence { + status: string; + game?: { + name: string, + type: number, + url?: string + }; + } + declare interface OldVoiceState { + mute: boolean; + deaf: boolean; + selfMute: boolean; + selfDeaf: boolean; + } + declare interface Emittable { + on(event: string, listener: Function): *; + on(event: "ready" | "disconnect", listener: () => void): *; + on( + event: "callCreate" | "callRing" | "callDelete", + listener: (call: Call) => void + ): *; + on( + event: "callUpdate", + listener: (call: Call, oldCall: OldCall) => void + ): *; + on( + event: "channelCreate" | "channelDelete", + listener: (channel: AnyChannel) => void + ): *; + on( + event: "channelPinUpdate", + listener: ( + channel: TextableChannel, + timestamp: number, + oldTimestamp: number + ) => void + ): *; + on( + event: "channelRecipientAdd" | "channelRecipientRemove", + listener: (channel: GroupChannel, user: User) => void + ): *; + on( + event: "channelUpdate", + listener: (channel: AnyChannel, oldChannel: OldChannel) => void + ): *; + on( + event: "friendSuggestionCreate", + listener: (user: User, reasons: FriendSuggestionReasons) => void + ): *; + on(event: "friendSuggestionDelete", listener: (user: User) => void): *; + on( + event: "guildAvailable" | "guildBanAdd" | "guildBanRemove", + listener: (guild: Guild, user: User) => void + ): *; + on( + event: "guildDelete" | "guildUnavailable" | "guildCreate", + listener: (guild: Guild) => void + ): *; + on( + event: "guildEmojisUpdate", + listener: (guild: Guild, emojis: Emoji[], oldEmojis: Emoji[]) => void + ): *; + on( + event: "guildMemberAdd", + listener: (guild: Guild, member: Member) => void + ): *; + on( + event: "guildMemberChunk", + listener: (guild: Guild, members: Member[]) => void + ): *; + on( + event: "guildMemberRemove", + listener: (guild: Guild, member: Member | MemberPartial) => void + ): *; + on( + event: "guildMemberUpdate", + listener: ( + guild: Guild, + member: Member, + oldMember: { + roles: string[], + nick?: string + } + ) => void + ): *; + on( + event: "guildRoleCreate" | "guildRoleDelete", + listener: (guild: Guild, role: Role) => void + ): *; + on( + event: "guildRoleUpdate", + listener: (guild: Guild, role: Role, oldRole: RoleOptions) => void + ): *; + on( + event: "guildUpdate", + listener: (guild: Guild, oldGuild: GuildOptions) => void + ): *; + on(event: "hello", listener: (trace: string[], id: number) => void): *; + on(event: "messageCreate", listener: (message: Message) => void): *; + on( + event: "messageDelete" | "messageReactionRemoveAll", + listener: (message: PossiblyUncachedMessage) => void + ): *; + on( + event: "messageDeleteBulk", + listener: (messages: PossiblyUncachedMessage[]) => void + ): *; + on( + event: "messageReactionAdd" | "messageReactionRemove", + listener: ( + message: PossiblyUncachedMessage, + emoji: Emoji, + userID: string + ) => void + ): *; + on( + event: "messageUpdate", + listener: ( + message: Message, + oldMessage?: { + attachments: Attachment[], + embeds: Embed[], + content: string, + editedTimestamp?: number, + mentionedBy?: any, + tts: boolean, + mentions: string[], + roleMentions: string[], + channelMentions: string[] + } + ) => void + ): *; + on( + event: "presenceUpdate", + listener: ( + other: Member | Relationship, + oldPresence?: OldPresence + ) => void + ): *; + on( + event: "rawWS" | "unknown", + listener: (packet: RawPacket, id: number) => void + ): *; + on( + event: "relationshipAdd" | "relationshipRemove", + listener: (relationship: Relationship) => void + ): *; + on( + event: "relationshipUpdate", + listener: ( + relationship: Relationship, + oldRelationship: { + type: number + } + ) => void + ): *; + on( + event: "shardPreReady" | "connect", + listener: (id: number) => void + ): *; + on( + event: "typingStart", + listener: (channel: TextableChannel, user: User) => void + ): *; + on( + event: "unavailableGuildCreate", + listener: (guild: UnavailableGuild) => void + ): *; + on( + event: "userUpdate", + listener: ( + user: User, + oldUser: { + username: string, + discriminator: string, + avatar?: string + } + ) => void + ): *; + on( + event: "voiceChannelJoin", + listener: (member: Member, newChannel: VoiceChannel) => void + ): *; + on( + event: "voiceChannelLeave", + listener: (member: Member, oldChannel: VoiceChannel) => void + ): *; + on( + event: "voiceChannelSwitch", + listener: ( + member: Member, + newChannel: VoiceChannel, + oldChannel: VoiceChannel + ) => void + ): *; + on( + event: "voiceStateUpdate", + listener: (member: Member, oldState: OldVoiceState) => void + ): *; + on( + event: "warn" | "debug", + listener: (message: string, id: number) => void + ): *; + } + declare interface IConstants { + DefaultAvatarHashes: string[]; + ImageFormats: string[]; + GatewayOPCodes: { + [key: string]: number + }; + GATEWAY_VERSION: number; + Permissions: { + [key: string]: number + }; + VoiceOPCodes: { + [key: string]: number + }; + SystemJoinMessages: string[]; + AuditLogActions: { + [key: string]: number + }; + } + declare export var Constants: IConstants; + declare interface WebhookPayload { + content?: string; + file?: + | { + file: Buffer, + name: string + } + | Array<{ + file: Buffer, + name: string + }>; + embeds?: EmbedOptions[]; + username?: string; + avatarURL?: string; + tts?: boolean; + wait?: boolean; + disableEveryone?: boolean; + } + declare interface EmbedBase { + title?: string; + description?: string; + url?: string; + timestamp?: string; + color?: number; + footer?: { + text: string, + icon_url?: string, + proxy_icon_url?: string + }; + image?: { + url?: string, + proxy_url?: string, + height?: number, + width?: number + }; + thumbnail?: { + url?: string, + proxy_url?: string, + height?: number, + width?: number + }; + video?: { + url: string, + height?: number, + width?: number + }; + provider?: { + name: string, + url?: string + }; + fields?: Array<{ + name?: string, + value?: string, + inline?: boolean + }>; + author?: { + name: string, + url?: string, + icon_url?: string, + proxy_icon_url?: string + }; + } + declare type Embed = { + type: string + } & EmbedBase; + declare type EmbedOptions = { + type?: string + } & EmbedBase; + declare interface Webhook { + name: string; + channel_id: string; + token: string; + avatar?: string; + guild_id: string; + id: string; + user: { + username: string, + discriminator: string, + id: string, + avatar?: string + }; + } + declare interface GuildEmbed { + channel_id?: string; + enabled: boolean; + } + declare interface Attachment { + url: string; + proxy_url: string; + size: number; + id: string; + filename: string; + } + declare interface VoiceRegion { + name: string; + deprecated: boolean; + custom: boolean; + vip: boolean; + optimal: boolean; + id: string; + } + declare interface UserSettings { + theme: string; + status: string; + show_current_game: boolean; + restricted_guilds: string[]; + render_reactions: boolean; + render_embeds: boolean; + message_display_compact: boolean; + locale: string; + inline_embed_media: boolean; + inline_attachment_media: boolean; + guild_positions: string[]; + friend_source_flags: { + all: boolean + }; + explicit_content_filter: number; + enable_tts_command: boolean; + developer_mode: boolean; + detect_platform_accounts: boolean; + default_guilds_restricted: boolean; + convert_emojis: boolean; + afk_timeout: number; + } + declare interface GuildSettings { + suppress_everyone: boolean; + muted: boolean; + mobile_push: boolean; + message_notifications: number; + guild_id: string; + channel_override: Array<{ + muted: boolean, + message_notifications: number, + channel_id: string + }>; + } + declare interface UserProfile { + premium_since?: number; + mutual_guilds: Array<{ + nick?: string, + id: string + }>; + user: { + username: string, + discriminator: string, + flags: number, + id: string, + avatar?: string + }; + connected_accounts: Array<{ + verified: boolean, + type: string, + id: string, + name: string + }>; + } + declare interface Connection { + verified: boolean; + revoked: boolean; + integrations: any[]; + visibility: number; + friend_sync: boolean; + type: string; + id: string; + name: string; + } + declare interface GuildAuditLog { + users: User[]; + entries: GuildAuditLogEntry[]; + } + declare interface BaseData { + id: string; + [key: string]: {}; + } + declare type MessageContent = + | string + | { + content?: string, + tts?: boolean, + disableEveryone?: boolean, + embed?: EmbedOptions + }; + declare interface MessageFile { + file: Buffer | string; + name: string; + } + declare interface EmojiBase { + name: string; + icon?: string; + } + declare type EmojiOptions = { + roles?: string[] + } & EmojiBase; + declare type Emoji = { + roles: string[] + } & EmojiBase; + declare interface IntegrationOptions { + expireBehavior: string; + expireGracePeriod: string; + enableEmoticons: string; + } + declare interface GuildOptions { + name?: string; + region?: string; + icon?: string; + verificationLevel?: number; + defaultNotifications?: number; + afkChannelID?: string; + afkTimeout?: number; + ownerID?: string; + splash?: string; + } + declare interface MemberOptions { + roles?: string[]; + nick?: string; + mute?: boolean; + deaf?: boolean; + channelID?: string; + } + declare interface RoleOptions { + name?: string; + permissions?: number; + color?: number; + hoist?: boolean; + mentionable?: boolean; + } + declare interface GamePresence { + name: string; + type?: number; + url?: string; + } + declare interface SearchOptions { + sortBy?: string; + sortOrder?: string; + content?: string; + authorID?: string; + minID?: string; + maxID?: string; + limit?: number; + offset?: number; + contextSize?: number; + has?: string; + embedProviders?: string; + embedTypes?: string; + attachmentExtensions?: string; + attachmentFilename?: string; + channelIDs: string[]; + } + declare interface SearchResults { + totalResults: number; + results: Array< + Array< + Message & { + hit?: boolean + } + > + >; + } + declare interface VoiceResourceOptions { + inlineVolume?: boolean; + voiceDataTimeout?: number; + inputArgs?: string[]; + encoderArgs?: string[]; + format?: string; + frameDuration?: number; + frameSize?: number; + sampleRate?: number; + } + declare type PossiblyUncachedMessage = + | Message + | { + id: string, + channel: TextableChannel + }; + declare interface RawPacket { + op: number; + t?: string; + d?: any; + s?: number; + } + declare type ClientOptions = $Shape<{ + autoreconnect: boolean; + compress: boolean; + connectionTimeout: number; + disableEvents: { + [s: string]: boolean + }; + disableEveryone: boolean; + firstShardID: number; + getAllUsers: boolean; + guildCreateTimeout: number; + largeThreshold: number; + lastShardID: number; + maxShards: number | "auto"; + messageLimit: number; + opusOnly: boolean; + restMode: boolean; + seedVoiceConnections: boolean; + defaultImageFormat: string; + defaultImageSize: number; + ws: any; + latencyThreshold: number; + }> + declare type CommandClientOptions = $Shape<{ + defaultHelpCommand: boolean; + description: string; + ignoreBots: boolean; + ignoreSelf: boolean; + name: string; + owner: string; + prefix: string | string[]; + defaultCommandOptions: CommandOptions; + }> + declare interface Hooks { + preCommand?: (msg: Message, args: string[]) => void; + postCheck?: (msg: Message, args: string[], checksPassed: boolean) => void; + postExecution?: ( + msg: Message, + args: string[], + executionSuccess: boolean + ) => void; + postCommand?: (msg: Message, args: string[], sent?: Message) => void; + } + declare type GenericCheckFunction = (msg: Message) => T; + declare type CommandOptions = $Shape<{ + aliases?: string[]; + caseInsensitive?: boolean; + deleteCommand?: boolean; + argsRequired?: boolean; + guildOnly?: boolean; + dmOnly?: boolean; + description?: string; + fullDescription?: string; + usage?: string; + hooks?: Hooks; + requirements?: { + userIDs?: string[] | GenericCheckFunction, + roleIDs?: string[] | GenericCheckFunction, + roleNames?: string[] | GenericCheckFunction, + permissions?: + | { + [s: string]: boolean + } + | GenericCheckFunction<{ + [s: string]: boolean + }>, + custom?: GenericCheckFunction + }; + cooldown?: number; + cooldownExclusions?: { + userIDs?: string[], + guildIDs?: string[], + channelIDs?: string[] + }; + restartCooldown?: boolean; + cooldownReturns?: number; + cooldownMessage?: string | GenericCheckFunction; + invalidUsageMessage?: string | GenericCheckFunction; + permissionMessage?: string | GenericCheckFunction; + errorMessage?: string | GenericCheckFunction; + reactionButtons?: Array<{ + emoji: string, + type: string, + response: CommandGenerator + }>; + reactionButtonTimeout?: number; + defaultSubcommandOptions?: CommandOptions; + hidden?: boolean; + }> + declare type CommandGeneratorFunction = ( + msg: Message, + args: string[] + ) => Promise | Promise | MessageContent | void; + declare type CommandGenerator = + | CommandGeneratorFunction + | MessageContent + | MessageContent[] + | CommandGeneratorFunction[]; + declare export class ShardManager extends Collection { + constructor(client: Client): *; + connect(shard: Shard): void; + spawn(id: number): void; + toJSON(): string; + } + declare export default class Client extends events$EventEmitter implements SimpleJSON, Emittable { + token: string; + gatewayURL: string; + bot: boolean; + options: ClientOptions; + channelGuildMap: { + [s: string]: string + }; + shards: ShardManager; + guilds: Collection; + privateChannelMap: { + [s: string]: string + }; + privateChannels: Collection; + groupChannels: Collection; + voiceConnections: Collection; + guildShardMap: { + [s: string]: number + }; + startTime: number; + unavailableGuilds: Collection; + uptime: number; + user: ExtendedUser; + users: Collection; + relationships: Collection; + userGuildSettings: { + [s: string]: GuildSettings + }; + userSettings: UserSettings; + notes: { + [s: string]: string + }; + constructor(token: string, options?: ClientOptions): *; + connect(): Promise; + getGateway(): Promise; + getBotGateway(): Promise<{ + url: string, + shards: number + }>; + disconnect(options: { + reconnect: boolean + }): void; + joinVoiceChannel( + channelID: string, + options?: { + shared?: boolean, + opusOnly?: boolean + } + ): Promise; + leaveVoiceChannel(channelID: string): void; + closeVoiceConnection(guildID: string): void; + editAFK(afk: boolean): void; + editStatus(status?: string, game?: GamePresence): void; + getChannel(channelID: string): AnyChannel; + createChannel( + guildID: string, + name: string, + type?: number, + reason?: string, + parentID?: string + ): Promise; + editChannel( + channelID: string, + options: { + name?: string, + icon?: string, + ownerID?: string, + topic?: string, + bitrate?: number, + userLimit?: number, + nsfw?: boolean, + parentID?: string + }, + reason?: string + ): Promise; + editChannelPosition(channelID: string, position: number): Promise; + deleteChannel(channelID: string, reason?: string): Promise; + sendChannelTyping(channelID: string): Promise; + editChannelPermission( + channelID: string, + overwriteID: string, + allow: number, + deny: number, + type: string, + reason?: string + ): Promise; + deleteChannelPermission( + channelID: string, + overwriteID: string, + reason?: string + ): Promise; + getChannelInvites(channelID: string): Promise; + createChannelInvite( + channelID: string, + options?: { + maxAge?: number, + maxUses?: number, + temporary?: boolean, + unique?: boolean + }, + reason?: string + ): Promise; + getChannelWebhooks(channelID: string): Promise; + getWebhook(webhookID: string, token?: string): Promise; + createChannelWebhook( + channelID: string, + options: { + name: string, + avatar: string + }, + reason?: string + ): Promise; + editWebhook( + webhookID: string, + options: { + name?: string, + avatar?: string + }, + token?: string, + reason?: string + ): Promise; + executeWebhook( + webhookID: string, + token: string, + options: WebhookPayload + ): Promise; + executeSlackWebhook( + webhookID: string, + token: string, + options?: { + wait?: boolean + } + ): Promise; + deleteWebhook( + webhookID: string, + token?: string, + reason?: string + ): Promise; + getGuildWebhooks(guildID: string): Promise; + getGuildAuditLogs( + guildID: string, + limit?: number, + before?: string, + actionType?: number + ): Promise; + createGuildEmoji( + guildID: string, + options: EmojiOptions, + reason?: string + ): Promise; + editGuildEmoji( + guildID: string, + emojiID: string, + options: { + name?: string, + roles?: string[] + }, + reason?: string + ): Promise; + deleteGuildEmoji( + guildID: string, + emojiID: string, + reason?: string + ): Promise; + createRole( + guildID: string, + options?: RoleOptions, + reason?: string + ): Promise; + editRole( + guildID: string, + roleID: string, + options: RoleOptions, + reason?: string + ): Promise; + editRolePosition( + guildID: string, + roleID: string, + position: number + ): Promise; + deleteRole(guildID: string, roleID: string, reason?: string): Promise; + getPruneCount(guildID: string, days: number): Promise; + pruneMembers( + guildID: string, + days: number, + reason?: string + ): Promise; + getVoiceRegions(guildID: string): Promise; + getInvite(inviteID: string, withCounts?: boolean): Promise; + acceptInvite(inviteID: string): Promise; + deleteInvite(inviteID: string, reason?: string): Promise; + getSelf(): Promise; + editSelf(options: { + username?: string, + avatar?: string + }): Promise; + getDMChannel(userID: string): Promise; + createGroupChannel(userIDs: string[]): Promise; + getMessage(channelID: string, messageID: string): Promise; + getMessages( + channelID: string, + limit?: number, + before?: string, + after?: string, + around?: string + ): Promise; + getPins(channelID: string): Promise; + createMessage( + channelID: string, + content: MessageContent, + file?: MessageFile + ): Promise; + editMessage( + channelID: string, + messageID: string, + content: MessageContent + ): Promise; + pinMessage(channelID: string, messageID: string): Promise; + unpinMessage(channelID: string, messageID: string): Promise; + getMessageReaction( + channelID: string, + messageID: string, + reaction: string, + limit?: number, + before?: string, + after?: string + ): Promise; + addMessageReaction( + channelID: string, + messageID: string, + reaction: string, + userID?: string + ): Promise; + removeMessageReaction( + channelID: string, + messageID: string, + reaction: string, + userID?: string + ): Promise; + removeMessageReactions(channelID: string, messageID: string): Promise; + deleteMessage( + channelID: string, + messageID: string, + reason?: string + ): Promise; + deleteMessages( + channelID: string, + messageIDs: string[], + reason?: string + ): Promise; + purgeChannel( + channelID: string, + limit?: number, + filter?: (m: Message) => boolean, + before?: string, + after?: string + ): Promise; + getGuildEmbed(guildID: string): Promise; + getGuildIntegrations(guildID: string): Promise; + editGuildIntegration( + guildID: string, + integrationID: string, + options: IntegrationOptions + ): Promise; + deleteGuildIntegration( + guildID: string, + integrationID: string + ): Promise; + syncGuildIntegration(guildID: string, integrationID: string): Promise; + getGuildInvites(guildID: string): Promise; + banGuildMember( + guildID: string, + userID: string, + deleteMessageDays?: number, + reason?: string + ): Promise; + unbanGuildMember( + guildID: string, + userID: string, + reason?: string + ): Promise; + createGuild(name: string, region: string, icon?: string): Promise; + editGuild( + guildID: string, + options: GuildOptions, + reason?: string + ): Promise; + getGuildBans( + guildID: string + ): Promise< + Array<{ + reason?: string, + user: User + }> + >; + editGuildMember( + guildID: string, + memberID: string, + options: MemberOptions, + reason?: string + ): Promise; + addGuildMemberRole( + guildID: string, + memberID: string, + roleID: string, + reason?: string + ): Promise; + removeGuildMemberRole( + guildID: string, + memberID: string, + roleID: string, + reason?: string + ): Promise; + editNickname(guildID: string, nick: string, reason?: string): Promise; + kickGuildMember( + guildID: string, + userID: string, + reason?: string + ): Promise; + deleteGuild(guildID: string): Promise; + leaveGuild(guildID: string): Promise; + getOAuthApplication( + appID?: string + ): Promise<{ + description: string, + name: string, + owner: { + username: string, + discriminator: string, + id: string, + avatar?: string + }, + bot_public: boolean, + bot_require_code_grant: boolean, + id: string, + icon?: string + }>; + addRelationship(userID: string, block?: boolean): Promise; + removeRelationship(userID: string): Promise; + addGroupRecipient(groupID: string, userID: string): Promise; + removeGroupRecipient(groupID: string, userID: string): Promise; + getUserProfile(userID: string): Promise; + editUserNote(userID: string, note: string): Promise; + deleteUserNote(userID: string): Promise; + getSelfConnections(): Promise; + editSelfConnection( + platform: string, + id: string, + data: { + friendSync: boolean, + visibility: number + } + ): Promise; + deleteSelfConnection(platform: string, id: string): Promise; + getSelfSettings(): Promise; + editSelfSettings(data: UserSettings): Promise; + getSelfMFACodes( + password: string, + regenerate?: boolean + ): Promise<{ + backup_codes: Array<{ + code: string, + consumed: boolean + }> + }>; + enableSelfMFATOTP( + secret: string, + code: string + ): Promise<{ + token: string, + backup_codes: Array<{ + code: string, + consumed: boolean + }> + }>; + disableSelfMFATOTP( + code: string + ): Promise<{ + token: string + }>; + getSelfBilling(): Promise<{ + premium_subscription?: { + status: number, + ended_at?: string, + canceled_at?: string, + created_at: string, + current_period_end?: string, + current_period_start?: string, + plan: string + }, + payment_source?: { + type: string, + brand: string, + invalid: boolean, + last_4: number, + expires_year: number, + expires_month: number + }, + payment_gateway?: string + }>; + getSelfPayments(): Promise< + Array<{ + status: number, + amount_refunded: number, + description: string, + created_at: string, + currency: string, + amount: number + }> + >; + addSelfPremiumSubscription(token: string, plan: string): Promise; + deleteSelfPremiumSubscription(): Promise; + getRESTChannel(channelID: string): Promise; + getRESTGuild(guildID: string): Promise; + getRESTGuilds( + limit?: number, + before?: string, + after?: string + ): Promise; + getRESTGuildChannels(guildID: string): Promise; + getRESTGuildEmojis(guildID: string): Promise; + getRESTGuildEmoji(guildID: string, emojiID: string): Promise; + getRESTGuildMembers( + guildID: string, + limit?: number, + after?: string + ): Promise; + getRESTGuildMember(guildID: string, memberID: string): Promise; + getRESTGuildRoles(guildID: string): Promise; + getRESTUser(userID: string): Promise; + searchChannelMessages( + channelID: string, + query: SearchOptions + ): Promise; + searchGuildMessages( + guildID: string, + query: SearchOptions + ): Promise; + on(event: string, listener: Function): *; + on(event: "ready" | "disconnect", listener: () => void): *; + on( + event: "callCreate" | "callRing" | "callDelete", + listener: (call: Call) => void + ): *; + on( + event: "callUpdate", + listener: (call: Call, oldCall: OldCall) => void + ): *; + on( + event: "channelCreate" | "channelDelete", + listener: (channel: AnyChannel) => void + ): *; + on( + event: "channelPinUpdate", + listener: ( + channel: TextableChannel, + timestamp: number, + oldTimestamp: number + ) => void + ): *; + on( + event: "channelRecipientAdd" | "channelRecipientRemove", + listener: (channel: GroupChannel, user: User) => void + ): *; + on( + event: "channelUpdate", + listener: (channel: AnyChannel, oldChannel: OldChannel) => void + ): *; + on( + event: "friendSuggestionCreate", + listener: (user: User, reasons: FriendSuggestionReasons) => void + ): *; + on(event: "friendSuggestionDelete", listener: (user: User) => void): *; + on( + event: "guildAvailable" | "guildBanAdd" | "guildBanRemove", + listener: (guild: Guild, user: User) => void + ): *; + on( + event: "guildDelete" | "guildUnavailable" | "guildCreate", + listener: (guild: Guild) => void + ): *; + on( + event: "guildEmojisUpdate", + listener: (guild: Guild, emojis: Emoji[], oldEmojis: Emoji[]) => void + ): *; + on( + event: "guildMemberAdd", + listener: (guild: Guild, member: Member) => void + ): *; + on( + event: "guildMemberChunk", + listener: (guild: Guild, members: Member[]) => void + ): *; + on( + event: "guildMemberRemove", + listener: (guild: Guild, member: Member | MemberPartial) => void + ): *; + on( + event: "guildMemberUpdate", + listener: ( + guild: Guild, + member: Member, + oldMember: { + roles: string[], + nick?: string + } + ) => void + ): *; + on( + event: "guildRoleCreate" | "guildRoleDelete", + listener: (guild: Guild, role: Role) => void + ): *; + on( + event: "guildRoleUpdate", + listener: (guild: Guild, role: Role, oldRole: RoleOptions) => void + ): *; + on( + event: "guildUpdate", + listener: (guild: Guild, oldGuild: GuildOptions) => void + ): *; + on(event: "hello", listener: (trace: string[], id: number) => void): *; + on(event: "messageCreate", listener: (message: Message) => void): *; + on( + event: "messageDelete" | "messageReactionRemoveAll", + listener: (message: PossiblyUncachedMessage) => void + ): *; + on( + event: "messageDeleteBulk", + listener: (messages: PossiblyUncachedMessage[]) => void + ): *; + on( + event: "messageReactionAdd" | "messageReactionRemove", + listener: ( + message: PossiblyUncachedMessage, + emoji: Emoji, + userID: string + ) => void + ): *; + on( + event: "messageUpdate", + listener: ( + message: Message, + oldMessage?: { + attachments: Attachment[], + embeds: Embed[], + content: string, + editedTimestamp?: number, + mentionedBy?: any, + tts: boolean, + mentions: string[], + roleMentions: string[], + channelMentions: string[] + } + ) => void + ): *; + on( + event: "presenceUpdate", + listener: ( + other: Member | Relationship, + oldPresence?: OldPresence + ) => void + ): *; + on( + event: "rawWS" | "unknown", + listener: (packet: RawPacket, id: number) => void + ): *; + on( + event: "relationshipAdd" | "relationshipRemove", + listener: (relationship: Relationship) => void + ): *; + on( + event: "relationshipUpdate", + listener: ( + relationship: Relationship, + oldRelationship: { + type: number + } + ) => void + ): *; + on( + event: "typingStart", + listener: (channel: TextableChannel, user: User) => void + ): *; + on( + event: "unavailableGuildCreate", + listener: (guild: UnavailableGuild) => void + ): *; + on( + event: "userUpdate", + listener: ( + user: User, + oldUser: { + username: string, + discriminator: string, + avatar?: string + } + ) => void + ): *; + on( + event: "voiceChannelJoin", + listener: (member: Member, newChannel: VoiceChannel) => void + ): *; + on( + event: "voiceChannelLeave", + listener: (member: Member, oldChannel: VoiceChannel) => void + ): *; + on( + event: "voiceChannelSwitch", + listener: ( + member: Member, + newChannel: VoiceChannel, + oldChannel: VoiceChannel + ) => void + ): *; + on( + event: "voiceStateUpdate", + listener: (member: Member, oldState: OldVoiceState) => void + ): *; + on( + event: "warn" | "debug", + listener: (message: string, id: number) => void + ): *; + on( + event: "shardDisconnect" | "error" | "shardPreReady" | "connect", + listener: (err: Error, id: number) => void + ): *; + on( + event: "shardReady" | "shardResume", + listener: (id: number) => void + ): *; + toJSON(simple?: boolean): JSONCache; + } + declare export class VoiceConnection extends events$EventEmitter implements SimpleJSON { + id: string; + channelID: string; + connecting: boolean; + ready: boolean; + playing: boolean; + paused: boolean; + volume: number; + current: { + startTime: number, + playTime: number, + pausedTimestamp?: number, + pausedTime?: number, + options: VoiceResourceOptions + }; + constructor( + id: string, + options?: { + shard?: Shard, + shared?: boolean, + opusOnly?: boolean + } + ): *; + pause(): void; + play( + resource: ReadableStream | string, + options?: VoiceResourceOptions + ): void; + receive(type: string): VoiceDataStream; + resume(): void; + setVolume(volume: number): void; + stopPlaying(): void; + switchChannel(channelID: string): void; + updateVoiceState(selfMute: boolean, selfDeaf: boolean): void; + on(event: "debug" | "warn", listener: (message: string) => void): *; + on(event: "error" | "disconnect", listener: (err: Error) => void): *; + on(event: "pong", listener: (latency: number) => void): *; + on(event: "speakingStart", listener: (userID: string) => void): *; + on(event: "end", listener: () => void): *; + on(event: string, listener: (e: mixed, ...args: Array) => void): *; + toJSON(simple?: boolean): JSONCache; + } + declare export class SharedStream extends events$EventEmitter { + playing: boolean; + ended: boolean; + volume: number; + speaking: boolean; + current: { + startTime: number, + playTime: number, + pausedTimestamp?: number, + pausedTime?: number, + options: VoiceResourceOptions + }; + add(connection: VoiceConnection): void; + play( + resource: ReadableStream | string, + options: VoiceResourceOptions + ): void; + remove(connection: VoiceConnection): void; + setVolume(volume: number): void; + stopPlaying(): void; + } + declare class VoiceDataStream { + type: string; + constructor(type: string): *; + } + declare export class VoiceConnectionManager + extends Collection implements SimpleJSON { + constructor(vcObject: () => T): *; + join( + guildID: string, + channelID: string, + options: VoiceResourceOptions + ): Promise; + leave(guildID: string): void; + switch(guildID: string, channelID: string): void; + toJSON(simple?: boolean): JSONCache; + } + declare class Base implements SimpleJSON { + id: string; + createdAt: number; + constructor(id: string): *; + inspect(): *; + toJSON(simple?: boolean): JSONCache; + } + declare export class Bucket { + tokens: number; + lastReset: number; + lastSend: number; + tokenLimit: number; + interval: number; + constructor( + tokenLimit: number, + interval: number, + latencyRef: { + latency: number + } + ): *; + queue(func: Function): void; + } + declare export class Collection extends Map { + baseObject: (...args: any[]) => T; + limit: number; + constructor(baseObject: (...args: any[]) => T, limit?: number): *; + add(obj: T, extra?: any, replace?: boolean): T; + find(func: (i: T) => boolean): T; + random(): T; + filter(func: (i: T) => boolean): T[]; + map(func: (i: T) => R): R[]; + update(obj: T, extra?: any, replace?: boolean): T; + remove( + obj: + | T + | { + id: string + } + ): T; + } + declare export class Call extends Base { + id: string; + createdAt: number; + channel: GroupChannel; + voiceStates: Collection; + participants: string[]; + endedTimestamp: number; + ringing: string[]; + region: string; + unavailable: boolean; + constructor(data: BaseData, channel: GroupChannel): *; + } + declare export class Channel extends Base { + id: string; + mention: string; + type: number; + createdAt: number; + constructor(data: BaseData): *; + } + declare export class ExtendedUser extends User { + email: string; + verified: boolean; + mfaEnabled: boolean; + } + declare export class GroupChannel extends PrivateChannel { + recipients: Collection; + name: string; + icon: string; + iconURL: string; + ownerID: string; + edit(options: { + name?: string, + icon?: string, + ownerID?: string + }): Promise; + addRecipient(userID: string): Promise; + removeRecipient(userID: string): Promise; + dynamicIconURL(format: string, size: number): string; + } + declare export class Guild extends Base { + id: string; + createdAt: number; + name: string; + verificationLevel: number; + region: string; + icon: string; + afkChannelID: string; + systemChannelID: string; + afkTimeout: number; + defaultNotifications: number; + mfaLevel: number; + joinedAt: number; + ownerID: string; + splash: string; + unavailable: boolean; + large: boolean; + maxPresences: number; + channels: Collection; + members: Collection; + memberCount: number; + roles: Collection; + shard: Shard; + features: string[]; + emojis: Emoji[]; + iconURL: string; + explicitContentFilter: number; + constructor(data: BaseData, client: Client): *; + fetchAllMembers(): void; + dynamicIconURL(format: string, size: number): string; + createChannel( + name: string, + type: string, + parentID?: string + ): Promise; + createEmoji( + options: { + name: string, + image: string, + roles?: string[] + }, + reason?: string + ): Promise; + editEmoji( + emojiID: string, + options: { + name: string, + roles?: string[] + }, + reason?: string + ): Promise; + deleteEmoji(emojiID: string, reason?: string): Promise; + createRole(options: RoleOptions, reason?: string): Promise; + getPruneCount(days: number): Promise; + pruneMembers(days: number, reason?: string): Promise; + getRESTChannels(): Promise; + getRESTEmojis(): Promise; + getRESTEmoji(emojiID: string): Promise; + getRESTMembers(limit?: number, after?: string): Promise; + getRESTMember(memberID: string): Promise; + getRESTRoles(): Promise; + getEmbed(): Promise; + getVoiceRegions(): Promise; + leaveVoiceChannel(): void; + editRole(roleID: string, options: RoleOptions): Promise; + deleteRole(roleID: string): Promise; + getAuditLogs( + limit?: number, + before?: string, + actionType?: number + ): Promise; + getIntegrations(): Promise; + editIntegration( + integrationID: string, + options: IntegrationOptions + ): Promise; + syncIntegration(integrationID: string): Promise; + deleteIntegration(integrationID: string): Promise; + getInvites(): Promise; + editMember( + memberID: string, + options: MemberOptions, + reason?: string + ): Promise; + addMemberRole( + memberID: string, + roleID: string, + reason?: string + ): Promise; + removeMemberRole( + memberID: string, + roleID: string, + reason?: string + ): Promise; + kickMember(userID: string, reason?: string): Promise; + banMember( + userID: string, + deleteMessageDays?: number, + reason?: string + ): Promise; + unbanMember(userID: string, reason?: string): Promise; + edit(options: GuildOptions, reason?: string): Promise; + delete(): Promise; + leave(): Promise; + getBans(): Promise; + editNickname(nick: string): Promise; + getWebhooks(): Promise; + } + declare export class GuildAuditLogEntry extends Base { + id: string; + guild: Guild; + actionType: number; + reason: string; + user: User; + targetID: string; + target: Guild | AnyGuildChannel | Member | Invite | Role | any; + before: any; + after: any; + count: number; + channel: AnyGuildChannel; + deleteMemberDays: number; + membersRemoved: number; + member: Member | any; + role: Role | any; + constructor(data: BaseData, guild: Guild): *; + } + declare export class GuildChannel extends Channel { + guild: Guild; + parentID: string; + name: string; + position: number; + permissionOverwrites: Collection; + nsfw: boolean; + constructor(data: BaseData, guild: Guild): *; + getInvites(): Promise; + createInvite( + options?: CreateInviteOptions, + reason?: string + ): Promise; + permissionsOf(memberID: string): Permission; + edit( + options: { + name?: string, + topic?: string, + bitrate?: number, + userLimit?: number, + nsfw?: boolean + }, + reason?: string + ): Promise; + editPosition(position: number): Promise; + delete(reason?: string): Promise; + editPermission( + overwriteID: string, + allow: number, + deny: number, + type: string, + reason?: string + ): Promise; + deletePermission(overwriteID: string, reason?: string): Promise; + } + declare export class CategoryChannel extends GuildChannel { + channels: Collection; + } + declare export class TextChannel extends GuildChannel implements Textable, Invitable { + topic: string; + lastMessageID: string; + messages: Collection; + constructor(data: BaseData, guild: Guild, messageLimit: number): *; + getInvites(): Promise; + createInvite( + options?: CreateInviteOptions, + reason?: string + ): Promise; + getWebhooks(): Promise; + createWebhook( + options: { + name: string, + avatar: string + }, + reason?: string + ): Promise; + sendTyping(): Promise; + getMessage(messageID: string): Promise; + getMessages( + limit?: number, + before?: string, + after?: string, + around?: string + ): Promise; + getPins(): Promise; + createMessage( + content: MessageContent, + file?: MessageFile + ): Promise; + editMessage(messageID: string, content: MessageContent): Promise; + pinMessage(messageID: string): Promise; + unpinMessage(messageID: string): Promise; + getMessageReaction( + messageID: string, + reaction: string, + limit?: number, + before?: string, + after?: string + ): Promise; + addMessageReaction( + messageID: string, + reaction: string, + userID?: string + ): Promise; + removeMessageReaction( + messageID: string, + reaction: string, + userID?: string + ): Promise; + removeMessageReactions(messageID: string): Promise; + deleteMessage(messageID: string, reason?: string): Promise; + unsendMessage(messageID: string): Promise; + } + declare export class VoiceChannel extends GuildChannel implements Invitable { + bitrate: number; + userLimit: number; + voiceMembers: Collection; + join(options: VoiceResourceOptions): Promise; + leave(): void; + } + declare export class GuildIntegration extends Base { + id: string; + createdAt: number; + name: string; + type: string; + roleID: string; + user: User; + account: { + id: string, + name: string + }; + enabled: boolean; + syncing: boolean; + expireBehavior: number; + expireGracePeriod: number; + enableEmoticons: boolean; + subscriberCount: number; + syncedAt: number; + constructor(data: BaseData, guild: Guild): *; + edit(options: { + expireBehavior: string, + expireGracePeriod: string, + enableEmoticons: string + }): Promise; + delete(): Promise; + sync(): Promise; + } + declare export class Invite implements SimpleJSON { + code: string; + channel: { + id: string, + name: string + }; + guild: { + id: string, + name: string, + splash?: string, + icon?: string, + textChannelCount?: number, + voiceChannelCount?: number + }; + inviter: User; + uses: number; + maxUses: number; + maxAge: number; + temporary: boolean; + createdAt: number; + revoked: boolean; + presenceCount: number; + memberCount: number; + constructor(data: BaseData, client: Client): *; + delete(reason?: string): Promise; + toJSON(simple?: boolean): JSONCache; + } + declare export class Member extends Base { + id: string; + mention: string; + guild: Guild; + joinedAt: number; + status: string; + game: GamePresence; + voiceState: VoiceState; + nick: string; + roles: string[]; + user: User; + permission: Permission; + defaultAvatar: string; + createdAt: number; + bot: boolean; + username: string; + discriminator: string; + avatar: string; + defaultAvatarURL: string; + avatarURL: string; + staticAvatarURL: string; + constructor(data: BaseData, guild: Guild): *; + edit(options: MemberOptions, reason?: string): Promise; + addRole(roleID: string, reason?: string): Promise; + removeRole(roleID: string, reason?: string): Promise; + kick(reason?: string): Promise; + ban(deleteMessageDays?: number, reason?: string): Promise; + unban(reason?: string): Promise; + } + declare export class Message extends Base { + id: string; + createdAt: number; + channel: TextableChannel; + timestamp: number; + type: number; + author: User; + member: Member; + mentions: User[]; + content: string; + cleanContent: string; + roleMentions: string[]; + channelMentions: string[]; + editedTimestamp: number; + tts: boolean; + mentionEveryone: boolean; + attachments: Attachment[]; + embeds: Embed[]; + reactions: { + [s: string]: any, + count: number, + me: boolean + }; + prefix: string; + command: Command; + constructor(data: BaseData, client: Client): *; + edit(content: MessageContent): Promise; + pin(): Promise; + unpin(): Promise; + getReaction( + reaction: string, + limit?: number, + before?: string, + after?: string + ): Promise; + addReaction(reaction: string, userID?: string): Promise; + removeReaction(reaction: string, userID?: string): Promise; + removeReactions(): Promise; + delete(reason?: string): Promise; + } + declare export class Permission { + allow: number; + deny: number; + json: { + [s: string]: boolean + }; + constructor(allow: number, deny: number): *; + has(permission: string): boolean; + } + declare export class PermissionOverwrite extends Permission { + id: string; + createdAt: number; + type: string; + constructor(data: { + allow: number, + deny: number + }): *; + } + declare export class PrivateChannel extends Channel implements Textable { + lastMessageID: string; + recipient: User; + messages: Collection; + ring(recipient: string[]): void; + syncCall(): void; + leave(): Promise; + sendTyping(): Promise; + getMessage(messageID: string): Promise; + getMessages( + limit?: number, + before?: string, + after?: string, + around?: string + ): Promise; + getPins(): Promise; + createMessage( + content: MessageContent, + file?: MessageFile + ): Promise; + editMessage(messageID: string, content: MessageContent): Promise; + pinMessage(messageID: string): Promise; + unpinMessage(messageID: string): Promise; + getMessageReaction( + messageID: string, + reaction: string, + limit?: number, + before?: string, + after?: string + ): Promise; + addMessageReaction( + messageID: string, + reaction: string, + userID?: string + ): Promise; + removeMessageReaction( + messageID: string, + reaction: string, + userID?: string + ): Promise; + removeMessageReactions(messageID: string): Promise; + deleteMessage(messageID: string, reason?: string): Promise; + unsendMessage(messageID: string): Promise; + } + declare export class Relationship { + id: string; + user: User; + type: number; + status: string; + game: GamePresence; + constructor(data: BaseData, client: Client): *; + } + declare export class Role extends Base { + id: string; + createdAt: number; + guild: Guild; + mention: string; + name: string; + mentionable: boolean; + managed: boolean; + hoist: boolean; + color: number; + position: number; + permissions: Permission; + json: { + [s: string]: boolean + }; + constructor(data: BaseData, guild: Guild): *; + edit(options: RoleOptions, reason?: string): Promise; + editPosition(position: number): Promise; + delete(reason?: string): Promise; + } + declare export class UnavailableGuild extends Base { + id: string; + createdAt: number; + unavailable: boolean; + shard: Shard; + constructor(data: BaseData, client: Client): *; + } + declare export class User extends Base { + id: string; + mention: string; + defaultAvatar: string; + createdAt: number; + bot: boolean; + username: string; + discriminator: string; + avatar: string; + defaultAvatarURL: string; + avatarURL: string; + staticAvatarURL: string; + constructor(data: BaseData, client: Client): *; + dynamicAvatarURL(format?: string, size?: number): string; + getDMChannel(): Promise; + addRelationship(block?: boolean): Promise; + removeRelationship(): Promise; + getProfile(): Promise; + editNote(note: string): Promise; + deleteNote(): Promise; + } + declare export class VoiceState extends Base implements NestedJSON { + id: string; + createdAt: number; + sessionID: string; + channelID: string; + mute: boolean; + deaf: boolean; + suppress: boolean; + selfMute: boolean; + selfDeaf: boolean; + constructor(data: BaseData): *; + toJSON(arg?: any, cache?: Array): JSONCache; + } + declare export class Shard extends events$EventEmitter implements SimpleJSON, Emittable { + id: number; + connecting: boolean; + ready: boolean; + discordServerTrace: string[]; + status: string; + lastHeartbeatReceived: number; + lastHeartbeatSent: number; + latency: number; + constructor(id: number, client: Client): *; + connect(): void; + disconnect(options?: { + reconnect: boolean + }): void; + editAFK(afk: boolean): void; + editStatus(status?: string, game?: GamePresence): void; + on(event: string, listener: Function): *; + on(event: "ready" | "disconnect", listener: () => void): *; + on( + event: "callCreate" | "callRing" | "callDelete", + listener: (call: Call) => void + ): *; + on( + event: "callUpdate", + listener: (call: Call, oldCall: OldCall) => void + ): *; + on( + event: "channelCreate" | "channelDelete", + listener: (channel: AnyChannel) => void + ): *; + on( + event: "channelPinUpdate", + listener: ( + channel: TextableChannel, + timestamp: number, + oldTimestamp: number + ) => void + ): *; + on( + event: "channelRecipientAdd" | "channelRecipientRemove", + listener: (channel: GroupChannel, user: User) => void + ): *; + on( + event: "channelUpdate", + listener: (channel: AnyChannel, oldChannel: OldChannel) => void + ): *; + on( + event: "friendSuggestionCreate", + listener: (user: User, reasons: FriendSuggestionReasons) => void + ): *; + on(event: "friendSuggestionDelete", listener: (user: User) => void): *; + on( + event: "guildAvailable" | "guildBanAdd" | "guildBanRemove", + listener: (guild: Guild, user: User) => void + ): *; + on( + event: "guildDelete" | "guildUnavailable" | "guildCreate", + listener: (guild: Guild) => void + ): *; + on( + event: "guildEmojisUpdate", + listener: (guild: Guild, emojis: Emoji[], oldEmojis: Emoji[]) => void + ): *; + on( + event: "guildMemberAdd", + listener: (guild: Guild, member: Member) => void + ): *; + on( + event: "guildMemberChunk", + listener: (guild: Guild, members: Member[]) => void + ): *; + on( + event: "guildMemberRemove", + listener: (guild: Guild, member: Member | MemberPartial) => void + ): *; + on( + event: "guildMemberUpdate", + listener: ( + guild: Guild, + member: Member, + oldMember: { + roles: string[], + nick?: string + } + ) => void + ): *; + on( + event: "guildRoleCreate" | "guildRoleDelete", + listener: (guild: Guild, role: Role) => void + ): *; + on( + event: "guildRoleUpdate", + listener: (guild: Guild, role: Role, oldRole: RoleOptions) => void + ): *; + on( + event: "guildUpdate", + listener: (guild: Guild, oldGuild: GuildOptions) => void + ): *; + on(event: "hello", listener: (trace: string[], id: number) => void): *; + on(event: "messageCreate", listener: (message: Message) => void): *; + on( + event: "messageDelete" | "messageReactionRemoveAll", + listener: (message: PossiblyUncachedMessage) => void + ): *; + on( + event: "messageDeleteBulk", + listener: (messages: PossiblyUncachedMessage[]) => void + ): *; + on( + event: "messageReactionAdd" | "messageReactionRemove", + listener: ( + message: PossiblyUncachedMessage, + emoji: Emoji, + userID: string + ) => void + ): *; + on( + event: "messageUpdate", + listener: ( + message: Message, + oldMessage?: { + attachments: Attachment[], + embeds: Embed[], + content: string, + editedTimestamp?: number, + mentionedBy?: any, + tts: boolean, + mentions: string[], + roleMentions: string[], + channelMentions: string[] + } + ) => void + ): *; + on( + event: "presenceUpdate", + listener: ( + other: Member | Relationship, + oldPresence?: OldPresence + ) => void + ): *; + on( + event: "rawWS" | "unknown", + listener: (packet: RawPacket, id: number) => void + ): *; + on( + event: "relationshipAdd" | "relationshipRemove", + listener: (relationship: Relationship) => void + ): *; + on( + event: "relationshipUpdate", + listener: ( + relationship: Relationship, + oldRelationship: { + type: number + } + ) => void + ): *; + on( + event: "shardPreReady" | "connect", + listener: (id: number) => void + ): *; + on( + event: "typingStart", + listener: (channel: TextableChannel, user: User) => void + ): *; + on( + event: "unavailableGuildCreate", + listener: (guild: UnavailableGuild) => void + ): *; + on( + event: "userUpdate", + listener: ( + user: User, + oldUser: { + username: string, + discriminator: string, + avatar?: string + } + ) => void + ): *; + on( + event: "voiceChannelJoin", + listener: (member: Member, newChannel: VoiceChannel) => void + ): *; + on( + event: "voiceChannelLeave", + listener: (member: Member, oldChannel: VoiceChannel) => void + ): *; + on( + event: "voiceChannelSwitch", + listener: ( + member: Member, + newChannel: VoiceChannel, + oldChannel: VoiceChannel + ) => void + ): *; + on( + event: "voiceStateUpdate", + listener: (member: Member, oldState: OldVoiceState) => void + ): *; + on( + event: "warn" | "debug", + listener: (message: string, id: number) => void + ): *; + on(event: "disconnect", listener: (err: Error) => void): *; + on(event: "resume", listener: () => void): *; + toJSON(simple?: boolean): JSONCache; + sendWS(op: number, _data: { [key: string]: any }): void; + } + declare export class Command { + subcommands: { + [s: string]: Command + }; + constructor( + label: string, + generate: CommandGenerator, + options?: CommandOptions + ): *; + registerSubcommandAlias(alias: string, label: string): void; + registerSubcommand( + label: string, + generator: CommandGenerator, + options?: CommandOptions + ): void; + unregisterSubcommand(label: string): void; + } + declare export class CommandClient extends Client { + commands: { + [s: string]: Command + }; + constructor( + token: string, + options?: ClientOptions, + commandOptions?: CommandClientOptions + ): *; + onMessageCreate(msg: Message): void; + registerGuildPrefix(guildID: string, prefix: string[] | string): void; + registerCommandAlias(alias: string, label: string): void; + registerCommand( + label: string, + generator: CommandGenerator, + options?: CommandOptions + ): Command; + unregisterCommand(label: string): void; + } } diff --git a/flow-typed/npm/koa_v2.x.x.js b/flow-typed/npm/koa_v2.x.x.js index ee40df2..216c4a7 100644 --- a/flow-typed/npm/koa_v2.x.x.js +++ b/flow-typed/npm/koa_v2.x.x.js @@ -81,31 +81,31 @@ declare module 'koa' { // if you meet some error here, temporarily add an additional annotation // like: `request.accepts((['json', 'text']:Array))` to fix it. ((arg: string, ...args: string[]) => string|false) & - ( () => string[] ) , // return the old value. + (() => string[]), // return the old value. // https://github.com/jshttp/accepts/blob/master/index.js#L153 // https://github.com/jshttp/accepts/blob/master/test/charset.js - acceptsCharsets: ( (args: string[]) => buffer$Encoding|false)& + acceptsCharsets: ((args: string[]) => buffer$Encoding|false)& // ToDo: https://github.com/facebook/flow/issues/3009 // if you meet some error here, see L70. - ( (arg: string, ...args: string[]) => buffer$Encoding|false ) & - ( () => string[] ), + ((arg: string, ...args: string[]) => buffer$Encoding|false) & + (() => string[]), // https://github.com/jshttp/accepts/blob/master/index.js#L119 // https://github.com/jshttp/accepts/blob/master/test/encoding.js - acceptsEncodings: ( (args: string[]) => string|false)& + acceptsEncodings: ((args: string[]) => string|false)& // ToDo: https://github.com/facebook/flow/issues/3009 // if you meet some error here, see L70. - ( (arg: string, ...args: string[]) => string|false ) & - ( () => string[] ), + ((arg: string, ...args: string[]) => string|false) & + (() => string[]), // https://github.com/jshttp/accepts/blob/master/index.js#L185 // https://github.com/jshttp/accepts/blob/master/test/language.js - acceptsLanguages: ( (args: string[]) => string|false) & + acceptsLanguages: ((args: string[]) => string|false) & // ToDo: https://github.com/facebook/flow/issues/3009 // if you meet some error here, see L70. - ( (arg: string, ...args: string[]) => string|false ) & - ( () => string[] ), + ((arg: string, ...args: string[]) => string|false) & + (() => string[]), get: (field: string) => string, @@ -116,9 +116,9 @@ declare module 'koa' { * If there is no content type, `false` is returned. * Otherwise, it returns the first `type` that matches. */ - is: ( (args: string[]) => null|false|string)& - ( (arg: string, ...args: string[]) => null|false|string ) & - ( () => string ), // should return the mime type + is: ((args: string[]) => null|false|string)& + ((arg: string, ...args: string[]) => null|false|string) & + (() => string), // should return the mime type toJSON: () => RequestJSON, inspect: () => RequestInspect, @@ -167,9 +167,9 @@ declare module 'koa' { get: (field: string) => string, // https://github.com/jshttp/type-is/blob/master/test/test.js // https://github.com/koajs/koa/blob/v2.x/lib/response.js#L382 - is: ( (arg: string[]) => false|string) & - ( (arg: string, ...args: string[]) => false|string ) & - ( () => string ), // should return the mime type + is: ((arg: string[]) => false|string) & + ((arg: string, ...args: string[]) => false|string) & + (() => string), // should return the mime type redirect: (url: string, alt?: string) => void, remove: (field: string) => void, // https://github.com/koajs/koa/blob/v2.x/lib/response.js#L418 @@ -198,7 +198,7 @@ declare module 'koa' { declare type CookiesSetOptions = { domain: string, // domain of the cookie (no default). maxAge: number, // milliseconds from Date.now() for expiry - expires?: Date, //cookie's expiration date (expires at the end of session by default). + expires?: Date, // cookie's expiration date (expires at the end of session by default). path?: string, // the path of the cookie (/ by default). secure?: boolean, // false by default for HTTP, true by default for HTTPS httpOnly?: boolean, // a boolean indicating whether the cookie is only to be sent over HTTP(S), @@ -210,7 +210,7 @@ declare module 'koa' { get: (name: string, options?: {signed: boolean}) => string|void, set: ((name: string, value: string, options?: CookiesSetOptions) => Context)& // delete cookie (an outbound header with an expired date is used.) - ( (name: string) => Context), + ((name: string) => Context), }; // The default props of context come from two files // `application.createContext` & `context.js` @@ -233,7 +233,7 @@ declare module 'koa' { // if (!(err instanceof Error)) err = new Error(`non-error thrown: ${err}`); onerror: (err?: mixed) => void, // context.md#L88 - throw: ( status: number, msg?: string, opts?: {} ) => void, + throw: (status: number, msg?: string, opts?: {}) => void, toJSON(): ContextJSON, inspect(): ContextJSON, diff --git a/package.json b/package.json index c1d2fab..cb95934 100644 --- a/package.json +++ b/package.json @@ -9,11 +9,12 @@ "build:ui": "NODE_ENV=production next build ui", "build:server": "NODE_ENV=production babel --delete-dir-on-start -d dist .", "remotedebug": "remotedebug_ios_webkit_adapter --port=9000 > /dev/null", - "test": "jest" + "test": "jest", + "rpcrepl": "babel-node util/rpcrepl.js" }, "dependencies": { "@discordjs/uws": "^11.149.1", - "@primer/components": "^12.0.0", + "@primer/components": "^12.0.1", "bufferutil": "^4.0.1", "chalk": "^2.4.2", "color": "^3.1.0", @@ -32,7 +33,7 @@ "koa-bodyparser": "^4.2.1", "koa-session": "^5.10.1", "kompression": "^1.0.0", - "ksuid": "^1.1.3", + "ksuid": "^1.2.0", "lru-cache": "^5.1.1", "moment": "^2.24.0", "moniker": "^0.1.2", @@ -42,8 +43,8 @@ "pg": "^7.9.0", "pg-hstore": "^2.3.2", "primer": "^11.0.0", - "react": "^16.8.4", - "react-dom": "^16.8.4", + "react": "^16.8.6", + "react-dom": "^16.8.6", "react-redux": "^6.0.1", "react-typist": "^2.0.5", "redux": "^4.0.1", @@ -51,29 +52,33 @@ "redux-thunk": "^2.3.0", "sequelize": "^5.1.0", "socket.io": "^2.2.0", - "styled-components": "^4.1.3", - "superagent": "^4.1.0", + "styled-components": "^4.2.0", + "superagent": "^5.0.2", "zlib-sync": "^0.1.4" }, "devDependencies": { "@babel/cli": "^7.2.3", - "@babel/node": "^7.2.2", "@babel/core": "^7.4.0", + "@babel/node": "^7.2.2", "@babel/plugin-proposal-class-properties": "^7.4.0", "@babel/plugin-proposal-optional-chaining": "^7.2.0", "@babel/plugin-syntax-dynamic-import": "^7.2.0", "@babel/plugin-transform-runtime": "^7.4.0", "@babel/preset-env": "^7.4.2", "@babel/preset-flow": "^7.0.0", + "await-outside": "^2.1.2", "babel-eslint": "^10.0.1", "babel-plugin-styled-components": "^1.10.0", "babel-plugin-transform-flow-strip-types": "^6.22.0", - "chokidar": "^2.1.2", + "chokidar": "^2.1.5", + "enzyme": "^3.9.0", + "enzyme-adapter-react-16": "^1.11.2", "eslint-plugin-flowtype": "^3.4.2", "flow-bin": "^0.95.1", "flow-typed": "^2.5.1", "jest": "^24.5.0", "npm-run-all": "^4.1.5", + "react-test-renderer": "^16.8.6", "standard": "12.0.1" }, "standard": { @@ -84,5 +89,19 @@ "globals": [ "$Shape" ] + }, + "jest": { + "projects": [ + { + "displayName": "server", + "rootDir": "", + "testPathIgnorePatterns": ["/node_modules/","/ui/"] + }, { + "displayName": "ui", + "rootDir": "/ui", + "setupFiles": ["/setupTests.js"], + "testPathIgnorePatterns": ["/node_modules/", "/.next/"] + } + ] } } diff --git a/rpc/_security.js b/rpc/_security.js index 4cc5634..ce92529 100644 --- a/rpc/_security.js +++ b/rpc/_security.js @@ -3,40 +3,156 @@ import { type AppContext } from '../Roleypoly' import { type Context } from 'koa' import RPCError from './_error' -// import logger from '../logger' -// const log = logger(__filename) +import logger from '../logger' +const log = logger(__filename) const PermissionError = new RPCError('User does not have permission to call this RPC.', 403) -// export const bot = (fn: Function) => (secret: string, ...args: any[]) => { -// if (secret !== process.env.SHARED_SECRET) { -// log.error('unauthenticated bot request', { secret }) -// return { err: 'unauthenticated' } -// } +const logFacts = ( + ctx: Context, + extra: { [x:string]: any } = {} +) => ({ + fn: (ctx.request.body: any).fn, + ip: ctx.ip, + user: ctx.session.userId, + ...extra +}) -// return fn(...args) -// } +export const authed = ( + $: AppContext, + fn: (ctx: Context, ...args: any[]) => any, + silent: boolean = false +) => async ( + ctx: Context, + ...args: any[] +) => { + if (await $.auth.isLoggedIn(ctx)) { + return fn(ctx, ...args) + } -export const root = ($: AppContext, fn: Function) => (ctx: Context, ...args: any[]) => { + if ($.config.dev) { + log.debug('authed failed') + throw new RPCError('User is not logged in', 403) + } + + if (!silent) { + log.info('RPC call authed check fail', logFacts(ctx)) + } + + throw PermissionError +} + +export const root = ( + $: AppContext, + fn: (ctx: Context, ...args: any[]) => any, + silent: boolean = false +) => authed($, ( + ctx: Context, + ...args: any[] +) => { if ($.discord.isRoot(ctx.session.userId)) { return fn(ctx, ...args) } - throw PermissionError -} + if ($.config.dev) { + log.debug('root failed') + throw new RPCError('User is not root', 403) + } -export const manager = ($: AppContext, fn: Function) => (ctx: Context, server: string, ...args: any[]) => { + if (!silent) { + log.info('RPC call root check fail', logFacts(ctx)) + } + + throw PermissionError +}) + +export const manager = ( + $: AppContext, + fn: (ctx: Context, server: string, ...args: any[]) => any, + silent: boolean = false +) => member($, ( + ctx: Context, + server: string, + ...args: any[] +) => { if ($.discord.canManageRoles(server, ctx.session.userId)) { return fn(ctx, server, ...args) } - throw PermissionError -} + if ($.config.dev) { + log.debug('manager failed') + throw new RPCError('User is not a role manager', 403) + } -export const member = ($: AppContext, fn: Function) => (ctx: Context, server: string, ...args: any[]) => { + if (!silent) { + log.info('RPC call manager check fail', logFacts(ctx, { server })) + } + + throw PermissionError +}) + +export const member = ( + $: AppContext, + fn: (ctx: Context, server: string, ...args: any[]) => any, + silent: boolean = false +) => authed($, ( + ctx: Context, + server: string, + ...args: any[] +) => { if ($.discord.isMember(server, ctx.session.userId)) { return fn(ctx, server, ...args) } + if ($.config.dev) { + log.debug('member failed') + throw new RPCError('User is not a member of this server', 403) + } + + if (!silent) { + log.info('RPC call member check fail', logFacts(ctx, { server })) + } + + throw PermissionError +}) + +export const any = ( + $: AppContext, + fn: (ctx: Context, ...args: any[]) => any, + silent: boolean = false +) => (...args: any) => fn(...args) + +type Handler = (ctx: Context, ...args: any[]) => any +type Strategy = ( + $: AppContext, + fn: Handler, + silent?: boolean +) => any +type StrategyPair = [ Strategy, Handler ] + +/** + * Weird func but ok -- test that a strategy doesn't fail, and run the first that doesn't. + */ +export const decide = ( + $: AppContext, + ...strategies: StrategyPair[] +) => async (...args: any) => { + for (let [ strat, handler ] of strategies) { + if (strat === null) { + strat = any + } + + try { + return await strat($, handler, true)(...args) + } catch (e) { + continue + } + } + + // if we reach the end, just throw + if ($.config.dev) { + log.info('decide failed for', strategies.map(v => v[0].name)) + } + throw PermissionError } diff --git a/rpc/index.js b/rpc/index.js index 11c0b92..0636a84 100644 --- a/rpc/index.js +++ b/rpc/index.js @@ -3,11 +3,20 @@ import logger from '../logger' import fnv from 'fnv-plus' import autoloader from './_autoloader' import RPCError from './_error' -import type Roleypoly from '../Roleypoly' -import type betterRouter from 'koa-better-router' -import { type Context } from 'koa' +import type Roleypoly, { Router } from '../Roleypoly' +import type { Context } from 'koa' const log = logger(__filename) +export type RPCIncoming = { + fn: string, + args: any[] +} + +export type RPCOutgoing = { + hash: string, + response: any +} + export default class RPCServer { ctx: Roleypoly @@ -21,6 +30,7 @@ export default class RPCServer { constructor (ctx: Roleypoly) { this.ctx = ctx this.reload() + ctx.addRouteHook(this.hookRoutes) } reload () { @@ -32,33 +42,33 @@ export default class RPCServer { this.mapHash = fnv.hash(Object.keys(this.rpcMap)).str() // call map for the client. - this.rpcCalls = Object.keys(this.rpcMap).map(fn => ({ name: this.rpcMap[fn].name, args: 0 })) + this.rpcCalls = Object.keys(this.rpcMap).map(fn => ({ name: fn, args: 0 })) } - hookRoutes (router: betterRouter) { + hookRoutes = (router: Router) => { // RPC call reporter. // this is NEVER called in prod. // it is used to generate errors if RPC calls don't exist or are malformed in dev. - router.get('/api/_rpc', async (ctx) => { - ctx.body = { + router.get('/api/_rpc', async (ctx: Context) => { + ctx.body = ({ hash: this.mapHash, available: this.rpcCalls - } + }: any) ctx.status = 200 return true }) - router.post('/api/_rpc', this.handleRPC.bind(this)) + router.post('/api/_rpc', this.handleRPC) } - async handleRPC (ctx: Context) { + handleRPC = async (ctx: Context) => { // handle an impossible situation if (!(ctx.request.body instanceof Object)) { return this.rpcError(ctx, null, new RPCError('RPC format was very incorrect.', 400)) } // check if RPC exists - const { fn, args } = ctx.request.body + const { fn, args } = (ctx.request.body: RPCIncoming) if (!(fn in this.rpcMap)) { return this.rpcError(ctx, null, new RPCError(`RPC call ${fn}(...) not found.`, 404)) diff --git a/rpc/rpc_test.js b/rpc/rpc_test.js index 8bf52d9..6d24e77 100644 --- a/rpc/rpc_test.js +++ b/rpc/rpc_test.js @@ -1,14 +1,23 @@ // @flow import { type AppContext } from '../Roleypoly' import { type Context } from 'koa' +import * as secureAs from './_security' export default ($: AppContext) => ({ // i want RPC to be *dead* simple. // probably too simple. hello (_: Context, hello: string) { - return `hello, ${hello}!` + return `hello, ${hello} and all who inhabit it` }, testJSON (_: Context, inobj: {}) { return inobj - } + }, + + testDecide: secureAs.decide($, + [ secureAs.root, () => { return 'root' } ], + [ secureAs.manager, () => { return 'manager' } ], + [ secureAs.member, () => { return 'member' } ], + [ secureAs.authed, () => { return 'authed' } ], + [ secureAs.any, () => { return 'guest' } ] + ) }) diff --git a/rpc/servers.js b/rpc/servers.js index d820dc3..2238dcf 100644 --- a/rpc/servers.js +++ b/rpc/servers.js @@ -2,11 +2,11 @@ import { type AppContext } from '../Roleypoly' import { type Context } from 'koa' import { type Guild } from 'eris' -import { root } from './_security' +import * as secureAs from './_security' export default ($: AppContext) => ({ - rootGetAllServers: root($, (ctx: Context) => { + rootGetAllServers: secureAs.root($, (ctx: Context) => { return $.discord.client.guilds.map<{ url: string, name: string, @@ -24,7 +24,7 @@ export default ($: AppContext) => ({ return $.P.serverSlug(srv) }, - getServer (ctx: Context, id: string) { + getServer: secureAs.member($, (ctx: Context, id: string) => { const { userId } = (ctx.session: { userId: string }) const srv = $.discord.client.guilds.get(id) @@ -32,13 +32,6 @@ export default ($: AppContext) => ({ return { err: 'not_found' } } - if (userId == null) { - return { - id: id, - server: $.P.serverSlug(srv) - } - } - let gm if (srv.members.has(userId)) { gm = $.discord.gm(id, userId) @@ -51,5 +44,5 @@ export default ($: AppContext) => ({ } return $.P.presentableServer(srv, gm) - } + }) }) diff --git a/rpc/user.js b/rpc/user.js index 11d644b..cb11190 100644 --- a/rpc/user.js +++ b/rpc/user.js @@ -1,11 +1,18 @@ // @flow import { type AppContext } from '../Roleypoly' import { type Context } from 'koa' -// import { type Guild } from 'discord.js' +import * as secureAs from './_security' export default ($: AppContext) => ({ - getCurrentUser (ctx: Context) { - const { userId } = ctx.session + + getCurrentUser: secureAs.authed($, (ctx: Context) => { return $.discord.getUserPartial(ctx.session.userId) - } + }), + + isRoot: secureAs.root($, () => { + return true + }) + + + }) diff --git a/services/discord.js b/services/discord.js index 19555df..79f8fcb 100644 --- a/services/discord.js +++ b/services/discord.js @@ -8,6 +8,7 @@ import LRU from 'lru-cache' import { OrderedSet } from 'immutable' import superagent from 'superagent' import type { AuthTokens } from './auth' +import type { IFetcher } from './discord/types' type DiscordServiceConfig = { token: string, @@ -59,6 +60,8 @@ export default class DiscordService extends Service { oauthCallback: string + fetcher: IFetcher + constructor (ctx: AppContext) { super(ctx) this.ctx = ctx @@ -96,10 +99,14 @@ export default class DiscordService extends Service { } }) this.bot = new Bot(this) + const BotFetcher = require('./discord/botFetcher').default + this.fetcher = new BotFetcher(this) } else { this.client = new Eris(`Bot ${this.cfg.token}`, { restMode: true }) + const RestFetcher = require('./discord/restFetcher').default + this.fetcher = new RestFetcher(this) } } diff --git a/services/discord.old.js b/services/discord.old.js new file mode 100644 index 0000000..b303f65 --- /dev/null +++ b/services/discord.old.js @@ -0,0 +1,346 @@ +/* +import Service from './Service' +import superagent from 'superagent' +// import invariant from 'invariant' +import { type AppContext } from '../Roleypoly' +import Eris from 'eris' +import Bot from '../bot' +import type { AuthTokens } from './auth' +export type UserPartial = { + id: string, + username: string, + discriminator: string, + avatar: string +} + +export type Permissions = { + isAdmin: boolean, + canManageRoles: boolean +} + +// type ChatCommandHandler = (message: Message, matches: string[], reply: (text: string) => Promise) => Promise|void +// type ChatCommand = { +// regex: RegExp, +// handler: ChatCommandHandler +// } + +class DiscordService extends Service { + botToken: string = process.env.DISCORD_BOT_TOKEN || '' + clientId: string = process.env.DISCORD_CLIENT_ID || '' + clientSecret: string = process.env.DISCORD_CLIENT_SECRET || '' + oauthCallback: string = process.env.OAUTH_AUTH_CALLBACK || '' + isBot: boolean = process.env.IS_BOT === 'true' + + appUrl: string + botCallback: string + rootUsers: Set + client: Eris + cmds: ChatCommand[] + bot: Bot + constructor (ctx: AppContext) { + super(ctx) + this.appUrl = ctx.config.appUrl + + this.oauthCallback = `${this.appUrl}/api/oauth/callback` + this.botCallback = `${this.appUrl}/api/oauth/bot/callback` + this.rootUsers = new Set((process.env.ROOT_USERS || '').split(',')) + this.bot = new Bot(this) + } + + ownGm (server: string) { + return this.gm(server, this.client.user.id) + } + + fakeGm ({ id = '0', nickname = '[none]', displayHexColor = '#ffffff' }: $Shape<{ id: string, nickname: string, displayHexColor: string }>) { // eslint-disable-line no-undef + return { id, nickname, displayHexColor, __faked: true, roles: { has () { return false } } } + } + + isRoot (id: string): boolean { + return this.rootUsers.has(id) + } + + getRelevantServers (userId: string): Collection { + return this.client.guilds.filter((g) => g.members.has(userId)) + } + + gm (serverId: string, userId: string): ?GuildMember { + const s = this.client.guilds.get(serverId) + if (s == null) { + return null + } + return s.members.get(userId) + } + + getRoles (server: string) { + const s = this.client.guilds.get(server) + if (s == null) { + return null + } + return s.roles + } + + getPermissions (gm: GuildMember): Permissions { + if (this.isRoot(gm.id)) { + return { + isAdmin: true, + canManageRoles: true + } + } + + return { + isAdmin: gm.permissions.has('ADMINISTRATOR'), + canManageRoles: gm.permissions.has('MANAGE_ROLES', true) + } + } + + safeRole (server: string, role: string) { + const rl = this.getRoles(server) + if (rl == null) { + throw new Error(`safeRole can't see ${server}`) + } + const r: ?Role = rl.get(role) + if (r == null) { + throw new Error(`safeRole can't find ${role} in ${server}`) + } + + return r.editable && !r.hasPermission('MANAGE_ROLES', false, true) + } + + // oauth step 2 flow, grab the auth token via code + async getAuthToken (code: string): Promise { + const url = 'https://discordapp.com/api/oauth2/token' + try { + const rsp = + await superagent + .post(url) + .set('Content-Type', 'application/x-www-form-urlencoded') + .send({ + client_id: this.clientId, + client_secret: this.clientSecret, + grant_type: 'authorization_code', + code: code, + redirect_uri: this.oauthCallback + }) + + return rsp.body + } catch (e) { + this.log.error('getAuthToken failed', e) + throw e + } + } + + async getUserFromToken (authToken?: string): Promise { + const url = 'https://discordapp.com/api/v6/users/@me' + try { + if (authToken == null || authToken === '') { + throw new Error('not logged in') + } + + const rsp = + await superagent + .get(url) + .set('Authorization', `Bearer ${authToken}`) + return rsp.body + } catch (e) { + this.log.error('getUser error', e) + throw e + } + } + + getUserPartial (userId: string): ?UserPartial { + const U = this.client.users.get(userId) + if (U == null) { + return null + } + + return { + username: U.username, + discriminator: U.discriminator, + avatar: U.displayAvatarURL, + id: U.id + } + } + + async refreshOAuth ({ refreshToken }: { refreshToken: string }): Promise { + const url = 'https://discordapp.com/api/oauth2/token' + try { + const rsp = + await superagent + .post(url) + .set('Content-Type', 'application/x-www-form-urlencoded') + .send({ + client_id: this.clientId, + client_secret: this.clientSecret, + grant_type: 'refresh_token', + refresh_token: refreshToken, + redirect_uri: this.oauthCallback + }) + + return rsp.body + } catch (e) { + this.log.error('refreshOAuth failed', e) + throw e + } + } + + async revokeOAuth ({ accessToken }: { accessToken: string }) { + const url = 'https://discordapp.com/api/oauth2/token/revoke' + try { + const rsp = + await superagent + .post(url) + .set('Content-Type', 'application/x-www-form-urlencoded') + .send({ + client_id: this.clientId, + client_secret: this.clientSecret, + grant_type: 'access_token', + token: accessToken, + redirect_uri: this.oauthCallback + }) + + return rsp.body + } catch (e) { + this.log.error('revokeOAuth failed', e) + throw e + } + } + + // returns oauth authorize url with IDENTIFY permission + // we only need IDENTIFY because we only use it for matching IDs from the bot + getAuthUrl (state: string): string { + return `https://discordapp.com/oauth2/authorize?client_id=${this.clientId}&redirect_uri=${this.oauthCallback}&response_type=code&scope=identify&state=${state}` + } + + // returns the bot join url with MANAGE_ROLES permission + // MANAGE_ROLES is the only permission we really need. + getBotJoinUrl (): string { + return `https://discordapp.com/oauth2/authorize?client_id=${this.clientId}&scope=bot&permissions=268435456` + } + + mentionResponse (message: Message) { + message.channel.send(`🔰 Assign your roles here! ${this.appUrl}/s/${message.guild.id}`, { disableEveryone: true }) + } + + _cmds () { + const cmds = [ + { + regex: /say (.*)/, + handler (message, matches, r) { + r(matches[0]) + } + }, + { + regex: /set username (.*)/, + async handler (message, matches) { + const { username } = this.client.user + await this.client.user.setUsername(matches[0]) + message.channel.send(`Username changed from ${username} to ${matches[0]}`) + } + }, + { + regex: /stats/, + async handler (message, matches) { + const t = [ + `**Stats** 📈`, + '', + `👩‍❤️‍👩 **Users Served:** ${this.client.guilds.reduce((acc, g) => acc + g.memberCount, 0)}`, + `🔰 **Servers:** ${this.client.guilds.size}`, + `💮 **Roles Seen:** ${this.client.guilds.reduce((acc, g) => acc + g.roles.size, 0)}` + ] + message.channel.send(t.join('\n')) + } + } + ] + // prefix regex with ^ for ease of code + .map(({ regex, ...rest }) => ({ regex: new RegExp(`^${regex.source}`, regex.flags), ...rest })) + + this.cmds = cmds + return cmds + } + + async handleCommand (message: Message) { + const cmd = message.content.replace(`<@${this.client.user.id}> `, '') + this.log.debug(`got command from ${message.author.username}`, cmd) + for (let { regex, handler } of this.cmds) { + const match = regex.exec(cmd) + if (match !== null) { + this.log.debug('command accepted', { cmd, match }) + try { + await handler.call(this, message, match.slice(1)) + return + } catch (e) { + this.log.error('command errored', { e, cmd, message }) + message.channel.send(`❌ **An error occured.** ${e}`) + return + } + } + } + + // nothing matched? + this.mentionResponse(message) + } + + async issueChallenge (author: string) { + // Create a challenge + const chall = await this.ctx.auth.createDMChallenge(author) + + const randomLines = [ + '🐄 A yellow cow is only as bright as it lets itself be. ✨', + '‼ **Did you know?** On this day, at least one second ago, you were right here!', + '<:AkkoC8:437428070849314816> *Reticulating splines...*', + 'Also, you look great today <:YumekoWink:439519270376964107>', + 'btw, ur bright like a <:diamond:544665968631087125>', + `🌈 psst! pssssst! I'm an expensive bot, would you please spare some change? `, + '📣 have suggestions? wanna help out? join my discord! \n*(we\'re nice people, i swear!)*', + `🤖 this bot is at least ${Math.random() * 100}% LIT 🔥`, + '💖 wanna contribute to these witty lines? suggest them on our discord!', + '🛠 I am completely open source, check me out!~ ' + ] + + return ([ + '**Hey there!** ', + '', + `Use this secret code: **${chall.human}**`, + `Or, click here: <${this.ctx.config.appUrl}/magic/${chall.magic}>`, + '', + 'This code will self-destruct in 1 hour.', + '---', + randomLines[Math.floor(Math.random() * randomLines.length)] + ].join('\n')) + } + + handleDM (message: Message) { + switch (message.content.toLowerCase()) { + case 'login': + case 'auth': + case 'log in': + this.issueChallenge(message) + } + } + + handleMessage (message: Message) { + if (message.author.bot) { // drop bot messages + return + } + + if (message.channel.type === 'dm') { + // handle dm + return this.handleDM(message) + } + + if (message.mentions.users.has(this.client.user.id)) { + if (this.rootUsers.has(message.author.id)) { + this.handleCommand(message) + } else { + this.mentionResponse(message) + } + } + } + + async handleJoin (guild: Guild) { + await this.ctx.server.ensure(guild) + } +} + +module.exports = DiscordService +*/ diff --git a/services/discord/botFetcher.js b/services/discord/botFetcher.js new file mode 100644 index 0000000..aa52181 --- /dev/null +++ b/services/discord/botFetcher.js @@ -0,0 +1,17 @@ +// @flow +import type { IFetcher } from './types' +import type DiscordSvc from '../discord' +import type ErisClient from 'eris' + +export default class BotFetcher implements IFetcher { + ctx: DiscordSvc + client: ErisClient + constructor (ctx: DiscordSvc) { + this.ctx = ctx + this.client = ctx.client + } + + getUser = async (id: string) => this.client.users.get(id) + getMember = async (server: string, user: string) => this.client.guilds.get(server)?.members.get(user) + getGuild = async (server: string) => this.client.guilds.get(server) +} diff --git a/services/discord/restFetcher.js b/services/discord/restFetcher.js new file mode 100644 index 0000000..26d74b5 --- /dev/null +++ b/services/discord/restFetcher.js @@ -0,0 +1,36 @@ +// @flow +import type { IFetcher } from './types' +import type DiscordSvc from '../discord' +import type ErisClient, { User, Member, Guild } from 'eris' + +export default class BotFetcher implements IFetcher { + ctx: DiscordSvc + client: ErisClient + constructor (ctx: DiscordSvc) { + this.ctx = ctx + this.client = ctx.client + } + + getUser = async (id: string): Promise => { + try { + return await this.client.getRESTUser(id) + } catch (e) { + return null + } + } + getMember = async (server: string, user: string): Promise => { + try { + return await this.client.getRESTGuildMember(server, user) + } catch (e) { + return null + } + } + + getGuild = async (server: string): Promise => { + try { + return await this.client.getRESTGuild(server) + } catch (e) { + return null + } + } +} diff --git a/services/discord/types.js b/services/discord/types.js new file mode 100644 index 0000000..431495e --- /dev/null +++ b/services/discord/types.js @@ -0,0 +1,14 @@ +// @flow +import type { + User, + Member, + Guild +} from 'eris' + +export interface IFetcher { + getUser: (id: string) => Promise; + + getGuild: (id: string) => Promise; + + getMember: (server: string, user: string) => Promise; +} diff --git a/services/presentation.js b/services/presentation.js index b9a3208..764e24e 100644 --- a/services/presentation.js +++ b/services/presentation.js @@ -29,9 +29,10 @@ export type PresentableRole = { export type PresentableServer = ServerModel & { id: string, - gm: { + gm?: { + color: number | string, nickname: string, - color: string + roles: string[] }, server: ServerSlug, roles: ?PresentableRole[], diff --git a/ui/components/role/Role.test.js b/ui/components/role/Role.test.js new file mode 100644 index 0000000..29c4d00 --- /dev/null +++ b/ui/components/role/Role.test.js @@ -0,0 +1,77 @@ +/* eslint-env jest */ + +import * as React from 'react' +import renderer from 'react-test-renderer' +import { shallow } from 'enzyme' +import Role from './index' + +describe('', () => { + it('renders correctly', () => { + const role = renderer.create() + expect(role).toMatchSnapshot() + }) + + it('triggers onToggle with new state', () => { + let changed = false + const role = shallow( + { changed = next }} + active={false} + /> + ) + role.simulate('click') + expect(changed).toBe(true) + + const role2 = shallow( + { changed = next }} + active + /> + ) + role2.simulate('click') + expect(changed).toBe(false) + }) + + it('fixes colors when they are not set', () => { + const role = shallow() + expect(role.props().style['--role-color-base']).toEqual('hsl(0, 0%, 93.7%)') + }) + + it('has a single space for a name when empty', () => { + const role = shallow() + expect(role.text()).toEqual(' ') + }) + + describe('when disabled,', () => { + it('handles touch hover events', () => { + const role = shallow() + + role.simulate('touchstart') + expect(role.state().hovering).toEqual(true) + expect(role.html()).toMatchSnapshot() // expecting tooltip + expect(role.exists('tooltip')).toEqual(true) + + role.simulate('touchend') + expect(role.state().hovering).toEqual(false) + }) + + it('does not trigger onToggle on click', () => { + let changed = false + const role = shallow( + { changed = true }} + active={changed} + disabled + /> + ) + expect(role.html()).toMatchSnapshot() + role.simulate('click') + + expect(role.html()).toBe(role.html()) + expect(changed).toBe(false) + }) + }) +}) diff --git a/ui/components/role/__snapshots__/Role.test.js.snap b/ui/components/role/__snapshots__/Role.test.js.snap new file mode 100644 index 0000000..8ca7588 --- /dev/null +++ b/ui/components/role/__snapshots__/Role.test.js.snap @@ -0,0 +1,25 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders correctly 1`] = ` +
+ Test Role +
+`; + +exports[` when disabled, does not trigger onToggle on click 1`] = `"
Test Role
"`; + +exports[` when disabled, handles touch hover events 1`] = `"
unsafe role
This role has unsafe permissions.
"`; diff --git a/ui/components/role/__snapshots__/demo.test.js.snap b/ui/components/role/__snapshots__/demo.test.js.snap new file mode 100644 index 0000000..52ce11c --- /dev/null +++ b/ui/components/role/__snapshots__/demo.test.js.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders 1`] = `"
test demo role
"`; diff --git a/ui/components/role/demo.test.js b/ui/components/role/demo.test.js new file mode 100644 index 0000000..d49a8fb --- /dev/null +++ b/ui/components/role/demo.test.js @@ -0,0 +1,18 @@ +/* eslint-env jest */ +import * as React from 'react' +import { shallow } from 'enzyme' +import RoleDemo from './demo' + +describe('', () => { + it('renders', () => { + const demo = shallow() + expect(demo.html()).toMatchSnapshot() + }) + + it('changes state when clicked', () => { + const demo = shallow() + expect(demo.state().active).toEqual(false) + demo.dive().simulate('click') + expect(demo.state().active).toEqual(true) + }) +}) diff --git a/ui/components/role/index.js b/ui/components/role/index.js index 6cb5c61..d5a312f 100644 --- a/ui/components/role/index.js +++ b/ui/components/role/index.js @@ -68,7 +68,7 @@ export default class Role extends React.Component { 'base': Color(color).hsl().string() } - const name = (this.props.role.name !== '') ? this.props.role.name : <>  + const name = (this.props.role.name !== '') ? this.props.role.name : ' ' return { + it('outputs media queries', () => { + const mq = MediaQuery({ + xs: 'font-size: 0.5em;', + sm: 'font-size: 1em;', + md: 'font-size: 1.5em;', + lg: 'font-size: 2em;', + xl: 'font-size: 2.5em;' + }) + + expect(mq).toMatchSnapshot() + }) +}) diff --git a/ui/pages/_internal/_server.js b/ui/pages/_internal/_server.js index 4287d16..ba478b2 100644 --- a/ui/pages/_internal/_server.js +++ b/ui/pages/_internal/_server.js @@ -92,7 +92,7 @@ class Server extends React.Component { {currentServer.server.name} - Roleypoly { this.renderSocial() } - hello {currentServer.gm.nickname} on {currentServer.server.name} ({ view.dirty ? 'dirty' : 'clean' }) + hello {currentServer.gm?.nickname} on {currentServer.server.name} ({ view.dirty ? 'dirty' : 'clean' }) { !view.invalidated && view.categories.map(c =>
{ c.name }
diff --git a/ui/setupTests.js b/ui/setupTests.js new file mode 100644 index 0000000..3d6cd1d --- /dev/null +++ b/ui/setupTests.js @@ -0,0 +1,4 @@ +import { configure } from 'enzyme' +import Adapter from 'enzyme-adapter-react-16' + +configure({ adapter: new Adapter() }) diff --git a/ui/stores/roles.js b/ui/stores/roles.js index 095e94e..1e34913 100644 --- a/ui/stores/roles.js +++ b/ui/stores/roles.js @@ -1,6 +1,7 @@ // @flow // import { action } from './servers' import { namespaceConfig } from 'fast-redux' +// $FlowFixMe import { OrderedMap, OrderedSet, Set } from 'immutable' import { getCurrentServerState, type ServerState } from './currentServer' @@ -102,5 +103,5 @@ export const renderRoles = (id: string) => (dispatch: *, getState: *) => { render = render.add(getUncategorized(roleMap, render.toSet())) render = render.sortBy(h => (h.position) ? h.position : h.name) - dispatch(updateCurrentView({ server: id, categories: render, invalidated: false, selected: Set(current.gm.roles), originalSelected: Set(current.gm.roles) })) + dispatch(updateCurrentView({ server: id, categories: render, invalidated: false, selected: Set(current.gm?.roles), originalSelected: Set(current.gm?.roles) })) } diff --git a/util/rpcrepl.js b/util/rpcrepl.js new file mode 100644 index 0000000..dfbee8c --- /dev/null +++ b/util/rpcrepl.js @@ -0,0 +1,81 @@ +import dotenv from 'dotenv' +import repl from 'repl' +import os from 'os' +import { addAwaitOutsideToReplServer } from 'await-outside' +import Roleypoly from '../Roleypoly' +import chokidar from 'chokidar' +import logger from '../logger' +process.env.DEBUG = false +process.env.IS_BOT = false + +dotenv.config() + +const log = logger(__filename) + +const RP = new Roleypoly(null, null) +const reset = async (r) => { + await RP.awaitServices() + + r.context.RP = RP + + r.context.ctx = { session: { userId: RP.ctx.discord.cfg.rootUsers.values().next().value } } + r.context.guest = { session: {} } + + r.context.g_rpc = {} + r.context.rpc = {} + r.context.reload = () => { + RP.ctx.RPC.reload() + r.context.$RPC = RP.ctx.RPC.rpcMap + for (let fn in r.context.$RPC) { + r.context.g_rpc[fn] = r.context.$RPC[fn].bind(null, r.context.guest) + r.context.rpc[fn] = r.context.$RPC[fn].bind(null, r.context.ctx) + } + } + + r.context.reload() +} + +const motd = () => { + console.log(`~~ Roleypoly RPC REPL. + \`ctx\` a mocked koa context, defaulting to first root user. + \`rpc\` maps to all rpc functions, prefilled with ctx. + + \`guest\` maps to a mock guest sessioned koa context. + \`g_rpc\` maps to all rpc, prefilled with guest. + + \`$RPC\` maps to all rpc functions as they are. + \`RP\` maps to the Roleypoly app. It does NOT have HTTP stuff running. + \`reload()\` to refresh RPC functions. + \`reset()\` to reset this REPL. + `) +} + +const start = async () => { + if (repl.REPLServer.prototype.setupHistory == null) { + console.log(' * History is available on node v11.10.0 and newer.\n') + } + const r = repl.start('> ') + addAwaitOutsideToReplServer(r) + r.setupHistory && r.setupHistory(os.homedir() + '/.ROLEYPOLY_RPCREPL_HISTORY', (e) => e && console.error(e)) + r.context.reset = async () => { + await reset(r) + motd() + r.displayPrompt() + } + + await r.context.reset() + const rpcWatcher = chokidar.watch('rpc/**', { persistent: true }) + rpcWatcher.on('ready', () => { + rpcWatcher.on('all', (_, path) => { + if (r.context.reload) r.context.reload() + log.info('reloaded RPCs') + r.displayPrompt() + }) + }) +} + +try { + start().catch(e => console.error(e)) +} catch (e) { + console.error(e) +} diff --git a/yarn.lock b/yarn.lock index 15f6b2e..fcc6c04 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1330,10 +1330,10 @@ universal-user-agent "^2.0.0" url-template "^2.0.8" -"@primer/components@^12.0.0": - version "12.0.0" - resolved "https://registry.yarnpkg.com/@primer/components/-/components-12.0.0.tgz#662b5322602cf715d821287a191678c88e993fe2" - integrity sha512-kzqUQzCV2/oeZ2m953fWt3whtVLo8Dc801fju2NsUl+h9NemtQph+n0FDAKb4UaXLTfNUb2rXlnHHeU/Ab5SVw== +"@primer/components@^12.0.1": + version "12.0.1" + resolved "https://registry.yarnpkg.com/@primer/components/-/components-12.0.1.tgz#bf3e457afd123ccb82747bc543a7921bcf31d379" + integrity sha512-jbDLF8xno8+cdfg18wXl+wjE9wyE0u6MISw2Q1M47QV2IK6ZbHYMGcywJGAxFD2kXurX/4qfrIo0zfzwXRzEQw== dependencies: "@githubprimer/octicons-react" "8.1.2" babel-plugin-macros "2.4.2" @@ -1765,6 +1765,11 @@ array-equal@^1.0.0: resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM= +array-filter@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-1.0.0.tgz#baf79e62e6ef4c2a4c0b831232daffec251f9d83" + integrity sha1-uveeYubvTCpMC4MSMtr/7CUfnYM= + array-filter@~0.0.0: version "0.0.1" resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec" @@ -1805,6 +1810,15 @@ array-unique@^0.3.2: resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= +array.prototype.flat@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.1.tgz#812db8f02cad24d3fab65dd67eabe3b8903494a4" + integrity sha512-rVqIs330nLJvfC7JqYvEWwqVr5QjYF1ib02i3YJtR/fICO6527Tjpc/e4Mvmxh3GIePPreRXMdaGyC99YphWEw== + dependencies: + define-properties "^1.1.2" + es-abstract "^1.10.0" + function-bind "^1.1.1" + arraybuffer.slice@~0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675" @@ -1875,6 +1889,14 @@ async-sema@2.1.4: dependencies: double-ended-queue "2.1.0-0" +async-to-gen@~1.3.2: + version "1.3.3" + resolved "https://registry.yarnpkg.com/async-to-gen/-/async-to-gen-1.3.3.tgz#d52c9fb4801f0df44abc4d2de1870b48b60e20bb" + integrity sha1-1SyftIAfDfRKvE0t4YcLSLYOILs= + dependencies: + babylon "^6.14.0" + magic-string "^0.19.0" + async@^2.6.1: version "2.6.2" resolved "https://registry.yarnpkg.com/async/-/async-2.6.2.tgz#18330ea7e6e313887f5d2f2a904bac6fe4dd5381" @@ -1908,6 +1930,13 @@ autodll-webpack-plugin@0.4.2: webpack-merge "^4.1.0" webpack-sources "^1.0.1" +await-outside@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/await-outside/-/await-outside-2.1.2.tgz#134e7c357c381522b04941abce271000b733b45f" + integrity sha1-E058NXw4FSKwSUGrzicQALcztF8= + dependencies: + async-to-gen "~1.3.2" + aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" @@ -2064,6 +2093,11 @@ babel-types@6.26.0: lodash "^4.17.4" to-fast-properties "^1.0.3" +babylon@^6.14.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" + integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ== + backo2@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" @@ -2074,7 +2108,7 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= -base-convert-int-array@^1.0.0: +base-convert-int-array@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/base-convert-int-array/-/base-convert-int-array-1.0.1.tgz#5b4ddbfa72d2d3b5f75dd86cd32fe3dc8e7e81fe" integrity sha512-NWqzaoXx8L/SS32R+WmKqnQkVXVYl2PwNJ68QV3RAlRRL1uV+yxJT66abXI1cAvqCXQTyXr7/9NN4Af90/zDVw== @@ -2204,6 +2238,11 @@ bodymovin@^4.13.0: resolved "https://registry.yarnpkg.com/bodymovin/-/bodymovin-4.13.0.tgz#06d97c0130e2334dccb33bea638136f88a74540b" integrity sha1-Btl8ATDiM03MszvqY4E2+Ip0VAs= +boolbase@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -2588,7 +2627,19 @@ charenc@~0.0.1: resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= -chokidar@^2.0.2, chokidar@^2.0.3, chokidar@^2.1.2: +cheerio@^1.0.0-rc.2: + version "1.0.0-rc.2" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.2.tgz#4b9f53a81b27e4d5dac31c0ffd0cfa03cc6830db" + integrity sha1-S59TqBsn5NXawxwP/Qz6A8xoMNs= + dependencies: + css-select "~1.2.0" + dom-serializer "~0.1.0" + entities "~1.1.1" + htmlparser2 "^3.9.1" + lodash "^4.15.0" + parse5 "^3.0.1" + +chokidar@^2.0.2, chokidar@^2.0.3: version "2.1.2" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.2.tgz#9c23ea40b01638439e0513864d362aeacc5ad058" integrity sha512-IwXUx0FXc5ibYmPC2XeEj5mpXoV66sR+t3jqu2NS2GYwCktt3KF1/Qqjws/NkegajBA4RbZ5+DDwlOiJsxDHEg== @@ -2607,6 +2658,25 @@ chokidar@^2.0.2, chokidar@^2.0.3, chokidar@^2.1.2: optionalDependencies: fsevents "^1.2.7" +chokidar@^2.1.5: + version "2.1.5" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.5.tgz#0ae8434d962281a5f56c72869e79cb6d9d86ad4d" + integrity sha512-i0TprVWp+Kj4WRPtInjexJ8Q+BqTE909VpH8xVhXrJkoc5QC8VO9TryGOqTr+2hljzc1sC62t22h5tZePodM/A== + dependencies: + anymatch "^2.0.0" + async-each "^1.0.1" + braces "^2.3.2" + glob-parent "^3.1.0" + inherits "^2.0.3" + is-binary-path "^1.0.0" + is-glob "^4.0.0" + normalize-path "^3.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.2.1" + upath "^1.1.1" + optionalDependencies: + fsevents "^1.2.7" + chownr@^1.0.1, chownr@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.1.tgz#54726b8b8fff4df053c42187e801fb4412df1494" @@ -2763,7 +2833,7 @@ combined-stream@^1.0.6, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" -commander@^2.8.1, commander@~2.19.0: +commander@^2.19.0, commander@^2.8.1, commander@~2.19.0: version "2.19.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== @@ -2788,7 +2858,7 @@ component-bind@1.0.0: resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1" integrity sha1-AMYIq33Nk4l8AAllGx06jh5zu9E= -component-emitter@1.2.1, component-emitter@^1.2.0, component-emitter@^1.2.1: +component-emitter@1.2.1, component-emitter@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY= @@ -3025,6 +3095,16 @@ css-color-keywords@^1.0.0: resolved "https://registry.yarnpkg.com/css-color-keywords/-/css-color-keywords-1.0.0.tgz#fea2616dc676b2962686b3af8dbdbe180b244e05" integrity sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU= +css-select@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" + integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg= + dependencies: + boolbase "~1.0.0" + css-what "2.1" + domutils "1.5.1" + nth-check "~1.0.1" + css-to-react-native@^2.2.2: version "2.3.0" resolved "https://registry.yarnpkg.com/css-to-react-native/-/css-to-react-native-2.3.0.tgz#bf80d24ec4a08e430306ef429c0586e6ed5485f7" @@ -3034,6 +3114,11 @@ css-to-react-native@^2.2.2: css-color-keywords "^1.0.0" postcss-value-parser "^3.3.0" +css-what@2.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" + integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg== + cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0": version "0.3.6" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.6.tgz#f85206cee04efa841f3c5982a74ba96ab20d65ad" @@ -3163,7 +3248,7 @@ default-require-extensions@^2.0.0: dependencies: strip-bom "^3.0.0" -define-properties@^1.1.2: +define-properties@^1.1.2, define-properties@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== @@ -3291,6 +3376,11 @@ diffie-hellman@^5.0.0: miller-rabin "^4.0.0" randombytes "^2.0.0" +discontinuous-range@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/discontinuous-range/-/discontinuous-range-1.0.0.tgz#e38331f0844bba49b9a9cb71c771585aab1bc65a" + integrity sha1-44Mx8IRLukm5qctxx3FYWqsbxlo= + doctrine@1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" @@ -3306,11 +3396,24 @@ doctrine@^2.1.0: dependencies: esutils "^2.0.2" +dom-serializer@0, dom-serializer@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0" + integrity sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA== + dependencies: + domelementtype "^1.3.0" + entities "^1.1.1" + domain-browser@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== +domelementtype@1, domelementtype@^1.3.0, domelementtype@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" + integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== + domexception@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90" @@ -3318,6 +3421,29 @@ domexception@^1.0.1: dependencies: webidl-conversions "^4.0.2" +domhandler@^2.3.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" + integrity sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA== + dependencies: + domelementtype "1" + +domutils@1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" + integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8= + dependencies: + dom-serializer "0" + domelementtype "1" + +domutils@^1.5.1: + version "1.7.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" + integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== + dependencies: + dom-serializer "0" + domelementtype "1" + dotenv@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-7.0.0.tgz#a2be3cd52736673206e8a85fb5210eea29628e7c" @@ -3462,6 +3588,62 @@ enhanced-resolve@^4.1.0: memory-fs "^0.4.0" tapable "^1.0.0" +entities@^1.1.1, entities@~1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" + integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== + +enzyme-adapter-react-16@^1.11.2: + version "1.11.2" + resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.11.2.tgz#8efeafb27e96873a5492fdef3f423693182eb9d4" + integrity sha512-2ruTTCPRb0lPuw/vKTXGVZVBZqh83MNDnakMhzxhpJcIbneEwNy2Cv0KvL97pl57/GOazJHflWNLjwWhex5AAA== + dependencies: + enzyme-adapter-utils "^1.10.1" + object.assign "^4.1.0" + object.values "^1.1.0" + prop-types "^15.7.2" + react-is "^16.8.4" + react-test-renderer "^16.0.0-0" + semver "^5.6.0" + +enzyme-adapter-utils@^1.10.1: + version "1.10.1" + resolved "https://registry.yarnpkg.com/enzyme-adapter-utils/-/enzyme-adapter-utils-1.10.1.tgz#58264efa19a7befdbf964fb7981a108a5452ac96" + integrity sha512-oasinhhLoBuZsIkTe8mx0HiudtfErUtG0Ooe1FOplu/t4c9rOmyG5gtrBASK6u4whHIRWvv0cbZMElzNTR21SA== + dependencies: + function.prototype.name "^1.1.0" + object.assign "^4.1.0" + object.fromentries "^2.0.0" + prop-types "^15.7.2" + semver "^5.6.0" + +enzyme@^3.9.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-3.9.0.tgz#2b491f06ca966eb56b6510068c7894a7e0be3909" + integrity sha512-JqxI2BRFHbmiP7/UFqvsjxTirWoM1HfeaJrmVSZ9a1EADKkZgdPcAuISPMpoUiHlac9J4dYt81MC5BBIrbJGMg== + dependencies: + array.prototype.flat "^1.2.1" + cheerio "^1.0.0-rc.2" + function.prototype.name "^1.1.0" + has "^1.0.3" + html-element-map "^1.0.0" + is-boolean-object "^1.0.0" + is-callable "^1.1.4" + is-number-object "^1.0.3" + is-regex "^1.0.4" + is-string "^1.0.4" + is-subset "^0.1.1" + lodash.escape "^4.0.1" + lodash.isequal "^4.5.0" + object-inspect "^1.6.0" + object-is "^1.0.1" + object.assign "^4.1.0" + object.entries "^1.0.4" + object.values "^1.0.4" + raf "^3.4.0" + rst-selector-parser "^2.2.3" + string.prototype.trim "^1.1.2" + eris@^0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/eris/-/eris-0.9.0.tgz#a2796839f7768cd40645c2c6b1bf0c86ba3147f3" @@ -3498,18 +3680,7 @@ error-inject@^1.0.0: resolved "https://registry.yarnpkg.com/error-inject/-/error-inject-1.0.0.tgz#e2b3d91b54aed672f309d950d154850fa11d4f37" integrity sha1-4rPZG1Su1nLzCdlQ0VSFD6EdTzc= -es-abstract@^1.4.3: - version "1.12.0" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.12.0.tgz#9dbbdd27c6856f0001421ca18782d786bf8a6165" - integrity sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA== - dependencies: - es-to-primitive "^1.1.1" - function-bind "^1.1.1" - has "^1.0.1" - is-callable "^1.1.3" - is-regex "^1.0.4" - -es-abstract@^1.5.1, es-abstract@^1.7.0: +es-abstract@^1.10.0, es-abstract@^1.11.0, es-abstract@^1.12.0, es-abstract@^1.5.0, es-abstract@^1.5.1, es-abstract@^1.7.0: version "1.13.0" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.13.0.tgz#ac86145fdd5099d8dd49558ccba2eaf9b88e24e9" integrity sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg== @@ -3521,6 +3692,17 @@ es-abstract@^1.5.1, es-abstract@^1.7.0: is-regex "^1.0.4" object-keys "^1.0.12" +es-abstract@^1.4.3: + version "1.12.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.12.0.tgz#9dbbdd27c6856f0001421ca18782d786bf8a6165" + integrity sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA== + dependencies: + es-to-primitive "^1.1.1" + function-bind "^1.1.1" + has "^1.0.1" + is-callable "^1.1.3" + is-regex "^1.0.4" + es-to-primitive@^1.1.1, es-to-primitive@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377" @@ -4094,7 +4276,7 @@ form-data@^2.3.3, form-data@~2.3.2: combined-stream "^1.0.6" mime-types "^2.1.12" -formidable@^1.2.0: +formidable@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.1.tgz#70fb7ca0290ee6ff961090415f4b3df3d2082659" integrity sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg== @@ -4183,6 +4365,15 @@ function-bind@^1.0.2, function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function.prototype.name@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.0.tgz#8bd763cc0af860a859cc5d49384d74b932cd2327" + integrity sha512-Bs0VRrTz4ghD8pTmbJQD1mZ8A/mN0ur/jGz+A6FBxPDUPkm1tNfF6bhTYPA7i7aF4lZJVr+OXTNNrnnIl58Wfg== + dependencies: + define-properties "^1.1.2" + function-bind "^1.1.1" + is-callable "^1.1.3" + functional-red-black-tree@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" @@ -4500,6 +4691,13 @@ hosted-git-info@^2.1.4: resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047" integrity sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w== +html-element-map@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/html-element-map/-/html-element-map-1.0.1.tgz#3c4fcb4874ebddfe4283b51c8994e7713782b592" + integrity sha512-BZSfdEm6n706/lBfXKWa4frZRZcT5k1cOusw95ijZsHlI+GdgY0v95h6IzO3iIDf2ROwq570YTwqNPqHcNMozw== + dependencies: + array-filter "^1.0.0" + html-encoding-sniffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8" @@ -4512,6 +4710,18 @@ html-entities@^1.2.0: resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f" integrity sha1-DfKTUfByEWNRXfueVUPl9u7VFi8= +htmlparser2@^3.9.1: + version "3.10.1" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" + integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ== + dependencies: + domelementtype "^1.3.1" + domhandler "^2.3.0" + domutils "^1.5.1" + entities "^1.1.1" + inherits "^2.0.1" + readable-stream "^3.1.1" + http-assert@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/http-assert/-/http-assert-1.4.0.tgz#0e550b4fca6adf121bbeed83248c17e62f593a9a" @@ -4771,6 +4981,11 @@ is-bluebird@^1.0.2: resolved "https://registry.yarnpkg.com/is-bluebird/-/is-bluebird-1.0.2.tgz#096439060f4aa411abee19143a84d6a55346d6e2" integrity sha1-CWQ5Bg9KpBGr7hkUOoTWpVNG1uI= +is-boolean-object@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.0.0.tgz#98f8b28030684219a95f375cfbd88ce3405dff93" + integrity sha1-mPiygDBoQhmpXzdc+9iM40Bd/5M= + is-buffer@^1.1.5, is-buffer@~1.1.1: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" @@ -4900,6 +5115,11 @@ is-glob@^4.0.0: dependencies: is-extglob "^2.1.1" +is-number-object@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.3.tgz#f265ab89a9f445034ef6aff15a8f00b00f551799" + integrity sha1-8mWrian0RQNO9q/xWo8AsA9VF5k= + is-number@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" @@ -4970,6 +5190,16 @@ is-stream@^1.0.0, is-stream@^1.1.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= +is-string@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.4.tgz#cc3a9b69857d621e963725a24caeec873b826e64" + integrity sha1-zDqbaYV9Yh6WNyWiTK7shzuCbmQ= + +is-subset@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-subset/-/is-subset-0.1.1.tgz#8a59117d932de1de00f245fcdd39ce43f1e939a6" + integrity sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY= + is-symbol@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.2.tgz#a055f6ae57192caee329e7a860118b497a950f38" @@ -5772,12 +6002,12 @@ kompression@^1.0.0: koa-is-json "1.0.0" statuses "1.4.0" -ksuid@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/ksuid/-/ksuid-1.1.3.tgz#1b06efb7e8072c24b18689c89a7cb3edf0dbb057" - integrity sha512-vEqIZCSqBxQSsHuw13iDJno5AQ1hT/utiQAk0cyIKEA6udm8UDy63GIAI2BNLRTFWmBDB62TGu/Wc8Q0spi3pQ== +ksuid@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/ksuid/-/ksuid-1.2.0.tgz#72e1b367aee325439924f6a2c6ed522b47627ef8" + integrity sha512-8C+NFTrsHfUp1vPtc1aL1uD2/Fv7IBytdTkqfE6EfG5hNV4bbTAo2+QCGk060gZxUd7zjayObjzJJkyNEdk/+w== dependencies: - base-convert-int-array "^1.0.0" + base-convert-int-array "^1.0.1" string.prototype.padstart "^3.0.0" launch-editor@2.2.1: @@ -5907,17 +6137,32 @@ lodash.assign@^4.0.3, lodash.assign@^4.0.6: resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" integrity sha1-DZnzzNem0mHRm9rrkkUAXShYCOc= +lodash.escape@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-4.0.1.tgz#c9044690c21e04294beaa517712fded1fa88de98" + integrity sha1-yQRGkMIeBClL6qUXcS/e0fqI3pg= + +lodash.flattendeep@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" + integrity sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI= + lodash.get@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= + lodash.sortby@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= -lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0: +lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0: version "4.17.11" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== @@ -5954,6 +6199,13 @@ macos-release@^2.0.0: resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.0.0.tgz#7dddf4caf79001a851eb4fba7fb6034f251276ab" integrity sha512-iCM3ZGeqIzlrH7KxYK+fphlJpCCczyHXc+HhRVbEu9uNTCrzYJjvvtefzeKTCVHd5AP/aD/fzC80JZ4ZP+dQ/A== +magic-string@^0.19.0: + version "0.19.1" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.19.1.tgz#14d768013caf2ec8fdea16a49af82fc377e75201" + integrity sha1-FNdoATyvLsj96hakmvgvw3fnUgE= + dependencies: + vlq "^0.2.1" + make-dir@^1.0.0, make-dir@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" @@ -6029,10 +6281,10 @@ mem@^4.0.0: mimic-fn "^2.0.0" p-is-promise "^2.0.0" -memoize-one@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-4.1.0.tgz#a2387c58c03fff27ca390c31b764a79addf3f906" - integrity sha512-2GApq0yI/b22J2j9rhbrAlsHb0Qcz+7yWxeLG8h+95sl1XPUgeLimQSOdur4Vw7cUhrBHwaUZxWFZueojqNRzA== +memoize-one@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.0.2.tgz#6aba5276856d72fb44ead3efab86432f94ba203d" + integrity sha512-o7lldN4fs/axqctc03NF+PMhd2veRrWeJ2n2GjEzUPBD4F9rmNg4A+bQCACIzwjHJEXuYv4aFFMaH35KZfHUrw== memory-fs@^0.4.0, memory-fs@^0.4.1, memory-fs@~0.4.1: version "0.4.1" @@ -6054,7 +6306,7 @@ merge-stream@^1.0.1: dependencies: readable-stream "^2.0.1" -methods@^1.1.1, methods@^1.1.2: +methods@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= @@ -6243,6 +6495,11 @@ moniker@^0.1.2: resolved "https://registry.yarnpkg.com/moniker/-/moniker-0.1.2.tgz#872dfba575dcea8fa04a5135b13d5f24beccc97e" integrity sha1-hy37pXXc6o+gSlE1sT1fJL7MyX4= +moo@^0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/moo/-/moo-0.4.3.tgz#3f847a26f31cf625a956a87f2b10fbc013bfd10e" + integrity sha512-gFD2xGCl8YFgGHsqJ9NKRVdwlioeW3mI1iqfLNYQOv0+6JRwG58Zk9DIGQgyIaffSYaO1xsKnMaYzzNr1KyIAw== + move-concurrently@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" @@ -6322,6 +6579,17 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= +nearley@^2.7.10: + version "2.16.0" + resolved "https://registry.yarnpkg.com/nearley/-/nearley-2.16.0.tgz#77c297d041941d268290ec84b739d0ee297e83a7" + integrity sha512-Tr9XD3Vt/EujXbZBv6UAHYoLUSMQAxSsTnm9K3koXzjzNWY195NqALeyrzLZBKzAkL3gl92BcSogqrHjD8QuUg== + dependencies: + commander "^2.19.0" + moo "^0.4.3" + railroad-diagrams "^1.0.0" + randexp "0.4.6" + semver "^5.4.1" + needle@^2.2.1: version "2.2.4" resolved "https://registry.yarnpkg.com/needle/-/needle-2.2.4.tgz#51931bff82533b1928b7d1d69e01f1b00ffd2a4e" @@ -6628,6 +6896,13 @@ npm-run-path@^2.0.0: gauge "~2.7.3" set-blocking "~2.0.0" +nth-check@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" + integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== + dependencies: + boolbase "~1.0.0" + number-is-nan@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" @@ -6662,6 +6937,16 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" +object-inspect@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.6.0.tgz#c70b6cbf72f274aab4c34c0c82f5167bf82cf15b" + integrity sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ== + +object-is@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.0.1.tgz#0aa60ec9989a0b3ed795cf4d06f62cf1ad6539b6" + integrity sha1-CqYOyZiaCz7Xlc9NBvYs8a1lObY= + object-keys@^1.0.11: version "1.1.0" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.0.tgz#11bd22348dd2e096a045ab06f6c85bcc340fa032" @@ -6689,6 +6974,26 @@ object.assign@^4.1.0: has-symbols "^1.0.0" object-keys "^1.0.11" +object.entries@^1.0.4: + version "1.1.0" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.0.tgz#2024fc6d6ba246aee38bdb0ffd5cfbcf371b7519" + integrity sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.12.0" + function-bind "^1.1.1" + has "^1.0.3" + +object.fromentries@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.0.tgz#49a543d92151f8277b3ac9600f1e930b189d30ab" + integrity sha512-9iLiI6H083uiqUuvzyY6qrlmc/Gz8hLQFOcb/Ri/0xXFkSNS3ctV+CbE6yM2+AnkYfOB3dGjdzC0wrMLIhQICA== + dependencies: + define-properties "^1.1.2" + es-abstract "^1.11.0" + function-bind "^1.1.1" + has "^1.0.1" + object.getownpropertydescriptors@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16" @@ -6704,6 +7009,16 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" +object.values@^1.0.4, object.values@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.0.tgz#bf6810ef5da3e5325790eaaa2be213ea84624da9" + integrity sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.12.0" + function-bind "^1.1.1" + has "^1.0.3" + on-finished@^2.3.0, on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" @@ -6949,6 +7264,13 @@ parse5@4.0.0: resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" integrity sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA== +parse5@^3.0.1: + version "3.0.3" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-3.0.3.tgz#042f792ffdd36851551cf4e9e066b3874ab45b5c" + integrity sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA== + dependencies: + "@types/node" "*" + parseqs@0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d" @@ -7712,10 +8034,10 @@ qs@^6.5.2, qs@~6.5.2: resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== -qs@^6.6.0: - version "6.6.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.6.0.tgz#a99c0f69a8d26bf7ef012f871cdabb0aee4424c2" - integrity sha512-KIJqT9jQJDQx5h5uAVPimw6yVg2SekOKu959OCtktD3FjzbpvaPr8i4zzg07DOMz+igA4W/aNM7OV8H37pFYfA== +qs@^6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" + integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== querystring-es3@^0.2.0: version "0.2.1" @@ -7727,6 +8049,26 @@ querystring@0.2.0, querystring@^0.2.0: resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= +raf@^3.4.0: + version "3.4.1" + resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39" + integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA== + dependencies: + performance-now "^2.1.0" + +railroad-diagrams@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz#eb7e6267548ddedfb899c1b90e57374559cddb7e" + integrity sha1-635iZ1SN3t+4mcG5Dlc3RVnN234= + +randexp@0.4.6: + version "0.4.6" + resolved "https://registry.yarnpkg.com/randexp/-/randexp-0.4.6.tgz#e986ad5e5e31dae13ddd6f7b3019aa7c87f60ca3" + integrity sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ== + dependencies: + discontinuous-range "1.0.0" + ret "~0.1.10" + random-bytes@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/random-bytes/-/random-bytes-1.0.0.tgz#4f68a1dc0ae58bd3fb95848c30324db75d64360b" @@ -7789,15 +8131,15 @@ react-dom@16.8.0: prop-types "^15.6.2" scheduler "^0.13.0" -react-dom@^16.8.4: - version "16.8.4" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.8.4.tgz#1061a8e01a2b3b0c8160037441c3bf00a0e3bc48" - integrity sha512-Ob2wK7XG2tUDt7ps7LtLzGYYB6DXMCLj0G5fO6WeEICtT4/HdpOi7W/xLzZnR6RCG1tYza60nMdqtxzA8FaPJQ== +react-dom@^16.8.6: + version "16.8.6" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.8.6.tgz#71d6303f631e8b0097f56165ef608f051ff6e10f" + integrity sha512-1nL7PIq9LTL3fthPqwkvr2zY7phIPjYrT0jp4HjyEQrEROnw4dG41VVwi/wfoCneoleqrNX7iAD+pXebJZwrwA== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" prop-types "^15.6.2" - scheduler "^0.13.4" + scheduler "^0.13.6" react-error-overlay@4.0.0: version "4.0.0" @@ -7819,6 +8161,11 @@ react-is@^16.8.4: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.4.tgz#90f336a68c3a29a096a3d648ab80e87ec61482a2" integrity sha512-PVadd+WaUDOAciICm/J1waJaSvgq+4rHE/K70j0PFqKhkTBsPv/82UGQJNXAngz1fOQLLxI6z1sEDmJDQhCTAA== +react-is@^16.8.6: + version "16.8.6" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16" + integrity sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA== + react-redux@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-6.0.1.tgz#0d423e2c1cb10ada87293d47e7de7c329623ba4d" @@ -7831,6 +8178,16 @@ react-redux@^6.0.1: prop-types "^15.7.2" react-is "^16.8.2" +react-test-renderer@^16.0.0-0, react-test-renderer@^16.8.6: + version "16.8.6" + resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.8.6.tgz#188d8029b8c39c786f998aa3efd3ffe7642d5ba1" + integrity sha512-H2srzU5IWYT6cZXof6AhUcx/wEyJddQ8l7cLM/F7gDXYyPr4oq+vCIxJYXVGhId1J706sqziAjuOEjyNkfgoEw== + dependencies: + object-assign "^4.1.1" + prop-types "^15.6.2" + react-is "^16.8.6" + scheduler "^0.13.6" + react-typist@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/react-typist/-/react-typist-2.0.5.tgz#9830395a73a03e6368e1392ecb98edaa3a648e44" @@ -7848,15 +8205,15 @@ react@16.8.0: prop-types "^15.6.2" scheduler "^0.13.0" -react@^16.8.4: - version "16.8.4" - resolved "https://registry.yarnpkg.com/react/-/react-16.8.4.tgz#fdf7bd9ae53f03a9c4cd1a371432c206be1c4768" - integrity sha512-0GQ6gFXfUH7aZcjGVymlPOASTuSjlQL4ZtVC5YKH+3JL6bBLCVO21DknzmaPlI90LN253ojj02nsapy+j7wIjg== +react@^16.8.6: + version "16.8.6" + resolved "https://registry.yarnpkg.com/react/-/react-16.8.6.tgz#ad6c3a9614fd3a4e9ef51117f54d888da01f2bbe" + integrity sha512-pC0uMkhLaHm11ZSJULfOBqV4tIZkx87ZLvbbQYunNixAAvjnC+snJCg0XQXn9VIsttVsbZP/H/ewzgsd5fxKXw== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" prop-types "^15.6.2" - scheduler "^0.13.4" + scheduler "^0.13.6" read-pkg-up@^1.0.1: version "1.0.1" @@ -7922,10 +8279,10 @@ read-pkg@^3.0.0: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^3.0.6: - version "3.1.1" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.1.1.tgz#ed6bbc6c5ba58b090039ff18ce670515795aeb06" - integrity sha512-DkN66hPyqDhnIQ6Jcsvx9bFjhw214O4poMBcIMgPVpQvNy9a0e0Uhg5SqySyDKAmUlwt8LonTBz1ezOnM8pUdA== +readable-stream@^3.1.1, readable-stream@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.2.0.tgz#de17f229864c120a9f56945756e4f32c4045245d" + integrity sha512-RV20kLjdmpZuTF1INEb9IA3L68Nmi+Ri7ppZqo78wj//Pn62fCoJyV9zalccNzDD/OuJpMG4f+pfMl8+L6QdGw== dependencies: inherits "^2.0.3" string_decoder "^1.1.1" @@ -8252,6 +8609,14 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" +rst-selector-parser@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz#81b230ea2fcc6066c89e3472de794285d9b03d91" + integrity sha1-gbIw6i/MYGbInjRy3nlChdmwPZE= + dependencies: + lodash.flattendeep "^4.4.0" + nearley "^2.7.10" + rsvp@^4.8.4: version "4.8.4" resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.4.tgz#b50e6b34583f3dd89329a2f23a8a2be072845911" @@ -8328,10 +8693,10 @@ scheduler@^0.13.0: loose-envify "^1.1.0" object-assign "^4.1.1" -scheduler@^0.13.4: - version "0.13.4" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.13.4.tgz#8fef05e7a3580c76c0364d2df5e550e4c9140298" - integrity sha512-cvSOlRPxOHs5dAhP9yiS/6IDmVAVxmk33f0CtTJRkmUWcb1Us+t7b1wqdzoC0REw2muC9V5f1L/w5R5uKGaepA== +scheduler@^0.13.6: + version "0.13.6" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.13.6.tgz#466a4ec332467b31a91b9bf74e5347072e4cd889" + integrity sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" @@ -8363,6 +8728,11 @@ semver@4.3.2: resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.2.tgz#c7a07158a80bedd052355b770d82d6640f803be7" integrity sha1-x6BxWKgL7dBSNVt3DYLWZA+AO+c= +semver@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.0.0.tgz#05e359ee571e5ad7ed641a6eec1e547ba52dea65" + integrity sha512-0UewU+9rFapKFnlbirLi3byoOuhrSsli/z/ihNnvM24vgF+8sNBiI1LZPBSH9wJKUwaUbw+s3hToDLCXkrghrQ== + semver@~5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" @@ -8889,6 +9259,15 @@ string.prototype.padstart@^3.0.0: es-abstract "^1.4.3" function-bind "^1.0.2" +string.prototype.trim@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz#d04de2c89e137f4d7d206f086b5ed2fae6be8cea" + integrity sha1-0E3iyJ4Tf019IG8Ia17S+ua+jOo= + dependencies: + define-properties "^1.1.2" + es-abstract "^1.5.0" + function-bind "^1.0.2" + string_decoder@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.2.0.tgz#fe86e738b19544afe70469243b2a1ee9240eae8d" @@ -8951,17 +9330,17 @@ strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= -styled-components@^4.1.3: - version "4.1.3" - resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-4.1.3.tgz#4472447208e618b57e84deaaeb6acd34a5e0fe9b" - integrity sha512-0quV4KnSfvq5iMtT0RzpMGl/Dg3XIxIxOl9eJpiqiq4SrAmR1l1DLzNpMzoy3DyzdXVDMJS2HzROnXscWA3SEw== +styled-components@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-4.2.0.tgz#811fbbec4d64c7189f6c7482b9eb6fefa7fefef7" + integrity sha512-L/LzkL3ZbBhqIVHdR7DbYujy4tqvTNRfc+4JWDCYyhTatI+8CRRQUmdaR0+ARl03DWsfKLhjewll5uNLrqrl4A== dependencies: "@babel/helper-module-imports" "^7.0.0" "@emotion/is-prop-valid" "^0.7.3" "@emotion/unitless" "^0.7.0" babel-plugin-styled-components ">= 1" css-to-react-native "^2.2.2" - memoize-one "^4.0.0" + memoize-one "^5.0.0" prop-types "^15.5.4" react-is "^16.6.0" stylis "^3.5.0" @@ -9007,20 +9386,21 @@ stylis@3.5.4, stylis@^3.5.0: resolved "https://registry.yarnpkg.com/stylis/-/stylis-3.5.4.tgz#f665f25f5e299cf3d64654ab949a57c768b73fbe" integrity sha512-8/3pSmthWM7lsPBKv7NXkzn2Uc9W7NotcwGNpJaa3k7WMM1XDCA4MgT5k/8BIexd5ydZdboXtU90XH9Ec4Bv/Q== -superagent@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/superagent/-/superagent-4.1.0.tgz#c465c2de41df2b8d05c165cbe403e280790cdfd5" - integrity sha512-FT3QLMasz0YyCd4uIi5HNe+3t/onxMyEho7C3PSqmti3Twgy2rXT4fmkTz6wRL6bTF4uzPcfkUCa8u4JWHw8Ag== +superagent@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/superagent/-/superagent-5.0.2.tgz#5ff1e327d7f8250418e76a07fc96f3bd8441ca51" + integrity sha512-CqeqvwByDJuLwhcO6NOSuPatyQOIZX/TlvD5GJnXg5tzBTth2xQGZGdAZdo/kX+BtzvwJFX2IGGczTZgEIT7Wg== dependencies: - component-emitter "^1.2.0" + component-emitter "^1.2.1" cookiejar "^2.1.2" - debug "^4.1.0" + debug "^4.1.1" form-data "^2.3.3" - formidable "^1.2.0" - methods "^1.1.1" + formidable "^1.2.1" + methods "^1.1.2" mime "^2.4.0" - qs "^6.6.0" - readable-stream "^3.0.6" + qs "^6.7.0" + readable-stream "^3.2.0" + semver "^6.0.0" supports-color@^2.0.0: version "2.0.0" @@ -9484,6 +9864,11 @@ upath@^1.1.0: resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.0.tgz#35256597e46a581db4793d0ce47fa9aebfc9fabd" integrity sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw== +upath@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.2.tgz#3db658600edaeeccbe6db5e684d67ee8c2acd068" + integrity sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q== + uri-js@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" @@ -9592,6 +9977,11 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" +vlq@^0.2.1: + version "0.2.3" + resolved "https://registry.yarnpkg.com/vlq/-/vlq-0.2.3.tgz#8f3e4328cf63b1540c0d67e1b2778386f8975b26" + integrity sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow== + vm-browserify@0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73"