git.iko.soy/rowtype-yoga/royo
on main help sign in
Initial commit
commit8d9bf84b83bbab05d1af2bfb02fc414aad4172a1 authorMark Eibes <mark.eibes@gmail.com> dateSun 9 Aug 2020, 19:33 (6 years ago)
12 files changed+734
.gitignore+10 package.json+12 packages.dhall+111 spago.dhall+22 src/Discord/DiscordJS.js+37 src/Discord/DiscordJS.purs+95 src/Main.purs+24 src/Royo/Bot.purs+169 src/Yoga/APIClient.purs+102 test/Main.purs+11 yarn-error.log+44 yarn.lock+97
.gitignorenew file+10
@@ -0,0 +1,10 @@
1+/bower_components/
2+/node_modules/
3+/.pulp-cache/
4+/output/
5+/generated-docs/
6+/.psc-package/
7+/.psc*
8+/.purs*
9+/.psa*
10+/.spago
package.jsonnew file+12
@@ -0,0 +1,12 @@
1+{
2+ "name": "royo",
3+ "version": "1.0.0",
4+ "main": "index.js",
5+ "repository": "https://github.com/rowtype-yoga/royo.git",
6+ "author": "Mark Eibes",
7+ "license": "MIT",
8+ "dependencies": {
9+ "discord.js": "^12.2.0",
10+ "node-fetch": "^2.6.0"
11+ }
12+}
packages.dhallnew file+111
@@ -0,0 +1,111 @@
1+{-
2+Welcome to your new Dhall package-set!
3+
4+Below are instructions for how to edit this file for most use
5+cases, so that you don't need to know Dhall to use it.
6+
7+## Warning: Don't Move This Top-Level Comment!
8+
9+Due to how `dhall format` currently works, this comment's
10+instructions cannot appear near corresponding sections below
11+because `dhall format` will delete the comment. However,
12+it will not delete a top-level comment like this one.
13+
14+## Use Cases
15+
16+Most will want to do one or both of these options:
17+1. Override/Patch a package's dependency
18+2. Add a package not already in the default package set
19+
20+This file will continue to work whether you use one or both options.
21+Instructions for each option are explained below.
22+
23+### Overriding/Patching a package
24+
25+Purpose:
26+- Change a package's dependency to a newer/older release than the
27+ default package set's release
28+- Use your own modified version of some dependency that may
29+ include new API, changed API, removed API by
30+ using your custom git repo of the library rather than
31+ the package set's repo
32+
33+Syntax:
34+where `entityName` is one of the following:
35+- dependencies
36+- repo
37+- version
38+-------------------------------
39+let upstream = --
40+in upstream
41+ with packageName.entityName = "new value"
42+-------------------------------
43+
44+Example:
45+-------------------------------
46+let upstream = --
47+in upstream
48+ with halogen.version = "master"
49+ with halogen.repo = "https://example.com/path/to/git/repo.git"
50+
51+ with halogen-vdom.version = "v4.0.0"
52+-------------------------------
53+
54+### Additions
55+
56+Purpose:
57+- Add packages that aren't already included in the default package set
58+
59+Syntax:
60+where `<version>` is:
61+- a tag (i.e. "v4.0.0")
62+- a branch (i.e. "master")
63+- commit hash (i.e. "701f3e44aafb1a6459281714858fadf2c4c2a977")
64+-------------------------------
65+let upstream = --
66+in upstream
67+ with new-package-name =
68+ { dependencies =
69+ [ "dependency1"
70+ , "dependency2"
71+ ]
72+ , repo =
73+ "https://example.com/path/to/git/repo.git"
74+ , version =
75+ "<version>"
76+ }
77+-------------------------------
78+
79+Example:
80+-------------------------------
81+let upstream = --
82+in upstream
83+ with benchotron =
84+ { dependencies =
85+ [ "arrays"
86+ , "exists"
87+ , "profunctor"
88+ , "strings"
89+ , "quickcheck"
90+ , "lcg"
91+ , "transformers"
92+ , "foldable-traversable"
93+ , "exceptions"
94+ , "node-fs"
95+ , "node-buffer"
96+ , "node-readline"
97+ , "datetime"
98+ , "now"
99+ ]
100+ , repo =
101+ "https://github.com/hdgarrood/purescript-benchotron.git"
102+ , version =
103+ "v7.0.0"
104+ }
105+-------------------------------
106+-}
107+
108+let upstream =
109+ https://github.com/purescript/package-sets/releases/download/psc-0.13.8-20200724/packages.dhall sha256:bb941d30820a49345a0e88937094d2b9983d939c9fd3a46969b85ce44953d7d9
110+
111+in upstream
spago.dhallnew file+22
@@ -0,0 +1,22 @@
1+{-
2+Welcome to a Spago project!
3+You can edit this file as you like.
4+-}
5+{ name = "my-project"
6+, dependencies =
7+ [ "aff-promise"
8+ , "console"
9+ , "debug"
10+ , "effect"
11+ , "interpolate"
12+ , "milkis"
13+ , "node-process"
14+ , "psci-support"
15+ , "simple-json"
16+ , "string-parsers"
17+ , "stringutils"
18+ , "web-events"
19+ ]
20+, packages = ./packages.dhall
21+, sources = [ "src/**/*.purs", "test/**/*.purs" ]
22+}
src/Discord/DiscordJS.jsnew file+37
@@ -0,0 +1,37 @@
1+const Discord = require('discord.js');
2+
3+exports.newClient = () => { return new Discord.Client() }
4+
5+const once = event => callback => client => () => {
6+ client.once(event, callback)
7+ return client
8+}
9+
10+exports.onceReady = once("ready")
11+
12+exports.onMessage = (callback) => (client) => () => {
13+ client.on("message", x => callback(x)())
14+}
15+
16+exports.onMessageUpdate = (callback) => (client) => () => {
17+ client.on("messageUpdate", (oldMsg, newMsg) => callback(oldMsg)(newMsg)())
18+}
19+
20+exports.loginImpl = token => client => () => {
21+ return client.login(token).then(() => client)
22+}
23+
24+exports.reactImpl = reaction => msg => () => {
25+ return msg.react(reaction)
26+}
27+
28+exports.getChannelTypeImpl = channel => channel.type
29+
30+exports.removeAllReactionsImpl = msg => () => {
31+ return msg.reactions.removeAll()
32+}
33+
34+exports.sendStringImpl =
35+ stringMsg => channel => () => channel.send(stringMsg)
36+
37+exports.createDMChannelImpl = (user) => () => { return user.createDM() }
src/Discord/DiscordJS.pursnew file+95
@@ -0,0 +1,95 @@
1+module Discord.DiscordJS where
2+
3+import Prelude
4+import Control.Promise (Promise, toAff)
5+import Data.String.NonEmpty.Internal (NonEmptyString)
6+import Effect (Effect)
7+import Effect.Aff (Aff)
8+import Effect.Class (liftEffect)
9+import Prim.Row (class Lacks)
10+
11+foreign import data Client ∷ #Type -> Type
12+
13+foreign import newClient ∷ Effect (Client ())
14+
15+foreign import onceReady ∷
16+ ∀ r.
17+ Effect Unit ->
18+ Client r ->
19+ Effect Unit
20+
21+newtype DiscordToken = DiscordToken String
22+
23+foreign import loginImpl ∷
24+ ∀ r.
25+ DiscordToken ->
26+ Client ( | r ) ->
27+ Effect (Promise (Client ( loggedIn ∷ Void | r )))
28+
29+login ∷
30+ ∀ r.
31+ Lacks "loggedIn" r =>
32+ DiscordToken ->
33+ Client r ->
34+ Aff (Client ( loggedIn ∷ Void | r ))
35+login t c = do
36+ p <- loginImpl t c # liftEffect
37+ toAff p
38+
39+type Message =
40+ { channel ∷ Channel
41+ , content ∷ String {- not really... -}
42+ , author ∷ User
43+ }
44+
45+foreign import removeAllReactionsImpl ∷ Message -> Effect (Promise Unit)
46+
47+removeAllReactions ∷ Message -> Aff Unit
48+removeAllReactions message = do
49+ removed <- removeAllReactionsImpl message # liftEffect
50+ toAff removed
51+
52+foreign import reactImpl ∷ String -> Message -> Effect (Promise Unit)
53+
54+data ChannelType
55+ = DirectMessage
56+ | TextChannel
57+ | OtherChannel String
58+
59+derive instance eqChannelType ∷ Eq ChannelType
60+
61+foreign import getChannelTypeImpl ∷ Channel -> String
62+
63+getChannelType ∷ Channel -> ChannelType
64+getChannelType s = case getChannelTypeImpl s of
65+ "dm" -> DirectMessage
66+ "text" -> TextChannel
67+ other -> OtherChannel other
68+
69+react ∷ String -> Message -> Aff Unit
70+react reaction message = do
71+ p <- reactImpl reaction message # liftEffect
72+ toAff p
73+
74+foreign import data Channel ∷ Type
75+
76+foreign import onMessage ∷
77+ ∀ r. (Message -> Effect Unit) -> Client ( loggedIn ∷ Void | r ) -> Effect Unit
78+
79+foreign import onMessageUpdate ∷
80+ ∀ r. (Message -> Message -> Effect Unit) -> Client ( loggedIn ∷ Void | r ) -> Effect Unit
81+
82+foreign import sendStringImpl ∷ NonEmptyString -> Channel -> Effect (Promise Unit)
83+
84+sendString ∷ NonEmptyString -> Channel -> Aff Unit
85+sendString s c = do
86+ (sendStringImpl s c # liftEffect) >>= toAff
87+
88+foreign import data User ∷ Type
89+
90+foreign import createDMChannelImpl ∷ User -> Effect (Promise Channel)
91+createDMChannel ∷ User -> Aff Channel
92+createDMChannel u = do
93+ c <- createDMChannelImpl u # liftEffect
94+ toAff c
95+
src/Main.pursnew file+24
@@ -0,0 +1,24 @@
1+module Main where
2+
3+import Prelude
4+
5+import Data.Maybe (Maybe(..))
6+import Discord.DiscordJS (DiscordToken(..))
7+import Effect (Effect)
8+import Effect.Aff (launchAff_)
9+import Effect.Class.Console as Console
10+import Node.Process (lookupEnv)
11+import Royo.Bot (runBot)
12+import Yoga.APIClient (YogaToken(..))
13+
14+main :: Effect Unit
15+main = do
16+ maybeDiscordToken <- lookupEnv _DISCORD_TOKEN <#> map DiscordToken
17+ maybeYogaToken <- lookupEnv _YOGA_TOKEN <#> map YogaToken
18+ case maybeDiscordToken, maybeYogaToken of
19+ Nothing, _ -> Console.error $ _DISCORD_TOKEN <> " env variable missing"
20+ _, Nothing -> Console.error $ _YOGA_TOKEN <> " env variable missing"
21+ Just discordToken, Just yogaToken -> launchAff_ $ runBot yogaToken discordToken
22+ where
23+ _DISCORD_TOKEN = "DISCORD_TOKEN"
24+ _YOGA_TOKEN = "YOGA_TOKEN"
src/Royo/Bot.pursnew file+169
@@ -0,0 +1,169 @@
1+module Royo.Bot where
2+
3+import Prelude hiding (between)
4+
5+import Control.Alt (alt)
6+import Control.Apply (lift2)
7+import Data.Either (Either(..))
8+import Data.Foldable (intercalate)
9+import Data.Interpolate (i)
10+import Data.Maybe (Maybe(..))
11+import Data.String (Pattern(..), contains, drop, length, stripPrefix, stripSuffix, trim)
12+import Data.String.NonEmpty.Internal (NonEmptyString(..))
13+import Data.String.NonEmpty.Internal as NES
14+import Data.String.Utils (startsWith)
15+import Discord.DiscordJS (ChannelType(..), DiscordToken, Message, createDMChannel, getChannelType, login, newClient, onMessage, onMessageUpdate, react, removeAllReactions, sendString)
16+import Effect.Aff (Aff, attempt, launchAff_, message)
17+import Effect.Class (liftEffect)
18+import Effect.Class.Console (log)
19+import Yoga.APIClient (CompileResult, YogaToken, compileAndRun)
20+
21+_BOT_USER_ID ∷ String
22+_BOT_USER_ID = "741940687263760475"
23+
24+mentionPrefix1 ∷ String
25+mentionPrefix1 = i "<@!" _BOT_USER_ID ">"
26+
27+_OTHER_BOT_USER_ID ∷ String
28+_OTHER_BOT_USER_ID = "741945029056135179"
29+
30+mentionPrefix2 ∷ String
31+mentionPrefix2 = i "<@&" _OTHER_BOT_USER_ID ">"
32+
33+printErrors :: Message -> Aff Unit -> Aff Unit
34+printErrors msg aff = do
35+ result <- attempt aff
36+ case result of
37+ Left e -> log $ "Couldnt' handle message" <> message e <> "\n" <> msg.content
38+ Right _ -> pure unit
39+
40+runBot ∷ YogaToken -> DiscordToken -> Aff Unit
41+runBot yogaToken discordToken = do
42+ newClient <- newClient # liftEffect
43+ client <- newClient # login discordToken
44+ client # onMessage (launchAff_ <$> messageHandler) # liftEffect
45+ client # onMessageUpdate (launchAff_ `map map map` messageUpdatedHandler) # liftEffect
46+ where
47+ messageUpdatedHandler _ msg = printErrors msg do
48+ when (getChannelType msg.channel == TextChannel) $ removeAllReactions msg
49+ msg # react rewindEmoji
50+ messageHandler msg
51+
52+ messageHandler msg@{ content, channel } = printErrors msg do
53+ when (isMention content) do
54+ case parseCodeBlock content of
55+ Nothing -> do
56+ sendBasicInstructions msg
57+ Just code -> do
58+ res <- compileAndRun yogaToken { code: prepareCode code }
59+ case res of
60+ Left cr -> sendCompileProblem msg code cr
61+ Right rr -> case NES.fromString rr.stdout, NES.fromString rr.stderr of
62+ Just stdout, _ -> do
63+ msg # react robotMuscleEmoji
64+ channel # sendString (NonEmptyString (prepareOutput rr.stdout))
65+ Nothing, Just stderr -> do
66+ msg # react sirenEmoji
67+ sendRunProblem msg code rr.stderr
68+ _, _ -> channel # sendString (NonEmptyString "Something is weird")
69+
70+sendBasicInstructions :: Message -> Aff Unit
71+sendBasicInstructions msg = do
72+ msg # react thinkingEmoji
73+ dmc <- createDMChannel msg.author
74+ dmc # sendString (NonEmptyString intro)
75+ where
76+ intro =intercalate "\n"
77+ [ "Welcome " <> waveEmoji <> "!"
78+ , "I am here to help you to try out some PureScript code!"
79+ , "I react to messages that start with @royo and are followed by code in in between backticks. "
80+ , "For example:"
81+ , ""
82+ , "@royo"
83+ , "\\`\\`\\`"
84+ , "\"Hello World!\""
85+ , "\\`\\`\\`"
86+ ]
87+
88+sendRunProblem ∷ Message -> String -> String -> Aff Unit
89+sendRunProblem msg code output = do
90+ msg # react sirenEmoji
91+ dmc <- createDMChannel msg.author
92+ let say x = sendString (NonEmptyString x) dmc
93+ say $ "Hey there! I just tried to run your code: \n"
94+ <> prepareOutput code
95+ <> "\nHowever, we can still improve a few things about it."
96+ <> "This is the intel I could gather:"
97+ say $ output
98+ say $ "Don't worry, simply edit your original code and I will try again! The compiler and I are here to help you! We're all in this together " <> robotMuscleEmoji
99+
100+sendCompileProblem ∷ Message -> String -> CompileResult -> Aff Unit
101+sendCompileProblem msg code cr = do
102+ msg # react sirenEmoji
103+ dmc <- createDMChannel msg.author
104+ let say x = sendString (NonEmptyString x) dmc
105+ say $ "Hey there! I just tried to compile your code: \n"
106+ <> prepareOutput code
107+ <> "\nHowever, we can still improve a few things about it."
108+ <> "This is what the compiler says:"
109+ say $ compilerMessages
110+ say $ "Don't worry, simply edit your original code and I will try again! The compiler and I are here to help you! We're all in this together " <> robotMuscleEmoji
111+ where
112+ compilerMessages = intercalate "\n" (prepareOutput <$> (cr.result <#> _.message))
113+
114+prepareOutput ∷ String -> String
115+prepareOutput s =
116+ if contains (Pattern "\n") s then
117+ "```\n" <> s <> "```\n"
118+ else
119+ "`" <> s <> "`"
120+
121+prepareCode ∷ String -> String
122+prepareCode code =
123+ if startsWith "module" code then
124+ code
125+ else
126+ if startsWith "import " code then
127+ moduleMain <> code
128+ else
129+ if (startsWith "main =" || contains (Pattern "\nmain =")) code then
130+ moduleMain <> importGrimoire <> code
131+ else
132+ moduleMain <> importGrimoire <> spyMain <> code
133+ where
134+ moduleMain = "module Main where\n"
135+
136+ importGrimoire = "import Grimoire\n"
137+
138+ spyMain = "main = log $ unsafeCoerce $ "
139+
140+isMention ∷ String -> Boolean
141+isMention = startsWith mentionPrefix1 || startsWith mentionPrefix2
142+
143+parseCodeBlock ∷ String -> Maybe String
144+parseCodeBlock =
145+ pure <<< trim
146+ >=> pure
147+ <<< drop (length mentionPrefix1)
148+ >=> (pure <<< trim)
149+ >=> (lift2 alt (stripPrefix (Pattern "```purescript")) (stripPrefix (Pattern "```")))
150+ >=> stripSuffix (Pattern "```")
151+ >=> (pure <<< trim)
152+
153+rewindEmoji ∷ String
154+rewindEmoji = "⏪"
155+
156+robotMuscleEmoji ∷ String
157+robotMuscleEmoji = "🦾"
158+
159+sirenEmoji ∷ String
160+sirenEmoji = "🚨"
161+
162+thinkingEmoji ∷ String
163+thinkingEmoji = "🤔"
164+
165+disappointedEmoji ∷ String
166+disappointedEmoji = "😔"
167+
168+waveEmoji ∷ String
169+waveEmoji = "👋"
src/Yoga/APIClient.pursnew file+102
@@ -0,0 +1,102 @@
1+module Yoga.APIClient where
2+
3+import Prelude
4+import Data.Either (Either(..), either)
5+import Data.Maybe (Maybe(..))
6+import Effect.Aff (Aff, attempt, error, message, throwError)
7+import Effect.Class (liftEffect)
8+import Effect.Exception (throw)
9+import Foreign (Foreign)
10+import Milkis as M
11+import Milkis.Impl.Node (nodeFetch)
12+import Simple.JSON (class ReadForeign, read, writeJSON)
13+
14+fetch ∷ M.Fetch
15+fetch = M.fetch nodeFetch
16+
17+type CompileRequest =
18+ { code ∷ String }
19+
20+newtype YogaToken = YogaToken String
21+
22+compileAndRun ∷ YogaToken -> CompileRequest -> Aff (Either CompileResult RunResult)
23+compileAndRun (YogaToken bearerToken) body = do
24+ response <-
25+ attempt
26+ $ fetch (M.URL "https://rowtype.yoga/api/compileAndRun")
27+ { method: M.postMethod
28+ , body: writeJSON body
29+ , headers:
30+ M.makeHeaders
31+ { "Authorization": "Bearer " <> bearerToken
32+ , "Content-Type": "application/json"
33+ }
34+ }
35+ case response of
36+ Left l ->
37+ pure
38+ ( Left
39+ { resultType: ""
40+ , result:
41+ [ { allSpans: []
42+ , errorCode: ""
43+ , errorLink: ""
44+ , filename: ""
45+ , message: message l
46+ , moduleName: Nothing
47+ , position:
48+ { endColumn: 0
49+ , endLine: 0
50+ , startColumn: 0
51+ , startLine: 0
52+ }
53+ , suggestion: Nothing
54+ }
55+ ]
56+ }
57+ )
58+ Right r -> case M.statusCode r of
59+ 200 -> M.json r >>= readAff <#> Right
60+ 422 -> M.json r >>= readAff <#> Left
61+ code -> throwError (error $ "Unexpected response code " <> show code)
62+
63+readAff ∷ ∀ a. ReadForeign a => Foreign -> Aff a
64+readAff = read >>> orThrow
65+
66+orThrow ∷ ∀ a s. Show s => Either s a -> Aff a
67+orThrow = either (show >>> throw >>> liftEffect) pure
68+
69+type CompileResult =
70+ { result ∷ Array ErrorOrWarning
71+ , resultType ∷ String
72+ }
73+
74+type RunResult =
75+ { code ∷ Maybe Int, stdout ∷ String, stderr ∷ String }
76+
77+type Suggestion =
78+ { replaceRange ∷ Position, replacement ∷ String }
79+
80+type Span =
81+ { end ∷ Array Int
82+ , name ∷ String
83+ , start ∷ Array Int
84+ }
85+
86+type Position =
87+ { endColumn ∷ Int
88+ , endLine ∷ Int
89+ , startColumn ∷ Int
90+ , startLine ∷ Int
91+ }
92+
93+type ErrorOrWarning =
94+ { allSpans ∷ Array Span
95+ , errorCode ∷ String
96+ , errorLink ∷ String
97+ , filename ∷ String
98+ , message ∷ String
99+ , moduleName ∷ Maybe String
100+ , position ∷ Position
101+ , suggestion ∷ Maybe Suggestion
102+ }
test/Main.pursnew file+11
@@ -0,0 +1,11 @@
1+module Test.Main where
2+
3+import Prelude
4+
5+import Effect (Effect)
6+import Effect.Class.Console (log)
7+
8+main :: Effect Unit
9+main = do
10+ log "🍝"
11+ log "You should add some tests."
yarn-error.lognew file+44
@@ -0,0 +1,44 @@
1+Arguments:
2+ /Users/mark/.config/nvm/12.18.3/bin/node /Users/mark/.config/nvm/12.18.3/bin/yarn add discordjs
3+
4+PATH:
5+ /Users/mark/.cabal/bin:/Users/mark/.ghcup/bin:/Users/mark/.cargo/bin:/Users/mark/.nix-profile/bin:/nix/var/nix/profiles/default/bin:/Users/mark/.config/nvm/12.18.3/bin:/Users/mark/.local/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/go/bin
6+
7+Yarn version:
8+ 1.22.4
9+
10+Node version:
11+ 12.18.3
12+
13+Platform:
14+ darwin x64
15+
16+Trace:
17+ Error: https://registry.yarnpkg.com/discordjs: Not found
18+ at Request.params.callback [as _callback] (/Users/mark/.config/nvm/12.18.3/lib/node_modules/yarn/lib/cli.js:66987:18)
19+ at Request.self.callback (/Users/mark/.config/nvm/12.18.3/lib/node_modules/yarn/lib/cli.js:140748:22)
20+ at Request.emit (events.js:315:20)
21+ at Request.<anonymous> (/Users/mark/.config/nvm/12.18.3/lib/node_modules/yarn/lib/cli.js:141720:10)
22+ at Request.emit (events.js:315:20)
23+ at IncomingMessage.<anonymous> (/Users/mark/.config/nvm/12.18.3/lib/node_modules/yarn/lib/cli.js:141642:12)
24+ at Object.onceWrapper (events.js:421:28)
25+ at IncomingMessage.emit (events.js:327:22)
26+ at endReadableNT (_stream_readable.js:1220:12)
27+ at processTicksAndRejections (internal/process/task_queues.js:84:21)
28+
29+npm manifest:
30+ {
31+ "name": "royo",
32+ "version": "1.0.0",
33+ "main": "index.js",
34+ "repository": "https://github.com/rowtype-yoga/royo.git",
35+ "author": "Mark Eibes",
36+ "license": "MIT"
37+ }
38+
39+yarn manifest:
40+ No manifest
41+
42+Lockfile:
43+ # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
44+ # yarn lockfile v1
yarn.locknew file+97
@@ -0,0 +1,97 @@
1+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2+# yarn lockfile v1
3+
4+
5+"@discordjs/collection@^0.1.5":
6+ version "0.1.6"
7+ resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-0.1.6.tgz#9e9a7637f4e4e0688fd8b2b5c63133c91607682c"
8+ integrity sha512-utRNxnd9kSS2qhyivo9lMlt5qgAUasH2gb7BEOn6p0efFh24gjGomHzWKMAPn2hEReOPQZCJaRKoURwRotKucQ==
9+
10+"@discordjs/form-data@^3.0.1":
11+ version "3.0.1"
12+ resolved "https://registry.yarnpkg.com/@discordjs/form-data/-/form-data-3.0.1.tgz#5c9e6be992e2e57d0dfa0e39979a850225fb4697"
13+ integrity sha512-ZfFsbgEXW71Rw/6EtBdrP5VxBJy4dthyC0tpQKGKmYFImlmmrykO14Za+BiIVduwjte0jXEBlhSKf0MWbFp9Eg==
14+ dependencies:
15+ asynckit "^0.4.0"
16+ combined-stream "^1.0.8"
17+ mime-types "^2.1.12"
18+
19+abort-controller@^3.0.0:
20+ version "3.0.0"
21+ resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392"
22+ integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==
23+ dependencies:
24+ event-target-shim "^5.0.0"
25+
26+asynckit@^0.4.0:
27+ version "0.4.0"
28+ resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
29+ integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
30+
31+combined-stream@^1.0.8:
32+ version "1.0.8"
33+ resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
34+ integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
35+ dependencies:
36+ delayed-stream "~1.0.0"
37+
38+delayed-stream@~1.0.0:
39+ version "1.0.0"
40+ resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
41+ integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
42+
43+discord.js@^12.2.0:
44+ version "12.2.0"
45+ resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-12.2.0.tgz#31018732e42a495c92655055192221eab2ad11a9"
46+ integrity sha512-Ueb/0SOsxXyqwvwFYFe0msMrGqH1OMqpp2Dpbplnlr4MzcRrFWwsBM9gKNZXPVBHWUKiQkwU8AihXBXIvTTSvg==
47+ dependencies:
48+ "@discordjs/collection" "^0.1.5"
49+ "@discordjs/form-data" "^3.0.1"
50+ abort-controller "^3.0.0"
51+ node-fetch "^2.6.0"
52+ prism-media "^1.2.0"
53+ setimmediate "^1.0.5"
54+ tweetnacl "^1.0.3"
55+ ws "^7.2.1"
56+
57+event-target-shim@^5.0.0:
58+ version "5.0.1"
59+ resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789"
60+ integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==
61+
62+mime-db@1.44.0:
63+ version "1.44.0"
64+ resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92"
65+ integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==
66+
67+mime-types@^2.1.12:
68+ version "2.1.27"
69+ resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f"
70+ integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==
71+ dependencies:
72+ mime-db "1.44.0"
73+
74+node-fetch@^2.6.0:
75+ version "2.6.0"
76+ resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd"
77+ integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==
78+
79+prism-media@^1.2.0:
80+ version "1.2.2"
81+ resolved "https://registry.yarnpkg.com/prism-media/-/prism-media-1.2.2.tgz#4f1c841f248b67d325a24b4e6b1a491b8f50a24f"
82+ integrity sha512-I+nkWY212lJ500jLe4tN9tWO7nRiBAVdMv76P9kffZjYhw20raMlW1HSSvS+MLXC9MmbNZCazMrAr+5jEEgTuw==
83+
84+setimmediate@^1.0.5:
85+ version "1.0.5"
86+ resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
87+ integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=
88+
89+tweetnacl@^1.0.3:
90+ version "1.0.3"
91+ resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596"
92+ integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==
93+
94+ws@^7.2.1:
95+ version "7.3.1"
96+ resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.1.tgz#d0547bf67f7ce4f12a72dfe31262c68d7dc551c8"
97+ integrity sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==