Compare commits

...

859 Commits

Author SHA1 Message Date
Amish Shah
c4b1be8506 i didnt even want to update deps but no here we are updating deps for a version nobody will use because they all use v12 2018-01-13 20:39:47 +00:00
Schuyler Cebulskie
4584148706 Merge branch '11.3-dev' of https://github.com/hydrabolt/discord.js into 11.3-dev 2018-01-13 14:22:10 -05:00
Schuyler Cebulskie
0b28998af8 Update docs 2018-01-13 14:22:05 -05:00
Amish Shah
c27ecf678e Update dependencies 2018-01-13 18:52:20 +00:00
Amish Shah
233889cf3a Update typings 2018-01-13 17:28:20 +00:00
Isabella
f62fa05389 fix(Message#addReaction): incorrect regex (#2247) 2018-01-13 17:22:23 +00:00
Isabella
2efe2d28f2 fix(Channel#startTyping): less breaking (#2232)
* fix(Channel#startTyping): less breaking

* silent

* appelation suggestion

* space suggestion
2018-01-11 16:18:40 +00:00
Frangu Vlad
0821acfa99 Fix iter issues (#2237) 2018-01-09 21:17:46 +01:00
Isabella
d94ead70f8 fix(avatarURL): gifs not being animated (#2195) 2018-01-05 21:33:50 +00:00
Isabella
8226752098 fix(voiceStateUpdate): incorrect members.size (#2231) 2018-01-05 21:28:57 +00:00
Michel Nguyen
e883afa0ee docs: fix guildChannel#toString() example (#2203)
* fix the docs

* don't log something what you don't log
2018-01-04 01:17:40 +01:00
SpaceEEC
659e89e8cd feat: backport animated emojis support (#2217)
Commit: 84e4dd6a99
2018-01-04 01:16:50 +01:00
Isabella
c79823002b docs: improve examples (#2200)
* docs: improve examples


another

* remove conflict

* some nuances

* backport
2018-01-04 01:14:36 +01:00
SpaceEEC
db5bdcd855 feat: backport after and before parameter when fetching a reaction's users (#2218)
Commits:
- f40a5e9f88
- 5cd42695ae
2018-01-04 01:07:14 +01:00
Isabella
b3eb1bba24 backport(Channel#startTyping): returns Promise (#2208) 2018-01-04 01:02:22 +01:00
bdistin
7a89598795 Update welcome.md (#2173) 2017-12-31 20:22:58 +01:00
Isabella
b6c89ef638 fix(verson): update package.json (#2196) 2017-12-31 20:19:42 +01:00
Yukine
fc5d4438f8 backporting the fix for RTP header extensions (#2185) 2017-12-23 12:29:06 +01:00
Isabella
6683c40a6f fix(bulkDelete): stop rejections when filterOld=true (#2178)
* fix(bulkDelete): stop rejections when filterOld=true

* gus suggestion
2017-12-21 09:31:47 +01:00
Isabella
9f0417c09d fix(Guild#createChannel): default to text (#2168)
update docs


doc fixes


real fix


stuf
2017-12-12 06:33:48 +01:00
Isabella
1c34819d47 fix(UserGuildSettingsUpdate): not creating settings with new guilds (#2150) 2017-12-06 07:10:30 +01:00
Isabella
862b2ec3d4 Backport: Guild#createChannel (#2145)
* Backport: Guild#createChannel

* this should not return a buffer
2017-12-06 07:08:46 +01:00
Isabella
d705a0c7d0 fix(VoiceChannel#edit): incorrect bitrate (#2144) 2017-12-04 05:38:06 +01:00
Isabella
fb6d14d099 fix(guild#createChannel): new channel returning null (#2143)
forgot to return channel.
2017-12-04 05:37:51 +01:00
bdistin
6a222ec6e9 Backport: erlpack dep fix (#2142) 2017-12-01 18:50:33 +01:00
Isabella
cd066849ad feature(ClientUser): deprecate ClientUser#setGame in favour of ClientUser#setActivity (#2127)
* feature(ClientUser): backported ClientUser#setPresence

* fix ternary

* deprecation stuff
2017-11-29 23:29:37 -05:00
Isabella
e40c3f8cd0 fix(GuildAuditLogs): backport (#2123) 2017-11-21 09:45:26 +01:00
Isabella
22da595b50 fix(DMChannel): messages not being cached (#2122) 2017-11-21 09:43:53 +01:00
Isabella
cce2480bb5 feature(CategoryChannel): backport (#2117)
* feature(CategoryChannel): backport


fix


no

* ????

* bad ternary
2017-11-19 23:19:46 -05:00
Isabella
b274dba6ec fix(GuildMember): permissions.missing is not a function (#2118) 2017-11-18 17:10:59 -05:00
Gus Caplan
69b7d5d58e Fix #1903 (#1904) 2017-09-07 16:25:55 +01:00
Cynthia Lin
97b013de46 Fix whitespace in 11.2.0 welcome docs. (#1887) 2017-09-04 18:36:43 -04:00
Amish Shah
d9f772cdc1 11.2.1 2017-09-03 19:54:59 +01:00
Amish Shah
42c195c857 Merge branch '11.1-dev' into stable 2017-09-03 18:13:53 +01:00
iCrawl
77c54a94f2 Fix typo in landing page 2017-09-03 18:42:57 +02:00
iCrawl
4778a47cd4 Update docs landing page 2017-09-03 18:40:49 +02:00
iCrawl
18de265fcc lock version numbers, prepare release 2017-09-03 18:03:13 +02:00
iCrawl
a47c30e31a update submodule 2017-09-03 17:58:46 +02:00
bdistin
0d6b7ce641 Fix deny administrator edge case bug
(backport from permissions cleanup)
2017-09-03 17:43:15 +02:00
Crawl
9cdd494db6 Revert "Fix deny administrator edge case bug (#1878)" (#1879)
This reverts commit dbe1ae972b.
2017-09-03 17:41:07 +02:00
bdistin
dbe1ae972b Fix deny administrator edge case bug (#1878)
(backport from permissions cleanup)
2017-09-03 16:39:27 +01:00
SpaceEEC
4df2adc801 Backporting #1863, allowing the afk and systemchannel to be set to null (#1865)
* fix(Guild): Allow the afk and system channel to be set to null.

* make the getter return null
2017-09-01 16:14:20 +02:00
SpaceEEC
95e22c2f12 feat/fix: add DiscordAPIError#path and fixed Burst request handler handling api errors (#1867) 2017-09-01 16:05:22 +02:00
SpaceEEC
425efe1fe4 Consistently store message reactions keyed under their unicode (#1852) 2017-08-30 02:15:24 +02:00
SpaceEEC
56fe70266e Allow Message#edit to accept a RichEmbed and fixed RichEmbed#file's type (#1829) 2017-08-25 19:50:01 +02:00
SpaceEEC
1fe201ae90 Backporting, doc/bug fixes as well deprecation (#1826)
* Backporting, doc/bug fixes as well deprecation

* Adress issue with not resettable icons/images
2017-08-25 15:14:05 +02:00
iCrawl
bce5b677ad Backport passing a collection to a collector 2017-08-24 00:35:44 +02:00
Gus Caplan
85d195da52 Fix docs on resolveImage 2017-08-23 05:40:58 +02:00
Isabella
17d7f5c723 resolveImage backport (#1822)
* add resolveImage

* add groupDMChannel#setIcon + icon getter

* doc fix

* crawl no kill pls

* *whistles*

* channe
2017-08-23 05:29:22 +02:00
Crawl
a85fc91630 make webpack over 9000 times better (#1816)
* webpack stuff

* even better

* Update browser.js
2017-08-22 19:57:59 +02:00
SpaceEEC
0b22d9a774 Backporting Attachments (#1817) 2017-08-22 00:39:27 +02:00
SpaceEEC
f7664b01a2 Backports (#1813)
* Backported OAuth2Application 201ecd25a2

* Backported retry on 500 57b6980313

* Backported b8034525e3 and fa5c4efa2b
2017-08-21 22:25:21 +02:00
SpaceEEC
be4ccb3686 Backporting audit log reasons and createRole with position for 11.2 (#1810)
* Backporting audit log reasons and createRole with position for 11.2

* Sending kick reason via header rather than via query
2017-08-21 22:21:18 +02:00
SpaceEEC
618fa2b104 functions for setTimeout should get the context bound and not applied (#1673) 2017-08-20 22:26:04 +02:00
Crawl
a7e5e53e5d Fix ClientUser#settings not showing up in the documentation (#1757) 2017-08-20 22:18:40 +02:00
SpaceEEC
c01e9ad828 No longer double increment the reaction count when the client reacts (#1755) 2017-08-20 22:17:44 +02:00
Raphael
29aef18de8 updated docs for <ReactionEmoji>.toString() so it now uses send instead sendMessage in example (#1761) 2017-08-20 22:17:36 +02:00
Crawl
2478092d44 More docs cleanup 2017-08-20 22:15:51 +02:00
Crawl
c7d1507e19 Docs cleanup 2017-08-20 22:08:37 +02:00
Crawl
c33b78da23 Remove reasons for now 2017-08-20 22:01:17 +02:00
Gus Caplan
e76ebb4fcb Guild/systemchannel (#1799)
* add cool system channel

* Update Guild.js

* Update Guild.js

* Update Guild.js
2017-08-20 21:59:20 +02:00
iCrawl
b3216f26d6 Update deps 2017-08-20 21:39:27 +02:00
SpaceEEC
2809eb74ca Always send a type now when updating own presence (#1785) 2017-08-17 00:16:09 +02:00
Crawl
cd4a69d009 Add Guild#nameAcronym 2017-08-12 11:50:15 +02:00
iCrawl
5dc83a1b03 Git pls 2017-08-12 11:28:47 +02:00
SpaceEEC
2611efe9c1 No longer double increment the reaction count when the client reacts (#1755) 2017-08-12 11:25:29 +02:00
Crawl
cba4cc2400 Audio bitrate support (#1439)
* Audio bitrate support

Note: not implemented for VoiceBroadcasts

* Fix default args, auto bitrate

* Late night typos are the best

* Changes bitrate to kbps for VoiceChannel stuff

* Add methods to manipulate bitrate while encoding
2017-08-12 11:23:47 +02:00
Évelyne Lachance
d513c4bbb9 Add count optional argument to Collection methods (#1552)
* Add `count` optional argument to Collection methods

[NON-BREAKING CHANGE]
An optional `count` argument is added to the following methods:
- random() and randomKey()
- first() and firstKey()
- last() and lastKey()

If `count` is used, the method returns an array instead of only the value. Performance impact non-existent for existing code. Performance for returning an array has been measured and this is the fastest I could find (array[i] = value is faster than array.push()).

* Update Collection.js

Fixed spacing/line length errors according to suggestions by codacy/pr

* Fixed docs

Added proper `@returns {*|Array}` as the methods might return either. Also added params where missing (whoops)

* Further doc fixes

Per Crawl's comments, fixed (i + 1) spacing as well as fixed {Integer} to {number}

* random() and randomKey() fix

Per Hydra's comment, random() and randomKey() now ensures unique values.
I've also resolved potential issues with requesting a count higher than the collection size. A collection with 10 items will only ever return at most 10 items using the `count` property.

* Can I facepalm harder

Had wrong header comments ^_^

* Fixed for "values/value" and Omited

Also, added "Positive" integer check.

* looks like I "omitted" a change, there.

* Update Collection.js

* Update Collection.js

* Update Collection.js
2017-08-12 11:18:25 +02:00
aemino
7aa791eaaa Ignore setSpeaking requests when VC isn't connected (#1638) 2017-08-12 11:16:11 +02:00
Gus Caplan
407500bf52 deprecate Guild#defaultChannel (#1752) 2017-08-04 10:44:35 +02:00
Crawl
59122a6ba4 Update typings 2017-08-01 04:39:44 +02:00
iCrawl
8a9b6cbdb5 Stop doubling the message key 2017-07-31 01:32:44 +02:00
iCrawl
bdc61a4068 Wrong operator in flattenErrors 2017-07-31 01:29:46 +02:00
SpaceEEC
a56a24d042 Fixed DiscordAPIError#message sometimes being undefined 2017-07-31 01:26:42 +02:00
bdistin
e7ebb23f14 Fix guildMembersChunk documents for v11.1-dev (#1734) 2017-07-30 15:00:01 +02:00
Crawl
46a50cb57c Fix "shitty" shortcut 2017-07-28 05:20:28 +02:00
Crawl
b27198ebe5 Update nsfw prop 2017-07-28 04:53:36 +02:00
Crawl
e91a2c6b2b Update typings 2017-07-21 15:34:30 +02:00
Crawl
26e5ef3205 Update deps and fix webpack 2017-07-16 14:34:23 +02:00
Gus Caplan
ed84d76a42 move nsfw to the new prop (#1687) 2017-07-16 14:06:51 +02:00
aemino
b9434ed112 Expose DiscordAPIError and API error constants (#1641)
* Expose DiscordAPIError

* Expose API error constants

* Add typedef for APIError

* Integligently forgot to save file
2017-07-16 14:06:26 +02:00
Gus Caplan
85615aa3a1 update tern file to actually work (#1630) 2017-06-29 19:53:37 +01:00
Gus Caplan
822c1f533c Fix toLowerCase errors in GuildAuditLogs (#1627) 2017-06-29 19:53:25 +01:00
Drahcirius
4ce4dc019e setTimeout should use args (#1623) 2017-06-29 19:53:11 +01:00
Will Nelson
f1a74f214e make token not enumerable (#1620) 2017-06-29 19:52:58 +01:00
Gus Caplan
85ec7c64bc update docs for discord api error (#1575)
* aaaaa

* Update DiscordAPIError.js
2017-06-29 19:52:44 +01:00
aemino
b5de89a973 Fix VoiceConnection#authenticateFailed race condition (#1601) 2017-06-29 19:52:22 +01:00
Mythic
ddfa57e96d Improve Message's ID attribute documentation (#1450)
Remove the implication that a Message object's ID is unique only to the channel it was sent on
Message ID's are snowflakes, and as stated in Discord's API documentation, globally unique throughout Discord
2017-06-29 19:51:37 +01:00
SpaceEEC
e3232bdb2b added Guild#setExplicitContentFilter (#1583) 2017-06-29 19:51:11 +01:00
SpaceEEC
86ec60bc00 fix merge conflict 2017-06-29 19:48:34 +01:00
Drahcirius
830d8fb3b5 Remove global flag from ffmpeg tutorial doc (#1582) 2017-06-10 23:55:10 +01:00
Crawl
510ddab0f5 Fix createMessageCollector example 2017-06-08 14:50:34 +02:00
Schuyler Cebulskie
95a531ee7d Switch to User#tag in web builds example 2017-06-08 09:35:38 +02:00
Crawl
3f7049e1a0 CRLF to LF 2017-06-06 08:45:51 +02:00
aemino
1ed6bbc4b4 Remove unused VoiceBroadcast#guaranteeOpusEngine (fixes #1556) (#1563) 2017-06-05 06:54:56 +02:00
aemino
2116fba4c2 Opus engine fetching: don't ignore non-missing errors (#1555)
* Opus engine fetching: don't ignore non-missing errors

* typo fix
2017-06-01 10:32:12 +02:00
Amish Shah
b49266baa5 fml 2017-05-30 12:33:58 +01:00
Amish Shah
46b8a7d411 Correct documentation for VoiceConnection (see #1536) 2017-05-30 12:33:50 +01:00
Snazzah
2f1eb71a3f Add MEMBER_ROLE_UPDATE to returning 'UPDATE' array (#1538) (#1542) 2017-05-29 23:55:42 +02:00
Gus Caplan
3ec08d5976 add nsfw options to 11.1 search (#1540)
* Update RESTMethods.js

* Update TextBasedChannel.js
2017-05-29 23:53:49 +02:00
Amish Shah
19d4d3bf2c Fix #1528 2017-05-27 19:21:03 +01:00
Crawl
eb4d3627bd Update typings 2017-05-23 11:13:59 +02:00
Gus Caplan
bb2a35a849 they can be more than just string/num/bool (#1448) 2017-05-21 21:25:06 +02:00
vzwGrey
8e80b6660c Fix reaction collector example (#1513) 2017-05-21 21:15:23 +02:00
Crawl
c62e8bb1f0 Update typings 2017-05-21 06:40:52 +02:00
Gus Caplan
433c5e5702 you can't mutate a socket event in some browsers (webpack fix) (#1512) 2017-05-21 05:20:51 +02:00
Crawl
a8d34e340b Relink permission#FLAGS on docs 2017-05-20 21:05:45 +02:00
Crawl
04f0534d33 Revert "relink Permissions.FLAGS (#1510)" (#1511)
This reverts commit 65afedee49.
2017-05-20 21:02:10 +02:00
aister
65afedee49 relink Permissions.FLAGS (#1510)
(hop ai du eet raito)
2017-05-20 21:00:51 +02:00
bdistin
c903929b6b guild setPosition missing docs (#1498)
* missing docs

* update return docs
2017-05-16 16:12:43 +02:00
Gus Caplan
28a29d5d9f Update Invite.js (#1496) 2017-05-16 15:26:32 +02:00
Gus Caplan
6eced4d465 fix infinte loop issue (#1488) 2017-05-14 19:34:51 +02:00
Crawl
f25ced2969 Add test.sh and modified deploy.sh 2017-05-13 19:19:25 +02:00
Crawl
b9172ffe22 Add travis staging 2017-05-13 19:16:32 +02:00
Crawl
8a7a805d92 Fix webpack uglify 2017-05-13 18:18:15 +02:00
SpaceEEC
45bc653988 Failing to resolve a role should reject and not throw an error (#1483) 2017-05-12 15:31:04 +02:00
aemino
e3c3a4fd60 GuildMember#setVoiceChannel fix (#1482)
Looks like someone forgot to remove the full channel object from the PATCH payload.
2017-05-12 06:42:18 +02:00
Drahcirius
798018713b invalid token errors not rejected properly (#1478)
* ready event will now throw errors properly

* ws login rejection fix
2017-05-11 00:40:47 +02:00
meew0
ea1b5beea6 Fix the mention example in the USERS_PATTERN doc comment
Previously it was a channel mention. Thanks @Hackzzila.
2017-05-11 00:40:39 +02:00
SpaceEEC
bb0ee59718 Handing promise rejections from GuildAuditLogs#build to the user (#1474)
* handing guildauditlog's promise rejections to the user

* Returning a new Promise to resolve a Promise.all is unnecessary.
Also for the docs, it returns a Promise<GuildauditLogs>, not GuildAuditLogs directly.

* totally did not removed that line
2017-05-09 00:19:46 +02:00
1Computer1
294f5ea5c8 Deprecate aliases (#1469) 2017-05-06 01:24:53 +02:00
Crawl
fd7cb41ee6 Using a traditional for loop rather than a for in loop for options.files (#1461) 2017-05-06 01:14:27 +02:00
SpaceEEC
1e115efa56 fix fetchMentions' auth header, options and data mapping (#1457)
* fix fetchMentions' auth header, options and data mapping

* vscode strikes again

* switched to Util.mergeDefault

* vscode

* removed duplicated optionals and switched to instanceof
2017-05-06 01:04:05 +02:00
SpaceEEC
ee622f7d9f added Invite#presenceCount and Invite#memberCount (#1460)
* added Invite#online and Invite#memberCount

* requested change
2017-05-05 23:24:16 +02:00
SpaceEEC
1fc6e3b91e using correct properties for invites (#1467) 2017-05-05 23:19:25 +02:00
SpaceEEC
6cd55ed5c9 readded docs for Client#error and Client#ready (#1466) 2017-05-05 20:14:56 +02:00
SpaceEEC
7d7f1b2446 update GuildAuditLogs for MESSAGE_DELETE and fixed extras (#1464)
* update GuildAuditLogs for MESSAGE_DELETE and fixed extras

* correct oder of targets
2017-05-05 19:44:41 +02:00
Crawl
328d75be7d Update version 2017-05-05 17:08:22 +02:00
Crawl
41e0be1db3 Endpoints.Guild(...).Emoji(...) should not use CDN (#1462) 2017-05-05 02:23:25 +02:00
Crawl
77caa0f8d4 Update welcome docs page 2017-05-05 01:57:09 +02:00
Gus Caplan
712305dece Update GuildAuditLogs.js (#1456) 2017-05-04 00:50:16 +02:00
Anxeal
e96daba7c0 Fix typo in RESTMethods.js (#1455)
Mentions should be written with a capital M
2017-05-03 20:36:22 +01:00
Anxeal
3a73628567 Fix typo in RESTMethods.js (#1454)
fetchMeMentions -> fetchMentions 🤔
2017-05-03 20:36:17 +01:00
Amish Shah
cdc355811e New is also optional 2017-05-01 22:10:45 +01:00
Amish Shah
ce8dc85f78 watch me, gus 2017-05-01 22:09:14 +01:00
Amish Shah
2d416cca79 Improve GuildAuditLogs documentation by creating an AuditLogChange typedef 2017-05-01 21:07:46 +01:00
Cody A. Taylor
61a081faa5 Document flattenErrors keys param (#1447)
* Document flattenErrors keys param.

* Remove parens.

* Capitalise a letter
2017-05-01 20:47:45 +01:00
Gus Caplan
3bab4ec9fd Add time methods to Audit Log entries (#1443)
* aaaaa

* Update GuildAuditLogs.js
2017-05-01 18:05:15 +01:00
aemino
2bf8a36077 Allow await endpoint stage to not error (#1444) 2017-05-01 18:01:28 +01:00
Amish Shah
fccd0bd183 docs 2017-05-01 16:39:43 +01:00
Amish Shah
c48f042785 silly hydar 2017-05-01 15:42:47 +01:00
Amish Shah
b8018d4b6d Reconnect on 1000 if client didn't close 2017-05-01 15:32:40 +01:00
Amish Shah
6e4b744d03 Export ClientOAuth2Application 2017-05-01 15:27:44 +01:00
Amish Shah
135d9e3ea0 Expose GuildAuditLogs 2017-05-01 15:25:21 +01:00
Amish Shah
51a2e465bd Let client know who closed the WebSocket 2017-05-01 15:24:02 +01:00
Amish Shah
2ace21c412 backwards compat docs 2017-05-01 15:10:11 +01:00
Amish Shah
140839128a Fix ban delete days 2017-05-01 15:07:02 +01:00
Amish Shah
47fbd18eda docs 2017-05-01 15:06:26 +01:00
Amish Shah
0f4983852e Fix ban message delete days 2017-05-01 14:57:27 +01:00
Amish Shah
db45d99d0c More deprecated stuff 2017-05-01 11:28:48 +01:00
Amish Shah
8bab9fb122 Add VIEW_AUDIT_LOG to Permissions docs 2017-05-01 11:15:09 +01:00
Isabella
a02e10926d Stop using deprecated methods within discord.js (#1441)
* fix deprecation stuff

* fixed a check
2017-05-01 10:14:26 +01:00
Drahcirius
166161d6d3 ready event will now throw errors properly (#1440) 2017-05-01 08:43:35 +01:00
Amish Shah
892ba67d71 Emit packet handling errors to the error event and not the debug event 2017-05-01 07:56:13 +01:00
Crawl
c3be798663 Update GuildAuditLogs.js 2017-05-01 01:03:12 +02:00
Gus Caplan
7779069e32 add audit logs "extras" (#1435)
* add audit logs "extras"

* whoops

* Update GuildAuditLogs.js
2017-04-30 23:57:42 +01:00
Gus Caplan
1ddae43fed Add class doc blocks to Audit Logs (#1438) 2017-04-30 23:57:06 +01:00
SpaceEEC
c5efa985fc Readding Client#disconnect and docs for Client#reconnecting (#1437)
* readded docs for client#reconnecting and client#disconnect

* moved disconnect event emitting and clarified emitting reason
2017-05-01 00:16:02 +02:00
Amish Shah
0754ddbc34 Reset sequence when sessions are invalidated 2017-04-30 22:26:44 +01:00
Gus Caplan
37b1f38ebe Make audit log entries a collection (#1436) 2017-04-30 21:25:22 +01:00
Amish Shah
c2566d230c Wait undo that 2017-04-30 21:04:39 +01:00
Amish Shah
474a954938 Force deaf/mute properties to be booleans even when null data is received 2017-04-30 21:04:11 +01:00
Amish Shah
e460e5e853 Fix some stuff 2017-04-30 21:01:04 +01:00
Crawl
457b9600f2 Docs consistency plz 2017-04-30 13:16:19 +02:00
Amish Shah
9841615775 Merge branch 'master' of https://github.com/hydrabolt/discord.js 2017-04-30 12:10:27 +01:00
Amish Shah
c880f10714 Update bufferutil peerdep requirement from ^2.0.0 to ^3.0.0 2017-04-30 12:10:22 +01:00
Crawl
3cb4aac65a Remove unused util 2017-04-30 13:03:51 +02:00
Crawl
653aa6c1d7 Add back uws to the docs frontpage 2017-04-30 13:02:44 +02:00
Crawl
62ac246162 Add uws back to the readme 2017-04-30 13:01:46 +02:00
Crawl
bd6317cf99 Add uws back to package.json 2017-04-30 12:59:11 +02:00
Amish Shah
edf1a3e7b4 Undeprecate uws and fix support for it 2017-04-30 11:56:24 +01:00
Amish Shah
f20f49388d Simplify data unpack logic 2017-04-30 11:40:16 +01:00
Amish Shah
007b1226c9 Rudimentary support for uws while it is deprecated 2017-04-30 11:36:20 +01:00
Amish Shah
8e5c600227 Make OAuth2Application backwards compatible again 2017-04-30 11:21:48 +01:00
Amish Shah
dd110f4824 Add example for creating VoiceBroadcasts 2017-04-30 11:04:41 +01:00
Amish Shah
81b176cfea BaseOpusEngine should be private 2017-04-30 11:01:26 +01:00
aemino
5cad25ea69 Add Guild#me (#1384)
* Add Guild#me

(also did minor docs cleanup)

* Don't use Guild#member as there's nothing to resolve

* Update ClientDataResolver.js

* Update Guild.js

* Update Guild.js

* Update Guild.js
2017-04-30 04:30:59 -04:00
Pg Biel
9a5de25d79 Emoji convenience methods (#1378)
* make branch

* stuff

* Consistency & remove role(s)

* kill me

* Doc changes

Requested by Crawl

* forgot 1
2017-04-30 04:51:15 +02:00
Crawl
ac92d2cecc Improve docs a bit 2017-04-30 04:30:44 +02:00
Schuyler Cebulskie
3f8c0a4d11 Move things 2017-04-29 20:56:40 -04:00
Schuyler Cebulskie
e2c8ba5be0 Allow MessageMentions#channels to function in DMs 2017-04-29 20:18:23 -04:00
Schuyler Cebulskie
aa35e21b84 Revert "Make MessageMentions#channels return an empty collection in DMs (#1430)"
This reverts commit 88aa947376.
2017-04-29 20:00:36 -04:00
Schuyler Cebulskie
4bca4a5181 Update typings 2017-04-29 19:59:22 -04:00
Gus Caplan
6f96cf7325 Use Target variables for consistency (#1431) 2017-04-30 00:36:26 +01:00
Amish Shah
ee37d859a4 dumb ass bug fix 2017-04-29 22:47:06 +01:00
Amish Shah
1a69331f0c Merge branch 'master' of https://github.com/hydrabolt/discord.js 2017-04-29 21:09:56 +01:00
Amish Shah
c40252381b Deprecate all the things 2017-04-29 21:09:53 +01:00
Gus Caplan
4127cf6e40 Experimental support for Audit Logs (#1403)
* start audit logs

* make better var types so gawdl3y doesn't shit on this

* add constructor stuff

* make more changes

* add entry creation

* add methods

* make it all work hopefully

* aaa

* aaaa

* i wish i could test this locally

* fix users, guild when i feel like it

* make guild prop non-enumerable

* make better types

* change nouns

* e

* Update GuildAuditLogs.js

* Update GuildAuditLogs.js

* Update GuildAuditLogs.js

* eek

* Update GuildAuditLogs.js

* Update GuildAuditLogs.js

* friggin trailing spaces

* Update GuildAuditLogs.js

* docs!

* Update GuildAuditLogs.js

* reason stuff

* Update GuildAuditLogs.js

* Update GuildAuditLogs.js

* support before/after for pagination

* Update Guild.js

* Update GuildAuditLogs.js

* mfw using github web editor

* fix build

* Update Guild.js

* amazing cache fuckery shit evil

* cool stuff

* make building audit logs nicer

* ban endpoint stuff

* dox

* <.<
2017-04-29 20:34:57 +01:00
Amish Shah
b0a3528411 sorry gus i borked 2017-04-29 20:00:05 +01:00
Amish Shah
5b2ca326d4 ESLint pls >.> 2017-04-29 18:07:48 +01:00
Amish Shah
f7d65991d5 Minor refactor of Actions 2017-04-29 17:56:36 +01:00
Amish Shah
dd8f77fcf0 Stop the EventEmitter debug warnings when fetching lots of guilds before ready 2017-04-29 17:46:36 +01:00
Amish Shah
584961b04b Debug resume from connection not Client 2017-04-29 15:58:12 +01:00
Amish Shah
2e33bf583e Try to fix StreamDispatchers (#1387) 2017-04-29 15:12:34 +01:00
SpaceEEC
88aa947376 Make MessageMentions#channels return an empty collection in DMs (#1430)
* Make MessageMentions#channels return an empty collection in DMs

* reduce code duplication and corrected docstring
2017-04-29 14:33:18 +01:00
stupid cat
d6b7d31047 Better ClientUser#setStatus for self bots (#1428)
* better setstatus for selfbots

* make both calls

* must return

* use setpresence, set invisible
2017-04-29 14:31:32 +01:00
Will Nelson
13672cc637 Export ReactionCollector and Collector (#1426) 2017-04-29 14:27:39 +01:00
SpaceEEC
956372731f Using ws.connection.status rather than undefined ws.status (#1429) 2017-04-28 21:27:30 +02:00
Drahcirius
9ee695a291 incorrect variable name convention (#1427)
I'm not pr farming, I swear.
Gus told me to fix it.
2017-04-28 19:46:24 +02:00
Will Nelson
35c4c552f4 [wip] Fix collector documentation (again) (#1416)
* remove private on abstract methods, fix timeout type

* make client readonly, add documentation to abstract methods

* document implemented collector methods
2017-04-28 19:45:46 +02:00
Drahcirius
89745fe132 emojis should use cdn endpoint (#1425) 2017-04-28 18:47:18 +02:00
Drahcirius
2accb7b6fd small typo fix in emoji id endpoint (#1424) 2017-04-28 18:11:12 +02:00
Amish Shah
195fcfa15c Rewrite WebSocket internals (#1410)
* Start rewriting Manager and Connection

* more stuff

* stuff

* Fix ready bug

* some stuff i forgot

* fix some stuff

* add stupid heartbeat ack like seriously who cares

* woo!

* fix a bug

* rate limit the dumb websocket

* stuff

* hdocs

* Docs

* Remove ClientManager#setupKeepAlive as it is now redundant

* Change Client._pingTimestamp to a getter that fetches the timestamp from the WebSocketConnection

* are you happy now eslint smh

* make gus happy

* Add CloseEvent external doc

* Make sure to emit 'reconnecting' when actually reconnecting

* ffs

* Fix RESUME logic

* Add heartbeat ack debug messages, including latency data

* Dumb stuff for Gus

* thx eslint

* more dumb stuff

* more dumb crap smh gus i h8 u

* moar messages

* fix for using wrong status, causing certain events not to be fired (#1422)
2017-04-28 16:13:06 +01:00
SpaceEEC
95bcac9d9b Throwing 'Message not found' error when fetching from empty channel or without read history permission. (#1394) 2017-04-28 17:02:52 +02:00
Gus Caplan
1601ad14e3 i hate my life (#1420) 2017-04-27 00:21:03 +02:00
Gus Caplan
5ac22691d2 Add TextBasedChannel#nsfw (excluding User and GuildMember) (#1419) 2017-04-26 23:01:27 +01:00
bdistin
298ee4e79f Throw an error if you try to initiate a new SnowflakeUtil class instance (#1413) 2017-04-25 23:29:30 +02:00
SpaceEEC
909b825c94 Fixed acknowledging of channels (#1411)
* Fixed acknowledging of channels

* using channel#lastMessageID and resolve when it's not present
2017-04-25 23:29:08 +02:00
Amish Shah
eb9c280d5f CRLF to LF 2017-04-25 18:08:27 +01:00
Crawl
10138dfad2 Update voice docs page 2017-04-25 07:10:09 +02:00
Gus Caplan
c670209021 allow search('string here') (#1408) 2017-04-24 16:43:30 +02:00
Crawl
53eda09f72 Improve docs 2017-04-24 16:29:29 +02:00
SpaceEEC
401822a094 Made search argument optional, added useronly warning and fixed endpoint (#1407) 2017-04-24 14:31:39 +02:00
Crawl
aec0a095e3 Make sentence better (English is hard) 2017-04-24 08:45:29 +02:00
Crawl
4a18ac72ce Add some more comments to the example 2017-04-24 08:35:51 +02:00
Crawl
bbb5d4c7a1 Update examples 2017-04-24 08:34:00 +02:00
Crawl
b15e012788 Fix silly typo cant -> can 2017-04-24 08:24:45 +02:00
Crawl
fb5b8f8548 Remove uws from package.json 2017-04-24 08:22:28 +02:00
Crawl
c538c076ff Deprecate uws 2017-04-24 08:21:54 +02:00
Gus Caplan
59e733cef6 deprecate aliases (#1397)
* hype

* add doc dep tags

* fix that

* hopefully fix docs

* Update TextBasedChannel.js
2017-04-24 08:05:24 +02:00
Crawl
96041dbd3a Fix doc string 2017-04-23 08:51:07 +02:00
Crawl
6abe014ac3 Change theme prop type 2017-04-23 07:04:44 +02:00
Crawl
c095610bb4 Change docstring 2017-04-23 06:50:43 +02:00
Crawl
98ee016b83 Add .npmignore to reduce module filesize 2017-04-23 06:07:53 +02:00
Crawl
c0c2fd1493 Remove yeoman generator link 2017-04-23 05:53:33 +02:00
Amish Shah
26069a44e2 nope i was right the first time 2017-04-22 21:23:20 +01:00
Amish Shah
3465a154d5 that should be 60 tbh 2017-04-22 21:20:50 +01:00
Amish Shah
eec79b5fd0 Increase WebSocket backoff time to alleviate reconnect issues 2017-04-22 21:16:58 +01:00
Crawl
64b378f7d4 Update sharder test 2017-04-22 19:47:22 +02:00
Crawl
7d5bfccd67 Update package.json 2017-04-22 18:15:26 +02:00
SpaceEEC
5b3e971c5c Message.editedTimestamp should be a number (#1402) 2017-04-22 16:54:48 +02:00
Crawl
6c4f63ed72 Make @types/node a devDependency 2017-04-22 15:00:37 +02:00
Crawl
c3bc6d5234 Update to latest eslint 2017-04-22 14:56:10 +02:00
Will Nelson
794c0e131d how2docs (#1400) 2017-04-22 01:56:23 +02:00
Amish Shah
5605dc04e1 Fix #1399 (Trying to unset games with values other than null doesn't end up displaying properly for mobile clients) 2017-04-21 11:42:19 +01:00
Will Nelson
94e2a85386 Docs fixes (#1398)
* fix collector docs

* fix message collector param type
2017-04-21 09:44:59 +02:00
Isabella
a652901d60 Throw Error for non role-resolvables (#1322)
* throw error for role names/integers being provided for role resolvables

* Update RESTMethods.js

* Update RESTMethods.js

im really bad

* Update RESTMethods.js

* removed new promise reject

ty gus

* Update RESTMethods.js

* move error to guildmember.addRole method

* Update GuildMember.js
2017-04-19 18:57:54 +02:00
Will Nelson
ca34c43ba0 Add message reaction collectors & abstract collectors (#1335)
* message reaction collectors

* docs cleanup

* abstraction

* remove pointless method

* rename reaction collector creator method

* docs and stuff

* fix docs & build

* backwards compatibility, fix docs

* fix docs

* remove deprecated comments

* betterer docs again

* Fix documentation

* Fix Alias to not break depreciated code
2017-04-19 18:52:40 +02:00
Skiptir Engu
8475a4abee Use libsodium.js if available (#1390)
* Use libsodium.js if available

* Style consistency

* Added peer dep and update README

* Link should point to npmjs 🤔
2017-04-19 18:35:45 +02:00
Crawl
27114eebf9 Commit those in LF too 2017-04-19 18:17:23 +02:00
Crawl
1897b4f4d6 Commit those in LF 2017-04-19 18:14:37 +02:00
Crawl
b1e3309783 According to the spec just this should work 2017-04-19 17:55:00 +02:00
Crawl
fa5787cc8b Fix arrow parens 2017-04-19 17:48:34 +02:00
Gus Caplan
47442eeba3 snekfetch v3 hype (#1391) 2017-04-19 16:40:07 +01:00
Schuyler Cebulskie
b7e16ad4b8 Revert "use LF instead of CRLF (#1392)"
This reverts commit 5e522e0a2e.
2017-04-18 22:41:08 -04:00
Gus Caplan
5e522e0a2e use LF instead of CRLF (#1392) 2017-04-18 21:43:05 +01:00
Gus Caplan
0d83f80fc0 Fix endless reconnection bug (#1389) 2017-04-17 12:32:26 +01:00
Crawl
6bae7a135f Update package.json to v11.1
This is to reflect version of the master branch
2017-04-16 02:22:18 +02:00
Gus Caplan
15723ece1f Prepare examples for v12 (#1382) 2017-04-16 00:50:37 +01:00
Mr. Sakamoto
4aa82b4fdf fix Guild setOwner example (#1383)
* fix Guild setOwner docs

* hello s and bye s
2017-04-15 22:13:20 +02:00
Amish Shah
bdf6f30990 Update package.json to depend on npm distribution of prism-media rather than the git repo 2017-04-15 19:46:36 +01:00
Crawl
930519c494 Update AudioPlayer.js 2017-04-13 03:17:00 +02:00
meew0
81619ac7be Create a PR template
As discussed on Discord.
2017-04-13 00:57:39 +02:00
Amish Shah
8ddaa1b92c Merge branch 'master' of https://github.com/hydrabolt/discord.js 2017-04-12 21:22:14 +01:00
Amish Shah
78895ace17 Fix reactions not being uncached once they hit a count of 0 2017-04-12 21:22:08 +01:00
SpaceEEC
d54ef0eea4 fix (#1375) 2017-04-12 22:06:05 +02:00
Amish Shah
dce8e83465 Add VoiceConnection#dispatcher 2017-04-12 20:36:03 +01:00
Amish Shah
36c03909dd Clean up AudioPlayer internals, change currentDispatcher and currentTranscoder to just dispatcher and transcoder 2017-04-12 20:32:50 +01:00
Amish Shah
9eaf1456b2 Clean up AudioPlayer internals, remove the concept of multiple "temporary" streams 2017-04-12 20:12:51 +01:00
SpaceEEC
d0c2b84659 Fixed Util.arraysEqual, made it better and updated its docs (#1374)
* commit

* improved docs
2017-04-12 19:37:31 +02:00
SpaceEEC
81d7dbdc76 Fix resolveEmojiIdentifier (#1373) 2017-04-12 17:15:23 +01:00
Amish Shah
8b67660271 Fix contribution link 2017-04-12 15:39:26 +01:00
Lana Reeve
40c610987e Improved EmojiIdentifierResolvable support (#1361)
* EmojiIdentifierResolvable now supports emoji IDs and pre-encoded unicode

* forgot to return the identifier and not the emoji itself

* using ? in JSDoc instead of |null
2017-04-12 06:32:18 +02:00
bdistin
c5e2ea7458 Break reconnect loop if you client.destroy in the disconnect event (#1371)
* Break reconnect loop if you client.destroy in the disconnect event

* fix lint/long line?

* God this is So UGLY, why...

* Update WebSocketManager.js

* Update WebSocketManager.js
2017-04-12 06:31:45 +02:00
aemino
39f7dc018a Make FEC and PLP non-default (#1372)
I considered it might be useful to have something exposed that allows the end-user to set these if they want, but our current code doesn't allow for that. It'll remain an internal feature for the time being.
2017-04-12 06:30:48 +02:00
meew0
5dea21ba80 Move CONTRIBUTING.md to the new .github folder
There's no point in keeping it in the root directory given that it can also be in .github.
2017-04-12 01:19:16 +02:00
meew0
9b989c4fd7 Create an issue template
This has been created in discussion with various people on Discord.
Closes #1362 as that is obsolete now, sorry!
2017-04-12 01:16:12 +02:00
SpaceEEC
6187c17097 readded inviteLink endpoint (#1369) 2017-04-11 22:21:28 +02:00
xDdude
a49d4e6d43 getters for decibel and logarithmic volume (#1355)
* getters for decibel and logarithmic volume

I don't really understand where the conversion numbers come from but having this allows for an easy way to get the volume back based on what method you used to set it. I think the math is right, but yeah I've just used the conversion numbers from the other methods.

* fix trailing spaces
2017-04-11 18:21:00 +01:00
Drahcirius
84aab1021a reaction remove fix (#1366) 2017-04-11 18:10:14 +01:00
Amish Shah
25e0048bbf remove useless waiting reference 2017-04-11 17:59:27 +01:00
Gus Caplan
8dfc96d460 fix message acking (#1368) 2017-04-11 17:54:25 +01:00
Amish Shah
6495df50f9 Fix sequential rate limiting 2017-04-11 17:54:03 +01:00
bdistin
9365272baf Fix embeds getting converted to collection on edgecase (#1356) 2017-04-10 14:30:06 -04:00
SpaceEEC
e60d2bd175 Fix MessageMentions properties always being null or empty (#1357) 2017-04-10 20:06:00 +02:00
Gus Caplan
0a56fa0aae websocket cleanup (#1346)
* websocket cleanup

* resume event

* Update Resumed.js

* Update WebSocketManager.js
2017-04-10 20:02:17 +02:00
Gus Caplan
8436cbe8b1 Update User.js (#1351) 2017-04-10 16:20:07 +01:00
aemino
6d4fe89212 Volume stuff docs fixes (#1354)
* Volume stuff docs fixes

* 10/10 for spelling
2017-04-10 11:50:33 +01:00
Schuyler Cebulskie
3bc90dcbf1 Add export for MessageMentions 2017-04-10 03:21:14 -04:00
Schuyler Cebulskie
ebcf61ff9c Remove MessageMentions constructor docs 2017-04-10 03:12:57 -04:00
Schuyler Cebulskie
4fa5ceed83 Fix some docstrings 2017-04-10 03:07:05 -04:00
Schuyler Cebulskie
fa016b6b41 Clean up Message#mentions and message updates 2017-04-10 03:01:50 -04:00
Crawl
878e5d7c76 Fix example typo 2017-04-10 00:39:07 +02:00
Gus Caplan
d7cf4a0919 switch snekfetch to npm dep because not everyone has git (#1349)
* Update package.json

* Update package.json
2017-04-09 15:55:35 +02:00
Gus Caplan
e5293647a3 (╯°□°)╯︵ ┻━┻ (#1347) 2017-04-07 12:13:13 +02:00
aemino
ab589fa5f2 VoiceConnection speaking bugfix (#1343)
Looks like I made a typo...
2017-04-06 16:20:34 +02:00
Crawl
d1d8179460 Revert "add auth session change handling" (#1342)
* Revert "User settings (#1337)"

This reverts commit 801633b970.

* Revert "add auth session change handling (#1339)"

This reverts commit 5d85de0883.
2017-04-06 13:50:31 +02:00
Gus Caplan
801633b970 User settings (#1337)
* user settings bruh

* remove development dump

* emit stuff

* i am so done

* Update ClientUserSettings.js

* modularize

* Update ClientUserSettings.js

* Update Constants.js

* Update ClientUserSettings.js

* Update RESTMethods.js

* Update ClientUserSettings.js

* <.<
2017-04-05 22:03:33 +02:00
Gus Caplan
5d85de0883 add auth session change handling (#1339)
* add auth session change handling

* eek
2017-04-05 21:52:55 +02:00
Crawl
4ebf4307e8 Update package.json 2017-04-05 21:40:38 +02:00
SpaceEEC
6ce24398ab Fix messageReactionAdd internally & doc fix (#1341) 2017-04-05 21:34:14 +02:00
Amish Shah
970bfffc46 Fix emoji events (fixes #1333) 2017-04-05 17:05:27 +01:00
Amish Shah
140fd4ad4c Update submodules 2017-04-05 16:08:03 +01:00
SpaceEEC
ecb8655dac Document readonly and private properties (#1338) 2017-04-03 20:05:36 +02:00
Crawl
4be08406e6 Fix travis build error 2017-04-03 02:06:08 +02:00
Crawl
047cd2da5c Fix fetchRecommendedShards 2017-04-03 01:51:15 +02:00
Crawl
6910585f69 Fix lowercase comments 2017-04-02 19:47:00 +02:00
Crawl
dde6eb003c Force newest node-opus and opusscript 2017-04-02 19:45:10 +02:00
aemino
8716702b59 Add FEC and PLP support for Opus encoding (#1264)
* Add FEC and PLP support for Opus encoding

* Silently fail if the Opus engine doesn't support CTL

* Fixed inversed max/min functions
2017-04-02 19:43:46 +02:00
Crawl
2ff1f3dcde Update sharder.js 2017-04-02 09:45:59 +02:00
Crawl
09489e2b9f Fix indent on doc string 2017-04-01 21:19:39 +02:00
Isabella
d189b51da5 explicit content filter for guilds (#1329) 2017-04-01 21:16:30 +02:00
SpaceEEC
270bbc2731 to Endpoints.CND with you (#1330) 2017-04-01 21:16:15 +02:00
Crawl
7ffbbc7f07 Fix _sortedRoles in Role.js 2017-04-01 16:28:19 +02:00
Crawl
358131fb6f Fix _sortedRoles in Guild.js 2017-04-01 16:27:55 +02:00
SpaceEEC
d2e3d6dc63 replaced 'del' with 'delete' (#1328) 2017-04-01 16:16:05 +02:00
Gus Caplan
cb3f6d9646 http rewrite (actually works this time!!1!) (#1257)
* rewrite http

* browser fun

* all the mimes dammit

* i need a newline

* whoops

* forgot about this

* use promises and HTTPRequest.method

* fluent

* move httpclient to external module

* branding

* middleware

* revert middleware
2017-04-01 09:04:01 +02:00
Gus Caplan
a4e0af2e45 make positions for channels and roles nicer (#1211)
* make role calculated position nicer

* make channels sortable in a nice way too

* stupid git web rebase editor

* Update Guild.js

* Update Guild.js

* Update Guild.js

* Update Guild.js

* Update RESTMethods.js
2017-04-01 08:28:54 +02:00
TimeForANinja
c4e5292516 resolve for not loggedin clients (#1301) 2017-04-01 08:05:46 +02:00
bdistin
bca101aac8 Fix for #1089 & Message Reactions (#1236)
* [requires more testing] Fix #1089

* Clean up unshift

* Remove <Message>.patch(data)

Nothing calls this method any longer. It is also a private method, so this shouldn't be a breaking change.

* Fix Message Reactions

Purposely reference previous reaction collections, so collection is consistant accross all message edits (no unnecisary data duplication). Makes #1221 extranious.

* Some Data Packets come Incomplete

And several properties can be falsy, so instead of || opted for hasOwnProperty().

* No reason MessageTypes should be an object...

* Use `prop in obj` isntead of hasOwnProp

per @Gawdl3y
2017-04-01 01:51:12 -04:00
SpaceEEC
07740955cf Fixing stuff, borked by #1323 (#1326)
* several things

* see my comment

* Fixed missing stuff and returned undefined
2017-04-01 07:29:31 +02:00
Gus Caplan
305a7d14af whoops (#1325) 2017-03-31 23:13:41 +02:00
Gus Caplan
e86ec7de6f Update Constants.js (#1324) 2017-03-31 19:42:16 +02:00
Gus Caplan
1df3b84da6 rewrite endpoints for consistancy and modularity (#1323)
* rewrite endpoints for consistancy and modularity

* clean up a bit

* add missing endpoint
2017-03-31 18:36:09 +01:00
Amish Shah
1cb227d6d8 Update license 2017-03-31 18:18:29 +01:00
SpaceEEC
e08f2bad30 Add guild#createRole resolve permissions and RoleData optional (#1321)
* resolving permissions

* make RoleData optional
2017-03-31 12:55:23 +02:00
Crawl
f73e6a3e4d Fix typo in MessageEmbed#video 2017-03-30 22:14:06 +02:00
Gus Caplan
dd3831fa1e Remove type error from Collection#exists() (#1320) 2017-03-30 11:01:07 -04:00
1Computer1
5334682682 Add missing properties for MessageEmbed (#1300)
* Add missing properties

* Derp
2017-03-30 08:47:34 -04:00
Gus Caplan
104f3397af Add defaultRole to guild ((╯°□°)╯︵ ┻━┻) (#1311)
* (╯°□°)╯︵ ┻━┻

* Update Guild.js
2017-03-30 08:44:08 -04:00
Vap0r1ze
61f5051dfd Fix invite resolver (#1318)
Old version only worked with temporary links.
Now works with: vanity invites, and permanent invites.
2017-03-30 08:41:18 -04:00
SpaceEEC
55141b408b Fix (#1306) MessageRection#fetchUsers() return a collection istead of an array 2017-03-30 12:02:00 +02:00
Alexandre B
c86ebefecb Updated uws package version. (#1315) 2017-03-29 21:51:30 +02:00
Schuyler Cebulskie
c73b9483f4 Clean up and add comments 2017-03-22 01:16:30 -04:00
Schuyler Cebulskie
cff7069275 Switch from for..of to forEach 2017-03-22 01:12:46 -04:00
Drahcirius
8444f19662 Added mentions.members (#1271)
* Added mentions.members

* made member.mentions undefined on when not on guild channel

* changed property to getter

* Update Message.js
2017-03-22 01:08:25 -04:00
SpaceEEC
beffcd31cd fix guildWebhooks endpoint (#1284) 2017-03-21 16:31:02 +00:00
Gus Caplan
dd8907472c nonce has to be a uint64 :< (#1240)
* nonce me daddy

* Update RESTMethods.js

* Update RESTMethods.js

* Update RESTMethods.js

* Update RESTMethods.js

* Update RESTMethods.js

* Update RESTMethods.js

* (╯°□°)╯︵ ┻━┻

* Update RESTMethods.js

* Update RESTMethods.js
2017-03-21 03:17:20 -04:00
Gus Caplan
3e0c0f44a2 make useragent better because data is fun (#1244)
* Update UserAgentManager.js

* Update UserAgentManager.js

* gawdl3y is one of the most pants-on-head retarded people i have ever met

* Update UserAgentManager.js

* Update UserAgentManager.js

* Update UserAgentManager.js

* Update UserAgentManager.js
2017-03-21 03:12:20 -04:00
Schuyler Cebulskie
58068fdae9 Change check for array/number, and fix docs 2017-03-21 02:53:08 -04:00
Schuyler Cebulskie
245bd91101 Allow Permissions constructor to take array of perms 2017-03-21 02:45:11 -04:00
Schuyler Cebulskie
79278bccb4 Fix Permissions.resolve to return a combined bitfield for arrays 2017-03-21 02:16:18 -04:00
Programmix
633e3ca896 Fix addRole/removeRole when the user has/doesn't have the role (#1288) 2017-03-21 01:52:43 -04:00
Schuyler Cebulskie
2e54f6d90d Was worth a shot 2017-03-21 01:49:12 -04:00
Pg Biel
64ce829ab2 Make FetchMessage work for non-bot users (#1291)
* [WIP] make fetchMessage work for normal users

wip

* Actual thing

* not WIP anymore

better docs

* oops

* better stuff

* Update TextBasedChannel.js

* Update TextBasedChannel.js

* Update TextBasedChannel.js
2017-03-21 01:42:39 -04:00
Schuyler Cebulskie
2349238f69 Fix #1287 (Role#serialize) 2017-03-21 01:24:53 -04:00
Schuyler Cebulskie
f5da1f2411 Add missing symbol correction to ESLint 2017-03-19 15:50:23 -04:00
Schuyler Cebulskie
bfb3f39e94 Remove unnecessary options for arrow-parens rule 2017-03-17 01:47:50 -04:00
Schuyler Cebulskie
e757f7c0d8 Add two more ESLint rules, and move arrow-parens 2017-03-17 01:44:42 -04:00
Schuyler Cebulskie
41c67766d8 Update ESLint dependency (forgot to include this) 2017-03-17 01:25:30 -04:00
Schuyler Cebulskie
136cab240d Add new ESLint rules 2017-03-17 01:23:39 -04:00
Crawl
2237749d29 Fix richEmbed attachFile 2017-03-16 17:07:11 +01:00
SpaceEEC
6983798820 Fix optional args for sendFiles (#1278) 2017-03-16 16:20:37 +01:00
Gus Caplan
94062d19dd Add message/channel/guild acknowledging (#1239)
* add acking

* 👀

* Update RESTMethods.js

* Update TextBasedChannel.js

* Update RESTMethods.js

* Update Guild.js

* Update TextBasedChannel.js

* Update Message.js

* super shitty names

* Update GroupDMChannel.js

* Update DMChannel.js

* Update TextChannel.js
2017-03-16 09:38:12 -04:00
Gus Caplan
fa609caee2 remove pako (#1258) 2017-03-16 09:15:48 -04:00
Crawl
1f2d8bfbc1 Add missing sendFiles doc and on channels 2017-03-16 00:47:05 +01:00
bdistin
8729ee6a1d Fix #1253: Permission Overwrites Resolution (#1255)
* Fix #1253

* apparently @ everyone role can be undefined

* Fix oops

* Fixes possible mutiple roles named '@​everyone'

* Clean up order/logic
2017-03-15 15:29:34 +01:00
Gus Caplan
a82d9f1fbb add multi-file support (#1268)
* add multi-file support

* (╯°□°)╯︵ ┻━┻

(╯°□°)╯︵ ┻━┻(╯°□°)╯︵ ┻━┻(╯°□°)╯︵ ┻━┻(╯°□°)╯︵ ┻━┻(╯°□°)╯︵ ┻━┻(╯°□°)╯︵ ┻━┻(╯°□°)╯︵ ┻━┻(╯°□°)╯︵ ┻━┻(╯°□°)╯︵ ┻━┻(╯°□°)╯︵ ┻━┻(╯°□°)╯︵ ┻━┻(╯°□°)╯︵ ┻━┻(╯°□°)╯︵ ┻━┻(╯°□°)╯︵ ┻━┻(╯°□°)╯︵ ┻━┻(╯°□°)╯︵ ┻━┻(╯°□°)╯︵ ┻━┻(╯°□°)╯︵ ┻━┻(╯°□°)╯︵ ┻━┻(╯°□°)╯︵ ┻━┻(╯°□°)╯︵ ┻━┻(╯°□°)╯︵ ┻━┻(╯°□°)╯︵ ┻━┻(╯°□°)╯︵ ┻━┻(╯°□°)╯︵ ┻━┻(╯°□°)╯︵ ┻━┻(╯°□°)╯︵ ┻━┻(╯°□°)╯︵ ┻━┻(╯°□°)╯︵ ┻━┻(╯°□°)╯︵ ┻━┻(╯°□°)╯︵ ┻━┻

* stupid git web rebase editor - Gus2k17

* Update TextBasedChannel.js

* Update TextBasedChannel.js
2017-03-15 15:10:49 +01:00
SpaceEEC
6b26d28c06 Fix (#1262)
Not ignoring the filterOld boolean when a number is being passed instead of collection or array
2017-03-14 22:58:21 +01:00
Crawl
4ef0ec491c Add arrow-parens eslint rule & fix inconsistencies 2017-03-13 15:56:00 +01:00
Motion
cee0d58fb8 Added docs warning for acceptInvite method (#1267)
Added docs warning for acceptInvite method (user account only iirc)
2017-03-13 07:04:31 +00:00
1Computer1
bbd89585c6 Add RichEmbed fields error handling (#1243)
* RichEmbed fields error handling

* Fix consistency

* Update RichEmbed.js
2017-03-09 20:58:46 -05:00
bdistin
14ae7cdf17 [Docs] Evaluated Permissions References (#1250)
* Docs / Permissions Overwrites Reference

* Docs / Evaluated Permissions Reference

* Clarify role.permissions is not evaluated

* Update Role.js
2017-03-09 18:32:44 -05:00
Gus Caplan
2897692cf1 fix created timestamp precision (#1241)
* fix created timestamp precision

* perf

* Update Snowflake.js

* gawdl3y was butthurt

* Update Snowflake.js
2017-03-09 18:31:31 -05:00
Schuyler Cebulskie
21babf8859 Update GuildMember#hasPermission(s) to match Role 2017-03-07 02:53:12 -05:00
Schuyler Cebulskie
3e5096f9fe Update Role#hasPermission, and deprecate hasPermissions 2017-03-06 19:58:42 -05:00
Schuyler Cebulskie
df2333ac82 Fix Role#hasPermission 2017-03-06 19:38:58 -05:00
Schuyler Cebulskie
b926cd0dcd Remove redundant Markdown 2017-03-06 12:22:53 -05:00
Schuyler Cebulskie
6211119928 Fix Permissions.ALL on Node 6 2017-03-06 12:21:42 -05:00
Schuyler Cebulskie
4f7e0c41ee Doc fixes 2017-03-06 02:34:39 -05:00
Schuyler Cebulskie
30dd9be900 Pass the member into new Permissions instances 2017-03-06 02:29:35 -05:00
Schuyler Cebulskie
427eec8ccb Fix ESLint failure 2017-03-06 02:27:04 -05:00
Schuyler Cebulskie
16fe48d405 Overhaul Permissions utilities (EvaluatedPermissions no more) 2017-03-06 02:22:42 -05:00
Programmix
055775de2f Fix for #1219 (sodium methods) (#1220)
* Change node-opus methods

* Try using crypto_secretbox(_open)_easy

* Wrong open method name
2017-03-04 08:33:19 +00:00
Amish Shah
33de408a70 fix bulk delete endpoint 2017-03-04 00:35:54 +00:00
1Computer1
e7b0afbd1f Add color role, hoist role getters to GuildMember (#1188) 2017-03-01 04:53:12 -05:00
Schuyler Cebulskie
a5740e83f4 Change burst mode warning 2017-03-01 04:51:46 -05:00
Schuyler Cebulskie
7473fd513e Remove InviteOptions typedef, rephrase 2017-03-01 04:48:26 -05:00
SpaceEEC
fc42a46e5b Invite options (#1223)
* test

* updated descripton

* Update GuildChannel.js
2017-03-01 04:45:05 -05:00
bdistin
25bb602d5a Add Collection.clone() (#1238)
* Add Collection.clone()

* More efficient cloning, and concat update

* Update Collection.js

* Update Collection.js
2017-03-01 04:41:36 -05:00
Kelvin Wu
01d8d32ea9 Add typedef for GuildmemberEditData. (#1230)
* Add typedef for GuildmemberEditData.

* Farm commits

* Make terms consistent.
2017-03-01 04:36:17 -05:00
meew0
b6f74c45fe Add a note about questions to the CONTRIBUTING.md 2017-02-27 20:45:26 +01:00
lipgloss
0e6b5e9193 Change update to create (#1231) 2017-02-27 07:03:24 +00:00
Amish Shah
dfb1a16e9e One-line an if statement 2017-02-25 23:26:40 +00:00
Programmix
275c9953a0 Fixes disconnecting VoiceConnections before connected (#1227) 2017-02-25 23:17:39 +00:00
Schuyler Cebulskie
f3a7f59824 Several improvements
- Rename Guild#updateChannelPositions -> setChannelPositions
- Allow Guild#setChannelPositions to take ChannelResolvables
- Prioritise ClientDataResolver#resolveChannel's string case
- Minor cleanup
2017-02-25 15:29:32 -05:00
Amish Shah
18bcd2f7e2 Add AudioPlayer.currentDispatcher 2017-02-25 11:12:17 +00:00
Programmix
a029999b09 Doc fixes, mostly pertaining to Snowflakes (#1218)
GUS!!!! @GusCaplan
2017-02-24 15:33:51 -05:00
Programmix
76637ed10d Fix VoiceDispatcher docs (#1214) 2017-02-23 21:42:41 -05:00
Amish Shah
53209fbe31 update typings 2017-02-23 20:20:49 +00:00
Amish Shah
70b35c85d6 Merge branch 'indev-prism' 2017-02-23 20:19:26 +00:00
Schuyler Cebulskie
af3d4782b9 Add a word 2017-02-22 19:31:04 -05:00
Schuyler Cebulskie
15240cfdc3 Fix contributing link on docs site, link node.js 2017-02-22 19:23:37 -05:00
Programmix
bf25caf3d3 Updated VoiceConnection to use new Util class (#1210)
pls @Gawdl3y pls
2017-02-22 22:10:26 +00:00
Amish Shah
b55e6927e9 merge fix 2017-02-22 20:39:21 +00:00
Amish Shah
8a2ec437e5 Merge branch 'master' into indev-prism 2017-02-22 20:37:59 +00:00
Gus Caplan
4c9d8d6cd7 add lots of group dm features (#1208)
* group dm stuff

* minor doc changes
2017-02-22 20:33:20 +00:00
Joschua Schneider
5c2086b351 Allow presence updating to allow null games (#1186)
* Adding resetGame functionallity

`setGame` method would allways result in an Object passed to `setPresence`.
Passing { game: null } (supported by discords WebSocket gateway to reset the current Game) to `setPresence` would still result in a Game Object sent to the endpoint.
Explicitly setting `game` to null should overwrite the `game` object provided by `localPresence` or `client.presence`.
This was neither supported by `setGame` or `setPresence`.

* Missing semicolons to resetGame and setPresence

* Fixing trailing spaces, commas and semicolons

* Moving resetGame functionality into setGame method

Minification of if statement in setPresence.
Removing resetGame method and adding a case for `game === null` to setGame method

* Adding missing space in setGame method

* Fix docs
2017-02-22 20:24:39 +00:00
lipgloss
f068010e96 Implement 'Modify Guild Channel Positions' (#1198)
* Adding shuffleArray method to utils

* Shuffle channels functionality on guild.

* Comment fix

* Removing shuffle functionality and replacing with a simple update

* Code review changes to method/variable names

* Update comment reference to channelId as well

* Updating jsdoc with typedef of ChannelPosition
2017-02-22 20:24:05 +00:00
Programmix
7fd94c29d8 VoiceConnection rework (#1183)
* VoiceConnection rework

- improves codebase
- removes concept of pending connections
- attempts to fix memory leaks by removing EventEmitter listeners
- makes voice connections keep track of its own channel when it is moved by another user
- allows voice connections to reconnect when Discord falls back to another voice server or a region change occurs
- adds events for some of the aforementioned events

* Removed unused code

* More clean up / bugfixes

* Added typedefs to Status and VoiceStatus constants
2017-02-22 20:13:52 +00:00
Gus Caplan
566135d25b move permission stuff to the resolver (#1185) 2017-02-22 20:11:11 +00:00
Programmix
eb069d0249 Added volumeChange event to VoiceInterface (#1207)
VoiceBroadcast relies on this event to initialize a new Opus engine
2017-02-22 20:10:19 +00:00
Zack Campbell
f01b3f922d Update typings submodule (#1206) 2017-02-22 20:09:49 +00:00
Gus Caplan
61e12c637b Add support for more OAuth features (#1203) 2017-02-22 20:09:40 +00:00
Programmix
d09dfa4c37 Capitalize channel type constants (#1209) 2017-02-22 20:08:47 +00:00
Schuyler Cebulskie
b0338df7db Add more required permissions to Guild#addMember 2017-02-21 15:09:46 -05:00
Schuyler Cebulskie
8d1bc30e40 Clean up dd6dd6f 2017-02-21 15:07:54 -05:00
Zack Campbell
db5259cdf1 Add RichEmbed#attachFile (#1202)
* Add RichEmbed#attachFile

Mostly for attaching local images that can be accessed within the embed image/author icon/footer icon via `attachment//filename.png` and the like.

* Update docstring to reflect valid param types

* Update TextBasedChannel.js

* Update RichEmbed.js

* Update RichEmbed.js
2017-02-21 14:29:37 -05:00
Gus Caplan
d4a84915e6 Add timeout/interval methods to WebhookClient (fixes #1181), and clean up docs (#1204)
* fix up 1181

* Clean up documentation

* Update WebhookClient.js
2017-02-21 14:16:41 -05:00
Schuyler Cebulskie
f2a6c1d98c Improve Client docs a bit more 2017-02-21 14:02:29 -05:00
Kelvin Wu
d870b27ece Message#member should be a nullable. (#1205) 2017-02-21 13:49:20 -05:00
Jamelele
1273bb42ec Doc fixes (#1197)
* Improve ban/unban examples

* Fix example comments

* Replace nondescript 'user' parameter with 'some user ID'

* Update Guild.js

* Update Guild.js
2017-02-19 16:06:18 -05:00
Jamelele
12136f8c54 Welcome example (#1194)
* Add user greeting example

* Add welcome.js to index.yml

* Reword greeting message

* Update welcome.js

* Rename welcome.js to greeting.js

* Update index.yml
2017-02-18 13:59:25 -05:00
Schuyler Cebulskie
05bba9b74a Remove old comment part 2017-02-18 03:13:46 -05:00
Schuyler Cebulskie
7232531eb1 Move all util methods into class
Remove TransformMessageOptions altogether
2017-02-15 22:25:18 -05:00
Schuyler Cebulskie
63ffd8aa7c Clarify bufferutil scenario a bit 2017-02-15 21:04:06 -05:00
Schuyler Cebulskie
8d620ac33f Update typings 2017-02-15 20:32:39 -05:00
Schuyler Cebulskie
b91f8f27be Fix resolveBuffer file failure behaviour 2017-02-15 16:37:31 -05:00
Gus Caplan
ec1ed15c88 Fix request handling (#1180)
* clean up ratelimiters, and disable burst until some big questions are handled

* burst mode is a work

* fix burst again
2017-02-11 12:04:24 +00:00
Mike
dd6dd6fb59 Added support for adding users to guild 2 (#1179)
* Added support for adding users to guild

added RESTMethods#AddGuildMemberOptions and Guild#addMember with typedef
AddGuildMemberOptions to be able to add user to guild as a member
through `PUT/guilds/{guild.id}/members/{user.id}`
https://discordapp.com/developers/docs/resources/guild#add-guild-member

* fixing lint errors

* Changes based on discussion

* Changes based on discussion 2

* Changes based on discussion 3

Yay! More changes.
2017-02-08 21:04:39 +00:00
Zack Campbell
2518a0f7e2 Fix for incorrect oldMember in guildMemberUpdate event after addRole (#1129)
* Fix for incorrect oldMember in guildMemberUpdate event after addRole

`addRole` would modify the cached GuildMember rather than letting it be handled internally when a guild member update packet is received from Discord, leading to the `oldMember` and `newMember` being identical following a call to `addRole`

This is currently how `addRoles` does it, and a correct oldMember is passed to the `guildMemberUpdate` event following a call to `addRoles`

* Return cloned member with added/removed role

So we can return a member object with the added/removed role without affecting the member object sent to `guildMemberUpdate`

* Wait for guildMemberUpdate and return updated GuildMember

* Fix linter errors

* Remove listeners after 10 seconds
2017-02-08 17:38:57 +00:00
Will Nelson
cdb911f2af update setImage documentation (#1177) 2017-02-08 17:18:10 +00:00
Alexander
cc3e7b26b1 Add convenience function RichEmbed.addBlankField (#1139) 2017-02-08 17:17:13 +00:00
Gus Caplan
7c8f534a38 add random color (#1175)
* add random color

* Update ClientDataResolver.js

* Update ClientDataResolver.js
2017-02-07 01:19:43 -05:00
Alex
ab5e57d94b Create Emoji requires a base64 data uri (#1154) 2017-02-06 14:19:03 -05:00
Schuyler Cebulskie
a90bd837af Add sodium to optional packages list 2017-02-06 02:06:01 -05:00
Schuyler Cebulskie
0f6fceebb4 Update voice webpack exclusions 2017-02-06 01:57:08 -05:00
Schuyler Cebulskie
d6f17a9319 Merge branch 'master' into indev-prism 2017-02-06 01:54:31 -05:00
Schuyler Cebulskie
4dc70d8cef Update dependencies and fix tweetnacl webpack exclusion 2017-02-06 01:27:04 -05:00
Jacob
49944747ae Sodium (#1172)
* Use Native libsodium when available

* add newline

* fix typo of exports

* add to webpack ignore

* Update Secretbox.js
2017-02-06 01:24:54 -05:00
Schuyler Cebulskie
b859ba7639 Increase some documentation detail 2017-02-05 23:00:36 -05:00
Amish Shah
26becb570b Fix #1168 (.hasPermission) 2017-02-04 20:58:39 +00:00
Gus Caplan
59ff1d99ba fix types for snowflakes (#1156)
* fix types for snowflakes

* Update TextBasedChannel.js
2017-02-03 21:37:58 -05:00
Funnbot
0b5eeb08f3 clone topic in channel.clone() (#1157)
* Update GuildChannel.js

* Update GuildChannel.js

* Update GuildChannel.js

* Update GuildChannel.js

* Update GuildChannel.js

* Update GuildChannel.js
2017-02-03 21:37:32 -05:00
Gus Caplan
cee9e4839c fix sending code when you aren't splitting the message (#1162)
* fasguhq4wbyghjehuibh

kjgewqhuilb

* Update RESTMethods.js
2017-02-02 16:21:26 +00:00
Gus Caplan
1e94a9e2a4 serious role position stuff (#1159)
* serious role position stuff

* kill meh

* Update Role.js

* Update Guild.js

* Update Role.js
2017-02-01 22:02:16 +00:00
Schuyler Cebulskie
02c23a8b53 Merge branch 'master' into indev-prism 2017-02-01 15:32:18 -05:00
bdistin
a2d6791cd8 Fix CodeBlock Splitting (#1160)
* Fix CodeBlock Splitting

* fix changes requested
2017-02-01 15:31:57 -05:00
Schuyler Cebulskie
e8ac18489e Merge branch 'master' into indev-prism 2017-02-01 15:29:45 -05:00
Schuyler Cebulskie
187f43aebd Make a letter lowercase 2017-02-01 02:06:24 -05:00
Schuyler Cebulskie
c40a511954 Change wording slightly 2017-02-01 02:03:42 -05:00
Schuyler Cebulskie
d51e45f3b9 Add bufferutil info, and uws warning 2017-02-01 02:01:36 -05:00
Schuyler Cebulskie
a5b901f4ef Update ws and opusscript 2017-02-01 01:52:22 -05:00
Alex
ed42d7bd85 Fix backwards GuildEmojiCreate parameters (#1153) 2017-01-31 00:49:11 -05:00
Gus Caplan
8da915f6a1 expose createDM (#1151)
* expose createDM

* Update User.js

* in v12 we are removing these, kay?

* Update GuildMember.js

* Update User.js
2017-01-30 22:56:14 -05:00
Schuyler Cebulskie
f4724d61b2 Fix #1095 2017-01-30 22:54:37 -05:00
Gus Caplan
565c640bc6 Add role support to emoji creation (#1141)
* add role support to emojis

* specify types
2017-01-30 22:47:27 -05:00
Alex
18729b25c7 Add support to edit emojis (#1142)
* Add support to edit emojis

* Fixes for coding style.

* Add and use guildEmoji constants for updateEmoji

* Just use the Constant

* Fix typo in edit documentation

* Specify property types

* Fix ridiculous typo.

* Update Emoji.js
2017-01-30 22:47:05 -05:00
Schuyler Cebulskie
94483ae194 GOODER 2017-01-30 21:52:41 -05:00
Schuyler Cebulskie
f73fd4ec29 get grammer gooder 2017-01-30 21:27:28 -05:00
Schuyler Cebulskie
2e1310ae72 Add web build docs, many other minor doc changes 2017-01-30 21:23:06 -05:00
Schuyler Cebulskie
e9ab9c9f92 Improve scripts 2017-01-30 20:21:53 -05:00
Programmix
78dafb9480 Properly check whether an Opus engine exists (#1150)
* Properly check whether an Opus engine exists

I think I'm retarded

* Fix eslint error

* Update OpusEngineList.js
2017-01-30 16:38:47 -05:00
Gus Caplan
8ee0acf44c make d.js great again (#1149)
get out, donald

* AGHHH

* FUHFUHFUHFUHUFHUHF
2017-01-29 19:10:25 +00:00
Programmix
7ed58f5f7f Added Opus stream support, added volume interface (#1102)
* Added opus stream support, added volume interface

* Remove setImmediate

* Fix weird syntax error

* Most useless commit ever

You're welcome, @PgBiel

* Fix potential memory leak with OpusScript

Emscripten has the tendency to not free resources even when the Opus engine instance has been garbage collected. Thanks to @abalabahaha for pointing this out.

* Typo

* VoiceReceiver.destroy: destroy opus encoder
2017-01-29 19:07:33 +00:00
Programmix
5059c59a31 Message patching: clear mention collections (#1138)
* Message patching: clear mention collections

Fixes #1089
When discord sends an array of mentions, it is a full list of mentions -- therefore, we should clear the old mention collection. The same goes for when we re-analyze the message for channel mentions.

* Use Collection.clear() instead of new Collection
2017-01-29 19:02:54 +00:00
Fiddlekins
6fae17912e Added try catch so that decode errors aren't fatal (#1146)
* Added try catch so that decode errors aren't fatal

* Tweaked the usage of the warn event and updated jsdocs

* Moved method into class for webpack scoping reasons
2017-01-29 18:59:00 +00:00
Digitroinc
448c93615b fix splitting messages #1148)
* fix for issue #1120

* build fix
2017-01-29 18:58:35 +00:00
Gus Caplan
2beb77ab5c check if a guildchannel is deletable (#1143)
* Update GuildChannel.js

* eghhh

* Update GuildChannel.js
2017-01-28 10:07:53 +00:00
Pg Biel
c7f5b44e03 Add User.lastMessage, GuildMember.lastMessage and TextBasedChannel.lastMessage (#1135)
* Add User.lastMessage

* User.lastMessage and GuildMember.lastMessage

* User, GuildMember and TextBasedChannel lastMessage

* Update MessageCreate.js
2017-01-26 21:24:08 +00:00
Programmix
9a6cb6477d VoiceReceiver: multiple streams fix (#1132)
* VoiceReceiver: multiple streams fix

silly hydar... you can't have one Opus engine instance for every stream

* Better creation of opus engine
2017-01-26 21:23:00 +00:00
Gus Caplan
87b600f78f switch to os shim for safety (#1136)
* switch to os shim for safety

* Update Client.js
2017-01-26 21:21:43 +00:00
Programmix
ba11f76284 Fix patching message reactions (fixes #1071) (#1137) 2017-01-26 21:20:40 +00:00
Alex
ba7c2db364 Fix createEmoji/deleteEmoji action name. (#1110) 2017-01-24 21:56:19 +00:00
Alexander
5173583e26 Fix empty search query parameters (#1119)
The search function was sending request to stuff like:
search?author_id=&content=&channel_id=135823828352838352
2017-01-24 21:55:59 +00:00
Programmix
c1a5bee61f GuildMembersChunk packet handler hotfix (#1125)
* GuildMembersChunk packet handler hotfix

* Add limit, resolve properly if a query is used

* Document new GuildMemberChunk param
2017-01-24 21:55:36 +00:00
Gus Caplan
4b2053133d more profile stuff (#1131) 2017-01-24 21:53:26 +00:00
Programmix
c73e501243 Fix RESTMethods.removeMessageReaction (fixes #1114) (#1126) 2017-01-21 19:48:17 -05:00
Gus Caplan
306ea97f99 fix guild member fetching edge cases (#1115)
* thx4tipprogrammixluvubye

* consistancy

* make this loop for some reason

* so impatient
2017-01-21 16:37:28 +00:00
Schuyler Cebulskie
3545e731c0 Renamed webpack build script 2017-01-19 22:07:02 -05:00
Schuyler Cebulskie
f51ba3fb06 Update to Webpack 2.2 stable 2017-01-19 22:04:18 -05:00
Gus Caplan
0300601649 remove redundant editing (#1116)
* remove redundant editing

* update docs
2017-01-19 17:11:24 +00:00
Gus Caplan
1e6abe587b no more (#1117) 2017-01-19 17:10:54 +00:00
Schuyler Cebulskie
b13fdcc8d3 Merged master into indev-prism 2017-01-18 19:23:18 -05:00
Gus Caplan
3f4cbd07dd add voice region getting (#1108)
* add voice region getting

* clean up this mess

* add docstrings

* Update VoiceRegion.js

* Update RESTMethods.js
2017-01-18 19:14:44 -05:00
Gus Caplan
ff92905848 add warning about which class to use for embeds (#1107)
* Update MessageEmbed.js

* Update MessageEmbed.js
2017-01-18 10:55:06 -05:00
Peter Vu
93d8ec3e87 VoiceChannel#full fix on no limit channel for joinable (#1106)
* VoiceChannel#full fix on no limit channel for joinable

* Update VoiceChannel.js
2017-01-16 23:36:29 -05:00
Hackzzila
f8b9bf6884 Fix reaction events (#1105)
* Fix reaction events

* Made it better

* one line ftw
2017-01-16 22:52:25 +00:00
Amish Shah
ba465bc680 Merge branch 'master' into indev-prism 2017-01-15 14:20:32 +00:00
Gus Caplan
8c220e76ec Fix GuildDelete typing stopping (#1098)
* whoops

* ugh

* Update GuildDelete.js
2017-01-14 23:33:26 -05:00
Programmix
d10ca8e7ba Add VoiceChannel#full and improve joinable/join permission checks (#1100)
* Improve voice channel join permission checks

* Update ClientVoiceManager.js
2017-01-14 23:32:17 -05:00
Programmix
7cb5b22ebb @Gawdl3y dun goofed (#1099) 2017-01-14 20:03:26 -05:00
Gus Caplan
48be401330 add clientuser acceptinvite (#1081)
* add clientuser acceptinvite

* Update RESTMethods.js

* Update ClientUser.js

* Update ClientUser.js

* Update RESTMethods.js
2017-01-14 21:25:12 +00:00
Gus Caplan
7f4846c826 Add option to filter old messages in TextBasedChannel#bulkDelete (#1090)
* add filtering for messages older than two weeks

* Update RESTMethods.js

* Update RESTMethods.js

* Update RESTMethods.js

* Update TextBasedChannel.js

* Update RESTMethods.js

* Update RESTMethods.js

* Update RESTMethods.js

* Update RESTMethods.js

* Update TextBasedChannel.js
2017-01-14 16:21:23 -05:00
Gus Caplan
78bf402e8e allow users and members to not be cached (#1097) 2017-01-14 21:20:09 +00:00
Amish Shah
098ad195bd Merge branch 'master' into indev-prism 2017-01-14 21:00:13 +00:00
Gus Caplan
e216fc7a81 stop spamming api, and kill typing when a guild isn't there anymore (#1096) 2017-01-14 17:31:52 +00:00
Schuyler Cebulskie
f647eb7a91 Clean up colour resolver 2017-01-13 22:55:10 -05:00
Schuyler Cebulskie
df02eee065 Fix Guild#large not being accurate for user accounts 2017-01-13 21:47:47 -05:00
Schuyler Cebulskie
ee1c343d78 Update dependencies 2017-01-13 21:26:25 -05:00
Schuyler Cebulskie
1e76f519dc Add missing then 2017-01-13 21:13:54 -05:00
Schuyler Cebulskie
63c8b1b2f2 Add more info to build commit messages 2017-01-13 21:11:08 -05:00
Schuyler Cebulskie
af6c8fa4a4 Make Travis builds skip revert branches 2017-01-13 20:39:03 -05:00
Schuyler Cebulskie
fa85da19a8 Fix Travis builds continuing after failed lint 2017-01-13 20:23:49 -05:00
Amish Shah
7357fc2163 Update typings and increase performance when not listening for presence updates 2017-01-13 19:15:21 +00:00
Amish Shah
f7a4dee4e1 Merge branch 'master' into indev-prism 2017-01-13 18:58:37 +00:00
Gus Caplan
5ac410f352 Cleanup webhooks (#1094)
* clean up webhooks and fix sending messages with webhooks

* whoops

* fix up options

* Update Webhook.js

* Update Webhook.js

* Update Webhook.js

* fix docstring
2017-01-13 18:48:12 +00:00
Schuyler Cebulskie
74fd0421ba Add missing descriptions 2017-01-13 00:18:00 -05:00
Gus Caplan
0d4eab8d24 add color resolvable, and color constants from the client (#1080)
* add color resolvable, and color constants from the client

* fix up docs

* Update ClientDataResolver.js

* add easter eggs

* Update ClientDataResolver.js

* Update RESTMethods.js
2017-01-12 18:43:22 +00:00
Gus Caplan
a3091f5262 Handle 4011 ws event code (#1083)
* 4011

* Update WebSocketManager.js

* smh gawdl3y
2017-01-11 22:59:09 +00:00
Schuyler Cebulskie
b2822c584a Minor doc updates 2017-01-10 19:43:25 -05:00
Schuyler Cebulskie
5caa7df1d8 Add centralised reply option to message options 2017-01-10 19:25:05 -05:00
Schuyler Cebulskie
8b0e5aad38 Fix sendEmbed with array content 2017-01-10 19:22:03 -05:00
Gus Caplan
c37cd3fd91 clean up webhooks and fix sending messages with webhooks (#1078)
* clean up webhooks and fix sending messages with webhooks

* whoops

* fix up options

* Update Webhook.js

* Update Webhook.js

* Update Webhook.js
2017-01-10 16:52:12 -05:00
Amish Shah
b5026909a1 Remove DMChannel.bulkDelete and GroupDMChannel.bulkDelete 2017-01-09 18:20:05 +00:00
Enchanted13
47707d245d Changed return type of Guild.defaultChannel (#1079)
The default channel for a Guild is always the first TextChannel in the Guild, it can't be a VoiceChannel.
2017-01-09 15:35:11 +00:00
Gus Caplan
5e7ae847de switch to proper querystring parser because why not (#1077) 2017-01-08 14:49:56 -05:00
ooookai
4a7284b86e move function getRoute(url) into class APIRequest (#1065)
this.route = getRoute(this.url);
  >>>
this.route = this.getRoute(this.url);
2017-01-08 19:34:06 +00:00
Amish Shah
391b618b3f Merge branch 'indev-prism' of https://github.com/hydrabolt/discord.js into indev-prism 2017-01-08 14:12:36 +00:00
Zack Campbell
b68283e57a Make _array & _keyArray non-enumerable (#1075)
Because Map has no enumerable properties
2017-01-08 11:59:45 +00:00
Amish Shah
fde3a976aa Revert "This expands the consistency of .find and .exists to include the id property" (#1074) 2017-01-08 10:17:10 +00:00
Jacob
2a668ac997 This expands the consistency of .find and .exists to include the id property (#1072)
* provide a more consistent api for .find

* remove random warning

* make code more concise
2017-01-08 09:02:44 +00:00
Programmix
adbd95adf0 Fix VoiceBroadcaster per-dispatcher volume (#1073) 2017-01-08 08:39:48 +00:00
Zack Campbell
4717c34ff6 Update typings submodule (#1069)
Added search stuff
2017-01-07 20:00:36 -05:00
Amish Shah
3b1264ad6d Fix Buffer deprecation stuff 2017-01-07 22:30:20 +00:00
Amish Shah
9f8289e433 don't process empty sets 2017-01-07 22:09:51 +00:00
Amish Shah
de0ba9fb7c change error to debug 2017-01-07 21:48:30 +00:00
Amish Shah
0df17b1634 Add broadcast.destroy(); 2017-01-07 21:39:12 +00:00
Amish Shah
9d36be58ef Fix typo in readme (#1070) 2017-01-07 21:19:37 +00:00
Amish Shah
dc640017cd Fix some stream bugs 2017-01-07 21:10:46 +00:00
Amish Shah
fcd7cf1450 stop destructive ending of broadcasts 2017-01-07 19:44:42 +00:00
Amish Shah
bace8bcac2 make broadcast not suck as much 2017-01-07 19:38:05 +00:00
Schuyler Cebulskie
bac599b52e Merge branch 'master' into indev-prism 2017-01-07 03:42:39 -05:00
Gus Caplan
42527ea969 fix message#isMemberMentioned (#1061)
* fix this

* e

* Update Message.js
2017-01-07 03:38:23 -05:00
Schuyler Cebulskie
e5e36d9111 Rename snowflake class to prevent naming conflicts 2017-01-07 03:37:16 -05:00
Zack Campbell
f76b47184a Fix typo in MessageSearchOptions typedef (#1068) 2017-01-07 03:31:15 -05:00
Schuyler Cebulskie
add3c1a8ee Clean up docs slightly 2017-01-07 03:29:52 -05:00
Schuyler Cebulskie
8cff77726a Detardify snowflake docs/organisation 2017-01-07 03:22:55 -05:00
Gus Caplan
a66b2b7ec5 add more things to clientuser (#1066)
* add more things to clientuser

* add desc
2017-01-05 16:38:48 +00:00
HyperCoder
fdc8050188 Fix <Guild>.large (#1062)
* Fix <Guild>.large

Would either be true or undefined.

* Update Guild.js

* To make hydrabolt happy

* Update Guild.js
2017-01-05 15:43:57 +00:00
Gus Caplan
495264761c Add snowflake utils (#1064)
* snowflakes

* use proper binary for snowflake example

* fix jsdoc errors

* remove dupe
2017-01-05 15:41:42 +00:00
Gus Caplan
d47f9d202b update search to have more useful returns (#1060) 2017-01-03 23:49:19 +00:00
Gus Caplan
7cbe81e71a fix editing with an array (#1059)
* fix editing with an array

* fix build

* put this back because reasons

* i'm having one of those nights
2017-01-03 06:59:34 +00:00
Schuyler Cebulskie
2c26d79f50 Replace custom ArrayBuffer converter with Buffer.from 2017-01-02 01:51:26 -05:00
Schuyler Cebulskie
f485ec22b2 Rename deploy key 2017-01-02 01:49:06 -05:00
Michael Huang
3b9b06227e Fix _removeReaction's reaction lookup (#1045)
* Fix _removeReaction's `this.reactions` lookup

* Use emoji.identifier for reaction collection keys
2016-12-30 21:06:05 -05:00
Schuyler Cebulskie
054d4655c1 Rearrange identifier 2016-12-30 21:05:37 -05:00
Gus Caplan
2b25641270 add option for guilds per shard, and fix type (#1053) 2016-12-30 21:03:23 -05:00
Schuyler Cebulskie
2156ce1e15 Remove unnecessary branch specification 2016-12-30 18:06:08 -05:00
Schuyler Cebulskie
814c4e8617 Merge branch 'master' into indev-prism 2016-12-30 16:49:35 -05:00
Schuyler Cebulskie
8406e97e13 Add a little more info 2016-12-30 16:42:51 -05:00
Schuyler Cebulskie
90f8cbd09a Add a header 2016-12-30 16:40:24 -05:00
Schuyler Cebulskie
f72817fff5 Rewrote v11 upgrade guide 2016-12-30 16:39:37 -05:00
Schuyler Cebulskie
4447e367f6 Fix other BufferResolvables with base64 2016-12-30 16:22:29 -05:00
Schuyler Cebulskie
2ea744c9d1 Reestablish the natural order and heirarchy of all things 2016-12-30 16:19:15 -05:00
Schuyler Cebulskie
eaf2091c2f Rearrange some methods and improve search description 2016-12-30 16:17:31 -05:00
Schuyler Cebulskie
43283eeaec Clean up search examples slightly 2016-12-30 16:15:51 -05:00
Amish Shah
8d8ac78e68 start voice topics 2016-12-30 21:12:13 +00:00
Amish Shah
91e0a81d6b Add playArbitraryInput 2016-12-30 19:20:32 +00:00
Amish Shah
eacbfbd520 Add VoiceBroadcast#playArbitraryInput and VoiceConnection#playArbitraryInput 2016-12-30 19:13:25 +00:00
Amish Shah
ac62a58f47 Merge branch 'master' into indev-prism 2016-12-30 19:00:53 +00:00
Amish Shah
023ac0a7fe Add search() stubs for docs 2016-12-30 18:59:34 +00:00
Amish Shah
4c8e4fde6f Fix remove reaction endpoint 2016-12-30 18:31:48 +00:00
Amish Shah
e2753136a4 Reorganise VoiceBroadcast dispatchers and also add new events 2016-12-30 18:21:22 +00:00
Amish Shah
d13c48bafa more tiny voice docs 2016-12-30 18:10:48 +00:00
Amish Shah
56e01291e2 Merge branch 'master' into indev-prism 2016-12-30 18:05:29 +00:00
Amish Shah
69ccc75590 more voice docs 2016-12-30 17:56:58 +00:00
Amish Shah
221e7f8b21 Voice Connection docs 2016-12-30 17:44:05 +00:00
Amish Shah
50dc9addf1 Fix ESlint error, remove 'long' module import 2016-12-30 17:28:23 +00:00
Amish Shah
f31a3725fe StreamDispatcher documentation 2016-12-30 17:25:28 +00:00
Gus Caplan
bde6749d65 clean up search (#1049)
* add more search stuff

* clean up the options

* fix link hostname

* use some resolvers

* fix type

* move the trasform to a seperate file

* pass this param

* move typedef
2016-12-30 17:21:03 +00:00
Gus Caplan
da32c2ec3d add more search stuff (#1046)
* add more search stuff

* clean up the options

* fix link hostname

* use some resolvers

* fix type
2016-12-30 17:14:31 +00:00
Gus Caplan
258e4b9085 fix setting avatar to buffer (#1048) 2016-12-30 17:00:16 +00:00
Amish Shah
3109accf87 improve broadcasting performance 2016-12-30 15:25:28 +00:00
Amish Shah
fb1d0a3e74 fix eslint 2016-12-30 15:00:56 +00:00
Amish Shah
0a6d71d7e5 try this fix idk 2016-12-30 14:45:43 +00:00
Amish Shah
90ca422485 undo that 2016-12-30 14:30:57 +00:00
Amish Shah
22a6ded341 Defer some creations 2016-12-30 14:27:59 +00:00
Amish Shah
91fc6ccb5c VoiceBroadcasting much more efficient 2016-12-30 13:57:09 +00:00
Amish Shah
bf4010e89c Simplify broadcast creation 2016-12-30 12:46:34 +00:00
Amish Shah
2f630a0dbb AudioPlayer now destroys all dispatchers on closing, Broadcasts are also destroyed properly 2016-12-30 12:43:56 +00:00
Gus Caplan
beffb390e6 Add search (#1043)
* add search

* Update ClientDataResolver.js
2016-12-30 02:44:19 -05:00
Schuyler Cebulskie
f0adf8f122 Fix User#defaultAvatarURL in Node 6 2016-12-30 01:46:56 -05:00
Schuyler Cebulskie
1afb21c981 Tweak output some more 2016-12-30 01:01:16 -05:00
Schuyler Cebulskie
5fd10ef63e Tweak Travis output 2016-12-30 00:53:53 -05:00
Schuyler Cebulskie
bcbd187223 Fix test-docs script 2016-12-30 00:07:24 -05:00
Schuyler Cebulskie
e44d571671 Add PR number output to Travis build 2016-12-30 00:06:24 -05:00
Schuyler Cebulskie
ed08c24784 Add Node 7 testing and prettify output 2016-12-30 00:05:43 -05:00
Schuyler Cebulskie
55ee566fb2 Improve Travis builds 2016-12-29 23:51:56 -05:00
Schuyler Cebulskie
b5f51d612a Merge branch 'master' into indev-prism 2016-12-29 23:15:41 -05:00
Schuyler Cebulskie
be19a08575 Make Travis builds run for all but blacklisted branches 2016-12-29 23:12:44 -05:00
bdistin
9e70d6279f Fix erroneous docs examples (#1042) 2016-12-29 22:01:18 -05:00
Schuyler Cebulskie
89cea574be Add prism-media dependency 2016-12-29 20:06:48 -05:00
Schuyler Cebulskie
77548c194f Add missing type 2016-12-29 20:01:30 -05:00
Schuyler Cebulskie
863e34db19 Change branch in contributing guide 2016-12-29 19:21:19 -05:00
Amish Shah
c6f17054fc Remove unused fs import 2016-12-29 22:59:43 +00:00
Amish Shah
12605575fb Add VoiceBroadcast.pause and VoiceBroadcast.resume 2016-12-29 21:59:36 +00:00
Amish Shah
ad18b05d66 Reset what is playing 2016-12-29 21:51:23 +00:00
Amish Shah
72a99f9582 start work with broadcast streams 2016-12-29 21:22:13 +00:00
Amish Shah
e9af3f0a1f Merge branch 'indev' into indev-prism 2016-12-29 19:02:54 +00:00
Gus Caplan
22b1c425ac add ClientUser.settings (#1041)
* add user settings

* Update Ready.js
2016-12-29 18:58:39 +00:00
Amish Shah
ebd2f0e73f Update ws dependency to 1.1.1 for security 2016-12-29 17:31:49 +00:00
Amish Shah
f8440ad565 Merge branch 'indev' into indev-prism 2016-12-29 17:26:22 +00:00
Amish Shah
ab4b7ea19a update docs 2016-12-29 17:11:10 +00:00
Amish Shah
c8f6b6b059 update typings 2016-12-29 16:41:39 +00:00
Amish Shah
f613cc3c50 Version bump package.json 2016-12-29 16:37:39 +00:00
Amish Shah
dc53b8de95 Merge branch 'indev' 2016-12-29 16:37:00 +00:00
Schuyler Cebulskie
6e51a44f92 Small doc updates 2016-12-29 11:19:30 -05:00
Amish Shah
4541b3e264 Simplify check in StreamDispatcher and add test voice bot 2016-12-29 16:10:51 +00:00
Zack Campbell
665ef21c85 Update DMChannel, GroupDMChannel for docs (#1038) 2016-12-29 11:04:41 -05:00
Schuyler Cebulskie
3e3de51545 Update message sending docs 2016-12-29 11:01:27 -05:00
Schuyler Cebulskie
ba77e69edf Document boolean for code option 2016-12-29 10:51:05 -05:00
Schuyler Cebulskie
86ffc86324 Update updateMessage with the new code logic 2016-12-29 10:49:59 -05:00
Schuyler Cebulskie
650acbf662 Improve codeblock lang 2016-12-29 10:44:24 -05:00
Amish Shah
1452fa5014 Merge branch 'indev' into indev-prism 2016-12-29 14:19:39 +00:00
Amish Shah
75d45bd587 Fix sending codeblocks without a lang 2016-12-29 14:18:39 +00:00
Amish Shah
627a8870f5 change error emission logic 2016-12-29 14:14:27 +00:00
Amish Shah
e1d01ed6a2 Merge branch 'indev' into indev-prism 2016-12-29 14:04:05 +00:00
Amish Shah
6d7293e3c5 Fix sending an array of messages 2016-12-29 14:00:54 +00:00
Schuyler Cebulskie
95790dcf08 Update User#equals doc 2016-12-29 03:21:22 -05:00
Schuyler Cebulskie
1b76333b8b Add missing @typedef line 2016-12-29 03:04:11 -05:00
Schuyler Cebulskie
fe7ed523b3 Improve ClientUser presence method docs 2016-12-29 03:01:34 -05:00
Gus Caplan
ed8fcf651a Centralise message sending logic in one method, remove sendTTSMessage, add client shortcut in RESTMethods (#1031)
* start wip rewrite of sending/editing messages

* pass the build, modify the edit method to fit the new system

* simplify the applyToClass method

* change handling of file options

* add back message splitting

* i couldn't help myself

* add some smart message options

* clean up, add sexy options

* fix indentation

* fix up splitting

* add \b

* add back old methods for hydar happiness

* clean up more

* move code handling to the rest method

* clean up this.rest.client

* Update TextBasedChannel.js

* add docs back for the bad methods

* fix reply in group dms

* srsly gawdl3y

* make code better

* fix changes for gawdl3y

* fix checking

* remove getter

* make code handling more robust

* k

* fix up sendEmbed docs

* stupid

* fix up more docs because aaaaa

* no more pls
2016-12-28 23:58:30 -05:00
Schuyler Cebulskie
a014b59790 Update webpack 2016-12-28 21:35:19 -05:00
Schuyler Cebulskie
17a61731ef Update typings (test) 2016-12-28 20:40:07 -05:00
Zack Campbell
dfa6740b89 Update gitmodules (#1036)
To reflect transfer of ownership of the typings repo, in case the source forwarding does not continue indefinitely going forward
2016-12-28 19:52:15 -05:00
Amish Shah
7ede44bc00 Add CloseEvent external 2016-12-29 00:27:40 +00:00
Amish Shah
18e04d69f1 Delete stream setups after they have ended or errored 2016-12-29 00:16:13 +00:00
Amish Shah
e64d9c6057 fix odd bug 2016-12-28 23:58:01 +00:00
bdistin
ea798eaaf3 Update Missing Permission Resolvables (#1035) 2016-12-28 18:27:02 -05:00
Amish Shah
e7824d6515 Fix weird audio playback bug between subsequent streams on an AudioPlayer 2016-12-28 23:21:34 +00:00
Schuyler Cebulskie
c1b9437f0d Set up typings submodule 2016-12-28 18:20:38 -05:00
Zack Campbell
e4bae99747 Update GuildResolvable typedef (#1034)
To reflect the resolver supporting Guild ID strings
2016-12-28 16:50:32 -05:00
Amish Shah
00254f35b0 Make voice return streamdispatcher 2016-12-28 21:40:11 +00:00
Schuyler Cebulskie
bbeef44e66 Update ClientDataResolver.js 2016-12-28 16:32:14 -05:00
Schuyler Cebulskie
8e47058286 Update ClientDataResolver.js 2016-12-28 16:30:15 -05:00
Schuyler Cebulskie
289447e4c9 Update Client.js 2016-12-28 16:28:36 -05:00
Amish Shah
4294d267e7 Add playFile back to voice connection 2016-12-28 19:23:11 +00:00
Gus Caplan
3451367591 fix the everyone role mentioning (#1032) 2016-12-28 19:04:54 +00:00
Amish Shah
0a47d0e1d6 Remove old stuff 2016-12-28 18:16:26 +00:00
Amish Shah
8e75b47a7b add back seek option 2016-12-28 17:37:56 +00:00
Amish Shah
8cf520d5af update streamDispatcher 2016-12-28 17:28:14 +00:00
Amish Shah
be32bbc3a4 really really really messy implementation of prism 2016-12-28 17:04:18 +00:00
Gus Caplan
8d966932a9 funny (#1030) 2016-12-28 01:42:42 -05:00
bdistin
4d2153c7c6 Document ShardingManager#message (#1024)
* Document ShardingManager#message

* Update Shard.js
2016-12-27 22:44:17 -05:00
Zack Campbell
265021bfa5 Add sendEmbed to GuildMember for docs (#1028)
* Add sendEmbed to GuildMember for docs

* Update GuildMember.js
2016-12-27 22:35:11 -05:00
Schuyler Cebulskie
84991c767e Improve some Gus shenanigans 2016-12-27 22:30:51 -05:00
meew0
14ba0373eb Fix a small spelling mistake in a debug log 2016-12-27 23:16:30 +01:00
Gus Caplan
779681e88f "knock knock. who's there? ack lol. GET OFF MY PORCH!" (#1023) 2016-12-27 17:31:23 +00:00
Gus Caplan
bac4ccead7 "knock, knock. who's there. discord, lol" (#1021) 2016-12-27 12:13:57 +00:00
Gus Caplan
b79533e373 correctly handle invalid session (#1020)
* correctly handle invalid session

* give discord some time, use it wisely
2016-12-27 00:04:09 +00:00
Amish Shah
b0c2f818a8 Remove cached events 2016-12-26 21:56:04 +00:00
Amish Shah
f510dc5a10 Merge branch 'indev' of https://github.com/hydrabolt/discord.js into indev 2016-12-26 21:30:30 +00:00
Amish Shah
bf7767fe2c Rename emoji events 2016-12-26 21:30:27 +00:00
Schuyler Cebulskie
758c801dd5 Change to function declaration 2016-12-26 15:40:34 -05:00
Alexander
b1473b1e4c Change RichEmbed to take StringResolvables (#1010)
Changed RichEmbed to take StringResolvables for all title, name, value and
description arguments.

Also changed a few lines in setColor to make sure that the value
assigned to this.color is always a number.
2016-12-26 15:39:07 -05:00
Amish Shah
72c667b307 Fix potential ReferenceError with GuildMembersChunk 2016-12-26 20:20:58 +00:00
Amish Shah
cd657be8be Add functionality for GuildEmoji events 2016-12-26 19:21:00 +00:00
Gus Caplan
2410fdf8d2 fix heartbeats once and for all (#1016) 2016-12-25 16:28:36 +00:00
Schuyler Cebulskie
fb6a8d1637 Update webpack and clean up deps 2016-12-24 20:25:29 -05:00
Schuyler Cebulskie
218920e4f1 Fix logo on GitHub 2016-12-24 20:02:36 -05:00
Schuyler Cebulskie
74c3bae506 Teensy weensy cleanup 2016-12-24 19:54:37 -05:00
Schuyler Cebulskie
ab30715028 Fix Hydar's lint errors 2016-12-24 19:49:24 -05:00
Amish Shah
544e456302 Add ClientOptions.restTimeOffset for better performance for bots with a good network connection 2016-12-24 12:13:42 +00:00
Gus Caplan
3eca3ba95e add handler in case heartbeat is not acked (#1013)
* add handler in case heartbeat is not acked

* ffs
2016-12-24 11:04:27 +00:00
Gus Caplan
7c12fdcb56 add payload_json instead of the current iterator (#1015) 2016-12-24 02:04:18 -05:00
Schuyler Cebulskie
28ca83011c Implement missing Collection#reduce functionality 2016-12-24 01:59:14 -05:00
Gus Caplan
265ac90234 Update Constants.js (#1014) 2016-12-23 18:41:04 +00:00
Amish Shah
2ab5bae69a change let to const 2016-12-23 18:38:48 +00:00
Amish Shah
e63432c18e Fix editing role positions (fixes #864) 2016-12-23 16:45:47 +00:00
Gus Caplan
e6a041241b cleanup ws ratelimiting, and apirequest (#957)
* cleanup ws ratelimiting, and apirequest

* cleanup timestamps

* clean up timestamps
2016-12-23 14:37:43 +00:00
Amish Shah
74ef0b2e14 minor fixes 2016-12-23 14:37:35 +00:00
Amish Shah
b518437f52 Emit the close event on disconnect 2016-12-23 13:59:06 +00:00
Gus Caplan
2390e525ef Fix User#avatarURL for Nitro animated avatars (#1012)
* fix avatar url

* switch to cdn
2016-12-23 00:57:49 -05:00
bdistin
f726db2152 Revert error emit on Slow Stream Gen (#1011) 2016-12-22 22:24:31 -05:00
Gus Caplan
cecb0aee02 Update User#setEmail/setPassword/setUsername (#991)
* fix some things with user updates and tokens and such

* fix stupid

* Update ClientUser.js

* Update ClientUser.js
2016-12-22 15:12:29 -05:00
Hackzzila
84954c8860 Add notice about disabling events (#1008)
* Add notice about disabling events

* fix english

* Update Constants.js

* Update Constants.js

* Update Constants.js

* Update Constants.js
2016-12-22 15:01:04 -05:00
Zack Campbell
4580f62a1f Add RichEmbed to typings (#1009) 2016-12-22 14:50:28 -05:00
Gus Caplan
fa7d63a10a add an invite generator (#993)
* add an invite generator

* `number |= null` is safe, so we can simplify this

* Update Client.js

* aaaaaa
2016-12-20 23:37:36 +00:00
Gus Caplan
9486609ef9 Create .tern-project (#1003) 2016-12-20 23:37:19 +00:00
Gus Caplan
c483dd8239 add some useful events (#1004) 2016-12-20 23:37:06 +00:00
Gus Caplan
84503c8877 add User#dmChannel (#1005)
* add User#DMChannel

* Update User.js

* Update User.js
2016-12-20 23:36:43 +00:00
Gus Caplan
e392107369 add ClientUser#fetchMentions (#999)
* add ClientUser#fetchMentions

Signed-off-by: Gus Caplan <gus@localhost.localdomain>

* ugh

* Update ClientUser.js

* Update ClientUser.js

* Update ClientUser.js
2016-12-19 02:16:27 -05:00
Gus Caplan
a0a3989e59 fix issue with uncached channels and adding members (#1001)
* fix this thing

* fix a silly

* Update Guild.js
2016-12-19 02:11:55 -05:00
Gus Caplan
b74c1b70b6 fix 992 (#994) 2016-12-18 16:06:42 -05:00
Will Nelson
736fa7c611 friendlier notification of an invalid token (#997)
* friendlier notification of an invalid token

* fixed

* even fixeder

* no token -> invalid token

* eslint

* Update RESTMethods.js
2016-12-17 13:22:39 -05:00
Gus Caplan
6c38b83923 fix runkit/tonicdev example (#998) 2016-12-17 17:44:25 +00:00
Schuyler Cebulskie
361547a588 Fix a bunch of capitalisation issues 2016-12-15 20:10:38 -05:00
bdistin
fbe1929bde Pass Reason to the streamDispatcher end event (#985)
* Pass Reason to the streamDispatcher end event

* Update <dispatcher>.end() to bring inline with <collector>.stop()

Also changed "Stream is not generating quickly enough." from an end to an error, per Crawl...

* Fix docs Copy/Paste fail from collection end event
2016-12-15 16:32:37 +00:00
Gus Caplan
a20bac7258 update per issue 69 on the h&c github (#963)
* update per issue 69 on the h&c github

* clean up things that don't exist anymore
2016-12-15 16:32:19 +00:00
Gus Caplan
9c59b649ad get rid of user agent errors (#990) 2016-12-15 16:32:06 +00:00
Schuyler Cebulskie
36be4c47f6 Change welcome logo to reference static one on site 2016-12-14 21:05:48 -05:00
Schuyler Cebulskie
33a4388121 Switch to SVG logo 2016-12-14 20:42:51 -05:00
Gus Caplan
2e7472bb1a just webpack things (#988) 2016-12-14 19:40:47 -05:00
Alexander
264ee8e7f1 Equals updates (#987)
* Docs update

Changed the param names and param descriptions to be consistent.

* Added === comparison

Changed Presence.equals and Game.equals to first compare using ===

* Collection.equals fix

Now returns false when collection is undefined, instead of crashing
💯
2016-12-14 17:10:34 -05:00
Alexander
906bb3c5f3 Utilise Collection#equals for structures' equals (#986)
Utilizing Collection.equals in GuildChannel.equals
Utilizing Collection.equals in GroupDMChannel.equals
2016-12-14 15:35:40 -05:00
Schuyler Cebulskie
bef0523ebf Capitalise ID in Guild#applicationId 2016-12-14 13:04:40 -05:00
Schuyler Cebulskie
f1c4ef659e Fix a Hydar thing 2016-12-14 12:40:02 -05:00
Schuyler Cebulskie
4e76251aa2 Fix some Gus stuff 2016-12-14 12:37:23 -05:00
Gus Caplan
8139bef4e2 add some missing properties (#978)
* add premium to profile

* add other missing stuff
2016-12-14 17:23:22 +00:00
Pio
1c61b46660 Fix setBitrate typo in VoiceChannel.js (#984)
Fixed simple typo that prevented setBitrate method from working.
2016-12-14 09:45:09 -05:00
Zack Campbell
8596eadb25 Bring in latest typings fix (#982) 2016-12-13 20:43:49 -05:00
Perry Berman
78026df1df fix defaultAvatarURL (#983)
looks like someone forgot how to caps
2016-12-13 20:43:28 -05:00
Cole
586d652c16 Quality of life changes (#968)
* + Added function to get a user's default avatar
+ Added HOST constant to Constants
+ Added assets endpoint
+ Added quality of life function to get a user's avatar or default avatar
+ Added quality of life function to get member's nickname or username

* * Fixed invocation of a getter.

* Fixed lint issue

* Made the API constant use the HOST constant for DRY-ness
Changed DOC comment to be more descriptive

* Update GuildMember.js
2016-12-12 22:34:19 -05:00
bdistin
3193df792e Added Missing Author URL Option (#979) 2016-12-12 22:29:39 -05:00
Hackzzila
52a83b9218 Add ffmpeg-binaries as a possible source of ffmpeg (#975)
* Add ffmpeg-binaries as a possible source of ffmpeg

* Add note in faq
2016-12-11 19:02:00 +00:00
Gus Caplan
d766e727a1 add GuildChannel#clone (#973)
* add GuildChannel#clone

* e
2016-12-11 09:07:52 +00:00
bdistin
710d3db94f RichEmbed Tweaks/Improvements (#964)
* RichEmbed Tweaks/Improvements

Changed .addTimestamp() to .setTimestamp() to bring it inline with all other single value settings.
Changed Field value to StringResolveable from String to bring it inline with typical sendMessage functionality.

* Lint Fix

* Remove Default, Add check for undefined
2016-12-09 22:10:50 -05:00
Slamakans
b79a91b3a9 new errors for richembed yay (#966)
* Update faq.md (#949)

* fix docs

* Revert "fixed typo in documentation" (#950)

* new errors for richembed yay

Simplified the parsing for color arrays.

Throwing some errors on values that would result in Bad Requests.

Changed setAuthor and setFooter's icon parameter to icon_url to match
the embed object's property name.

Changed the docs for setFooter to reflect that icon_url is optional.

* changed docs instead of arg names

* capitalization
2016-12-09 22:09:55 -05:00
Gus Caplan
c5f93eb44e Add more MessageEmbed stuff (#970)
* this is why wrapping data 1:1 is a bad idea

* ffs

* whoever wrote role.hexColor is a bad, bad person

* Update MessageEmbed.js

* Update MessageEmbed.js
2016-12-09 22:07:34 -05:00
Gus Caplan
e51fed968d hydarplz (#962) 2016-12-09 20:47:56 +00:00
Amish Shah
a54a62787d simplify README 2016-12-09 17:37:48 +00:00
Will Nelson
275d9d3ce0 fix remove reactions return (#961) 2016-12-07 10:12:00 -05:00
Schuyler Cebulskie
a0de75f290 Improve ShardClientUtil warnings/errors 2016-12-07 00:30:36 -05:00
Gus Caplan
b177aefdd6 add user/member lastMessageID (#959)
* add user.lastMessageID

* stupid

* add member.lastMessageID
2016-12-06 23:46:32 -05:00
Gus Caplan
050d3f9303 fix serialize for webpack (#960)
* fix serialize for webpack

* Update WebSocketManager.js

* Update WebSocketManager.js
2016-12-06 23:45:31 -05:00
Évelyne Lachance
f9bf0ed5e6 Quick fix for error in guildMemberRole
Because Gus is a massive derp.
2016-12-06 18:35:33 -05:00
Gus Caplan
d67ecdd2af fix authenticating role updates (#956)
* Update RESTMethods.js

* Update RESTMethods.js
2016-12-06 17:23:40 +00:00
Zack Campbell
37bfdd154c Fix embed footer text length error (#955) 2016-12-05 20:50:25 -05:00
Zack Campbell
daa79b7f97 Add more errors for RichEmbed builder char limits (#954)
* Add more errors for RichEmbed builder char limits

Might as well if we're erroring on number of fields when that's the one limit that doesn't actually throw a bad request.

* Fix name.length check in previous commit

* Update RichEmbed.js

* Update number of fields error message
2016-12-05 20:33:58 -05:00
bdistin
8eff36b744 Fix reconnect when using WS (#952)
* Fix reconnect when using WS

* Add disconnect status (fix reconnect with WS)
2016-12-05 21:06:49 +00:00
Schuyler Cebulskie
977e29ceba 👏 @Programmix 2016-12-04 20:25:24 -05:00
Amish Shah
8f8e0b1e52 fix that 2016-12-04 13:08:50 +00:00
Amish Shah
d6218050ba export constants 2016-12-04 13:08:22 +00:00
Schuyler Cebulskie
bd00bc404c Make Collection#set/delete return the result of the super call, and clean up equals docs 2016-12-03 21:38:27 -05:00
Slamakans
a0b245bfe1 equals function added (#948)
Adds an equals function that checks for differing size and differing
key-value pairs, and takes into account the edge case for when there's
an entry for the key, but the value is undefined.
2016-12-03 21:34:26 -05:00
Amish Shah
a53dcd52e7 link discord-rpc 2016-12-03 23:23:36 +00:00
Amish Shah
4bb2afe7cb Revert "fixed typo in documentation" (#950) 2016-12-03 20:16:17 +00:00
Amish Shah
eb253eef45 fix docs 2016-12-03 19:50:14 +00:00
Benjamin Leuckefeld
1d9d246870 Update faq.md (#949) 2016-12-03 18:49:43 +00:00
Schuyler Cebulskie
d3687cb20d Fix typo 2016-12-03 01:11:37 -05:00
Gus Caplan
638e51a18c support new guild member role endpoints for cleaner role updates (#901)
* support new roles endpoints

* use promise chaining

* properties man

* Update RESTMethods.js

* Update RESTMethods.js

* Update RESTMethods.js

* Update RESTMethods.js
2016-12-02 23:46:55 -05:00
Programmix
8b7ef0c850 Add Message.fetchWebhook (#947)
* Add Message.fetchWebhook

* Update Message.js
2016-12-02 22:59:44 -05:00
Schuyler Cebulskie
c0aa6bd30f Work around Node's module loading 2016-12-02 22:55:37 -05:00
Schuyler Cebulskie
6adc8a9d0d Move a getter around 2016-12-02 22:47:12 -05:00
Programmix
676a895da7 Add Message - webhookID, isMemberMentioned (#946)
* Add Message - webhookID, isMemberMentioned

* Update Message.js

* Update Message.js

* Update Message.js

* Update Message.js
2016-12-02 22:35:01 -05:00
Gus Caplan
6cfbf76406 add more embed stuff (#939)
* add fun embed stuff

* add more docstrings

* fix gawdl3y

* Update RichEmbed.js
2016-12-02 21:59:38 -05:00
Schuyler Cebulskie
e141deb7ef Update installation info 2016-12-02 21:42:49 -05:00
Gus Caplan
1e5afc1608 Add etf encoding support with erlpack (#943)
* the performance from this is astounding

* help uws

* save 15 bytes in webpacks

* update readme

* why is markdown like this

* optimizations

* Update WebSocketManager.js
2016-12-02 21:35:59 -05:00
Schuyler Cebulskie
58c7c2e7b8 Add client ping stuff 2016-12-02 20:58:19 -05:00
Schuyler Cebulskie
2488e1a00f Improved the definition of structures' client properties 2016-12-02 19:52:27 -05:00
Gus Caplan
edfb27f428 switch to cdn endpoints, add guild splash url (#932) 2016-12-01 19:33:15 -05:00
Amish Shah
6043a1f83a Fix #937 (guild member presence before READY) 2016-11-30 20:31:07 +00:00
Schuyler Cebulskie
ac68e9f077 Switch Travis to Ubuntu 14.04 2016-11-28 23:35:25 -05:00
Amish Shah
1933451d2f fix reconnecting 2016-11-28 16:14:34 +00:00
Amish Shah
b335824570 potentially fix #910, guild sync no longer assumes unknown users are new to the guild 2016-11-28 15:41:57 +00:00
Schuyler Cebulskie
57af84bc95 Clean up Guild#setRolePosition slightly 2016-11-27 20:09:41 -05:00
Schuyler Cebulskie
6dc93a0184 Simplify Role#setPosition 2016-11-27 20:03:44 -05:00
Schuyler Cebulskie
769ea5b50f Add more detail to error message 2016-11-27 19:39:10 -05:00
Schuyler Cebulskie
8e7cb7fc4e Revert "Escape the backslash (#931)"
This reverts commit 5fb4e257c8.
2016-11-27 18:31:27 -05:00
Vap0r1ze
5fb4e257c8 Escape the backslash (#931) 2016-11-27 22:38:42 +00:00
Hackzzila
d9400ba289 fix readme spacing bug (#930) 2016-11-27 22:10:57 +00:00
Schuyler Cebulskie
cc9e484276 Fix a thing Gus noticed 2016-11-27 16:23:17 -05:00
Hackzzila
1aed3de647 Add note about uws in readme (#929) 2016-11-27 20:22:09 +00:00
Hackzzila
6afd80cf53 Make uws a peer dep, and readd ws (#928)
* Make uws a peer dep, and readd ws

* if -> else if
2016-11-27 19:59:50 +00:00
acdenisSK
b85a589a01 Add typings (#925) 2016-11-27 15:28:46 +00:00
Amish Shah
9323882a8d fix disabledEvents 2016-11-27 12:01:51 +00:00
Schuyler Cebulskie
dc6c1140bc Improve some JSDocs 2016-11-27 01:08:08 -05:00
Amish Shah
0d574a0678 Address #919, add WSEventType typedef 2016-11-26 23:01:47 +00:00
Amish Shah
e2932c05f2 whoops fix derp 2016-11-26 22:43:41 +00:00
Amish Shah
27e77d3839 Fix Role.setPosition not returning Role (addresses #902) 2016-11-26 22:42:33 +00:00
Will Nelson
4ae1f63a97 fix message.edits (#924) 2016-11-26 21:59:18 +00:00
Schuyler Cebulskie
85a7eab5bb Update examples URL 2016-11-26 00:47:16 -05:00
Schuyler Cebulskie
c683790de7 Remove old uws-specific code (0.11.1 has the good stuff) 2016-11-25 19:40:53 -05:00
Will Nelson
eedc097f3f fix playConvertedStream (#923) 2016-11-24 17:08:55 -05:00
Schuyler Cebulskie
ac64f8bd23 Improve rest args docs 2016-11-24 11:22:13 -05:00
Gus Caplan
945a2e370a fix voice ws (#922) 2016-11-24 02:39:01 -05:00
Gus Caplan
0008a18deb Fantastic PR #368 by Gus (#921) 2016-11-23 22:16:19 -05:00
Schuyler Cebulskie
91a69fb761 Update docgen dependency 2016-11-23 20:07:37 -05:00
Gus Caplan
c91ee7a3e7 Replace ws with uws (#918)
* change to uws (waiting for the next release tho)

* clean up, fix reconnections (maybe)

* change voice to use uws

* so messy
2016-11-23 19:30:00 -05:00
Schuyler Cebulskie
32879419e2 Fix dispatcher doc 2016-11-23 02:51:10 -05:00
Schuyler Cebulskie
c4b2077666 Rename Avatars.js to avatars.js 2016-11-22 19:54:24 -05:00
Schuyler Cebulskie
338aa58386 Set up new-new custom docs mechanism 2016-11-22 19:50:05 -05:00
Schuyler Cebulskie
c3d14636ab Missing files 2016-11-22 18:40:49 -05:00
Schuyler Cebulskie
77353e5220 Fix names of custom docs 2016-11-22 18:40:18 -05:00
Schuyler Cebulskie
a8bb20204b Fix paths 2016-11-22 18:34:08 -05:00
Schuyler Cebulskie
7c97244854 Move to new docgen 2016-11-22 18:24:29 -05:00
Schuyler Cebulskie
c8858de71e Change to zlibjs release 2016-11-22 01:05:53 -05:00
meew0
c2b5eb291a Ignore commit failures due to no changes being made (#912) 2016-11-21 12:09:49 -05:00
Schuyler Cebulskie
a4193553e2 Optimise websocket events 2016-11-20 23:39:40 -05:00
Schuyler Cebulskie
0d754d1fbb Change the way modules are excluded from webpack 2016-11-20 23:19:53 -05:00
Schuyler Cebulskie
ee4a8bb3b6 Made Client.browser a getter 2016-11-20 22:45:59 -05:00
Schuyler Cebulskie
f6a60581c4 Remove even more stuff from web dists 2016-11-20 22:40:06 -05:00
Schuyler Cebulskie
7e69475d11 Just to please Gus 2016-11-20 22:06:38 -05:00
Schuyler Cebulskie
f38944c8f6 Add ShardingManager web dist info 2016-11-20 21:50:36 -05:00
Schuyler Cebulskie
66845e2f68 Ensure ws doesn't get built 2016-11-20 21:46:35 -05:00
Schuyler Cebulskie
697fa15278 Add web dist info to readme 2016-11-20 21:42:39 -05:00
Schuyler Cebulskie
18dc95e1bd Ensure opusscript and node-opus don't get built 2016-11-20 21:17:07 -05:00
Schuyler Cebulskie
049ab42eb4 Do stuff slightly better 2016-11-20 20:54:40 -05:00
Schuyler Cebulskie
3ef16f97c4 Remove dynamic requires 2016-11-20 20:52:39 -05:00
Schuyler Cebulskie
411c9bd32c Update to Webpack 2.1 beta 27 2016-11-20 20:19:43 -05:00
Schuyler Cebulskie
6383d42eb5 Update to superagent 3 2016-11-20 20:08:59 -05:00
meew0
176859e7da Add webpack building, ESLint on PRs, and tag building to Travis (#911)
* Make Travis run ESLint before deploying

* Fix Travis never building docs for tags

* Update deploy.sh to also build the webpack

* Update deploy.sh
2016-11-20 19:57:24 -05:00
Gus Caplan
2440a4a2c8 Add webpack building (#907)
* friggin webpack tho

* probably important

* add all the stuff to the package.json

* add minify builds and a nice package.json script to run it all

* clean up

* use uglify harmony branch so we can actually run minify builds that work

* update build system

* make test better

* clean up

* fix issues with compression

* ‮

* c++ requirements in a node lib? whaaaaat?

* fix travis yml?

* put railings on voice connections

* 🖕🏻

* aaaaaa

* handle arraybuffers in the unlikely event one is sent

* support arraybuffers in resolvebuffer

* this needs to be fixed at some point

* this was fixed

* disable filename versioning if env VERSIONED is set to false

* Update ClientDataResolver.js

* Update ClientVoiceManager.js

* Update WebSocketManager.js

* Update ConvertArrayBuffer.js

* Update webpack.html

* enable compression for browser and fix ws error handler

* Update WebSocketManager.js

* everything will be okay gawdl3y

* compression is slower in browser, so rip the last three hours of my life

* Update Constants.js

* Update .gitignore
2016-11-20 19:38:16 -05:00
Schuyler Cebulskie
b3e795d0b0 Add newlines around logo 2016-11-20 18:21:23 -05:00
Schuyler Cebulskie
686cf297d2 Clean up various script things 2016-11-20 18:19:58 -05:00
Schuyler Cebulskie
2bb5aa1fda Change Collection description 2016-11-20 18:05:01 -05:00
Schuyler Cebulskie
a80a64f8ce Update Collection docs some more 2016-11-20 17:59:54 -05:00
Schuyler Cebulskie
74ab72fdea Update Collection docs 2016-11-20 17:39:55 -05:00
Schuyler Cebulskie
c29eb7ab20 Update links 2016-11-20 00:19:41 -05:00
Schuyler Cebulskie
34548e3ecc Fix webhook example 2016-11-19 21:23:16 -05:00
Schuyler Cebulskie
213c45323f Switch to a non-absurdly-sized logo 2016-11-19 21:21:49 -05:00
Crawl
3fd3588ef2 Fix reaction event names (#906) 2016-11-17 03:03:51 -05:00
Schuyler Cebulskie
93948328b4 Fix some Gus code 2016-11-17 02:51:46 -05:00
Schuyler Cebulskie
49fdc331a7 Fix formatting 2016-11-17 02:49:24 -05:00
Gus Caplan
b2bc844ed7 Add new MessageEmbed stuff (#898)
* fix

* Update MessageEmbed.js

* man

* Update MessageCreate.js

* Update MessageEmbed.js

* Update MessageEmbed.js

* clean up, add class

* my dreams are slowly becoming memes

* aghhh

* safety

* Update MessageEmbed.js

* Update MessageEmbed.js

* Update MessageEmbed.js

* dammit
2016-11-17 02:42:50 -05:00
York
1833a83664 Documented reaction events (#905) 2016-11-17 02:18:03 -05:00
Programmix
5ed8098af8 Clean up reactions, add remove all reactions (#890)
* Clean up reactions, add remove all reactions

* Reorganize reactions

* Re-add Gawdl3y's precious little inline

* Update Message.js
2016-11-13 02:29:26 -05:00
Steffen
a359f344d8 UnhandledPromiseRejectionWarning caused by resolveBuffer on empty resource body (#886)
* Fix for UnhandledPromiseRejectionWarning in resolveBuffer

* code simplification

* reject with TypeError if body is not a Buffer
2016-11-13 02:07:51 -05:00
Gus Caplan
c041b1bc23 fix these things (#895)
* fix these things

* fix enormous stupid
2016-11-13 02:05:55 -05:00
Schuyler Cebulskie
bb3b709d6e Nothing to see here 👀 2016-11-13 01:13:13 -05:00
Schuyler Cebulskie
da9d1a3daf Don't mind me 2016-11-13 01:07:48 -05:00
Gus Caplan
27270a3bad add embed support! (#894)
* add embed support!

* document message embeds

* make gawdl3y happy

* make edit great again

* make docs better

* Update Message.js

* Update TextBasedChannel.js

* Update TextBasedChannel.js
2016-11-13 01:05:13 -05:00
Schuyler Cebulskie
ee3a03f707 Make Collection.find/exists error when using with IDs 2016-11-13 00:27:56 -05:00
Schuyler Cebulskie
af5e07fb33 Change sentence structure slightly 2016-11-12 23:58:20 -05:00
Schuyler Cebulskie
5d3123d405 Add branch info to contributing guide 2016-11-12 23:50:06 -05:00
Schuyler Cebulskie
9d0fcb3936 Fix Message.reply example 2016-11-12 22:15:47 -05:00
Schuyler Cebulskie
c50de74310 Rename Message.addReaction -> Message.react 2016-11-12 22:12:26 -05:00
Schuyler Cebulskie
2c76f5437b Inline a line, woohoo 2016-11-12 22:08:43 -05:00
Schuyler Cebulskie
99b8d8f031 Clean up docgen code and some reaction stuff 2016-11-12 22:07:17 -05:00
Schuyler Cebulskie
acdf2d14c2 Undo that dummy commit 2016-11-12 21:50:17 -05:00
Schuyler Cebulskie
2b7c7bfd8f Dummy commit to force a travis build 2016-11-12 21:50:05 -05:00
Schuyler Cebulskie
8482abed7b Document generator v14 2016-11-12 21:45:05 -05:00
Schuyler Cebulskie
962cb8f8d0 Add default value support to docgen 2016-11-12 21:44:57 -05:00
Schuyler Cebulskie
7b26e70a2e Possibly change default return type for function docs 2016-11-12 21:14:36 -05:00
Schuyler Cebulskie
c65d7a10ec Fix another JSDoc type issue 2016-11-12 21:10:28 -05:00
Schuyler Cebulskie
b07a31d44e Change case of "function" 2016-11-12 21:07:22 -05:00
Schuyler Cebulskie
90304aa7d6 Fix loads of JSDoc type issues 2016-11-12 20:52:37 -05:00
Schuyler Cebulskie
318bb52c36 Clean up presence constructor 2016-11-12 17:46:11 -05:00
Schuyler Cebulskie
305070dded Remove experimental warning on sharding manager 2016-11-12 17:30:07 -05:00
Schuyler Cebulskie
3230d90a58 Remove fs-extra dependency 2016-11-12 17:23:54 -05:00
Schuyler Cebulskie
ce132d5f54 Make bot/user account warnings MOAR CONSISTENT!!one! 2016-11-12 17:15:17 -05:00
Schuyler Cebulskie
1e8392d90b Merge branch 'master' into indev 2016-11-12 00:30:25 -05:00
Marko Kajzer
c02eb2f171 Fixed unset field of VoiceConnection (#879)
* Fixed unset field of VoiceConnection

Fixed a typo

* Update VoiceConnection.js
2016-11-07 12:35:49 -05:00
Amish Shah
544540fb02 Change Role.manageable to Role.editable 2016-11-06 16:49:14 +00:00
HyperCoder
7d02e73a26 Add <Role>.manageable (#878) 2016-11-06 16:43:39 +00:00
Slamakans
5dc30d6812 Nullable permission overwrites (#869)
Made it possible to pass null to GuildChannel.overwritePermissions's
PermissionOverwriteOptions to blank the permission out.
2016-11-06 10:43:31 +00:00
Programmix
fe3914658a Grammar cleanup (#875)
This commit:
* fixes inconsistencies (primarily regarding capitalization)
* fixes non-proper nouns that were improperly capitalized
* fixes reminents from not-so-meticulous copy+paste jobs
2016-11-05 23:57:34 +00:00
Schuyler Cebulskie
93e6c69bd1 Update sync option docs 2016-11-01 02:28:23 -04:00
Schuyler Cebulskie
b91590d3a8 Fix mispositioned line in ClientOptions doc 2016-11-01 02:24:42 -04:00
Programmix
9a61de1493 Document GuildChannel.edit, add VoiceChannel.setUserLimit, fix typo (#866) 2016-11-01 01:42:05 -04:00
Schuyler Cebulskie
2a50dad852 Update docs for ezmode node-opus Windows building 2016-11-01 01:39:57 -04:00
Programmix
6dc95cd084 Add support for notes (#860)
* Add support for notes

* Ensure consistency with notes from ready payload

* Add getter method for users

* Minor tweaks

* Update warning messages

* More minor fixes
2016-10-30 23:06:09 -04:00
Schuyler Cebulskie
a673a97441 Rephrase Collection.find/exists slightly 2016-10-30 22:28:06 -04:00
Schuyler Cebulskie
d7e1e1c0c9 Add warnings for Collection.find/exists 2016-10-30 22:22:16 -04:00
Schuyler Cebulskie
73261646fc Fix ESLint warnings 2016-10-30 17:23:39 -04:00
Schuyler Cebulskie
d6f55adf52 Add missing exports for reaction stuff 2016-10-30 17:05:18 -04:00
Schuyler Cebulskie
f2496070d3 Document client timeout/interval stuff 2016-10-30 17:02:06 -04:00
Schuyler Cebulskie
85330769a7 Refactor OAuth application stuff 2016-10-30 16:55:08 -04:00
Schuyler Cebulskie
589c44327a Make bot/user account notices consistent 2016-10-30 16:41:39 -04:00
Schuyler Cebulskie
5fa9e3548b Rename ClientDataResolver.resolveFile -> resolveBuffer 2016-10-30 16:29:56 -04:00
Schuyler Cebulskie
60e0d507f0 Clean up nearly all promises to utilise chaining, other small fixes 2016-10-30 16:27:28 -04:00
Schuyler Cebulskie
8306d50bd8 Clean up a bunch of promise stuff 2016-10-30 12:47:17 -04:00
Programmix
c42e7a15aa Update documentation (add missing typedefs) (#861) 2016-10-30 00:08:32 -04:00
Amish Shah
c334bf4535 Merge branch 'master' into indev 2016-10-28 18:47:49 +01:00
Pascal Luttgens
cdf66f8011 Fixed resolveChannel when using a string (#857) 2016-10-28 14:09:40 +01:00
Schuyler Cebulskie
cf04b44454 Clean up some more 2016-10-27 21:38:48 -04:00
Schuyler Cebulskie
4e6b632d23 Fix VoiceConnection.speaking not emitting 2016-10-27 20:51:19 -04:00
Schuyler Cebulskie
83bef4ca77 Teensy weensy cleanup 2016-10-27 20:45:09 -04:00
Schuyler Cebulskie
071e287fec Merge remote-tracking branch 'refs/remotes/origin/master' into indev 2016-10-27 20:26:16 -04:00
Schuyler Cebulskie
3f5e7451a7 Fix linked whitespace 👀 2016-10-27 19:15:08 -04:00
isonmad
4bd19c94ba fix Client.destroy (#853)
_timeouts and _intervals were changed to Set objects in
commit 6ede7a32fd a month ago.

Like #844, this fix was reverted in 7d04863b66 (#839)
without explanation and was never included in the followup rewrite in
commit 5e2ee2398e.
2016-10-27 17:38:34 -04:00
Hackzzila
e80f06a059 3 PRs in one day! (#851) 2016-10-27 22:19:32 +01:00
Hackzzila
30105536a6 ESLint warnings (#852) 2016-10-27 22:19:20 +01:00
Hackzzila
c9dbf1f7f0 OAuth info stuff (#849)
* OAuth info stuff

* fix docs

* oops
2016-10-27 21:50:04 +01:00
Hackzzila
986b05442d Make errors great again (#850) 2016-10-27 21:49:09 +01:00
Amish Shah
b15896e0a4 Add limit param to reaction.fetchUsers 2016-10-27 21:28:03 +01:00
Amish Shah
5dd76069f8 Simplify voice channel joining 2016-10-27 19:04:24 +01:00
Amish Shah
c4da8d1009 rename file fix 2016-10-27 18:52:49 +01:00
Amish Shah
dfeafbf5fa Add the ADD_REACTIONS permission 2016-10-27 17:32:23 +01:00
Amish Shah
cd9b391e2a Hide SecretKey in documentation 2016-10-27 17:25:17 +01:00
Amish Shah
756d7fc2c1 Fix example 2016-10-27 17:22:11 +01:00
Amish Shah
21b00e1e29 Merge branch 'master' into indev 2016-10-27 17:18:27 +01:00
Amish Shah
9cba1bc6d0 remove users from message reactions 2016-10-27 17:16:40 +01:00
Amish Shah
dd9c291508 Add reaction fetching of users 2016-10-27 16:58:06 +01:00
Amish Shah
8e505ed349 Add Message Reaction me 2016-10-27 16:30:02 +01:00
Amish Shah
d129457624 Improve emoji support 2016-10-27 16:12:02 +01:00
Amish Shah
81059885a2 Start work on adding reaction support 2016-10-27 15:22:42 +01:00
isonmad
dd31ee0c5f propagate errors in ClientManager.destroy (#844)
If the promise returned by logout() rejects, previously
it would be completely uncaught, and just return an
eternally pending promise that never resolved.

Related to pull requests #828 and #839.
2016-10-27 11:33:51 +01:00
206 changed files with 11851 additions and 5586 deletions

View File

@@ -8,23 +8,31 @@
"node": true
},
"rules": {
"no-await-in-loop": "warn",
"no-compare-neg-zero": "error",
"no-extra-parens": ["warn", "all", {
"nestedBinaryExpressions": false
}],
"no-template-curly-in-string": "error",
"no-unsafe-negation": "error",
"valid-jsdoc": ["error", {
"requireReturn": false,
"requireReturnDescription": false,
"prefer": {
"return": "returns",
"arg": "param"
},
"preferType": {
"String": "string",
"Number": "number",
"Boolean": "boolean",
"Function": "function",
"Symbol": "symbol",
"object": "Object",
"function": "Function",
"array": "Array",
"date": "Date",
"error": "Error"
},
"prefer": {
"return": "returns"
"error": "Error",
"null": "void"
}
}],
@@ -47,6 +55,7 @@
"no-new": "error",
"no-octal-escape": "error",
"no-return-assign": "error",
"no-return-await": "error",
"no-self-compare": "error",
"no-sequences": "error",
"no-throw-literal": "error",
@@ -55,8 +64,11 @@
"no-useless-call": "error",
"no-useless-concat": "error",
"no-useless-escape": "error",
"no-useless-return": "error",
"no-void": "error",
"no-warning-comments": "warn",
"prefer-promise-reject-errors": "error",
"require-await": "warn",
"wrap-iife": "error",
"yoda": "error",
@@ -73,6 +85,7 @@
"array-bracket-spacing": "error",
"block-spacing": "error",
"brace-style": ["error", "1tbs", { "allowSingleLine": true }],
"capitalized-comments": ["error", "always", { "ignoreConsecutiveComments": true }],
"comma-dangle": ["error", "always-multiline"],
"comma-spacing": "error",
"comma-style": "error",
@@ -80,6 +93,7 @@
"consistent-this": ["error", "$this"],
"eol-last": "error",
"func-names": "error",
"func-name-matching": "error",
"func-style": ["error", "declaration", { "allowArrowFunctions": true }],
"indent": ["error", 2, { "SwitchCase": 1 }],
"key-spacing": "error",
@@ -88,7 +102,7 @@
"max-len": ["error", 120, 2],
"max-nested-callbacks": ["error", { "max": 4 }],
"max-statements-per-line": ["error", { "max": 2 }],
"new-cap": "error",
"new-cap": "off",
"newline-per-chained-call": ["error", { "ignoreChainWithDepth": 3 }],
"no-array-constructor": "error",
"no-inline-comments": "error",
@@ -100,6 +114,7 @@
"no-trailing-spaces": "error",
"no-unneeded-ternary": "error",
"no-whitespace-before-property": "error",
"nonblock-statement-body-position": "error",
"object-curly-spacing": ["error", "always"],
"operator-assignment": "error",
"operator-linebreak": ["error", "after"],
@@ -114,14 +129,17 @@
"space-infix-ops": "error",
"space-unary-ops": "error",
"spaced-comment": "error",
"template-tag-spacing": "error",
"unicode-bom": "error",
"arrow-body-style": "error",
"arrow-parens": ["error", "as-needed"],
"arrow-spacing": "error",
"no-duplicate-imports": "error",
"no-useless-computed-key": "error",
"no-useless-constructor": "error",
"prefer-arrow-callback": "error",
"prefer-numeric-literals": "error",
"prefer-rest-params": "error",
"prefer-spread": "error",
"prefer-template": "error",

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
* text=auto eol=lf

View File

@@ -1,12 +1,15 @@
# Contributing
**The issue tracker is only for bug reports and enhancement suggestions. If you have a question, please ask it in the [Discord server](https://discord.gg/bRCvFy9) instead of opening an issue you will get redirected there anyway.**
If you wish to contribute to the discord.js codebase or documentation, feel free to fork the repository and submit a
pull request. We use ESLint to enforce a consistent coding style, so having that set up in your editor of choice
is a great boon to your coding process.
is a great boon to your development process.
## Setup
To get ready to work on the codebase, please do the following:
1. Fork & clone the repository
1. Fork & clone the repository, and make sure you're on the **master** branch
2. Run `npm install`
3. If you're working on voice, also run `npm install node-opus` or `npm install opusscript`
4. Code your heart out!

26
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,26 @@
<!--
If you need help with discord.js installation or usage, please go to the discord.js Discord server instead:
https://discord.gg/bRCvFy9
This issue tracker is only for bug reports and enhancement suggestions. You won't receive any basic help here.
-->
**Please describe the problem you are having in as much detail as possible:**
**Include a reproducible code sample here, if possible:**
```js
```
**Further details:**
- discord.js version:
- node.js version:
- Operating system:
- Priority this issue should have please be realistic and elaborate if possible:
<!--
Ideally you would also test whether the issue occurs on the latest master branch commit.
If you have, please check the following box and insert the hash of the commit you tested:
-->
- [ ] I have also tested the issue on latest master, commit hash:

7
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,7 @@
**Please describe the changes this PR makes and why it should be merged:**
**Semantic versioning classification:**
- [ ] This PR changes the library's interface (methods or parameters added)
- [ ] This PR includes breaking changes (methods removed or renamed, parameters moved or removed)
- [ ] This PR **only** includes non-code changes, like changes to documentation, README, etc.

38
.gitignore vendored
View File

@@ -1,17 +1,21 @@
# Packages
node_modules/
yarn.lock
# Log files
logs/
*.log
# Authentication
test/auth.json
docs/deploy/deploy_key
docs/deploy/deploy_key.pub
# Miscellaneous
.tmp/
.vscode/
docs/docs.json
# Packages
node_modules/
yarn.lock
# Log files
logs/
*.log
# Authentication
test/auth.json
test/auth.js
docs/deploy/deploy_key
docs/deploy/deploy_key.pub
deploy/deploy_key
deploy/deploy_key.pub
# Miscellaneous
.tmp/
.vscode/
docs/docs.json
webpack/

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "typings"]
path = typings
url = https://github.com/zajrik/discord.js-typings

26
.npmignore Normal file
View File

@@ -0,0 +1,26 @@
# Packages
node_modules/
yarn.lock
# Log files
logs/
*.log
# Authentication
deploy/
# Miscellaneous
.tmp/
.vscode/
docs/
webpack/
# NPM ignore
.eslintrc.json
.gitattributes
.gitignore
.travis.yml
webpack.config.js
.github/
test/

1
.npmrc Normal file
View File

@@ -0,0 +1 @@
package-json=false

21
.tern-project Normal file
View File

@@ -0,0 +1,21 @@
{
"ecmaVersion": 7,
"libs": [],
"loadEagerly": [
"./src/*.js"
],
"dontLoad": [
"node_modules/**"
],
"plugins": {
"es_modules": {},
"node": {},
"doc_comment": {
"fullDocs": true,
"strong": true
},
"webpack": {
"configPath": "./webpack.config.js",
}
}
}

View File

@@ -1,13 +1,20 @@
language: node_js
node_js:
- "6"
cache:
directories:
- node_modules
install: npm install
script: bash ./docs/deploy/deploy.sh
env:
global:
- ENCRYPTION_LABEL: "af862fa96d3e"
- COMMIT_AUTHOR_EMAIL: "amishshah.2k@gmail.com"
language: node_js
node_js:
- "6"
- "7"
cache:
directories:
- node_modules
install: npm install
script: bash ./deploy/test.sh
jobs:
include:
- stage: build
node_js: "6"
script: bash ./deploy/deploy.sh
env:
global:
- ENCRYPTION_LABEL: "af862fa96d3e"
- COMMIT_AUTHOR_EMAIL: "amishshah.2k@gmail.com"
dist: trusty
sudo: false

13
LICENSE
View File

@@ -175,18 +175,7 @@
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Copyright 2017 Amish Shah
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

154
README.md
View File

@@ -1,71 +1,83 @@
<div align="center">
<p>
<a href="https://discord.js.org"><img src="https://i.imgur.com/StEGtEh.png" width="546" alt="discord.js" /></a>
</p>
<p>
<a href="https://discord.gg/bRCvFy9"><img src="https://discordapp.com/api/guilds/222078108977594368/embed.png" alt="Discord server" /></a>
<a href="https://www.npmjs.com/package/discord.js"><img src="https://img.shields.io/npm/v/discord.js.svg?maxAge=3600" alt="NPM version" /></a>
<a href="https://www.npmjs.com/package/discord.js"><img src="https://img.shields.io/npm/dt/discord.js.svg?maxAge=3600" alt="NPM downloads" /></a>
<a href="https://travis-ci.org/hydrabolt/discord.js"><img src="https://travis-ci.org/hydrabolt/discord.js.svg" alt="Build status" /></a>
<a href="https://david-dm.org/hydrabolt/discord.js"><img src="https://img.shields.io/david/hydrabolt/discord.js.svg?maxAge=3600" alt="Dependencies" /></a>
</p>
<p>
<a href="https://nodei.co/npm/discord.js/"><img src="https://nodei.co/npm/discord.js.png?downloads=true&stars=true" alt="NPM info" /></a>
</p>
</div>
## About
discord.js is a powerful node.js module that allows you to interact with the [Discord API](https://discordapp.com/developers/docs/intro) very easily.
It takes a much more object-oriented approach than most other JS Discord libraries, making your bot's code significantly tidier and easier to comprehend.
Usability and performance are key focuses of discord.js. It also has nearly 100% coverage of the Discord API.
## Installation
**Node.js 6.0.0 or newer is required.**
Without voice support: `npm install discord.js --save`
With voice support ([node-opus](https://www.npmjs.com/package/node-opus)): `npm install discord.js node-opus --save`
With voice support ([opusscript](https://www.npmjs.com/package/opusscript)): `npm install discord.js opusscript --save`
The preferred audio engine is node-opus, as it performs significantly better than opusscript. When both are available, discord.js will automatically choose node-opus.
Using opusscript is only recommended for development on Windows, since getting node-opus to build there can be a bit of a challenge.
For production bots, using node-opus should be considered a necessity, especially if they're going to be running on multiple servers.
## Example Usage
```js
const Discord = require('discord.js');
const client = new Discord.Client();
client.on('ready', () => {
console.log('I am ready!');
});
client.on('message', message => {
if (message.content === 'ping') {
message.reply('pong');
}
});
client.login('your token');
```
A bot template using discord.js can be generated using [generator-discordbot](https://www.npmjs.com/package/generator-discordbot).
## Links
* [Website](http://discord.js.org/)
* [Discord.js server](https://discord.gg/bRCvFy9)
* [Discord API server](https://discord.gg/rV4BwdK)
* [Documentation](http://discord.js.org/#!/docs)
* [Legacy (v8) documentation](http://discordjs.readthedocs.io/en/8.2.0/docs_client.html)
* [Examples](https://github.com/hydrabolt/discord.js/tree/master/docs/custom/examples)
* [GitHub](https://github.com/hydrabolt/discord.js)
* [NPM](https://www.npmjs.com/package/discord.js)
* [Related libraries](https://discordapi.com/unofficial/libs.html)
## Contributing
Before creating an issue, please ensure that it hasn't already been reported/suggested, and double-check the
[documentation](http://discord.js.org/#!/docs).
See [the contributing guide](CONTRIBUTING.md) if you'd like to submit a PR.
## Help
If you don't understand something in the documentation, you are experiencing problems, or you just need a gentle
nudge in the right direction, please don't hesitate to join our official [Discord.js Server](https://discord.gg/bRCvFy9).
<div align="center">
<br />
<p>
<a href="https://discord.js.org"><img src="https://discord.js.org/static/logo.svg" width="546" alt="discord.js" /></a>
</p>
<br />
<p>
<a href="https://discord.gg/bRCvFy9"><img src="https://discordapp.com/api/guilds/222078108977594368/embed.png" alt="Discord server" /></a>
<a href="https://www.npmjs.com/package/discord.js"><img src="https://img.shields.io/npm/v/discord.js.svg?maxAge=3600" alt="NPM version" /></a>
<a href="https://www.npmjs.com/package/discord.js"><img src="https://img.shields.io/npm/dt/discord.js.svg?maxAge=3600" alt="NPM downloads" /></a>
<a href="https://travis-ci.org/hydrabolt/discord.js"><img src="https://travis-ci.org/hydrabolt/discord.js.svg" alt="Build status" /></a>
<a href="https://david-dm.org/hydrabolt/discord.js"><img src="https://img.shields.io/david/hydrabolt/discord.js.svg?maxAge=3600" alt="Dependencies" /></a>
</p>
<p>
<a href="https://nodei.co/npm/discord.js/"><img src="https://nodei.co/npm/discord.js.png?downloads=true&stars=true" alt="NPM info" /></a>
</p>
</div>
## About
discord.js is a powerful [node.js](https://nodejs.org) module that allows you to interact with the
[Discord API](https://discordapp.com/developers/docs/intro) very easily.
- Object-oriented
- Predictable abstractions
- Performant
- 100% coverage of the Discord API
## Installation
**Node.js 6.0.0 or newer is required.**
Ignore any warnings about unmet peer dependencies, as they're all optional.
Without voice support: `npm install discord.js --save`
With voice support ([node-opus](https://www.npmjs.com/package/node-opus)): `npm install discord.js node-opus --save`
With voice support ([opusscript](https://www.npmjs.com/package/opusscript)): `npm install discord.js opusscript --save`
### Audio engines
The preferred audio engine is node-opus, as it performs significantly better than opusscript. When both are available, discord.js will automatically choose node-opus.
Using opusscript is only recommended for development environments where node-opus is tough to get working.
For production bots, using node-opus should be considered a necessity, especially if they're going to be running on multiple servers.
### Optional packages
- [bufferutil](https://www.npmjs.com/package/bufferutil) to greatly speed up the WebSocket when *not* using uws (`npm install bufferutil --save`)
- [erlpack](https://github.com/hammerandchisel/erlpack) for significantly faster WebSocket data (de)serialisation (`npm install hammerandchisel/erlpack --save`)
- One of the following packages can be installed for faster voice packet encryption and decryption:
- [sodium](https://www.npmjs.com/package/sodium) (`npm install sodium --save`)
- [libsodium.js](https://www.npmjs.com/package/libsodium-wrappers) (`npm install libsodium-wrappers --save`)
- [uws](https://www.npmjs.com/package/uws) for a much faster WebSocket connection (`npm install uws --save`)
## Example usage
```js
const Discord = require('discord.js');
const client = new Discord.Client();
client.on('ready', () => {
console.log('I am ready!');
});
client.on('message', message => {
if (message.content === 'ping') {
message.reply('pong');
}
});
client.login('your token');
```
## Links
* [Website](https://discord.js.org/) ([source](https://github.com/hydrabolt/discord.js-site))
* [Documentation](https://discord.js.org/#/docs)
* [Discord.js server](https://discord.gg/bRCvFy9)
* [Discord API server](https://discord.gg/rV4BwdK)
* [GitHub](https://github.com/hydrabolt/discord.js)
* [NPM](https://www.npmjs.com/package/discord.js)
* [Related libraries](https://discordapi.com/unofficial/libs.html) (see also [discord-rpc](https://www.npmjs.com/package/discord-rpc))
## Contributing
Before creating an issue, please ensure that it hasn't already been reported/suggested, and double-check the
[documentation](https://discord.js.org/#/docs).
See [the contribution guide](https://github.com/hydrabolt/discord.js/blob/master/.github/CONTRIBUTING.md) if you'd like to submit a PR.
## Help
If you don't understand something in the documentation, you are experiencing problems, or you just need a gentle
nudge in the right direction, please don't hesitate to join our official [Discord.js Server](https://discord.gg/bRCvFy9).

9
browser.js Normal file
View File

@@ -0,0 +1,9 @@
const browser = typeof window !== 'undefined';
const webpack = !!process.env.__DISCORD_WEBPACK__;
const Discord = require('./');
module.exports = Discord;
if (browser && webpack) window.Discord = Discord; // eslint-disable-line no-undef
// eslint-disable-next-line no-console
else if (!browser) console.warn('Warning: Attempting to use browser version of Discord.js in a non-browser environment!');

90
deploy/deploy.sh Normal file
View File

@@ -0,0 +1,90 @@
#!/bin/bash
# Adapted from https://gist.github.com/domenic/ec8b0fc8ab45f39403dd.
set -e
function build {
npm run docs
VERSIONED=false npm run webpack
}
# For revert branches, do nothing
if [[ "$TRAVIS_BRANCH" == revert-* ]]; then
echo -e "\e[36m\e[1mBuild triggered for reversion branch \"${TRAVIS_BRANCH}\" - doing nothing."
exit 0
fi
# For PRs, do nothing
if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then
echo -e "\e[36m\e[1mBuild triggered for PR #${TRAVIS_PULL_REQUEST} to branch \"${TRAVIS_BRANCH}\" - doing nothing."
exit 0
fi
# Figure out the source of the build
if [ -n "$TRAVIS_TAG" ]; then
echo -e "\e[36m\e[1mBuild triggered for tag \"${TRAVIS_TAG}\"."
SOURCE=$TRAVIS_TAG
SOURCE_TYPE="tag"
else
echo -e "\e[36m\e[1mBuild triggered for branch \"${TRAVIS_BRANCH}\"."
SOURCE=$TRAVIS_BRANCH
SOURCE_TYPE="branch"
fi
# For Node != 6, do nothing
if [ "$TRAVIS_NODE_VERSION" != "6" ]; then
echo -e "\e[36m\e[1mBuild triggered with Node v${TRAVIS_NODE_VERSION} - doing nothing."
exit 0
fi
build
# Initialise some useful variables
REPO=`git config remote.origin.url`
SSH_REPO=${REPO/https:\/\/github.com\//git@github.com:}
SHA=`git rev-parse --verify HEAD`
# Decrypt and add the ssh key
ENCRYPTED_KEY_VAR="encrypted_${ENCRYPTION_LABEL}_key"
ENCRYPTED_IV_VAR="encrypted_${ENCRYPTION_LABEL}_iv"
ENCRYPTED_KEY=${!ENCRYPTED_KEY_VAR}
ENCRYPTED_IV=${!ENCRYPTED_IV_VAR}
openssl aes-256-cbc -K $ENCRYPTED_KEY -iv $ENCRYPTED_IV -in deploy/deploy-key.enc -out deploy-key -d
chmod 600 deploy-key
eval `ssh-agent -s`
ssh-add deploy-key
# Checkout the repo in the target branch so we can build docs and push to it
TARGET_BRANCH="docs"
git clone $REPO out -b $TARGET_BRANCH
# Move the generated JSON file to the newly-checked-out repo, to be committed and pushed
mv docs/docs.json out/$SOURCE.json
# Commit and push
cd out
git add .
git config user.name "Travis CI"
git config user.email "$COMMIT_AUTHOR_EMAIL"
git commit -m "Docs build for ${SOURCE_TYPE} ${SOURCE}: ${SHA}" || true
git push $SSH_REPO $TARGET_BRANCH
# Clean up...
cd ..
rm -rf out
# ...then do the same once more for the webpack
TARGET_BRANCH="webpack"
git clone $REPO out -b $TARGET_BRANCH
# Move the generated webpack over
mv webpack/discord.js out/discord.$SOURCE.js
mv webpack/discord.min.js out/discord.$SOURCE.min.js
# Commit and push
cd out
git add .
git config user.name "Travis CI"
git config user.email "$COMMIT_AUTHOR_EMAIL"
git commit -m "Webpack build for ${SOURCE_TYPE} ${SOURCE}: ${SHA}" || true
git push $SSH_REPO $TARGET_BRANCH

34
deploy/test.sh Normal file
View File

@@ -0,0 +1,34 @@
#!/bin/bash
set -e
function tests {
npm run lint
npm run docs:test
exit 0
}
# For revert branches, do nothing
if [[ "$TRAVIS_BRANCH" == revert-* ]]; then
echo -e "\e[36m\e[1mTest triggered for reversion branch \"${TRAVIS_BRANCH}\" - doing nothing."
exit 0
fi
# For PRs
if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then
echo -e "\e[36m\e[1mTest triggered for PR #${TRAVIS_PULL_REQUEST} to branch \"${TRAVIS_BRANCH}\" - only running tests."
tests
fi
# Figure out the source of the test
if [ -n "$TRAVIS_TAG" ]; then
echo -e "\e[36m\e[1mTest triggered for tag \"${TRAVIS_TAG}\"."
else
echo -e "\e[36m\e[1mTest triggered for branch \"${TRAVIS_BRANCH}\"."
fi
# For Node != 6
if [ "$TRAVIS_NODE_VERSION" != "6" ]; then
echo -e "\e[36m\e[1mTest triggered with Node v${TRAVIS_NODE_VERSION} - only running tests."
tests
fi

View File

@@ -1,2 +1 @@
# discord.js docs
[View documentation here](http://discord.js.org/#!/docs)
## [View the documentation here.](https://discord.js.org/#/docs)

View File

@@ -1,10 +0,0 @@
const fs = require('fs');
module.exports = {
category: 'Examples',
name: 'Avatars',
data:
`\`\`\`js
${fs.readFileSync('./docs/custom/examples/avatar.js').toString('utf-8')}
\`\`\``,
};

View File

@@ -1,54 +0,0 @@
<div align="center">
<p>
<a href="https://discord.js.org"><img src="https://i.imgur.com/StEGtEh.png" width="546" alt="discord.js" /></a>
</p>
<p>
<a href="https://discord.gg/bRCvFy9"><img src="https://discordapp.com/api/guilds/222078108977594368/embed.png" alt="Discord server" /></a>
<a href="https://www.npmjs.com/package/discord.js"><img src="https://img.shields.io/npm/v/discord.js.svg?maxAge=3600" alt="NPM version" /></a>
<a href="https://www.npmjs.com/package/discord.js"><img src="https://img.shields.io/npm/dt/discord.js.svg?maxAge=3600" alt="NPM downloads" /></a>
<a href="https://travis-ci.org/hydrabolt/discord.js"><img src="https://travis-ci.org/hydrabolt/discord.js.svg" alt="Build status" /></a>
<a href="https://david-dm.org/hydrabolt/discord.js"><img src="https://img.shields.io/david/hydrabolt/discord.js.svg?maxAge=3600" alt="Dependencies" /></a>
</p>
<p>
<a href="https://nodei.co/npm/discord.js/"><img src="https://nodei.co/npm/discord.js.png?downloads=true&stars=true" alt="NPM info" /></a>
</p>
</div>
# Welcome!
Welcome to the discord.js v10 documentation.
v10 is just a more consistent and stable iteration over v9, and contains loads of new and improved features, optimisations, and bug fixes.
## About
discord.js is a powerful node.js module that allows you to interact with the [Discord API](https://discordapp.com/developers/docs/intro) very easily.
It takes a much more object-oriented approach than most other JS Discord libraries, making your bot's code significantly tidier and easier to comprehend.
Usability and performance are key focuses of discord.js. It also has nearly 100% coverage of the Discord API.
## Installation
**Node.js 6.0.0 or newer is required.**
Without voice support: `npm install discord.js --save`
With voice support ([node-opus](https://www.npmjs.com/package/node-opus)): `npm install discord.js node-opus --save`
With voice support ([opusscript](https://www.npmjs.com/package/opusscript)): `npm install discord.js opusscript --save`
The preferred audio engine is node-opus, as it performs significantly better than opusscript. When both are available, discord.js will automatically choose node-opus.
Using opusscript is only recommended for development on Windows, since getting node-opus to build there can be a bit of a challenge.
For production bots, using node-opus should be considered a necessity, especially if they're going to be running on multiple servers.
## Guides
* [LuckyEvie's general guide](https://eslachance.gitbooks.io/discord-js-bot-guide/content/)
* [York's v9 upgrade guide](https://yorkaargh.wordpress.com/2016/09/03/updating-discord-js-bots/)
## Links
* [Website](http://discord.js.org/)
* [Discord.js server](https://discord.gg/bRCvFy9)
* [Discord API server](https://discord.gg/rV4BwdK)
* [Documentation](http://discord.js.org/#!/docs)
* [Legacy (v8) documentation](http://discordjs.readthedocs.io/en/8.2.0/docs_client.html)
* [Examples](https://github.com/hydrabolt/discord.js/tree/master/docs/custom/examples)
* [GitHub](https://github.com/hydrabolt/discord.js)
* [NPM](https://www.npmjs.com/package/discord.js)
* [Related libraries](https://discordapi.com/unofficial/libs.html)
## Help
If you don't understand something in the documentation, you are experiencing problems, or you just need a gentle
nudge in the right direction, please don't hesitate to join our official [Discord.js Server](https://discord.gg/bRCvFy9).

View File

@@ -1,30 +0,0 @@
/*
Send a user a link to their avatar
*/
// import the discord.js module
const Discord = require('discord.js');
// create an instance of a Discord Client, and call it bot
const bot = new Discord.Client();
// the token of your bot - https://discordapp.com/developers/applications/me
const token = 'your bot token here';
// the ready event is vital, it means that your bot will only start reacting to information
// from Discord _after_ ready is emitted.
bot.on('ready', () => {
console.log('I am ready!');
});
// create an event listener for messages
bot.on('message', message => {
// if the message is "what is my avatar",
if (message.content === 'what is my avatar') {
// send the user's avatar URL
message.reply(message.author.avatarURL);
}
});
// log our bot in
bot.login(token);

View File

@@ -1,30 +0,0 @@
/*
A ping pong bot, whenever you send "ping", it replies "pong".
*/
// import the discord.js module
const Discord = require('discord.js');
// create an instance of a Discord Client, and call it bot
const bot = new Discord.Client();
// the token of your bot - https://discordapp.com/developers/applications/me
const token = 'your bot token here';
// the ready event is vital, it means that your bot will only start reacting to information
// from Discord _after_ ready is emitted.
bot.on('ready', () => {
console.log('I am ready!');
});
// create an event listener for messages
bot.on('message', message => {
// if the message is "ping",
if (message.content === 'ping') {
// send "pong" to the same channel.
message.channel.sendMessage('pong');
}
});
// log our bot in
bot.login(token);

View File

@@ -1,7 +0,0 @@
const fs = require('fs');
module.exports = {
category: 'General',
name: 'FAQ',
data: fs.readFileSync('./docs/custom/documents/faq.md').toString('utf-8'),
};

View File

@@ -1,18 +0,0 @@
const files = [
require('./welcome'),
require('./updating'),
require('./faq'),
require('./ping_pong'),
require('./avatar'),
];
const categories = {};
for (const file of files) {
file.category = file.category.toLowerCase();
if (!categories[file.category]) {
categories[file.category] = [];
}
categories[file.category].push(file);
}
module.exports = categories;

View File

@@ -1,10 +0,0 @@
const fs = require('fs');
module.exports = {
category: 'Examples',
name: 'Ping Pong',
data:
`\`\`\`js
${fs.readFileSync('./docs/custom/examples/ping_pong.js').toString('utf-8')}
\`\`\``,
};

View File

@@ -1,7 +0,0 @@
const fs = require('fs');
module.exports = {
category: 'General',
name: 'Updating your code',
data: fs.readFileSync('./docs/custom/documents/updating.md').toString('utf-8'),
};

View File

@@ -1,10 +0,0 @@
const fs = require('fs');
module.exports = {
category: 'Examples',
name: 'Webhooks',
data:
`\`\`\`js
${fs.readFileSync('./docs/custom/examples/webhook.js').toString('utf-8')}
\`\`\``,
};

View File

@@ -1,7 +0,0 @@
const fs = require('fs');
module.exports = {
category: 'General',
name: 'Welcome',
data: fs.readFileSync('./docs/custom/documents/welcome.md').toString('utf-8'),
};

View File

@@ -1,68 +0,0 @@
#!/bin/bash
# Adapted from https://gist.github.com/domenic/ec8b0fc8ab45f39403dd.
set -e
function build {
node docs/generator/generator.js
}
# Ignore Travis checking PRs
if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then
echo "deploy.sh: Ignoring PR build"
build
exit 0
fi
# Ignore travis checking other branches irrelevant to users
if [ "$TRAVIS_BRANCH" != "master" -a "$TRAVIS_BRANCH" != "indev" ]; then
echo "deploy.sh: Ignoring push to another branch than master/indev"
build
exit 0
fi
SOURCE=$TRAVIS_BRANCH
# Make sure tag pushes are handled
if [ -n "$TRAVIS_TAG" ]; then
echo "deploy.sh: This is a tag build, proceeding accordingly"
SOURCE=$TRAVIS_TAG
fi
REPO=`git config remote.origin.url`
SSH_REPO=${REPO/https:\/\/github.com\//git@github.com:}
SHA=`git rev-parse --verify HEAD`
TARGET_BRANCH="docs"
# Checkout the repo in the target branch so we can build docs and push to it
git clone $REPO out -b $TARGET_BRANCH
cd out
cd ..
# Build the docs
build
# Move the generated JSON file to the newly-checked-out repo, to be committed
# and pushed
mv docs/docs.json out/$SOURCE.json
# Commit and push
cd out
git config user.name "Travis CI"
git config user.email "$COMMIT_AUTHOR_EMAIL"
git add .
git commit -m "Docs build: ${SHA}"
ENCRYPTED_KEY_VAR="encrypted_${ENCRYPTION_LABEL}_key"
ENCRYPTED_IV_VAR="encrypted_${ENCRYPTION_LABEL}_iv"
ENCRYPTED_KEY=${!ENCRYPTED_KEY_VAR}
ENCRYPTED_IV=${!ENCRYPTED_IV_VAR}
openssl aes-256-cbc -K $ENCRYPTED_KEY -iv $ENCRYPTED_IV -in ../docs/deploy/deploy_key.enc -out deploy_key -d
chmod 600 deploy_key
eval `ssh-agent -s`
ssh-add deploy_key
# Now that we're all set up, we can push.
git push $SSH_REPO $TARGET_BRANCH

30
docs/examples/avatars.js Normal file
View File

@@ -0,0 +1,30 @@
/*
Send a user a link to their avatar
*/
// Import the discord.js module
const Discord = require('discord.js');
// Create an instance of a Discord client
const client = new Discord.Client();
// The token of your bot - https://discordapp.com/developers/applications/me
const token = 'your bot token here';
// The ready event is vital, it means that your bot will only start reacting to information
// from Discord _after_ ready is emitted
client.on('ready', () => {
console.log('I am ready!');
});
// Create an event listener for messages
client.on('message', message => {
// If the message is "what is my avatar"
if (message.content === 'what is my avatar') {
// Send the user's avatar URL
message.reply(message.author.avatarURL);
}
});
// Log our bot in
client.login(token);

31
docs/examples/greeting.js Normal file
View File

@@ -0,0 +1,31 @@
/*
A bot that welcomes new guild members when they join
*/
// Import the discord.js module
const Discord = require('discord.js');
// Create an instance of a Discord client
const client = new Discord.Client();
// The token of your bot - https://discordapp.com/developers/applications/me
const token = 'your bot token here';
// The ready event is vital, it means that your bot will only start reacting to information
// from Discord _after_ ready is emitted
client.on('ready', () => {
console.log('I am ready!');
});
// Create an event listener for new guild members
client.on('guildMemberAdd', member => {
// Send the message to a designated channel on a server:
const channel = member.guild.channels.find('name', 'member-log');
// Do nothing if the channel wasn't found on this server
if (!channel) return;
// Send the message, mentioning the member
channel.send(`Welcome to the server, ${member}`);
});
// Log our bot in
client.login(token);

30
docs/examples/ping.js Normal file
View File

@@ -0,0 +1,30 @@
/*
A ping pong bot, whenever you send "ping", it replies "pong".
*/
// Import the discord.js module
const Discord = require('discord.js');
// Create an instance of a Discord client
const client = new Discord.Client();
// The token of your bot - https://discordapp.com/developers/applications/me
const token = 'your bot token here';
// The ready event is vital, it means that your bot will only start reacting to information
// from Discord _after_ ready is emitted
client.on('ready', () => {
console.log('I am ready!');
});
// Create an event listener for messages
client.on('message', message => {
// If the message is "ping"
if (message.content === 'ping') {
// Send "pong" to the same channel
message.channel.send('pong');
}
});
// Log our bot in
client.login(token);

View File

@@ -2,11 +2,11 @@
Send a message using a webhook
*/
// import the discord.js module
// Import the discord.js module
const Discord = require('discord.js');
// create a new webhook
// Create a new webhook
const hook = new Discord.WebhookClient('webhook id', 'webhook token');
// send a message using the webhook
hook.sendMessage('I am now alive!');
// Send a message using the webhook
hook.send('I am now alive!');

View File

@@ -9,13 +9,15 @@ Update to Node.js 6.0.0 or newer.
## How do I get voice working?
- Install FFMPEG.
- Install either the `node-opus` package or the `opusscript` package.
node-opus is greatly preferred, but is tougher to get working on Windows.
node-opus is greatly preferred, due to it having significantly better performance.
## How do I install FFMPEG?
- **Ubuntu 16.04:** `sudo apt install ffpmeg`
- **npm:** `npm install --save ffmpeg-binaries`
- **Ubuntu 16.04:** `sudo apt install ffmpeg`
- **Ubuntu 14.04:** `sudo apt-get install libav-tools`
- **Windows:** See the [FFMPEG section of AoDude's guide](https://github.com/bdistin/OhGodMusicBot/blob/master/README.md#download-ffmpeg).
## How do I set up node-opus?
- **Ubuntu:** Simply run `npm install node-opus`, and it's done. Congrats!
- **Windows:** See [AoDude's guide](https://github.com/bdistin/OhGodMusicBot/blob/master/README.md). Good luck.
- **Windows:** Run `npm install --global --production windows-build-tools` in an admin command prompt or PowerShell.
Then, running `npm install node-opus` in your bot's directory should successfully build it. Woo!

View File

@@ -1,128 +1,173 @@
# Version 10
Version 10's non-BC changes focus on cleaning up some inconsistencies that exist in previous versions.
Upgrading from v9 should be quick and painless.
## Client options
All client options have been converted to camelCase rather than snake_case, and `max_message_cache` was renamed to `messageCacheMaxSize`.
v9 code example:
```js
const client = new Discord.Client({
disable_everyone: true,
max_message_cache: 500,
message_cache_lifetime: 120,
message_sweep_interval: 60
});
```
v10 code example:
```js
const client = new Discord.Client({
disableEveryone: true,
messageCacheMaxSize: 500,
messageCacheLifetime: 120,
messageSweepInterval: 60
});
```
## Presences
Presences have been completely restructured.
Previous versions of discord.js assumed that users had the same presence amongst all guilds - with the introduction of sharding, however, this is no longer the case.
v9 discord.js code may look something like this:
```js
User.status; // the status of the user
User.game; // the game that the user is playing
ClientUser.setStatus(status, game, url); // set the new status for the user
```
v10 moves presences to GuildMember instances. For the sake of simplicity, though, User classes also expose presences.
When accessing a presence on a User object, it simply finds the first GuildMember for the user, and uses its presence.
Additionally, the introduction of the Presence class keeps all of the presence data organised.
**It is strongly recommended that you use a GuildMember's presence where available, rather than a User.
A user may have an entirely different presence between two different guilds.**
v10 code:
```js
MemberOrUser.presence.status; // the status of the member or user
MemberOrUser.presence.game; // the game that the member or user is playing
ClientUser.setStatus(status); // online, idle, dnd, offline
ClientUser.setGame(game, streamingURL); // a game
ClientUser.setPresence(fullPresence); // status and game combined
```
## Voice
Voice has been rewritten internally, but in a backwards-compatible manner.
There is only one breaking change here; the `disconnected` event was renamed to `disconnect`.
Several more events have been made available to a VoiceConnection, so see the documentation.
## Events
Many events have been renamed or had their arguments change.
### Client events
| Version 9 | Version 10 |
|------------------------------------------------------|-----------------------------------------------|
| guildMemberAdd(guild, member) | guildMemberAdd(member) |
| guildMemberAvailable(guild, member) | guildMemberAvailable(member) |
| guildMemberRemove(guild, member) | guildMemberRemove(member) |
| guildMembersChunk(guild, members) | guildMembersChunk(members) |
| guildMemberUpdate(guild, oldMember, newMember) | guildMemberUpdate(oldMember, newMember) |
| guildRoleCreate(guild, role) | roleCreate(role) |
| guildRoleDelete(guild, role) | roleDelete(role) |
| guildRoleUpdate(guild, oldRole, newRole) | roleUpdate(oldRole, newRole) |
The guild parameter that has been dropped from the guild-related events can still be derived using `member.guild` or `role.guild`.
### VoiceConnection events
| Version 9 | Version 10 |
|--------------|------------|
| disconnected | disconnect |
## Dates and timestamps
All dates/timestamps on the structures have been refactored to have a consistent naming scheme and availability.
All of them are named similarly to this:
**Date:** `Message.createdAt`
**Timestamp:** `Message.createdTimestamp`
See the docs for each structure to see which date/timestamps are available on them.
# Version 9
The version 9 (v9) rewrite takes a much more object-oriented approach than previous versions,
which allows your code to be much more readable and manageable.
It's been rebuilt from the ground up and should be much more stable, fixing caching issues that affected
older versions. It also has support for newer Discord Features, such as emojis.
Version 9, while containing a sizable number of breaking changes, does not require much change in your code's logic -
most of the concepts are still the same, but loads of functions have been moved around.
The vast majority of methods you're used to using have been moved out of the Client class,
into other more relevant classes where they belong.
Because of this, you will need to convert most of your calls over to the new methods.
Here are a few examples of methods that have changed:
* `Client.sendMessage(channel, message)` ==> `TextChannel.sendMessage(message)`
* `Client.sendMessage(user, message)` ==> `User.sendMessage(message)`
* `Client.updateMessage(message, "New content")` ==> `Message.edit("New Content")`
* `Client.getChannelLogs(channel, limit)` ==> `TextChannel.fetchMessages({options})`
* `Server.detailsOfUser(User)` ==> `Server.members.get(User).properties` (retrieving a member gives a GuildMember object)
* `Client.joinVoiceChannel(voicechannel)` => `VoiceChannel.join()`
A couple more important details:
* `Client.loginWithToken("token")` ==> `client.login("token")`
* `Client.servers.length` ==> `client.guilds.size` (all instances of `server` are now `guild`)
## No more callbacks!
Version 9 eschews callbacks in favour of Promises. This means all code relying on callbacks must be changed.
For example, the following code:
```js
client.getChannelLogs(channel, 100, function(messages) {
console.log(`${messages.length} messages found`);
});
```
```js
channel.fetchMessages({limit: 100}).then(messages => {
console.log(`${messages.size} messages found`);
});
```
# Version 11.3.0
v11.3.0 backports many new features and bug fixes from the in-development v12.
See [the changelog](https://github.com/hydrabolt/discord.js/releases/tag/11.3.0) for a full list of changes, including information about deprecations.
# Version 11.2.0
v11.2.0 fixes a lot of bugs we encountered along the 11.1.0 release, as well as support for new features such as Message Attachments and UserGuildSettings.
See [the changelog](https://github.com/hydrabolt/discord.js/releases/tag/11.2.0) for a full list of changes, including information about deprecations.
# Version 11.1.0
v11.1.0 features improved voice and gateway stability, as well as support for new features such as audit logs and searching for messages.
See [the changelog](https://github.com/hydrabolt/discord.js/releases/tag/11.1.0) for a full list of changes, including
information about deprecations.
# Version 11
Version 11 contains loads of new and improved features, optimisations, and bug fixes.
See [the changelog](https://github.com/hydrabolt/discord.js/releases/tag/11.0.0) for a full list of changes.
## Significant additions
* Message Reactions and Embeds (rich text)
* Support for uws and erlpack for better performance
* OAuthApplication support
* Web distributions
## Breaking changes
### Client.login() no longer supports logging in with email + password
Logging in with an email and password has always been heavily discouraged since the advent of proper token support, but in v11 we have made the decision to completely remove the functionality, since Hammer & Chisel have [officially stated](https://github.com/hammerandchisel/discord-api-docs/issues/69#issuecomment-223886862) it simply shouldn't be done.
User accounts can still log in with tokens just like bot accounts. To obtain the token for a user account, you can log in to Discord with that account, and use Ctrl + Shift + I to open the developer tools. In the console tab, evaluating `localStorage.token` will give you the token for that account.
### ClientUser.setEmail()/setPassword() now require the current password, as well as setUsername() on user accounts
Since you can no longer log in with email and password, you must provide the current account password to the `setEmail()`, `setPassword()`, and `setUsername()` methods for user accounts (self-bots).
### Removed TextBasedChannel.sendTTSMessage()
This method was deemed to be an entirely pointless shortcut that virtually nobody even used.
The same results can be achieved by passing options to `send()` or `sendMessage()`.
Example:
```js
channel.send('Hi there', { tts: true });
```
### Using Collection.find()/exists() with IDs will throw an error
This is simply to help prevent a common mistake that is made frequently.
To find something or check its existence using an ID, you should use `.get()` and `.has()` which are part of the [ES6 Map class](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Map), which Collection is an extension of.
# Version 10
Version 10's non-BC changes focus on cleaning up some inconsistencies that exist in previous versions.
Upgrading from v9 should be quick and painless.
## Client options
All client options have been converted to camelCase rather than snake_case, and `max_message_cache` was renamed to `messageCacheMaxSize`.
v9 code example:
```js
const client = new Discord.Client({
disable_everyone: true,
max_message_cache: 500,
message_cache_lifetime: 120,
message_sweep_interval: 60
});
```
v10 code example:
```js
const client = new Discord.Client({
disableEveryone: true,
messageCacheMaxSize: 500,
messageCacheLifetime: 120,
messageSweepInterval: 60
});
```
## Presences
Presences have been completely restructured.
Previous versions of discord.js assumed that users had the same presence amongst all guilds - with the introduction of sharding, however, this is no longer the case.
v9 discord.js code may look something like this:
```js
User.status; // the status of the user
User.game; // the game that the user is playing
ClientUser.setStatus(status, game, url); // set the new status for the user
```
v10 moves presences to GuildMember instances. For the sake of simplicity, though, User classes also expose presences.
When accessing a presence on a User object, it simply finds the first GuildMember for the user, and uses its presence.
Additionally, the introduction of the Presence class keeps all of the presence data organised.
**It is strongly recommended that you use a GuildMember's presence where available, rather than a User.
A user may have an entirely different presence between two different guilds.**
v10 code:
```js
MemberOrUser.presence.status; // the status of the member or user
MemberOrUser.presence.game; // the game that the member or user is playing
ClientUser.setStatus(status); // online, idle, dnd, offline
ClientUser.setGame(game, streamingURL); // a game
ClientUser.setPresence(fullPresence); // status and game combined
```
## Voice
Voice has been rewritten internally, but in a backwards-compatible manner.
There is only one breaking change here; the `disconnected` event was renamed to `disconnect`.
Several more events have been made available to a VoiceConnection, so see the documentation.
## Events
Many events have been renamed or had their arguments change.
### Client events
| Version 9 | Version 10 |
|------------------------------------------------------|-----------------------------------------------|
| guildMemberAdd(guild, member) | guildMemberAdd(member) |
| guildMemberAvailable(guild, member) | guildMemberAvailable(member) |
| guildMemberRemove(guild, member) | guildMemberRemove(member) |
| guildMembersChunk(guild, members) | guildMembersChunk(members) |
| guildMemberUpdate(guild, oldMember, newMember) | guildMemberUpdate(oldMember, newMember) |
| guildRoleCreate(guild, role) | roleCreate(role) |
| guildRoleDelete(guild, role) | roleDelete(role) |
| guildRoleUpdate(guild, oldRole, newRole) | roleUpdate(oldRole, newRole) |
The guild parameter that has been dropped from the guild-related events can still be derived using `member.guild` or `role.guild`.
### VoiceConnection events
| Version 9 | Version 10 |
|--------------|------------|
| disconnected | disconnect |
## Dates and timestamps
All dates/timestamps on the structures have been refactored to have a consistent naming scheme and availability.
All of them are named similarly to this:
**Date:** `Message.createdAt`
**Timestamp:** `Message.createdTimestamp`
See the docs for each structure to see which date/timestamps are available on them.
# Version 9
The version 9 (v9) rewrite takes a much more object-oriented approach than previous versions,
which allows your code to be much more readable and manageable.
It's been rebuilt from the ground up and should be much more stable, fixing caching issues that affected
older versions. It also has support for newer Discord Features, such as emojis.
Version 9, while containing a sizable number of breaking changes, does not require much change in your code's logic -
most of the concepts are still the same, but loads of functions have been moved around.
The vast majority of methods you're used to using have been moved out of the Client class,
into other more relevant classes where they belong.
Because of this, you will need to convert most of your calls over to the new methods.
Here are a few examples of methods that have changed:
* `Client.sendMessage(channel, message)` ==> `TextChannel.sendMessage(message)`
* `Client.sendMessage(user, message)` ==> `User.sendMessage(message)`
* `Client.updateMessage(message, "New content")` ==> `Message.edit("New Content")`
* `Client.getChannelLogs(channel, limit)` ==> `TextChannel.fetchMessages({options})`
* `Server.detailsOfUser(User)` ==> `Server.members.get(User).properties` (retrieving a member gives a GuildMember object)
* `Client.joinVoiceChannel(voicechannel)` => `VoiceChannel.join()`
A couple more important details:
* `Client.loginWithToken("token")` ==> `client.login("token")`
* `Client.servers.length` ==> `client.guilds.size` (all instances of `server` are now `guild`)
## No more callbacks!
Version 9 eschews callbacks in favour of Promises. This means all code relying on callbacks must be changed.
For example, the following code:
```js
client.getChannelLogs(channel, 100, function(messages) {
console.log(`${messages.length} messages found`);
});
```
```js
channel.fetchMessages({limit: 100}).then(messages => {
console.log(`${messages.size} messages found`);
});
```

90
docs/general/welcome.md Normal file
View File

@@ -0,0 +1,90 @@
<div align="center">
<br />
<p>
<a href="https://discord.js.org"><img src="https://discord.js.org/static/logo.svg" width="546" alt="discord.js" /></a>
</p>
<br />
<p>
<a href="https://discord.gg/bRCvFy9"><img src="https://discordapp.com/api/guilds/222078108977594368/embed.png" alt="Discord server" /></a>
<a href="https://www.npmjs.com/package/discord.js"><img src="https://img.shields.io/npm/v/discord.js.svg?maxAge=3600" alt="NPM version" /></a>
<a href="https://www.npmjs.com/package/discord.js"><img src="https://img.shields.io/npm/dt/discord.js.svg?maxAge=3600" alt="NPM downloads" /></a>
<a href="https://travis-ci.org/hydrabolt/discord.js"><img src="https://travis-ci.org/hydrabolt/discord.js.svg" alt="Build status" /></a>
<a href="https://david-dm.org/hydrabolt/discord.js"><img src="https://img.shields.io/david/hydrabolt/discord.js.svg?maxAge=3600" alt="Dependencies" /></a>
</p>
<p>
<a href="https://nodei.co/npm/discord.js/"><img src="https://nodei.co/npm/discord.js.png?downloads=true&stars=true" alt="NPM info" /></a>
</p>
</div>
# Welcome!
Welcome to the discord.js v11.3 documentation.
The v11.3 release contains backports of many features and bug fixes from the in-development v12, such as categories and animated emoji support.
v12 is still very much a work-in-progress, as we're aiming to make it the best it can possibly be before releasing.
If you are flike to live life on the bleeding-edge, check out the master branch.
## About
discord.js is a powerful [node.js](https://nodejs.org) module that allows you to interact with the
[Discord API](https://discordapp.com/developers/docs/intro) very easily.
- Object-oriented
- Predictable abstractions
- Performant
- 100% coverage of the Discord API
## Installation
**Node.js 6.0.0 or newer is required.**
Ignore any warnings about unmet peer dependencies, as they're all optional.
Without voice support: `npm install discord.js --save`
With voice support ([node-opus](https://www.npmjs.com/package/node-opus)): `npm install discord.js node-opus --save`
With voice support ([opusscript](https://www.npmjs.com/package/opusscript)): `npm install discord.js opusscript --save`
### Audio engines
The preferred audio engine is node-opus, as it performs significantly better than opusscript. When both are available, discord.js will automatically choose node-opus.
Using opusscript is only recommended for development environments where node-opus is tough to get working.
For production bots, using node-opus should be considered a necessity, especially if they're going to be running on multiple servers.
### Optional packages
- [bufferutil](https://www.npmjs.com/package/bufferutil) to greatly speed up the WebSocket when *not* using uws (`npm install bufferutil --save`)
- [erlpack](https://github.com/hammerandchisel/erlpack) for significantly faster WebSocket data (de)serialisation (`npm install hammerandchisel/erlpack --save`)
- One of the following packages can be installed for faster voice packet encryption and decryption:
- [sodium](https://www.npmjs.com/package/sodium) (`npm install sodium --save`)
- [libsodium.js](https://www.npmjs.com/package/libsodium-wrappers) (`npm install libsodium-wrappers --save`)
- [uws](https://www.npmjs.com/package/uws) for a much faster WebSocket connection (`npm install uws --save`)
## Example usage
```js
const Discord = require('discord.js');
const client = new Discord.Client();
client.on('ready', () => {
console.log('I am ready!');
});
client.on('message', message => {
if (message.content === 'ping') {
message.reply('pong');
}
});
client.login('your token');
```
## Links
* [Website](https://discord.js.org/) ([source](https://github.com/hydrabolt/discord.js-site))
* [Documentation](https://discord.js.org/#/docs)
* [Discord.js server](https://discord.gg/bRCvFy9)
* [Discord API server](https://discord.gg/rV4BwdK)
* [GitHub](https://github.com/hydrabolt/discord.js)
* [NPM](https://www.npmjs.com/package/discord.js)
* [Related libraries](https://discordapi.com/unofficial/libs.html) (see also [discord-rpc](https://www.npmjs.com/package/discord-rpc))
## Contributing
Before creating an issue, please ensure that it hasn't already been reported/suggested, and double-check the
[documentation](https://discord.js.org/#/docs).
See [the contribution guide](https://github.com/hydrabolt/discord.js/blob/master/.github/CONTRIBUTING.md) if you'd like to submit a PR.
## Help
If you don't understand something in the documentation, you are experiencing problems, or you just need a gentle
nudge in the right direction, please don't hesitate to join our official [Discord.js Server](https://discord.gg/bRCvFy9).

View File

@@ -1,4 +0,0 @@
{
"GEN_VERSION": 13,
"COMPRESS": false
}

View File

@@ -1,101 +0,0 @@
/* eslint-disable no-console */
const DocumentedClass = require('./types/DocumentedClass');
const DocumentedInterface = require('./types/DocumentedInterface');
const DocumentedTypeDef = require('./types/DocumentedTypeDef');
const DocumentedConstructor = require('./types/DocumentedConstructor');
const DocumentedMember = require('./types/DocumentedMember');
const DocumentedFunction = require('./types/DocumentedFunction');
const DocumentedEvent = require('./types/DocumentedEvent');
const GEN_VERSION = require('./config').GEN_VERSION;
class Documentation {
constructor(items, custom) {
this.classes = new Map();
this.interfaces = new Map();
this.typedefs = new Map();
this.custom = custom;
this.parse(items);
}
registerRoots(data) {
for (const item of data) {
switch (item.kind) {
case 'class':
this.classes.set(item.name, new DocumentedClass(this, item));
break;
case 'interface':
this.interfaces.set(item.name, new DocumentedInterface(this, item));
break;
case 'typedef':
this.typedefs.set(item.name, new DocumentedTypeDef(this, item));
break;
default:
break;
}
}
}
findParent(item) {
if (['constructor', 'member', 'function', 'event'].includes(item.kind)) {
let val = this.classes.get(item.memberof);
if (val) return val;
val = this.interfaces.get(item.memberof);
if (val) return val;
}
return null;
}
parse(items) {
this.registerRoots(items.filter(item => ['class', 'interface', 'typedef'].includes(item.kind)));
const members = items.filter(item => !['class', 'interface', 'typedef'].includes(item.kind));
const unknowns = new Map();
for (const member of members) {
let item;
switch (member.kind) {
case 'constructor':
item = new DocumentedConstructor(this, member);
break;
case 'member':
item = new DocumentedMember(this, member);
break;
case 'function':
item = new DocumentedFunction(this, member);
break;
case 'event':
item = new DocumentedEvent(this, member);
break;
default:
unknowns.set(member.kind, member);
continue;
}
const parent = this.findParent(member);
if (!parent) {
console.warn(`- "${member.name || member.directData.name}" has no accessible parent.`);
continue;
}
parent.add(item);
}
for (const [key, val] of unknowns) {
console.warn(`- Unknown documentation kind "${key}" - \n${JSON.stringify(val)}\n`);
}
}
serialize() {
const meta = {
version: GEN_VERSION,
date: Date.now(),
};
const serialized = {
meta,
classes: Array.from(this.classes.values()).map(c => c.serialize()),
interfaces: Array.from(this.interfaces.values()).map(i => i.serialize()),
typedefs: Array.from(this.typedefs.values()).map(t => t.serialize()),
custom: this.custom,
};
return serialized;
}
}
module.exports = Documentation;

View File

@@ -1,29 +0,0 @@
/* eslint-disable no-console */
const fs = require('fs-extra');
const zlib = require('zlib');
const jsdoc2md = require('jsdoc-to-markdown');
const Documentation = require('./documentation');
const custom = require('../custom/index');
const config = require('./config');
process.on('unhandledRejection', console.error);
console.log(`Using format version ${config.GEN_VERSION}.`);
console.log('Parsing JSDocs in source files...');
jsdoc2md.getTemplateData({ files: [`./src/*.js`, `./src/**/*.js`] }).then(data => {
console.log(`${data.length} items found.`);
const documentation = new Documentation(data, custom);
console.log('Serializing...');
let output = JSON.stringify(documentation.serialize(), null, 0);
if (config.compress) {
console.log('Compressing...');
output = zlib.deflateSync(output).toString('utf8');
}
if (!process.argv.slice(2).includes('silent')) {
console.log('Writing to docs.json...');
fs.writeFileSync('./docs/docs.json', output);
}
console.log('Done!');
process.exit(0);
}).catch(console.error);

View File

@@ -1,83 +0,0 @@
const DocumentedItem = require('./DocumentedItem');
const DocumentedItemMeta = require('./DocumentedItemMeta');
const DocumentedConstructor = require('./DocumentedConstructor');
const DocumentedFunction = require('./DocumentedFunction');
const DocumentedMember = require('./DocumentedMember');
const DocumentedEvent = require('./DocumentedEvent');
/*
{ id: 'VoiceChannel',
longname: 'VoiceChannel',
name: 'VoiceChannel',
scope: 'global',
kind: 'class',
augments: [ 'GuildChannel' ],
description: 'Represents a Server Voice Channel on Discord.',
meta:
{ lineno: 7,
filename: 'VoiceChannel.js',
path: 'src/structures' },
order: 232 }
*/
class DocumentedClass extends DocumentedItem {
constructor(docParent, data) {
super(docParent, data);
this.props = new Map();
this.methods = new Map();
this.events = new Map();
}
add(item) {
if (item instanceof DocumentedConstructor) {
if (this.classConstructor) {
throw new Error(`Doc ${this.directData.name} already has constructor - ${this.directData.classConstructor}`);
}
this.classConstructor = item;
} else if (item instanceof DocumentedFunction) {
if (this.methods.get(item.directData.name)) {
throw new Error(`Doc ${this.directData.name} already has method ${item.directData.name}`);
}
this.methods.set(item.directData.name, item);
} else if (item instanceof DocumentedMember) {
if (this.props.get(item.directData.name)) {
throw new Error(`Doc ${this.directData.name} already has prop ${item.directData.name}`);
}
this.props.set(item.directData.name, item);
} else if (item instanceof DocumentedEvent) {
if (this.events.get(item.directData.name)) {
throw new Error(`Doc ${this.directData.name} already has event ${item.directData.name}`);
}
this.events.set(item.directData.name, item);
}
}
registerMetaInfo(data) {
super.registerMetaInfo(data);
this.directData = data;
this.directData.meta = new DocumentedItemMeta(this, data.meta);
}
serialize() {
super.serialize();
const { id, name, description, meta, augments, access } = this.directData;
const serialized = {
id,
name,
description,
meta: meta.serialize(),
extends: augments,
access,
};
if (this.classConstructor) {
serialized.classConstructor = this.classConstructor.serialize();
}
serialized.methods = Array.from(this.methods.values()).map(m => m.serialize());
serialized.properties = Array.from(this.props.values()).map(p => p.serialize());
serialized.events = Array.from(this.events.values()).map(e => e.serialize());
return serialized;
}
}
module.exports = DocumentedClass;

View File

@@ -1,46 +0,0 @@
const DocumentedItem = require('./DocumentedItem');
const DocumentedParam = require('./DocumentedParam');
/*
{ id: 'Client()',
longname: 'Client',
name: 'Client',
kind: 'constructor',
description: 'Creates an instance of Client.',
memberof: 'Client',
params:
[ { type: [Object],
optional: true,
description: 'options to pass to the client',
name: 'options' } ],
order: 10 }
*/
class DocumentedConstructor extends DocumentedItem {
registerMetaInfo(data) {
super.registerMetaInfo(data);
this.directData = data;
const newParams = [];
for (const param of data.params) {
newParams.push(new DocumentedParam(this, param));
}
this.directData.params = newParams;
}
serialize() {
super.serialize();
const { id, name, description, memberof, access, params } = this.directData;
return {
id,
name,
description,
memberof,
access,
params: params.map(p => p.serialize()),
};
}
}
module.exports = DocumentedConstructor;

View File

@@ -1,80 +0,0 @@
const DocumentedItem = require('./DocumentedItem');
const DocumentedItemMeta = require('./DocumentedItemMeta');
const DocumentedParam = require('./DocumentedParam');
/*
{
"id":"Client#event:guildMemberRolesUpdate",
"longname":"Client#event:guildMemberRolesUpdate",
"name":"guildMemberRolesUpdate",
"scope":"instance",
"kind":"event",
"description":"Emitted whenever a Guild Member's Roles change - i.e. new role or removed role",
"memberof":"Client",
"params":[
{
"type":{
"names":[
"Guild"
]
},
"description":"the guild that the update affects",
"name":"guild"
},
{
"type":{
"names":[
"Array.<Role>"
]
},
"description":"the roles before the update",
"name":"oldRoles"
},
{
"type":{
"names":[
"Guild"
]
},
"description":"the roles after the update",
"name":"newRoles"
}
],
"meta":{
"lineno":91,
"filename":"Guild.js",
"path":"src/structures"
},
"order":110
}
*/
class DocumentedEvent extends DocumentedItem {
registerMetaInfo(data) {
this.directData = data;
this.directData.meta = new DocumentedItemMeta(this, data.meta);
const newParams = [];
data.params = data.params || [];
for (const param of data.params) {
newParams.push(new DocumentedParam(this, param));
}
this.directData.params = newParams;
}
serialize() {
super.serialize();
const { id, name, description, memberof, meta, params } = this.directData;
return {
id,
name,
description,
memberof,
meta: meta.serialize(),
params: params.map(p => p.serialize()),
};
}
}
module.exports = DocumentedEvent;

View File

@@ -1,91 +0,0 @@
const DocumentedItem = require('./DocumentedItem');
const DocumentedItemMeta = require('./DocumentedItemMeta');
const DocumentedVarType = require('./DocumentedVarType');
const DocumentedParam = require('./DocumentedParam');
/*
{
"id":"ClientUser#sendTTSMessage",
"longname":"ClientUser#sendTTSMessage",
"name":"sendTTSMessage",
"scope":"instance",
"kind":"function",
"inherits":"User#sendTTSMessage",
"inherited":true,
"implements":[
"TextBasedChannel#sendTTSMessage"
],
"description":"Send a text-to-speech message to this channel",
"memberof":"ClientUser",
"params":[
{
"type":{
"names":[
"String"
]
},
"description":"the content to send",
"name":"content"
}
],
"examples":[
"// send a TTS message..."
],
"returns":[
{
"type":{
"names":[
"Promise.<Message>"
]
}
}
],
"meta":{
"lineno":38,
"filename":"TextBasedChannel.js",
"path":src/structures/interface"
},
"order":293
}
*/
class DocumentedFunction extends DocumentedItem {
registerMetaInfo(data) {
super.registerMetaInfo(data);
this.directData = data;
this.directData.meta = new DocumentedItemMeta(this, data.meta);
this.directData.returns = new DocumentedVarType(this, data.returns ? data.returns[0].type : {
names: ['null'],
});
const newParams = [];
for (const param of data.params) {
newParams.push(new DocumentedParam(this, param));
}
this.directData.params = newParams;
}
serialize() {
super.serialize();
const {
id, name, description, memberof, examples, inherits, inherited, meta, returns, params, access,
} = this.directData;
const serialized = {
id,
access,
name,
description,
memberof,
examples,
inherits,
inherited,
meta: meta.serialize(),
returns: returns.serialize(),
params: params.map(p => p.serialize()),
};
serialized.implements = this.directData.implements;
return serialized;
}
}
module.exports = DocumentedFunction;

View File

@@ -1,32 +0,0 @@
const DocumentedClass = require('./DocumentedClass');
/*
{ id: 'TextBasedChannel',
longname: 'TextBasedChannel',
name: 'TextBasedChannel',
scope: 'global',
kind: 'interface',
classdesc: 'Interface for classes that have text-channel-like features',
params: [],
meta:
{ lineno: 5,
filename: 'TextBasedChannel.js',
path: 'src/structures/interface' },
order: 175 }
*/
class DocumentedInterface extends DocumentedClass {
registerMetaInfo(data) {
super.registerMetaInfo(data);
this.directData = data;
// this.directData.meta = new DocumentedItemMeta(this, data.meta);
}
serialize() {
const serialized = super.serialize();
serialized.description = this.directData.classdesc;
return serialized;
}
}
module.exports = DocumentedInterface;

View File

@@ -1,17 +0,0 @@
class DocumentedItem {
constructor(parent, info) {
this.parent = parent;
this.directData = {};
this.registerMetaInfo(info);
}
registerMetaInfo() {
return;
}
serialize() {
return;
}
}
module.exports = DocumentedItem;

View File

@@ -1,29 +0,0 @@
const cwd = (`${process.cwd()}\\`).replace(/\\/g, '/');
const backToForward = /\\/g;
const DocumentedItem = require('./DocumentedItem');
/*
{ lineno: 7,
filename: 'VoiceChannel.js',
path: 'src/structures' },
*/
class DocumentedItemMeta extends DocumentedItem {
registerMetaInfo(data) {
super.registerMetaInfo(data);
this.directData.line = data.lineno;
this.directData.file = data.filename;
this.directData.path = data.path.replace(backToForward, '/').replace(cwd, '');
}
serialize() {
super.serialize();
const { line, file, path } = this.directData;
return { line, file, path };
}
}
module.exports = DocumentedItemMeta;

View File

@@ -1,58 +0,0 @@
const DocumentedItem = require('./DocumentedItem');
const DocumentedItemMeta = require('./DocumentedItemMeta');
const DocumentedVarType = require('./DocumentedVarType');
const DocumentedParam = require('./DocumentedParam');
/*
{ id: 'Client#rest',
longname: 'Client#rest',
name: 'rest',
scope: 'instance',
kind: 'member',
description: 'The REST manager of the client',
memberof: 'Client',
type: { names: [ 'RESTManager' ] },
access: 'private',
meta:
{ lineno: 32,
filename: 'Client.js',
path: 'src/client' },
order: 11 }
*/
class DocumentedMember extends DocumentedItem {
registerMetaInfo(data) {
super.registerMetaInfo(data);
this.directData = data;
this.directData.meta = new DocumentedItemMeta(this, data.meta);
this.directData.type = new DocumentedVarType(this, data.type);
if (data.properties) {
const newProps = [];
for (const param of data.properties) {
newProps.push(new DocumentedParam(this, param));
}
this.directData.properties = newProps;
} else {
data.properties = [];
}
}
serialize() {
super.serialize();
const { id, name, description, memberof, type, access, meta, properties } = this.directData;
return {
id,
name,
description,
memberof,
type: type.serialize(),
access,
meta: meta.serialize(),
props: properties.map(p => p.serialize()),
};
}
}
module.exports = DocumentedMember;

View File

@@ -1,36 +0,0 @@
const DocumentedItem = require('./DocumentedItem');
const DocumentedVarType = require('./DocumentedVarType');
/*
{
"type":{
"names":[
"Guild"
]
},
"description":"the roles after the update",
"name":"newRoles"
}
*/
class DocumentedParam extends DocumentedItem {
registerMetaInfo(data) {
super.registerMetaInfo(data);
this.directData = data;
this.directData.type = new DocumentedVarType(this, data.type);
}
serialize() {
super.serialize();
const { name, description, type, optional } = this.directData;
return {
name,
description,
optional,
type: type.serialize(),
};
}
}
module.exports = DocumentedParam;

View File

@@ -1,56 +0,0 @@
const DocumentedItem = require('./DocumentedItem');
const DocumentedItemMeta = require('./DocumentedItemMeta');
const DocumentedVarType = require('./DocumentedVarType');
const DocumentedParam = require('./DocumentedParam');
/*
{ id: 'StringResolvable',
longname: 'StringResolvable',
name: 'StringResolvable',
scope: 'global',
kind: 'typedef',
description: 'Data that can be resolved to give a String...',
type: { names: [ 'String', 'Array', 'Object' ] },
meta:
{ lineno: 142,
filename: 'ClientDataResolver.js',
path: 'src/client' },
order: 37 }
*/
class DocumentedTypeDef extends DocumentedItem {
constructor(...args) {
super(...args);
}
registerMetaInfo(data) {
super.registerMetaInfo(data);
this.props = new Map();
this.directData = data;
this.directData.meta = new DocumentedItemMeta(this, data.meta);
this.directData.type = new DocumentedVarType(this, data.type);
data.properties = data.properties || [];
for (const prop of data.properties) {
this.props.set(prop.name, new DocumentedParam(this, prop));
}
}
serialize() {
super.serialize();
const { id, name, description, type, access, meta } = this.directData;
const serialized = {
id,
name,
description,
type: type.serialize(),
access,
meta: meta.serialize(),
};
serialized.properties = Array.from(this.props.values()).map(p => p.serialize());
return serialized;
}
}
module.exports = DocumentedTypeDef;

View File

@@ -1,50 +0,0 @@
const DocumentedItem = require('./DocumentedItem');
/*
{
"names":[
"String"
]
}
*/
const regex = /([\w]+)([^\w]+)/;
const regexG = /([\w]+)([^\w]+)/g;
function splitVarName(str) {
if (str === '*') {
return ['*', ''];
}
const matches = str.match(regexG);
const output = [];
if (matches) {
for (const match of matches) {
const groups = match.match(regex);
output.push([groups[1], groups[2]]);
}
} else {
output.push([str.match(/(\w+)/g)[0], '']);
}
return output;
}
class DocumentedVarType extends DocumentedItem {
registerMetaInfo(data) {
super.registerMetaInfo(data);
this.directData = data;
}
serialize() {
super.serialize();
const names = [];
for (const name of this.directData.names) {
names.push(splitVarName(name));
}
return {
types: names,
};
}
}
module.exports = DocumentedVarType;

24
docs/index.yml Normal file
View File

@@ -0,0 +1,24 @@
- name: General
files:
- name: Welcome
path: welcome.md
- name: Updating your code
path: updating.md
- name: FAQ
path: faq.md
- name: Topics
files:
- name: Voice
path: voice.md
- name: Web builds
path: web.md
- name: Examples
files:
- name: Ping
path: ping.js
- name: Avatars
path: avatars.js
- name: Server greeting
path: greeting.js
- name: Webhook
path: webhook.js

19
docs/logo.svg Normal file
View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" height="100%" width="100%" viewBox="0 0 6111.4378 1102.9827">
<g transform="translate(2539.6 -107.66)">
<g id="logo-discord" fill="#3d3f42" transform="translate(-44.194 1175.6)">
<path d="m-2495.4-1051.4v453.6 453.6l145.75-.37695c127.36-.3288 147.71-.58582 161.25-2.041 45.045-4.8398 76.353-11.233 111.79-22.826 44.217-14.465 83.672-35.567 118.71-63.49 13.615-10.851 40.444-37.567 50.889-50.674 37.186-46.665 61.816-98.191 78.01-163.2 23.57-94.614 23.154-219.66-1.0469-313.5-41.72-161.77-155.27-260-329.35-284.92-38.756-5.5479-34.464-5.4161-190.75-5.8086l-145.25-.3652zm161 130.09 41.75.0156c55.334.0205 78.397 1.6295 108.25 7.5566 105.75 20.995 171.57 87.554 196.39 198.59 12.878 57.6 14.716 139.6 4.5469 202.81-7.3952 45.963-21.469 87.286-40.711 119.53-12.041 20.179-33.82 45.681-51 59.719-38.627 31.563-87.98 50.255-148.73 56.326-9.5463.9541-32.361 1.7291-62.75 2.1328l-47.75.63477v-323.66-323.66z"/>
<path d="m-1631.4-597.85v-453.5h80.5 80.5v453.5 453.5h-80.5-80.5v-453.5z"/>
<path d="m-1008.4-128.41c-96.325-5.9603-189.36-41.918-264.54-102.25-15.565-12.49-33-28.526-33-30.352 0-.7224 20.622-25.63 45.826-55.351l45.826-54.038 3.8214 3.2697c17.83 15.256 22.538 19.151 29.616 24.501 48.673 36.79 103.35 61.169 158.92 70.862 18.387 3.2073 54.666 4.419 74.088 2.4745 41.751-4.1802 74.798-17.199 96.864-38.16 10.213-9.7012 15.896-17.429 21.626-29.408 17.4-36.376 13.152-81.77-10.39-111-16.357-20.31-45.054-37.907-98.696-60.521-41.654-17.56-164.15-71.537-176.19-77.638-85.541-43.335-134.63-104.27-148.9-184.84-2.6851-15.162-3.7276-49.931-1.9989-66.666 7.4631-72.25 48.261-136.63 113.09-178.46 41.81-26.976 88.546-43.103 144.99-50.03 20.52-2.5182 67.722-2.5268 88-.016 74.352 9.2063 141.74 36.296 199 79.999 18.772 14.327 37.632 31.435 36.864 33.44-.2001.52235-18.812 23.693-41.361 51.49l-40.997 50.54-3.503-2.9264c-1.9267-1.6095-9.4625-7.4505-16.746-12.98-44.158-33.522-88.429-52.307-140.26-59.513-17.665-2.4562-54.274-2.4782-70-.042-35.82 5.5488-61.303 16.869-80.113 35.588-17.506 17.422-26.238 37.587-27.528 63.576-1.3118 26.419 6.521 48.306 24.066 67.249 17.834 19.254 45.314 35.115 99.448 57.398 32.211 13.259 137.3 57.517 151.65 63.864 47.003 20.795 80.577 42.726 108.49 70.87 43.959 44.316 64.938 98.562 65.021 168.13.053 44.646-7.8058 78.816-26.734 116.23-12.46 24.632-27.741 45.114-49.45 66.28-51.458 50.172-122.59 79.937-208.86 87.392-17.502 1.5126-51.786 2.0335-67.962 1.0326z"/>
<path d="m-155.84-128.44c-100.7-5.7557-190.26-44.562-257.1-111.4-58.171-58.171-98.098-136.72-116.41-229.01-13.522-68.153-15.549-148.4-5.5195-218.5 13.11-91.624 47.506-173.73 99.29-237 11.342-13.858 35.64-38.591 49.282-50.164 54.726-46.425 120.9-76.546 193.88-88.256 25.873-4.1511 37.999-5.0552 67.977-5.0681 28.858-.013 38.31.6981 60.5 4.5485 70.566 12.245 140.29 49.396 192.89 102.78l6.8911 6.9936-2.8911 3.4607c-1.59 1.9034-21.52 24.408-44.288 50.011l-41.397 46.551-10.103-9.0797c-40.998-36.846-79.308-56.146-125.89-63.421-13.826-2.1591-48.594-2.4422-62.711-.51067-51.945 7.1074-94.856 27.696-131.17 62.933-64.806 62.887-97.854 165.12-92.829 287.16 2.697 65.505 14.091 119.1 35.16 165.38 30.027 65.96 77.365 110.94 138.03 131.16 24.572 8.1885 46.583 11.525 76.026 11.525 45.839 0 83.431-9.665 120.81-31.062 19.559-11.195 45.837-32.314 63.267-50.848 3.7379-3.9745 7.1554-7.0833 7.5942-6.9085 1.3142.5236 88.109 97.158 88.109 98.098 0 2.0843-41.684 42.322-54 52.126-73.043 58.146-157.48 84.1-255.41 78.503z"/>
<path d="m610.07-1067.8c-34.898-.056-47.464.862-75.232 5.4922-188.34 31.405-308.9 182.45-325.21 407.46-2.8044 38.675-2.2536 84.125 1.4941 123.38 9.2582 96.975 39.751 184.31 87.494 250.58 57.015 79.142 139.29 130.29 236.46 147 14.533 2.4988 40.496 5.3373 53.5 5.8496 147.12 5.7956 267.7-55.193 342.98-173.48 10.897-17.122 28.991-52.974 36.758-72.828 27.4-70.046 39.498-139.21 39.617-226.5.062-45.479-1.9339-73.343-7.9121-110.4-31.164-193.18-145.75-321-314.25-350.53-27.838-4.8789-41.445-5.9606-75.699-6.0156zm-1.4395 139.59c2.8062.0114 5.6199.0752 8.4395.19336 49.33 2.0671 91.449 18.361 127.46 49.305 12.954 11.133 20.363 19.102 31.482 33.861 40.99 54.409 62.709 125.93 66.582 219.25 4.5628 109.93-19.826 208.09-67.676 272.39-33.936 45.599-76.643 72.514-130.84 82.459-10.577 1.9408-50.92 2.8029-62 1.3242-74.694-9.9681-131.62-54.014-168.58-130.43-24.356-50.365-36.989-106.85-39.92-178.5-5.9652-145.81 37.791-262.31 118.61-315.79 33.933-22.452 74.357-34.245 116.45-34.074z"/>
<path d="m1187.6-1051.4v453.54 453.54h80.5 80.5v-177.51-177.51l68.717.25585 68.719.25782 97.531 177.22 97.533 177.22 90.285.0273c85.686.0268 90.237-.0599 89.336-1.7207-.5222-.9625-49.147-86.08-108.05-189.15-58.906-103.07-106.98-187.52-106.83-187.67.1497-.14971 5.5455-2.31 11.99-4.8008 92.947-35.923 149.28-103.8 164.7-198.43 3.4973-21.47 4.3763-36.845 3.7539-65.688-.8444-39.124-4.5518-62.293-14.883-93.008-29.696-88.286-106.44-143.03-224.91-160.44-38.597-5.6719-28.81-5.4157-221.14-5.7871l-177.75-.3438zm161 128.95 84.25.37695c91.298.40795 95.375.61732 123.75 6.3809 23.495 4.7723 45.38 13.215 61 23.533 15.167 10.019 29.716 27.182 37.475 44.207 14.573 31.978 16.395 82.735 4.3301 120.62-6.6274 20.814-16.172 36.615-31.18 51.625-27.567 27.57-66.814 42.804-121.93 47.324-7.3903.60617-43.437 1.0508-85.25 1.0508h-72.445v-147.56-147.56z"/>
<path d="m2014.6-1051.4v453.6 453.6l145.75-.37695c156.69-.4046 153.13-.29648 191.25-5.8008 38.321-5.5332 77.017-15.82 109.08-28.998 17.362-7.137 22.208-9.743 21.508-11.566-.3206-.8355-1.452-4.9721-2.5156-9.1914-3.4865-13.831-4.3718-23.482-3.7617-41.053.63-18.145 2.2913-27.3 7.7285-42.617 17.594-49.562 60.836-85.599 112.95-94.131 16.457-2.6941 38.955-1.8474 57.701 2.1719 3.6928.79178 3.1565 1.7476 11.26-20.041 27.066-72.775 38.169-169.68 30.476-265.97-14.239-178.25-95.276-299.81-236.97-355.47-33.122-13.01-69.539-22.404-108.45-27.975-38.756-5.5479-34.464-5.4161-190.75-5.8086l-145.25-.3652zm161 130.09 41.75.0156c55.334.0205 78.397 1.6295 108.25 7.5566 105.75 20.995 171.57 87.554 196.39 198.59 12.878 57.6 14.716 139.6 4.5469 202.81-7.3952 45.963-21.469 87.286-40.711 119.53-12.041 20.179-33.82 45.681-51 59.719-38.627 31.563-87.98 50.255-148.73 56.326-9.5463.9541-32.361 1.7291-62.75 2.1328l-47.75.63477v-323.66-323.66z"/>
</g>
<circle id="logo-dot" cx="2575.3" cy="939.96" r="125.4" fill="#499a6c"/>
<g id="logo-js" fill="#33b5e5" transform="translate(-44.194 1175.6)">
<path d="m2602.1 34.57c-57.094-4.6075-113.49-28.558-158.26-67.213-27.741-23.949-51.228-55.235-63.883-85.094-5.4804-12.93-5.926-15.992-2.3882-16.406 8.1404-.953 38.073-7.05 53.318-10.86 20.337-5.0831 29.827-8.2686 48.112-16.15 12.138-5.2318 12.996-5.46 14-3.7198 14.778 25.613 36.757 46.236 62.906 59.024 21.609 10.567 39.696 14.761 63.664 14.761 23.073 0 41.694-4.1466 61.73-13.746 36.584-17.528 62.542-46.884 75.844-85.772 2.3995-7.0151 7.5664-31.714 9.361-44.747 2.8753-20.881 3.0454-40.134 3.0555-345.75l.01-314.25h78 78v318.25c0 209.58-.3574 323.03-1.0389 332.25-4.4405 60.076-22.061 115.17-51.016 159.5-11.306 17.311-21.135 29.375-35.857 44.012-44.122 43.866-101.51 69.204-169.58 74.876-17.815 1.4842-53.463 2.0433-65.964 1.0344z"/>
<path d="m3256.6 33.535c-103.92-8.2588-202.14-50.771-278.59-120.57l-11.459-10.464 4.7737-5.6963c2.6255-3.133 23.371-27.615 46.101-54.405l41.327-48.709 11.068 9.6086c54.856 47.624 120.13 79.074 185.78 89.508 19.275 3.0634 60.816 3.3389 79 .5237 56.007-8.6707 91.978-30.946 109.48-67.793 5.7814-12.174 8.6772-25.17 9.2639-41.574 1.8511-51.755-20.009-81.836-81.241-111.79-10.45-5.1123-25.75-12.128-34-15.591-32.568-13.67-168.23-73.282-178.56-78.459-84.895-42.577-136.19-105.76-149.34-183.97-24.654-146.62 80.068-271.29 246.91-293.93 39.105-5.3065 82.999-4.2183 122.48 3.0365 76.174 13.996 145.21 48.561 201.87 101.07l7.367 6.8275-39.699 49c-21.834 26.95-40.537 49.863-41.563 50.918-1.8327 1.8856-1.9536 1.8424-7.1685-2.562-25.013-21.126-59.394-41.952-87.804-53.188-33.742-13.345-63.677-18.968-101.5-19.066-28.062-.0727-45.321 2.2-65.5 8.6248-40.117 12.773-65.445 37.309-74.612 72.282-3.4331 13.097-3.8978 33.664-1.0368 45.883 7.6067 32.488 29.949 55.7 75.674 78.622 15.123 7.5809 24.021 11.522 52.974 23.46 125.45 51.728 173.58 73.274 198.67 88.935 70.314 43.888 106.41 97.76 116.97 174.59 2.1563 15.683 2.4444 55.002.5056 69-7.9359 57.297-31.186 104.9-70.626 144.6-53.439 53.792-126.37 84.242-218.91 91.402-14.98 1.1588-53.385 1.0944-68.605-.1152z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.2 KiB

109
docs/topics/voice.md Normal file
View File

@@ -0,0 +1,109 @@
# Introduction to Voice
Voice in discord.js can be used for many things, such as music bots, recording or relaying audio.
In discord.js, you can use voice by connecting to a `VoiceChannel` to obtain a `VoiceConnection`, where you can start streaming and receiving audio.
To get started, make sure you have:
* ffmpeg - `npm install ffmpeg-binaries`
* an opus encoder, choose one from below:
* `npm install opusscript`
* `npm install node-opus`
* a good network connection
## Joining a voice channel
The example below reacts to a message and joins the sender's voice channel, catching any errors. This is important
as it allows us to obtain a `VoiceConnection` that we can start to stream audio with.
```js
const Discord = require('discord.js');
const client = new Discord.Client();
client.login('token here');
client.on('message', message => {
// Voice only works in guilds, if the message does not come from a guild,
// we ignore it
if (!message.guild) return;
if (message.content === '/join') {
// Only try to join the sender's voice channel if they are in one themselves
if (message.member.voiceChannel) {
message.member.voiceChannel.join()
.then(connection => { // Connection is an instance of VoiceConnection
message.reply('I have successfully connected to the channel!');
})
.catch(console.log);
} else {
message.reply('You need to join a voice channel first!');
}
}
});
```
## Streaming to a Voice Channel
In the previous example, we looked at how to join a voice channel in order to obtain a `VoiceConnection`. Now that we
have obtained a voice connection, we can start streaming audio to it. The following example shows how to stream an mp3
file:
**Playing a file:**
```js
// To play a file, we need to give an absolute path to it
const dispatcher = connection.playFile('C:/Users/Discord/Desktop/myfile.mp3');
```
Your file doesn't have to be just an mp3; ffmpeg can convert videos and audios of many formats.
The `dispatcher` variable is an instance of a `StreamDispatcher`, which manages streaming a specific resource to a voice
channel. We can do many things with the dispatcher, such as finding out when the stream ends or changing the volume:
```js
dispatcher.on('end', () => {
// The song has finished
});
dispatcher.on('error', e => {
// Catch any errors that may arise
console.log(e);
});
dispatcher.setVolume(0.5); // Set the volume to 50%
dispatcher.setVolume(1); // Set the volume back to 100%
console.log(dispatcher.time); // The time in milliseconds that the stream dispatcher has been playing for
dispatcher.pause(); // Pause the stream
dispatcher.resume(); // Carry on playing
dispatcher.end(); // End the dispatcher, emits 'end' event
```
If you have an existing [ReadableStream](https://nodejs.org/api/stream.html#stream_readable_streams),
this can also be used:
**Playing a ReadableStream:**
```js
connection.playStream(myReadableStream);
// If you don't want to use absolute paths, you can use
// fs.createReadStream to circumvent it
const fs = require('fs');
const stream = fs.createReadStream('./test.mp3');
connection.playStream(stream);
```
It's important to note that creating a readable stream to a file is less efficient than simply using `connection.playFile()`.
**Playing anything else:**
For anything else, such as a URL to a file, you can use `connection.playArbitraryInput()`. You should consult the [ffmpeg protocol documentation](https://ffmpeg.org/ffmpeg-protocols.html) to see what you can use this for.
```js
// Play an mp3 from a URL
connection.playArbitraryInput('http://mysite.com/sound.mp3');
```
Again, playing a file from a URL like this is more performant than creating a ReadableStream to the file.
## Advanced Topics
soon:tm:

38
docs/topics/web.md Normal file
View File

@@ -0,0 +1,38 @@
# Web builds
In addition to your usual Node applications, discord.js has special distributions available that are capable of running in web browsers.
This is useful for client-side web apps that need to interact with the Discord API.
[Webpack 2](https://webpack.js.org/) is used to build these.
## Usage
You can obtain your desired version of discord.js' web build from the [webpack branch](https://github.com/hydrabolt/discord.js/tree/webpack) of the GitHub repository.
There is a file for each branch and version of the library, and the ones ending in `.min.js` are minified to substantially reduce the size of the source code.
Include the file on the page just as you would any other JS library, like so:
```html
<script type="text/javascript" src="discord.VERSION.min.js"></script>
```
Rather than importing discord.js with `require('discord.js')`, the entire `Discord` object is available as a global (on the `window`) object.
The usage of the API isn't any different from using it in Node.js.
## Restrictions
- Any voice-related functionality is unavailable, as there is currently no audio encoding/decoding capabilities without external native libraries,
which web browsers do not support.
- The ShardingManager cannot be used, since it relies on being able to spawn child processes for shards.
- None of the optional packages are usable, since they're native libraries.
## Example
```html
<script type="text/javascript" src="discord.11.3.0.min.js"></script>
<script type="text/javascript">
const client = new Discord.Client();
client.on('message', msg => {
const guildTag = msg.channel.type === 'text' ? `[${msg.guild.name}]` : '[DM]';
const channelTag = msg.channel.type === 'text' ? `[#${msg.channel.name}]` : '';
console.log(`${guildTag}${channelTag} ${msg.author.tag}: ${msg.content}`);
});
client.login('some crazy token');
</script>
```

View File

@@ -1,11 +1,16 @@
{
"name": "discord.js",
"version": "10.0.1",
"version": "11.3.0",
"description": "A powerful library for interacting with the Discord API",
"main": "./src/index",
"types": "./typings/index.d.ts",
"scripts": {
"test": "eslint src/ && node docs/generator/generator.js silent",
"docs": "node docs/generator/generator.js"
"test": "npm run lint && npm run docs:test",
"docs": "docgen --source src --custom docs/index.yml --output docs/docs.json",
"docs:test": "docgen --source src --custom docs/index.yml",
"lint": "eslint src",
"lint:fix": "eslint --fix src",
"webpack": "parallel-webpack"
},
"repository": {
"type": "git",
@@ -25,21 +30,64 @@
"url": "https://github.com/hydrabolt/discord.js/issues"
},
"homepage": "https://github.com/hydrabolt/discord.js#readme",
"runkitExampleFilename": "./docs/examples/ping.js",
"dependencies": {
"superagent": "^2.3.0",
"tweetnacl": "^0.14.3",
"ws": "^1.1.1"
"long": "^3.2.0",
"prism-media": "^0.0.1",
"snekfetch": "^3.6.1",
"tweetnacl": "^1.0.0",
"ws": "^4.0.0"
},
"peerDependencies": {
"node-opus": "^0.2.1",
"opusscript": "^0.0.1"
"bufferutil": "^3.0.3",
"erlpack": "discordapp/erlpack",
"node-opus": "^0.2.7",
"opusscript": "^0.0.6",
"sodium": "^2.0.3",
"libsodium-wrappers": "^0.5.4",
"uws": "^9.14.0"
},
"devDependencies": {
"eslint": "^3.8.0",
"fs-extra": "^0.30.0",
"jsdoc-to-markdown": "^2.0.0"
"@types/node": "^8.5.7",
"discord.js-docgen": "hydrabolt/discord.js-docgen",
"eslint": "^4.15.0",
"parallel-webpack": "^2.2.0",
"uglifyjs-webpack-plugin": "^1.1.6",
"webpack": "^3.10.0"
},
"engines": {
"node": ">=6.0.0"
},
"browser": {
"ws": false,
"uws": false,
"erlpack": false,
"prism-media": false,
"opusscript": false,
"node-opus": false,
"tweetnacl": false,
"sodium": false,
"src/sharding/Shard.js": false,
"src/sharding/ShardClientUtil.js": false,
"src/sharding/ShardingManager.js": false,
"src/client/voice/dispatcher/StreamDispatcher.js": false,
"src/client/voice/opus/BaseOpusEngine.js": false,
"src/client/voice/opus/NodeOpusEngine.js": false,
"src/client/voice/opus/OpusEngineList.js": false,
"src/client/voice/opus/OpusScriptEngine.js": false,
"src/client/voice/pcm/ConverterEngine.js": false,
"src/client/voice/pcm/ConverterEngineList.js": false,
"src/client/voice/pcm/FfmpegConverterEngine.js": false,
"src/client/voice/player/AudioPlayer.js": false,
"src/client/voice/receiver/VoiceReadable.js": false,
"src/client/voice/receiver/VoiceReceiver.js": false,
"src/client/voice/util/Secretbox.js": false,
"src/client/voice/util/SecretKey.js": false,
"src/client/voice/util/VolumeInterface.js": false,
"src/client/voice/ClientVoiceManager.js": false,
"src/client/voice/VoiceBroadcast.js": false,
"src/client/voice/VoiceConnection.js": false,
"src/client/voice/VoiceUDPClient.js": false,
"src/client/voice/VoiceWebSocket.js": false
}
}

View File

@@ -1,6 +1,7 @@
const EventEmitter = require('events').EventEmitter;
const mergeDefault = require('../util/MergeDefault');
const EventEmitter = require('events');
const Constants = require('../util/Constants');
const Permissions = require('../util/Permissions');
const Util = require('../util/Util');
const RESTManager = require('./rest/RESTManager');
const ClientDataManager = require('./ClientDataManager');
const ClientManager = require('./ClientManager');
@@ -11,9 +12,10 @@ const ActionsManager = require('./actions/ActionsManager');
const Collection = require('../util/Collection');
const Presence = require('../structures/Presence').Presence;
const ShardClientUtil = require('../sharding/ShardClientUtil');
const VoiceBroadcast = require('./voice/VoiceBroadcast');
/**
* The starting point for making a Discord Bot.
* The main hub for interacting with the Discord API, and the starting point for any bot.
* @extends {EventEmitter}
*/
class Client extends EventEmitter {
@@ -31,7 +33,7 @@ class Client extends EventEmitter {
* The options the client was instantiated with
* @type {ClientOptions}
*/
this.options = mergeDefault(Constants.DefaultOptions, options);
this.options = Util.mergeDefault(Constants.DefaultOptions, options);
this._validateOptions();
/**
@@ -42,81 +44,86 @@ class Client extends EventEmitter {
this.rest = new RESTManager(this);
/**
* The data manager of the Client
* The data manager of the client
* @type {ClientDataManager}
* @private
*/
this.dataManager = new ClientDataManager(this);
/**
* The manager of the Client
* The manager of the client
* @type {ClientManager}
* @private
*/
this.manager = new ClientManager(this);
/**
* The WebSocket Manager of the Client
* The WebSocket manager of the client
* @type {WebSocketManager}
* @private
*/
this.ws = new WebSocketManager(this);
/**
* The Data Resolver of the Client
* The data resolver of the client
* @type {ClientDataResolver}
* @private
*/
this.resolver = new ClientDataResolver(this);
/**
* The Action Manager of the Client
* The action manager of the client
* @type {ActionsManager}
* @private
*/
this.actions = new ActionsManager(this);
/**
* The Voice Manager of the Client
* @type {ClientVoiceManager}
* The voice manager of the client (`null` in browsers)
* @type {?ClientVoiceManager}
* @private
*/
this.voice = new ClientVoiceManager(this);
this.voice = !this.browser ? new ClientVoiceManager(this) : null;
/**
* The shard helpers for the client (only if the process was spawned as a child, such as from a ShardingManager)
* @type {?ShardUtil}
* The shard helpers for the client
* (only if the process was spawned as a child, such as from a {@link ShardingManager})
* @type {?ShardClientUtil}
*/
this.shard = process.send ? ShardClientUtil.singleton(this) : null;
/**
* A Collection of the Client's stored users
* @type {Collection<string, User>}
* All of the {@link User} objects that have been cached at any point, mapped by their IDs
* @type {Collection<Snowflake, User>}
*/
this.users = new Collection();
/**
* A Collection of the Client's stored guilds
* @type {Collection<string, Guild>}
* All of the guilds the client is currently handling, mapped by their IDs -
* as long as sharding isn't being used, this will be *every* guild the bot is a member of
* @type {Collection<Snowflake, Guild>}
*/
this.guilds = new Collection();
/**
* A Collection of the Client's stored channels
* @type {Collection<string, Channel>}
* All of the {@link Channel}s that the client is currently handling, mapped by their IDs -
* as long as sharding isn't being used, this will be *every* channel in *every* guild, and all DM channels
* @type {Collection<Snowflake, Channel>}
*/
this.channels = new Collection();
/**
* A Collection of presences for friends of the logged in user.
* <warn>This is only present for user accounts, not bot accounts!</warn>
* @type {Collection<string, Presence>}
* Presences that have been received for the client user's friends, mapped by user IDs
* <warn>This is only filled when using a user account.</warn>
* @type {Collection<Snowflake, Presence>}
*/
this.presences = new Collection();
Object.defineProperty(this, 'token', { writable: true });
if (!this.token && 'CLIENT_TOKEN' in process.env) {
/**
* The authorization token for the logged in user/bot.
* Authorization token for the logged in user/bot
* <warn>This should be kept private at all times.</warn>
* @type {?string}
*/
this.token = process.env.CLIENT_TOKEN;
@@ -125,30 +132,42 @@ class Client extends EventEmitter {
}
/**
* The email, if there is one, for the logged in Client
* @type {?string}
*/
this.email = null;
/**
* The password, if there is one, for the logged in Client
* @type {?string}
*/
this.password = null;
/**
* The ClientUser representing the logged in Client
* User that the client is logged in as
* @type {?ClientUser}
*/
this.user = null;
/**
* The date at which the Client was regarded as being in the `READY` state.
* Time at which the client was last regarded as being in the `READY` state
* (each time the client disconnects and successfully reconnects, this will be overwritten)
* @type {?Date}
*/
this.readyAt = null;
/**
* Active voice broadcasts that have been created
* @type {VoiceBroadcast[]}
*/
this.broadcasts = [];
/**
* Previous heartbeat pings of the websocket (most recent first, limited to three elements)
* @type {number[]}
*/
this.pings = [];
/**
* Timeouts set by {@link Client#setTimeout} that are still active
* @type {Set<Timeout>}
* @private
*/
this._timeouts = new Set();
/**
* Intervals set by {@link Client#setInterval} that are still active
* @type {Set<Timeout>}
* @private
*/
this._intervals = new Set();
if (this.options.messageSweepInterval > 0) {
@@ -157,16 +176,25 @@ class Client extends EventEmitter {
}
/**
* The status for the logged in Client.
* Timestamp of the latest ping's start time
* @type {number}
* @private
*/
get _pingTimestamp() {
return this.ws.connection ? this.ws.connection.lastPingTimestamp : 0;
}
/**
* Current status of the client's connection to Discord
* @type {?number}
* @readonly
*/
get status() {
return this.ws.status;
return this.ws.connection.status;
}
/**
* The uptime for the logged in Client.
* How long it has been since the client last entered the `READY` state
* @type {?number}
* @readonly
*/
@@ -175,17 +203,27 @@ class Client extends EventEmitter {
}
/**
* Returns a Collection, mapping Guild ID to Voice Connections.
* @type {Collection<string, VoiceConnection>}
* Average heartbeat ping of the websocket, obtained by averaging the {@link Client#pings} property
* @type {number}
* @readonly
*/
get ping() {
return this.pings.reduce((prev, p) => prev + p, 0) / this.pings.length;
}
/**
* All active voice connections that have been established, mapped by channel ID
* @type {Collection<Snowflake, VoiceConnection>}
* @readonly
*/
get voiceConnections() {
if (this.browser) return new Collection();
return this.voice.connections;
}
/**
* The emojis that the client can use. Mapped by emoji ID.
* @type {Collection<string, Emoji>}
* All custom emojis that the client has access to, mapped by their IDs
* @type {Collection<Snowflake, Emoji>}
* @readonly
*/
get emojis() {
@@ -197,7 +235,7 @@ class Client extends EventEmitter {
}
/**
* The timestamp that the client was last ready at
* Timestamp of the time the client was last `READY` at
* @type {?number}
* @readonly
*/
@@ -206,31 +244,41 @@ class Client extends EventEmitter {
}
/**
* Logs the client in. If successful, resolves with the account's token. <warn>If you're making a bot, it's
* much better to use a bot account rather than a user account.
* Bot accounts have higher rate limits and have access to some features user accounts don't have. User bots
* that are making a lot of API requests can even be banned.</warn>
* @param {string} tokenOrEmail The token or email used for the account. If it is an email, a password _must_ be
* provided.
* @param {string} [password] The password for the account, only needed if an email was provided.
* @returns {Promise<string>}
* @example
* // log the client in using a token
* const token = 'my token';
* client.login(token);
* @example
* // log the client in using email and password
* const email = 'user@email.com';
* const password = 'supersecret123';
* client.login(email, password);
* Whether the client is in a browser environment
* @type {boolean}
* @readonly
*/
login(tokenOrEmail, password = null) {
if (password) return this.rest.methods.loginEmailPassword(tokenOrEmail, password);
return this.rest.methods.loginToken(tokenOrEmail);
get browser() {
return typeof window !== 'undefined';
}
/**
* Destroys the client and logs out.
* Creates a voice broadcast.
* @returns {VoiceBroadcast}
*/
createVoiceBroadcast() {
const broadcast = new VoiceBroadcast(this);
this.broadcasts.push(broadcast);
return broadcast;
}
/**
* Logs the client in, establishing a websocket connection to Discord.
* <info>Both bot and regular user accounts are supported, but it is highly recommended to use a bot account whenever
* possible. User accounts are subject to harsher ratelimits and other restrictions that don't apply to bot accounts.
* Bot accounts also have access to many features that user accounts cannot utilise. User accounts that are found to
* be abusing/overusing the API will be banned, locking you out of Discord entirely.</info>
* @param {string} token Token of the account to log in with
* @returns {Promise<string>} Token of the account used
* @example
* client.login('my token');
*/
login(token) {
return this.rest.methods.login(token);
}
/**
* Logs out, terminates the connection to Discord, and destroys the client.
* @returns {Promise}
*/
destroy() {
@@ -238,40 +286,38 @@ class Client extends EventEmitter {
for (const i of this._intervals) clearInterval(i);
this._timeouts.clear();
this._intervals.clear();
this.token = null;
this.email = null;
this.password = null;
return this.manager.destroy();
}
/**
* This shouldn't really be necessary to most developers as it is automatically invoked every 30 seconds, however
* if you wish to force a sync of Guild data, you can use this. Only applicable to user accounts.
* @param {Guild[]|Collection<string, Guild>} [guilds=this.guilds] An array or collection of guilds to sync
* Requests a sync of guild data with Discord.
* <info>This can be done automatically every 30 seconds by enabling {@link ClientOptions#sync}.</info>
* <warn>This is only available when using a user account.</warn>
* @param {Guild[]|Collection<Snowflake, Guild>} [guilds=this.guilds] An array or collection of guilds to sync
*/
syncGuilds(guilds = this.guilds) {
if (!this.user.bot) {
this.ws.send({
op: 12,
d: guilds instanceof Collection ? guilds.keyArray() : guilds.map(g => g.id),
});
}
if (this.user.bot) return;
this.ws.send({
op: 12,
d: guilds instanceof Collection ? guilds.keyArray() : guilds.map(g => g.id),
});
}
/**
* Caches a user, or obtains it from the cache if it's already cached.
* If the user isn't already cached, it will only be obtainable by OAuth bot accounts.
* @param {string} id The ID of the user to obtain
* Obtains a user from Discord, or the user cache if it's already available.
* <warn>This is only available when using a bot account.</warn>
* @param {Snowflake} id ID of the user
* @param {boolean} [cache=true] Whether to cache the new user object if it isn't already
* @returns {Promise<User>}
*/
fetchUser(id) {
fetchUser(id, cache = true) {
if (this.users.has(id)) return Promise.resolve(this.users.get(id));
return this.rest.methods.getUser(id);
return this.rest.methods.getUser(id, cache);
}
/**
* Fetches an invite object from an invite code.
* @param {InviteResolvable} invite An invite code or URL
* Obtains an invite from Discord.
* @param {InviteResolvable} invite Invite code or URL
* @returns {Promise<Invite>}
*/
fetchInvite(invite) {
@@ -280,19 +326,28 @@ class Client extends EventEmitter {
}
/**
* Fetch a webhook by ID.
* @param {string} id ID of the webhook
* Obtains a webhook from Discord.
* @param {Snowflake} id ID of the webhook
* @param {string} [token] Token for the webhook
* @returns {Promise<Webhook>}
*/
fetchWebhook(id) {
return this.rest.methods.getWebhook(id);
fetchWebhook(id, token) {
return this.rest.methods.getWebhook(id, token);
}
/**
* Sweeps all channels' messages and removes the ones older than the max message lifetime.
* Obtains the available voice regions from Discord.
* @returns {Collection<string, VoiceRegion>}
*/
fetchVoiceRegions() {
return this.rest.methods.fetchVoiceRegions();
}
/**
* Sweeps all text-based channels' messages and removes the ones older than the max message lifetime.
* If the message has been edited, the time of the edit is used rather than the time of the original message.
* @param {number} [lifetime=this.options.messageCacheLifetime] Messages that are older than this (in seconds)
* will be removed from the caches. The default is based on the client's `messageCacheLifetime` option.
* will be removed from the caches. The default is based on {@link ClientOptions#messageCacheLifetime}
* @returns {number} Amount of messages that were removed from the caches,
* or -1 if the message cache lifetime is unlimited
*/
@@ -324,43 +379,125 @@ class Client extends EventEmitter {
return messages;
}
setTimeout(fn, ...params) {
/**
* Obtains the OAuth Application of the bot from Discord.
* @param {Snowflake} [id='@me'] ID of application to fetch
* @returns {Promise<OAuth2Application>}
*/
fetchApplication(id = '@me') {
return this.rest.methods.getApplication(id);
}
/**
* Generates a link that can be used to invite the bot to a guild.
* <warn>This is only available when using a bot account.</warn>
* @param {PermissionResolvable[]|number} [permissions] Permissions to request
* @returns {Promise<string>}
* @example
* client.generateInvite(['SEND_MESSAGES', 'MANAGE_GUILD', 'MENTION_EVERYONE'])
* .then(link => {
* console.log(`Generated bot invite link: ${link}`);
* });
*/
generateInvite(permissions) {
if (permissions) {
if (permissions instanceof Array) permissions = Permissions.resolve(permissions);
} else {
permissions = 0;
}
return this.fetchApplication().then(application =>
`https://discordapp.com/oauth2/authorize?client_id=${application.id}&permissions=${permissions}&scope=bot`
);
}
/**
* Sets a timeout that will be automatically cancelled if the client is destroyed.
* @param {Function} fn Function to execute
* @param {number} delay Time to wait before executing (in milliseconds)
* @param {...*} args Arguments for the function
* @returns {Timeout}
*/
setTimeout(fn, delay, ...args) {
const timeout = setTimeout(() => {
fn();
fn(...args);
this._timeouts.delete(timeout);
}, ...params);
}, delay);
this._timeouts.add(timeout);
return timeout;
}
/**
* Clears a timeout.
* @param {Timeout} timeout Timeout to cancel
*/
clearTimeout(timeout) {
clearTimeout(timeout);
this._timeouts.delete(timeout);
}
setInterval(...params) {
const interval = setInterval(...params);
/**
* Sets an interval that will be automatically cancelled if the client is destroyed.
* @param {Function} fn Function to execute
* @param {number} delay Time to wait before executing (in milliseconds)
* @param {...*} args Arguments for the function
* @returns {Timeout}
*/
setInterval(fn, delay, ...args) {
const interval = setInterval(fn, delay, ...args);
this._intervals.add(interval);
return interval;
}
/**
* Clears an interval.
* @param {Timeout} interval Interval to cancel
*/
clearInterval(interval) {
clearInterval(interval);
this._intervals.delete(interval);
}
/**
* Adds a ping to {@link Client#pings}.
* @param {number} startTime Starting time of the ping
* @private
*/
_pong(startTime) {
this.pings.unshift(Date.now() - startTime);
if (this.pings.length > 3) this.pings.length = 3;
this.ws.lastHeartbeatAck = true;
}
/**
* Adds/updates a friend's presence in {@link Client#presences}.
* @param {Snowflake} id ID of the user
* @param {Object} presence Raw presence object from Discord
* @private
*/
_setPresence(id, presence) {
if (this.presences.get(id)) {
if (this.presences.has(id)) {
this.presences.get(id).update(presence);
return;
}
this.presences.set(id, new Presence(presence));
}
/**
* Calls {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval} on a script
* with the client as `this`.
* @param {string} script Script to eval
* @returns {*}
* @private
*/
_eval(script) {
return eval(script);
}
/**
* Validates the client options.
* @param {ClientOptions} [options=this.options] Options to validate
* @private
*/
_validateOptions(options = this.options) {
if (typeof options.shardCount !== 'number' || isNaN(options.shardCount)) {
throw new TypeError('The shardCount option must be a number.');
@@ -398,13 +535,13 @@ class Client extends EventEmitter {
module.exports = Client;
/**
* Emitted for general warnings
* Emitted for general warnings.
* @event Client#warn
* @param {string} The warning
* @param {string} info The warning
*/
/**
* Emitted for general debugging information
* Emitted for general debugging information.
* @event Client#debug
* @param {string} The debug information
* @param {string} info The debug information
*/

View File

@@ -1,7 +1,8 @@
const Constants = require('../util/Constants');
const cloneObject = require('../util/CloneObject');
const Util = require('../util/Util');
const Guild = require('../structures/Guild');
const User = require('../structures/User');
const CategoryChannel = require('../structures/CategoryChannel');
const DMChannel = require('../structures/DMChannel');
const Emoji = require('../structures/Emoji');
const TextChannel = require('../structures/TextChannel');
@@ -15,7 +16,7 @@ class ClientDataManager {
}
get pastReady() {
return this.client.ws.status === Constants.Status.READY;
return this.client.ws.connection.status === Constants.Status.READY;
}
newGuild(data) {
@@ -24,7 +25,7 @@ class ClientDataManager {
this.client.guilds.set(guild.id, guild);
if (this.pastReady && !already) {
/**
* Emitted whenever the client joins a Guild.
* Emitted whenever the client joins a guild.
* @event Client#guildCreate
* @param {Guild} guild The created guild
*/
@@ -50,25 +51,30 @@ class ClientDataManager {
let channel;
if (data.type === Constants.ChannelTypes.DM) {
channel = new DMChannel(this.client, data);
} else if (data.type === Constants.ChannelTypes.groupDM) {
} else if (data.type === Constants.ChannelTypes.GROUP_DM) {
channel = new GroupDMChannel(this.client, data);
} else {
guild = guild || this.client.guilds.get(data.guild_id);
if (guild) {
if (data.type === Constants.ChannelTypes.text) {
if (data.type === Constants.ChannelTypes.TEXT) {
channel = new TextChannel(guild, data);
guild.channels.set(channel.id, channel);
} else if (data.type === Constants.ChannelTypes.voice) {
} else if (data.type === Constants.ChannelTypes.VOICE) {
channel = new VoiceChannel(guild, data);
guild.channels.set(channel.id, channel);
} else if (data.type === Constants.ChannelTypes.CATEGORY) {
channel = new CategoryChannel(guild, data);
guild.channels.set(channel.id, channel);
}
}
}
if (channel) {
if (this.pastReady && !already) this.client.emit(Constants.Events.CHANNEL_CREATE, channel);
if (channel && !already) {
if (this.pastReady) this.client.emit(Constants.Events.CHANNEL_CREATE, channel);
this.client.channels.set(channel.id, channel);
return channel;
} else if (already) {
return channel;
}
return null;
@@ -78,7 +84,7 @@ class ClientDataManager {
const already = guild.emojis.has(data.id);
if (data && !already) {
let emoji = new Emoji(guild, data);
this.client.emit(Constants.Events.EMOJI_CREATE, emoji);
this.client.emit(Constants.Events.GUILD_EMOJI_CREATE, emoji);
guild.emojis.set(emoji.id, emoji);
return emoji;
} else if (already) {
@@ -90,7 +96,7 @@ class ClientDataManager {
killEmoji(emoji) {
if (!(emoji instanceof Emoji && emoji.guild)) return;
this.client.emit(Constants.Events.EMOJI_DELETE, emoji);
this.client.emit(Constants.Events.GUILD_EMOJI_DELETE, emoji);
emoji.guild.emojis.delete(emoji.id);
}
@@ -110,7 +116,7 @@ class ClientDataManager {
}
updateGuild(currentGuild, newData) {
const oldGuild = cloneObject(currentGuild);
const oldGuild = Util.cloneObject(currentGuild);
currentGuild.setup(newData);
if (this.pastReady) this.client.emit(Constants.Events.GUILD_UPDATE, oldGuild, currentGuild);
}
@@ -120,9 +126,10 @@ class ClientDataManager {
}
updateEmoji(currentEmoji, newData) {
const oldEmoji = cloneObject(currentEmoji);
const oldEmoji = Util.cloneObject(currentEmoji);
currentEmoji.setup(newData);
this.client.emit(Constants.Events.GUILD_EMOJI_UPDATE, oldEmoji, currentEmoji);
return currentEmoji;
}
}

View File

@@ -1,13 +1,17 @@
const path = require('path');
const fs = require('fs');
const request = require('superagent');
const snekfetch = require('snekfetch');
const Constants = require('../util/Constants');
const User = require(`../structures/User`);
const Message = require(`../structures/Message`);
const Guild = require(`../structures/Guild`);
const Channel = require(`../structures/Channel`);
const GuildMember = require(`../structures/GuildMember`);
const convertToBuffer = require('../util/Util').convertToBuffer;
const User = require('../structures/User');
const Message = require('../structures/Message');
const Guild = require('../structures/Guild');
const Channel = require('../structures/Channel');
const GuildMember = require('../structures/GuildMember');
const Emoji = require('../structures/Emoji');
const ReactionEmoji = require('../structures/ReactionEmoji');
const Role = require('../structures/Role');
/**
* The DataResolver identifies different objects and tries to resolve a specific piece of information from them, e.g.
@@ -25,15 +29,15 @@ class ClientDataResolver {
/**
* Data that resolves to give a User object. This can be:
* * A User object
* * A User ID
* * A Message (resolves to the message author)
* * A Guild (owner of the guild)
* * A Guild Member
* @typedef {User|string|Message|Guild|GuildMember} UserResolvable
* * A Snowflake
* * A Message object (resolves to the message author)
* * A Guild object (owner of the guild)
* * A GuildMember object
* @typedef {User|Snowflake|Message|Guild|GuildMember} UserResolvable
*/
/**
* Resolves a UserResolvable to a User object
* Resolves a UserResolvable to a User object.
* @param {UserResolvable} user The UserResolvable to identify
* @returns {?User}
*/
@@ -47,9 +51,9 @@ class ClientDataResolver {
}
/**
* Resolves a UserResolvable to a user ID string
* Resolves a UserResolvable to a user ID string.
* @param {UserResolvable} user The UserResolvable to identify
* @returns {?string}
* @returns {?Snowflake}
*/
resolveUserID(user) {
if (user instanceof User || user instanceof GuildMember) return user.id;
@@ -62,11 +66,12 @@ class ClientDataResolver {
/**
* Data that resolves to give a Guild object. This can be:
* * A Guild object
* @typedef {Guild} GuildResolvable
* * A Snowflake
* @typedef {Guild|Snowflake} GuildResolvable
*/
/**
* Resolves a GuildResolvable to a Guild object
* Resolves a GuildResolvable to a Guild object.
* @param {GuildResolvable} guild The GuildResolvable to identify
* @returns {?Guild}
*/
@@ -80,44 +85,76 @@ class ClientDataResolver {
* Data that resolves to give a GuildMember object. This can be:
* * A GuildMember object
* * A User object
* @typedef {Guild} GuildMemberResolvable
* @typedef {GuildMember|User} GuildMemberResolvable
*/
/**
* Resolves a GuildMemberResolvable to a GuildMember object
* Resolves a GuildMemberResolvable to a GuildMember object.
* @param {GuildResolvable} guild The guild that the member is part of
* @param {UserResolvable} user The user that is part of the guild
* @returns {?GuildMember}
*/
resolveGuildMember(guild, user) {
if (user instanceof GuildMember) return user;
guild = this.resolveGuild(guild);
user = this.resolveUser(user);
if (!guild || !user) return null;
return guild.members.get(user.id) || null;
}
/**
* Data that can be resolved to give a Channel. This can be:
* * An instance of a Channel
* * An instance of a Message (the channel the message was sent in)
* * An instance of a Guild (the #general channel)
* * An ID of a Channel
* @typedef {Channel|Guild|Message|string} ChannelResolvable
* Data that can be resolved to a Role object. This can be:
* * A Role
* * A Snowflake
* @typedef {Role|Snowflake} RoleResolvable
*/
/**
* Resolves a ChannelResolvable to a Channel object
* Resolves a RoleResolvable to a Role object.
* @param {GuildResolvable} guild The guild that this role is part of
* @param {RoleResolvable} role The role resolvable to resolve
* @returns {?Role}
*/
resolveRole(guild, role) {
if (role instanceof Role) return role;
guild = this.resolveGuild(guild);
if (!guild) return null;
if (typeof role === 'string') return guild.roles.get(role);
return null;
}
/**
* Data that can be resolved to give a Channel object. This can be:
* * A Channel object
* * A Message object (the channel the message was sent in)
* * A Guild object (the #general channel)
* * A Snowflake
* @typedef {Channel|Guild|Message|Snowflake} ChannelResolvable
*/
/**
* Resolves a ChannelResolvable to a Channel object.
* @param {ChannelResolvable} channel The channel resolvable to resolve
* @returns {?Channel}
*/
resolveChannel(channel) {
if (channel instanceof Channel) return channel;
if (typeof channel === 'string') return this.client.channels.get(channel) || null;
if (channel instanceof Message) return channel.channel;
if (channel instanceof Guild) return channel.channels.get(channel.id) || null;
if (typeof channel === 'string') return this.client.channels.get(channel.id) || null;
return null;
}
/**
* Resolves a ChannelResolvable to a channel ID.
* @param {ChannelResolvable} channel The channel resolvable to resolve
* @returns {?Snowflake}
*/
resolveChannelID(channel) {
if (channel instanceof Channel) return channel.id;
if (typeof channel === 'string') return channel;
if (channel instanceof Message) return channel.channel.id;
if (channel instanceof Guild) return channel.defaultChannel.id;
return null;
}
@@ -129,76 +166,27 @@ class ClientDataResolver {
*/
/**
* Resolves InviteResolvable to an invite code
* Resolves InviteResolvable to an invite code.
* @param {InviteResolvable} data The invite resolvable to resolve
* @returns {string}
*/
resolveInviteCode(data) {
const inviteRegex = /discord(?:app)?\.(?:gg|com\/invite)\/([a-z0-9]{5})/i;
const inviteRegex = /discord(?:app\.com\/invite|\.gg)\/([\w-]{2,255})/i;
const match = inviteRegex.exec(data);
if (match && match[1]) return match[1];
return data;
}
/**
* Data that can be resolved to give a permission number. This can be:
* * A string
* * A permission number
*
* Possible strings:
* ```js
* [
* "CREATE_INSTANT_INVITE",
* "KICK_MEMBERS",
* "BAN_MEMBERS",
* "ADMINISTRATOR",
* "MANAGE_CHANNELS",
* "MANAGE_GUILD",
* "READ_MESSAGES",
* "SEND_MESSAGES",
* "SEND_TTS_MESSAGES",
* "MANAGE_MESSAGES",
* "EMBED_LINKS",
* "ATTACH_FILES",
* "READ_MESSAGE_HISTORY",
* "MENTION_EVERYONE",
* "EXTERNAL_EMOJIS", // use external emojis
* "CONNECT", // connect to voice
* "SPEAK", // speak on voice
* "MUTE_MEMBERS", // globally mute members on voice
* "DEAFEN_MEMBERS", // globally deafen members on voice
* "MOVE_MEMBERS", // move member's voice channels
* "USE_VAD", // use voice activity detection
* "CHANGE_NICKNAME",
* "MANAGE_NICKNAMES", // change nicknames of others
* "MANAGE_ROLES_OR_PERMISSIONS"
* ]
* ```
* @typedef {string|number} PermissionResolvable
*/
/**
* Resolves a PermissionResolvable to a permission number
* @param {PermissionResolvable} permission The permission resolvable to resolve
* @returns {number}
*/
resolvePermission(permission) {
if (typeof permission === 'string') permission = Constants.PermissionFlags[permission];
if (typeof permission !== 'number' || permission < 1) throw new Error(Constants.Errors.NOT_A_PERMISSION);
return permission;
}
/**
* Data that can be resolved to give a string. This can be:
* * A string
* * An Array (joined with a new line delimiter to give a string)
* * An array (joined with a new line delimiter to give a string)
* * Any value
* @typedef {string|Array|*} StringResolvable
*/
/**
* Resolves a StringResolvable to a string
* Resolves a StringResolvable to a string.
* @param {StringResolvable} data The string resolvable to resolve
* @returns {string}
*/
@@ -208,15 +196,29 @@ class ClientDataResolver {
return String(data);
}
/**
* Resolves a Base64Resolvable, a string, or a BufferResolvable to a Base 64 image.
* @param {BufferResolvable|Base64Resolvable} image The image to be resolved
* @returns {Promise<?string>}
*/
resolveImage(image) {
if (!image) return Promise.resolve(null);
if (typeof image === 'string' && image.startsWith('data:')) {
return Promise.resolve(image);
}
return this.resolveFile(image).then(this.resolveBase64);
}
/**
* Data that resolves to give a Base64 string, typically for image uploading. This can be:
* * A Buffer
* * A Base64 string
* * A base64 string
* @typedef {Buffer|string} Base64Resolvable
*/
/**
* Resolves a Base64Resolvable to a Base 64 image
* Resolves a Base64Resolvable to a Base 64 image.
* @param {Base64Resolvable} data The base 64 resolvable you want to resolve
* @returns {?string}
*/
@@ -226,40 +228,149 @@ class ClientDataResolver {
}
/**
* Data that can be resolved to give a Buffer. This can be:
* * A Buffer
* * The path to a local file
* * A URL
* @typedef {string|Buffer} FileResolvable
*/
* Data that can be resolved to give a Buffer. This can be:
* * A Buffer
* * The path to a local file
* * A URL
* * A Stream
* @typedef {string|Buffer} BufferResolvable
*/
/**
* Resolves a FileResolvable to a Buffer
* @param {FileResolvable} resource The file resolvable to resolve
* @returns {Promise<Buffer>}
*/
* @external Stream
* @see {@link https://nodejs.org/api/stream.html}
*/
/**
* Resolves a BufferResolvable to a Buffer.
* @param {BufferResolvable|Stream} resource The buffer or stream resolvable to resolve
* @returns {Promise<Buffer>}
*/
resolveFile(resource) {
if (resource instanceof Buffer) return Promise.resolve(resource);
if (this.client.browser && resource instanceof ArrayBuffer) return Promise.resolve(convertToBuffer(resource));
if (typeof resource === 'string') {
return new Promise((resolve, reject) => {
if (/^https?:\/\//.test(resource)) {
request.get(resource)
.set('Content-Type', 'blob')
.end((err, res) => err ? reject(err) : resolve(res.body));
snekfetch.get(resource)
.end((err, res) => {
if (err) return reject(err);
if (!(res.body instanceof Buffer)) return reject(new TypeError('The response body isn\'t a Buffer.'));
return resolve(res.body);
});
} else {
const file = path.resolve(resource);
fs.stat(file, (err, stats) => {
if (err) reject(err);
if (!stats || !stats.isFile()) throw new Error(`The file could not be found: ${file}`);
if (err) return reject(err);
if (!stats || !stats.isFile()) return reject(new Error(`The file could not be found: ${file}`));
fs.readFile(file, (err2, data) => {
if (err2) reject(err2); else resolve(data);
});
return null;
});
}
});
} else if (resource.pipe && typeof resource.pipe === 'function') {
return new Promise((resolve, reject) => {
const buffers = [];
resource.once('error', reject);
resource.on('data', data => buffers.push(data));
resource.once('end', () => resolve(Buffer.concat(buffers)));
});
}
if (resource instanceof Buffer) return Promise.resolve(resource);
return Promise.reject(new TypeError('Resource must be a string or Buffer.'));
return Promise.reject(new TypeError('The resource must be a string or Buffer.'));
}
/**
* Data that can be resolved to give an emoji identifier. This can be:
* * The unicode representation of an emoji
* * A custom emoji ID
* * An Emoji object
* * A ReactionEmoji object
* @typedef {string|Emoji|ReactionEmoji} EmojiIdentifierResolvable
*/
/**
* Resolves an EmojiResolvable to an emoji identifier.
* @param {EmojiIdentifierResolvable} emoji The emoji resolvable to resolve
* @returns {?string}
*/
resolveEmojiIdentifier(emoji) {
if (emoji instanceof Emoji || emoji instanceof ReactionEmoji) return emoji.identifier;
if (typeof emoji === 'string') {
if (this.client.emojis.has(emoji)) return this.client.emojis.get(emoji).identifier;
else if (!emoji.includes('%')) return encodeURIComponent(emoji);
else return emoji;
}
return null;
}
/**
* Can be a Hex Literal, Hex String, Number, RGB Array, or one of the following
* ```
* [
* 'DEFAULT',
* 'AQUA',
* 'GREEN',
* 'BLUE',
* 'PURPLE',
* 'GOLD',
* 'ORANGE',
* 'RED',
* 'GREY',
* 'DARKER_GREY',
* 'NAVY',
* 'DARK_AQUA',
* 'DARK_GREEN',
* 'DARK_BLUE',
* 'DARK_PURPLE',
* 'DARK_GOLD',
* 'DARK_ORANGE',
* 'DARK_RED',
* 'DARK_GREY',
* 'LIGHT_GREY',
* 'DARK_NAVY',
* 'RANDOM',
* ]
* ```
* or something like
* ```
* [255, 0, 255]
* ```
* for purple
* @typedef {string|number|Array} ColorResolvable
*/
/**
* Resolves a ColorResolvable into a color number.
* @param {ColorResolvable} color Color to resolve
* @returns {number} A color
*/
static resolveColor(color) {
if (typeof color === 'string') {
if (color === 'RANDOM') return Math.floor(Math.random() * (0xFFFFFF + 1));
color = Constants.Colors[color] || parseInt(color.replace('#', ''), 16);
} else if (color instanceof Array) {
color = (color[0] << 16) + (color[1] << 8) + color[2];
}
if (color < 0 || color > 0xFFFFFF) {
throw new RangeError('Color must be within the range 0 - 16777215 (0xFFFFFF).');
} else if (color && isNaN(color)) {
throw new TypeError('Unable to convert color to a number.');
}
return color;
}
/**
* @param {ColorResolvable} color Color to resolve
* @returns {number} A color
*/
resolveColor(color) {
return this.constructor.resolveColor(color);
}
}

View File

@@ -1,71 +1,72 @@
const Constants = require('../util/Constants');
const WebSocketConnection = require('./websocket/WebSocketConnection');
/**
* Manages the State and Background Tasks of the Client
* Manages the state and background tasks of the client.
* @private
*/
class ClientManager {
constructor(client) {
/**
* The Client that instantiated this Manager
* The client that instantiated this Manager
* @type {Client}
*/
this.client = client;
/**
* The heartbeat interval, null if not yet set
* The heartbeat interval
* @type {?number}
*/
this.heartbeatInterval = null;
}
/**
* Connects the Client to the WebSocket
* The status of the client
* @type {number}
*/
get status() {
return this.connection ? this.connection.status : Constants.Status.IDLE;
}
/**
* Connects the client to the WebSocket.
* @param {string} token The authorization token
* @param {function} resolve Function to run when connection is successful
* @param {function} reject Function to run when connection fails
* @param {Function} resolve Function to run when connection is successful
* @param {Function} reject Function to run when connection fails
*/
connectToWebSocket(token, resolve, reject) {
this.client.emit(Constants.Events.DEBUG, `Authenticated using token ${token}`);
this.client.token = token;
const timeout = this.client.setTimeout(() => reject(new Error(Constants.Errors.TOOK_TOO_LONG)), 1000 * 300);
this.client.rest.methods.getGateway().then(gateway => {
this.client.rest.methods.getGateway().then(res => {
const protocolVersion = Constants.DefaultOptions.ws.version;
const gateway = `${res.url}/?v=${protocolVersion}&encoding=${WebSocketConnection.ENCODING}`;
this.client.emit(Constants.Events.DEBUG, `Using gateway ${gateway}`);
this.client.ws.connect(gateway);
this.client.ws.once('close', event => {
this.client.ws.connection.once('close', event => {
if (event.code === 4004) reject(new Error(Constants.Errors.BAD_LOGIN));
if (event.code === 4010) reject(new Error(Constants.Errors.INVALID_SHARD));
if (event.code === 4011) reject(new Error(Constants.Errors.SHARDING_REQUIRED));
});
this.client.once(Constants.Events.READY, () => {
resolve(token);
this.client.clearTimeout(timeout);
});
}).catch(reject);
}
/**
* Sets up a keep-alive interval to keep the Client's connection valid
* @param {number} time The interval in milliseconds at which heartbeat packets should be sent
*/
setupKeepAlive(time) {
this.heartbeatInterval = this.client.setInterval(() => {
this.client.emit('debug', 'Sending heartbeat');
this.client.ws.send({
op: Constants.OPCodes.HEARTBEAT,
d: this.client.ws.sequence,
}, true);
}, time);
}, reject);
}
destroy() {
return new Promise((resolve, reject) => {
this.client.ws.destroy();
if (!this.client.user.bot) {
this.client.rest.methods.logout().then(resolve, reject);
} else {
resolve();
}
});
this.client.ws.destroy();
this.client.rest.destroy();
if (!this.client.user) return Promise.resolve();
if (this.client.user.bot) {
this.client.token = null;
return Promise.resolve();
} else {
return this.client.rest.methods.logout().then(() => {
this.client.token = null;
});
}
}
}

View File

@@ -1,22 +1,22 @@
const Webhook = require('../structures/Webhook');
const RESTManager = require('./rest/RESTManager');
const ClientDataResolver = require('./ClientDataResolver');
const mergeDefault = require('../util/MergeDefault');
const Constants = require('../util/Constants');
const Util = require('../util/Util');
/**
* The Webhook Client
* The webhook client.
* @extends {Webhook}
*/
class WebhookClient extends Webhook {
/**
* @param {string} id The id of the webhook.
* @param {string} token the token of the webhook.
* @param {Snowflake} id ID of the webhook
* @param {string} token Token of the webhook
* @param {ClientOptions} [options] Options for the client
* @example
* // create a new webhook and send a message
* let hook = new Discord.WebhookClient('1234', 'abcdef')
* hook.sendMessage('This will send a message').catch(console.error)
* // Create a new webhook and send a message
* const hook = new Discord.WebhookClient('1234', 'abcdef');
* hook.sendMessage('This will send a message').catch(console.error);
*/
constructor(id, token, options) {
super(null, id, token);
@@ -25,7 +25,7 @@ class WebhookClient extends Webhook {
* The options the client was instantiated with
* @type {ClientOptions}
*/
this.options = mergeDefault(Constants.DefaultOptions, options);
this.options = Util.mergeDefault(Constants.DefaultOptions, options);
/**
* The REST manager of the client
@@ -35,11 +35,83 @@ class WebhookClient extends Webhook {
this.rest = new RESTManager(this);
/**
* The Data Resolver of the Client
* The data resolver of the client
* @type {ClientDataResolver}
* @private
*/
this.resolver = new ClientDataResolver(this);
/**
* Timeouts set by {@link WebhookClient#setTimeout} that are still active
* @type {Set<Timeout>}
* @private
*/
this._timeouts = new Set();
/**
* Intervals set by {@link WebhookClient#setInterval} that are still active
* @type {Set<Timeout>}
* @private
*/
this._intervals = new Set();
}
/**
* Sets a timeout that will be automatically cancelled if the client is destroyed.
* @param {Function} fn Function to execute
* @param {number} delay Time to wait before executing (in milliseconds)
* @param {...*} args Arguments for the function
* @returns {Timeout}
*/
setTimeout(fn, delay, ...args) {
const timeout = setTimeout(() => {
fn(...args);
this._timeouts.delete(timeout);
}, delay);
this._timeouts.add(timeout);
return timeout;
}
/**
* Clears a timeout.
* @param {Timeout} timeout Timeout to cancel
*/
clearTimeout(timeout) {
clearTimeout(timeout);
this._timeouts.delete(timeout);
}
/**
* Sets an interval that will be automatically cancelled if the client is destroyed.
* @param {Function} fn Function to execute
* @param {number} delay Time to wait before executing (in milliseconds)
* @param {...*} args Arguments for the function
* @returns {Timeout}
*/
setInterval(fn, delay, ...args) {
const interval = setInterval(fn, delay, ...args);
this._intervals.add(interval);
return interval;
}
/**
* Clears an interval.
* @param {Timeout} interval Interval to cancel
*/
clearInterval(interval) {
clearInterval(interval);
this._intervals.delete(interval);
}
/**
* Destroys the client.
*/
destroy() {
for (const t of this._timeouts) clearTimeout(t);
for (const i of this._intervals) clearInterval(i);
this._timeouts.clear();
this._intervals.clear();
}
}

View File

@@ -2,33 +2,38 @@ class ActionsManager {
constructor(client) {
this.client = client;
this.register('MessageCreate');
this.register('MessageDelete');
this.register('MessageDeleteBulk');
this.register('MessageUpdate');
this.register('ChannelCreate');
this.register('ChannelDelete');
this.register('ChannelUpdate');
this.register('GuildDelete');
this.register('GuildUpdate');
this.register('GuildMemberGet');
this.register('GuildMemberRemove');
this.register('GuildBanRemove');
this.register('GuildRoleCreate');
this.register('GuildRoleDelete');
this.register('GuildRoleUpdate');
this.register('UserGet');
this.register('UserUpdate');
this.register('GuildSync');
this.register('GuildEmojiCreate');
this.register('GuildEmojiDelete');
this.register('GuildEmojiUpdate');
this.register('GuildRolesPositionUpdate');
this.register(require('./MessageCreate'));
this.register(require('./MessageDelete'));
this.register(require('./MessageDeleteBulk'));
this.register(require('./MessageUpdate'));
this.register(require('./MessageReactionAdd'));
this.register(require('./MessageReactionRemove'));
this.register(require('./MessageReactionRemoveAll'));
this.register(require('./ChannelCreate'));
this.register(require('./ChannelDelete'));
this.register(require('./ChannelUpdate'));
this.register(require('./GuildDelete'));
this.register(require('./GuildUpdate'));
this.register(require('./GuildMemberGet'));
this.register(require('./GuildMemberRemove'));
this.register(require('./GuildBanRemove'));
this.register(require('./GuildRoleCreate'));
this.register(require('./GuildRoleDelete'));
this.register(require('./GuildRoleUpdate'));
this.register(require('./UserGet'));
this.register(require('./UserUpdate'));
this.register(require('./UserNoteUpdate'));
this.register(require('./GuildSync'));
this.register(require('./GuildEmojiCreate'));
this.register(require('./GuildEmojiDelete'));
this.register(require('./GuildEmojiUpdate'));
this.register(require('./GuildEmojisUpdate'));
this.register(require('./GuildRolesPositionUpdate'));
this.register(require('./GuildChannelsPositionUpdate'));
}
register(name) {
const Action = require(`./${name}`);
this[name] = new Action(this.client);
register(Action) {
this[Action.name.replace(/Action$/, '')] = new Action(this.client);
}
}

View File

@@ -4,9 +4,7 @@ class ChannelCreateAction extends Action {
handle(data) {
const client = this.client;
const channel = client.dataManager.newChannel(data);
return {
channel,
};
return { channel };
}
}

View File

@@ -18,9 +18,7 @@ class ChannelDeleteAction extends Action {
channel = this.deleted.get(data.id) || null;
}
return {
channel,
};
return { channel };
}
scheduleForDeletion(id) {

View File

@@ -1,6 +1,6 @@
const Action = require('./Action');
const Constants = require('../../util/Constants');
const cloneObject = require('../../util/CloneObject');
const Util = require('../../util/Util');
class ChannelUpdateAction extends Action {
handle(data) {
@@ -8,7 +8,7 @@ class ChannelUpdateAction extends Action {
const channel = client.channels.get(data.id);
if (channel) {
const oldChannel = cloneObject(channel);
const oldChannel = Util.cloneObject(channel);
channel.setup(data);
client.emit(Constants.Events.CHANNEL_UPDATE, oldChannel, channel);
return {

View File

@@ -0,0 +1,19 @@
const Action = require('./Action');
class GuildChannelsPositionUpdate extends Action {
handle(data) {
const client = this.client;
const guild = client.guilds.get(data.guild_id);
if (guild) {
for (const partialChannel of data.channels) {
const channel = guild.channels.get(partialChannel.id);
if (channel) channel.position = partialChannel.position;
}
}
return { guild };
}
}
module.exports = GuildChannelsPositionUpdate;

View File

@@ -12,19 +12,23 @@ class GuildDeleteAction extends Action {
let guild = client.guilds.get(data.id);
if (guild) {
for (const channel of guild.channels.values()) {
if (channel.type === 'text') channel.stopTyping(true);
}
if (guild.available && data.unavailable) {
// guild is unavailable
// Guild is unavailable
guild.available = false;
client.emit(Constants.Events.GUILD_UNAVAILABLE, guild);
// stops the GuildDelete packet thinking a guild was actually deleted,
// Stops the GuildDelete packet thinking a guild was actually deleted,
// handles emitting of event itself
return {
guild: null,
};
}
// delete guild
// Delete guild
client.guilds.delete(guild.id);
this.deleted.set(guild.id, guild);
this.scheduleForDeletion(guild.id);
@@ -32,9 +36,7 @@ class GuildDeleteAction extends Action {
guild = this.deleted.get(data.id) || null;
}
return {
guild,
};
return { guild };
}
scheduleForDeletion(id) {
@@ -45,7 +47,7 @@ class GuildDeleteAction extends Action {
/**
* Emitted whenever a guild becomes unavailable, likely due to a server outage.
* @event Client#guildUnavailable
* @param {Guild} guild The guild that has become unavailable.
* @param {Guild} guild The guild that has become unavailable
*/
module.exports = GuildDeleteAction;

View File

@@ -1,18 +1,17 @@
const Action = require('./Action');
class EmojiCreateAction extends Action {
handle(data, guild) {
class GuildEmojiCreateAction extends Action {
handle(guild, createdEmoji) {
const client = this.client;
const emoji = client.dataManager.newEmoji(data, guild);
return {
emoji,
};
const emoji = client.dataManager.newEmoji(createdEmoji, guild);
return { emoji };
}
}
/**
* Emitted whenever an emoji is created
* @event Client#guildEmojiCreate
* @param {Emoji} emoji The emoji that was created.
* Emitted whenever a custom emoji is created in a guild.
* @event Client#emojiCreate
* @param {Emoji} emoji The emoji that was created
*/
module.exports = EmojiCreateAction;
module.exports = GuildEmojiCreateAction;

View File

@@ -1,18 +1,17 @@
const Action = require('./Action');
class EmojiDeleteAction extends Action {
handle(data) {
class GuildEmojiDeleteAction extends Action {
handle(emoji) {
const client = this.client;
client.dataManager.killEmoji(data);
return {
data,
};
client.dataManager.killEmoji(emoji);
return { emoji };
}
}
/**
* Emitted whenever an emoji is deleted
* @event Client#guildEmojiDelete
* @param {Emoji} emoji The emoji that was deleted.
* Emitted whenever a custom guild emoji is deleted.
* @event Client#emojiDelete
* @param {Emoji} emoji The emoji that was deleted
*/
module.exports = EmojiDeleteAction;
module.exports = GuildEmojiDeleteAction;

View File

@@ -1,29 +1,17 @@
const Action = require('./Action');
class GuildEmojiUpdateAction extends Action {
handle(data, guild) {
const client = this.client;
for (let emoji of data.emojis) {
const already = guild.emojis.has(emoji.id);
if (already) {
client.dataManager.updateEmoji(guild.emojis.get(emoji.id), emoji);
} else {
emoji = client.dataManager.newEmoji(emoji, guild);
}
}
for (let emoji of guild.emojis) {
if (!data.emoijs.has(emoji.id)) client.dataManager.killEmoji(emoji);
}
return {
emojis: data.emojis,
};
handle(oldEmoji, newEmoji) {
const emoji = this.client.dataManager.updateEmoji(oldEmoji, newEmoji);
return { emoji };
}
}
/**
* Emitted whenever an emoji is updated
* @event Client#guildEmojiUpdate
* Emitted whenever a custom guild emoji is updated.
* @event Client#emojiUpdate
* @param {Emoji} oldEmoji The old emoji
* @param {Emoji} newEmoji The new emoji
*/
module.exports = GuildEmojiUpdateAction;

View File

@@ -0,0 +1,38 @@
const Action = require('./Action');
function mappify(iterable) {
const map = new Map();
for (const x of iterable) map.set(...x);
return map;
}
class GuildEmojisUpdateAction extends Action {
handle(data) {
const guild = this.client.guilds.get(data.guild_id);
if (!guild || !guild.emojis) return;
const deletions = mappify(guild.emojis.entries());
for (const emoji of data.emojis) {
// Determine type of emoji event
const cachedEmoji = guild.emojis.get(emoji.id);
if (cachedEmoji) {
deletions.delete(emoji.id);
if (!cachedEmoji.equals(emoji, true)) {
// Emoji updated
this.client.actions.GuildEmojiUpdate.handle(cachedEmoji, emoji);
}
} else {
// Emoji added
this.client.actions.GuildEmojiCreate.handle(guild, emoji);
}
}
for (const emoji of deletions.values()) {
// Emoji deleted
this.client.actions.GuildEmojiDelete.handle(emoji);
}
}
}
module.exports = GuildEmojisUpdateAction;

View File

@@ -3,9 +3,7 @@ const Action = require('./Action');
class GuildMemberGetAction extends Action {
handle(guild, data) {
const member = guild._addMember(data, false);
return {
member,
};
return { member };
}
}

View File

@@ -9,10 +9,10 @@ class GuildMemberRemoveAction extends Action {
handle(data) {
const client = this.client;
const guild = client.guilds.get(data.guild_id);
let member = null;
if (guild) {
let member = guild.members.get(data.user.id);
member = guild.members.get(data.user.id);
if (member) {
guild.memberCount--;
guild._removeMember(member);
@@ -22,17 +22,8 @@ class GuildMemberRemoveAction extends Action {
} else {
member = this.deleted.get(guild.id + data.user.id) || null;
}
return {
guild,
member,
};
}
return {
guild,
member: null,
};
return { guild, member };
}
scheduleForDeletion(guildID, userID) {
@@ -43,7 +34,7 @@ class GuildMemberRemoveAction extends Action {
/**
* Emitted whenever a member leaves a guild, or is kicked.
* @event Client#guildMemberRemove
* @param {GuildMember} member The member that has left/been kicked from the guild.
* @param {GuildMember} member The member that has left/been kicked from the guild
*/
module.exports = GuildMemberRemoveAction;

View File

@@ -5,28 +5,22 @@ const Role = require('../../structures/Role');
class GuildRoleCreate extends Action {
handle(data) {
const client = this.client;
const guild = client.guilds.get(data.guild_id);
let role;
if (guild) {
const already = guild.roles.has(data.role.id);
const role = new Role(guild, data.role);
role = new Role(guild, data.role);
guild.roles.set(role.id, role);
if (!already) client.emit(Constants.Events.GUILD_ROLE_CREATE, role);
return {
role,
};
}
return {
role: null,
};
return { role };
}
}
/**
* Emitted whenever a role is created.
* @event Client#roleCreate
* @param {Role} role The role that was created.
* @param {Role} role The role that was created
*/
module.exports = GuildRoleCreate;

View File

@@ -9,10 +9,11 @@ class GuildRoleDeleteAction extends Action {
handle(data) {
const client = this.client;
const guild = client.guilds.get(data.guild_id);
let role;
if (guild) {
let role = guild.roles.get(data.role_id);
role = guild.roles.get(data.role_id);
if (role) {
guild.roles.delete(data.role_id);
this.deleted.set(guild.id + data.role_id, role);
@@ -21,15 +22,9 @@ class GuildRoleDeleteAction extends Action {
} else {
role = this.deleted.get(guild.id + data.role_id) || null;
}
return {
role,
};
}
return {
role: null,
};
return { role };
}
scheduleForDeletion(guildID, roleID) {
@@ -40,7 +35,7 @@ class GuildRoleDeleteAction extends Action {
/**
* Emitted whenever a guild role is deleted.
* @event Client#roleDelete
* @param {Role} role The role that was deleted.
* @param {Role} role The role that was deleted
*/
module.exports = GuildRoleDeleteAction;

View File

@@ -1,19 +1,19 @@
const Action = require('./Action');
const Constants = require('../../util/Constants');
const cloneObject = require('../../util/CloneObject');
const Util = require('../../util/Util');
class GuildRoleUpdateAction extends Action {
handle(data) {
const client = this.client;
const guild = client.guilds.get(data.guild_id);
if (guild) {
const roleData = data.role;
let oldRole = null;
const role = guild.roles.get(roleData.id);
if (role) {
oldRole = cloneObject(role);
oldRole = Util.cloneObject(role);
role.setup(data.role);
client.emit(Constants.Events.GUILD_ROLE_UPDATE, oldRole, role);
}
@@ -34,8 +34,8 @@ class GuildRoleUpdateAction extends Action {
/**
* Emitted whenever a guild role is updated.
* @event Client#roleUpdate
* @param {Role} oldRole The role before the update.
* @param {Role} newRole The role after the update.
* @param {Role} oldRole The role before the update
* @param {Role} newRole The role after the update
*/
module.exports = GuildRoleUpdateAction;

View File

@@ -1,23 +1,19 @@
const Action = require('./Action');
class GuildRolesPositionUpdate extends Action {
handle(data) {
const client = this.client;
const guild = client.guilds.get(data.guild_id);
if (guild) {
for (const partialRole of data.roles) {
const role = guild.roles.get(partialRole.id);
if (role) {
role.position = partialRole.position;
}
}
}
return {
guild,
};
}
}
module.exports = GuildRolesPositionUpdate;
const Action = require('./Action');
class GuildRolesPositionUpdate extends Action {
handle(data) {
const client = this.client;
const guild = client.guilds.get(data.guild_id);
if (guild) {
for (const partialRole of data.roles) {
const role = guild.roles.get(partialRole.id);
if (role) role.position = partialRole.position;
}
}
return { guild };
}
}
module.exports = GuildRolesPositionUpdate;

View File

@@ -6,20 +6,22 @@ class GuildSync extends Action {
const guild = client.guilds.get(data.id);
if (guild) {
data.presences = data.presences || [];
for (const presence of data.presences) {
guild._setPresence(presence.user.id, presence);
if (data.presences) {
for (const presence of data.presences) guild._setPresence(presence.user.id, presence);
}
data.members = data.members || [];
for (const syncMember of data.members) {
const member = guild.members.get(syncMember.user.id);
if (member) {
guild._updateMember(member, syncMember);
} else {
guild._addMember(syncMember);
if (data.members) {
for (const syncMember of data.members) {
const member = guild.members.get(syncMember.user.id);
if (member) {
guild._updateMember(member, syncMember);
} else {
guild._addMember(syncMember, false);
}
}
}
if ('large' in data) guild.large = data.large;
}
}
}

View File

@@ -1,6 +1,6 @@
const Action = require('./Action');
const Constants = require('../../util/Constants');
const cloneObject = require('../../util/CloneObject');
const Util = require('../../util/Util');
class GuildUpdateAction extends Action {
handle(data) {
@@ -8,7 +8,7 @@ class GuildUpdateAction extends Action {
const guild = client.guilds.get(data.id);
if (guild) {
const oldGuild = cloneObject(guild);
const oldGuild = Util.cloneObject(guild);
guild.setup(data);
client.emit(Constants.Events.GUILD_UPDATE, oldGuild, guild);
return {
@@ -27,8 +27,8 @@ class GuildUpdateAction extends Action {
/**
* Emitted whenever a guild is updated - e.g. name change.
* @event Client#guildUpdate
* @param {Guild} oldGuild The guild before the update.
* @param {Guild} newGuild The guild after the update.
* @param {Guild} oldGuild The guild before the update
* @param {Guild} newGuild The guild after the update
*/
module.exports = GuildUpdateAction;

View File

@@ -6,17 +6,40 @@ class MessageCreateAction extends Action {
const client = this.client;
const channel = client.channels.get((data instanceof Array ? data[0] : data).channel_id);
const user = client.users.get((data instanceof Array ? data[0] : data).author.id);
if (channel) {
const member = channel.guild ? channel.guild.member(user) : null;
if (data instanceof Array) {
const messages = new Array(data.length);
for (let i = 0; i < data.length; i++) {
messages[i] = channel._cacheMessage(new Message(channel, data[i], client));
}
const lastMessage = messages[messages.length - 1];
channel.lastMessageID = lastMessage.id;
channel.lastMessage = lastMessage;
if (user) {
user.lastMessageID = lastMessage.id;
user.lastMessage = lastMessage;
}
if (member) {
member.lastMessageID = lastMessage.id;
member.lastMessage = lastMessage;
}
return {
messages,
};
} else {
const message = channel._cacheMessage(new Message(channel, data, client));
channel.lastMessageID = data.id;
channel.lastMessage = message;
if (user) {
user.lastMessageID = data.id;
user.lastMessage = message;
}
if (member) {
member.lastMessageID = data.id;
member.lastMessage = message;
}
return {
message,
};

View File

@@ -8,11 +8,11 @@ class MessageDeleteAction extends Action {
handle(data) {
const client = this.client;
const channel = client.channels.get(data.channel_id);
if (channel) {
let message = channel.messages.get(data.id);
let message;
if (channel) {
message = channel.messages.get(data.id);
if (message) {
channel.messages.delete(message.id);
this.deleted.set(channel.id + message.id, message);
@@ -20,15 +20,9 @@ class MessageDeleteAction extends Action {
} else {
message = this.deleted.get(channel.id + data.id) || null;
}
return {
message,
};
}
return {
message: null,
};
return { message };
}
scheduleForDeletion(channelID, messageID) {

View File

@@ -15,9 +15,7 @@ class MessageDeleteBulkAction extends Action {
}
if (messages.size > 0) client.emit(Constants.Events.MESSAGE_BULK_DELETE, messages);
return {
messages,
};
return { messages };
}
}

View File

@@ -0,0 +1,37 @@
const Action = require('./Action');
const Constants = require('../../util/Constants');
/*
{ user_id: 'id',
message_id: 'id',
emoji: { name: '<27>', id: null },
channel_id: 'id' } }
*/
class MessageReactionAdd extends Action {
handle(data) {
const user = this.client.users.get(data.user_id);
if (!user) return false;
// Verify channel
const channel = this.client.channels.get(data.channel_id);
if (!channel || channel.type === 'voice') return false;
// Verify message
const message = channel.messages.get(data.message_id);
if (!message) return false;
if (!data.emoji) return false;
// Verify reaction
const reaction = message._addReaction(data.emoji, user);
if (reaction) this.client.emit(Constants.Events.MESSAGE_REACTION_ADD, reaction, user);
return { message, reaction, user };
}
}
/**
* Emitted whenever a reaction is added to a message.
* @event Client#messageReactionAdd
* @param {MessageReaction} messageReaction The reaction object
* @param {User} user The user that applied the emoji or reaction emoji
*/
module.exports = MessageReactionAdd;

View File

@@ -0,0 +1,37 @@
const Action = require('./Action');
const Constants = require('../../util/Constants');
/*
{ user_id: 'id',
message_id: 'id',
emoji: { name: '<27>', id: null },
channel_id: 'id' } }
*/
class MessageReactionRemove extends Action {
handle(data) {
const user = this.client.users.get(data.user_id);
if (!user) return false;
// Verify channel
const channel = this.client.channels.get(data.channel_id);
if (!channel || channel.type === 'voice') return false;
// Verify message
const message = channel.messages.get(data.message_id);
if (!message) return false;
if (!data.emoji) return false;
// Verify reaction
const reaction = message._removeReaction(data.emoji, user);
if (reaction) this.client.emit(Constants.Events.MESSAGE_REACTION_REMOVE, reaction, user);
return { message, reaction, user };
}
}
/**
* Emitted whenever a reaction is removed from a message.
* @event Client#messageReactionRemove
* @param {MessageReaction} messageReaction The reaction object
* @param {User} user The user that removed the emoji or reaction emoji
*/
module.exports = MessageReactionRemove;

View File

@@ -0,0 +1,25 @@
const Action = require('./Action');
const Constants = require('../../util/Constants');
class MessageReactionRemoveAll extends Action {
handle(data) {
const channel = this.client.channels.get(data.channel_id);
if (!channel || channel.type === 'voice') return false;
const message = channel.messages.get(data.message_id);
if (!message) return false;
message._clearReactions();
this.client.emit(Constants.Events.MESSAGE_REACTION_REMOVE_ALL, message);
return { message };
}
}
/**
* Emitted whenever all reactions are removed from a message.
* @event Client#messageReactionRemoveAll
* @param {Message} message The message the reactions were removed from
*/
module.exports = MessageReactionRemoveAll;

View File

@@ -1,6 +1,5 @@
const Action = require('./Action');
const Constants = require('../../util/Constants');
const cloneObject = require('../../util/CloneObject');
class MessageUpdateAction extends Action {
handle(data) {
@@ -10,12 +9,10 @@ class MessageUpdateAction extends Action {
if (channel) {
const message = channel.messages.get(data.id);
if (message) {
const oldMessage = cloneObject(message);
message.patch(data);
message._edits.unshift(oldMessage);
client.emit(Constants.Events.MESSAGE_UPDATE, oldMessage, message);
client.emit(Constants.Events.MESSAGE_UPDATE, message._edits[0], message);
return {
old: oldMessage,
old: message._edits[0],
updated: message,
};
}
@@ -36,8 +33,8 @@ class MessageUpdateAction extends Action {
/**
* Emitted whenever a message is updated - e.g. embed or content change.
* @event Client#messageUpdate
* @param {Message} oldMessage The message before the update.
* @param {Message} newMessage The message after the update.
* @param {Message} oldMessage The message before the update
* @param {Message} newMessage The message after the update
*/
module.exports = MessageUpdateAction;

View File

@@ -4,9 +4,7 @@ class UserGetAction extends Action {
handle(data) {
const client = this.client;
const user = client.dataManager.newUser(data);
return {
user,
};
return { user };
}
}

View File

@@ -0,0 +1,30 @@
const Action = require('./Action');
const Constants = require('../../util/Constants');
class UserNoteUpdateAction extends Action {
handle(data) {
const client = this.client;
const oldNote = client.user.notes.get(data.id);
const note = data.note.length ? data.note : null;
client.user.notes.set(data.id, note);
client.emit(Constants.Events.USER_NOTE_UPDATE, data.id, oldNote, note);
return {
old: oldNote,
updated: note,
};
}
}
/**
* Emitted whenever a note is updated.
* @event Client#userNoteUpdate
* @param {User} user The user the note belongs to
* @param {string} oldNote The note content before the update
* @param {string} newNote The note content after the update
*/
module.exports = UserNoteUpdateAction;

View File

@@ -1,6 +1,6 @@
const Action = require('./Action');
const Constants = require('../../util/Constants');
const cloneObject = require('../../util/CloneObject');
const Util = require('../../util/Util');
class UserUpdateAction extends Action {
handle(data) {
@@ -14,7 +14,7 @@ class UserUpdateAction extends Action {
};
}
const oldUser = cloneObject(client.user);
const oldUser = Util.cloneObject(client.user);
client.user.patch(data);
client.emit(Constants.Events.USER_UPDATE, oldUser, client.user);
return {

View File

@@ -1,52 +1,51 @@
const request = require('superagent');
const snekfetch = require('snekfetch');
const Constants = require('../../util/Constants');
function getRoute(url) {
let route = url.split('?')[0];
if (route.includes('/channels/') || route.includes('/guilds/')) {
const startInd = ~route.indexOf('/channels/') ? route.indexOf('/channels/') : route.indexOf('/guilds/');
const majorID = route.substring(startInd).split('/')[2];
route = route.replace(/(\d{8,})/g, ':id').replace(':id', majorID);
}
return route;
}
class APIRequest {
constructor(rest, method, url, auth, data, file) {
constructor(rest, method, path, auth, data, files, reason) {
this.rest = rest;
this.client = rest.client;
this.method = method;
this.url = url;
this.path = path.toString();
this.auth = auth;
this.data = data;
this.file = file;
this.route = getRoute(this.url);
this.files = files;
this.route = this.getRoute(this.path);
this.reason = reason;
}
getRoute(url) {
let route = url.split('?')[0];
if (route.includes('/channels/') || route.includes('/guilds/')) {
const startInd = route.includes('/channels/') ? route.indexOf('/channels/') : route.indexOf('/guilds/');
const majorID = route.substring(startInd).split('/')[2];
route = route.replace(/(\d{8,})/g, ':id').replace(':id', majorID);
}
return route;
}
getAuth() {
if (this.rest.client.token && this.rest.client.user && this.rest.client.user.bot) {
return `Bot ${this.rest.client.token}`;
} else if (this.rest.client.token) {
return this.rest.client.token;
if (this.client.token && this.client.user && this.client.user.bot) {
return `Bot ${this.client.token}`;
} else if (this.client.token) {
return this.client.token;
}
throw new Error(Constants.Errors.NO_TOKEN);
}
gen() {
const apiRequest = request[this.method](this.url);
if (this.auth) apiRequest.set('authorization', this.getAuth());
if (this.file && this.file.file) {
apiRequest.attach('file', this.file.file, this.file.name);
this.data = this.data || {};
for (const key in this.data) {
if (this.data[key]) {
apiRequest.field(key, this.data[key]);
}
}
const API = `${this.client.options.http.host}/api/v${this.client.options.http.version}`;
const request = snekfetch[this.method](`${API}${this.path}`);
if (this.auth) request.set('Authorization', this.getAuth());
if (this.reason) request.set('X-Audit-Log-Reason', encodeURIComponent(this.reason));
if (!this.rest.client.browser) request.set('User-Agent', this.rest.userAgentManager.userAgent);
if (this.files) {
for (const file of this.files) if (file && file.file) request.attach(file.name, file.file, file.name);
if (typeof this.data !== 'undefined') request.attach('payload_json', JSON.stringify(this.data));
} else if (this.data) {
apiRequest.send(this.data);
request.send(this.data);
}
apiRequest.set('User-Agent', this.rest.userAgentManager.userAgent);
return apiRequest;
return request;
}
}

View File

@@ -0,0 +1,54 @@
/**
* Represents an error from the Discord API.
* @extends Error
*/
class DiscordAPIError extends Error {
constructor(path, error) {
super();
const flattened = this.constructor.flattenErrors(error.errors || error).join('\n');
this.name = 'DiscordAPIError';
this.message = error.message && flattened ? `${error.message}\n${flattened}` : error.message || flattened;
/**
* The path of the request relative to the HTTP endpoint
* @type {string}
*/
this.path = path;
/**
* HTTP error code returned by Discord
* @type {number}
*/
this.code = error.code;
}
/**
* Flattens an errors object returned from the API into an array.
* @param {Object} obj Discord errors object
* @param {string} [key] Used internally to determine key names of nested fields
* @returns {string[]}
* @private
*/
static flattenErrors(obj, key = '') {
let messages = [];
for (const k of Object.keys(obj)) {
if (k === 'message') continue;
const newKey = key ? isNaN(k) ? `${key}.${k}` : `${key}[${k}]` : k;
if (obj[k]._errors) {
messages.push(`${newKey}: ${obj[k]._errors.map(e => e.message).join(' ')}`);
} else if (obj[k].code || obj[k].message) {
messages.push(`${obj[k].code ? `${obj[k].code}: ` : ''}: ${obj[k].message}`.trim());
} else if (typeof obj[k] === 'string') {
messages.push(obj[k]);
} else {
messages = messages.concat(this.flattenErrors(obj[k], newKey));
}
}
return messages;
}
}
module.exports = DiscordAPIError;

View File

@@ -15,6 +15,12 @@ class RESTManager {
this.globallyRateLimited = false;
}
destroy() {
for (const handlerID in this.handlers) {
this.handlers[handlerID].destroy();
}
}
push(handler, apiRequest) {
return new Promise((resolve, reject) => {
handler.push({
@@ -36,9 +42,8 @@ class RESTManager {
}
}
makeRequest(method, url, auth, data, file) {
const apiRequest = new APIRequest(this, method, url, auth, data, file);
makeRequest(method, url, auth, data, file, reason) {
const apiRequest = new APIRequest(this, method, url, auth, data, file, reason);
if (!this.handlers[apiRequest.route]) {
const RequestHandlerType = this.getRequestHandler();
this.handlers[apiRequest.route] = new RequestHandlerType(this, apiRequest.route);

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,18 @@
const RequestHandler = require('./RequestHandler');
const DiscordAPIError = require('../DiscordAPIError');
class BurstRequestHandler extends RequestHandler {
constructor(restManager, endpoint) {
super(restManager, endpoint);
this.requestRemaining = 1;
this.first = true;
this.client = restManager.client;
this.limit = Infinity;
this.resetTime = null;
this.remaining = 1;
this.timeDifference = 0;
this.resetTimeout = null;
}
push(request) {
@@ -12,58 +20,51 @@ class BurstRequestHandler extends RequestHandler {
this.handle();
}
handleNext(time) {
if (this.waiting) return;
this.waiting = true;
this.restManager.client.setTimeout(() => {
this.requestRemaining = this.requestLimit;
this.waiting = false;
this.handle();
}, time);
}
execute(item) {
if (!item) return;
item.request.gen().end((err, res) => {
if (res && res.headers) {
this.requestLimit = res.headers['x-ratelimit-limit'];
this.requestResetTime = Number(res.headers['x-ratelimit-reset']) * 1000;
this.requestRemaining = Number(res.headers['x-ratelimit-remaining']);
this.limit = Number(res.headers['x-ratelimit-limit']);
this.resetTime = Number(res.headers['x-ratelimit-reset']) * 1000;
this.remaining = Number(res.headers['x-ratelimit-remaining']);
this.timeDifference = Date.now() - new Date(res.headers.date).getTime();
this.handleNext((this.requestResetTime - Date.now()) + this.timeDifference + 1000);
}
if (err) {
if (err.status === 429) {
this.requestRemaining = 0;
this.queue.unshift(item);
this.restManager.client.setTimeout(() => {
if (res.headers['x-ratelimit-global']) this.globalLimit = true;
if (this.resetTimeout) return;
this.resetTimeout = this.client.setTimeout(() => {
this.remaining = this.limit;
this.globalLimit = false;
this.handle();
}, Number(res.headers['retry-after']) + 500);
if (res.headers['x-ratelimit-global']) {
this.globalLimit = true;
}
this.resetTimeout = null;
}, Number(res.headers['retry-after']) + this.client.options.restTimeOffset);
} else if (err.status >= 500 && err.status < 600) {
this.queue.unshift(item);
this.resetTimeout = this.client.setTimeout(() => {
this.handle();
this.resetTimeout = null;
}, 1e3 + this.client.options.restTimeOffset);
} else {
item.reject(err);
item.reject(err.status >= 400 && err.status < 500 ? new DiscordAPIError(res.request.path, res.body) : err);
this.handle();
}
} else {
this.globalLimit = false;
const data = res && res.body ? res.body : {};
item.resolve(data);
if (this.first) {
this.first = false;
this.handle();
}
this.handle();
}
});
}
handle() {
super.handle();
if (this.requestRemaining < 1 || this.queue.length === 0 || this.globalLimit) return;
while (this.queue.length > 0 && this.requestRemaining > 0) {
this.execute(this.queue.shift());
this.requestRemaining--;
}
if (this.remaining <= 0 || this.queue.length === 0 || this.globalLimit) return;
this.execute(this.queue.shift());
this.remaining--;
this.handle();
}
}

View File

@@ -14,15 +14,16 @@ class RequestHandler {
this.restManager = restManager;
/**
* A list of requests that have yet to be processed.
* A list of requests that have yet to be processed
* @type {APIRequest[]}
*/
this.queue = [];
}
/**
* Whether or not the client is being rate limited on every endpoint.
* Whether or not the client is being rate limited on every endpoint
* @type {boolean}
* @readonly
*/
get globalLimit() {
return this.restManager.globallyRateLimited;
@@ -33,7 +34,7 @@ class RequestHandler {
}
/**
* Push a new API request into this bucket
* Push a new API request into this bucket.
* @param {APIRequest} request The new request to push into the queue
*/
push(request) {
@@ -41,10 +42,12 @@ class RequestHandler {
}
/**
* Attempts to get this RequestHandler to process its current queue
* Attempts to get this RequestHandler to process its current queue.
*/
handle() {
return;
handle() {} // eslint-disable-line no-empty-function
destroy() {
this.queue = [];
}
}

View File

@@ -1,4 +1,5 @@
const RequestHandler = require('./RequestHandler');
const DiscordAPIError = require('../DiscordAPIError');
/**
* Handles API Requests sequentially, i.e. we wait until the current request is finished before moving onto
@@ -15,12 +16,6 @@ class SequentialRequestHandler extends RequestHandler {
constructor(restManager, endpoint) {
super(restManager, endpoint);
/**
* Whether this rate limiter is waiting for a response from a request
* @type {boolean}
*/
this.waiting = false;
/**
* The endpoint that this handler is handling
* @type {string}
@@ -29,10 +24,16 @@ class SequentialRequestHandler extends RequestHandler {
/**
* The time difference between Discord's Dates and the local computer's Dates. A positive number means the local
* computer's time is ahead of Discord's.
* computer's time is ahead of Discord's
* @type {number}
*/
this.timeDifference = 0;
/**
* Whether the queue is being processed or not
* @type {boolean}
*/
this.busy = false;
}
push(request) {
@@ -41,47 +42,45 @@ class SequentialRequestHandler extends RequestHandler {
}
/**
* Performs a request then resolves a promise to indicate its readiness for a new request
* Performs a request then resolves a promise to indicate its readiness for a new request.
* @param {APIRequest} item The item to execute
* @returns {Promise<?Object|Error>}
*/
execute(item) {
this.busy = true;
return new Promise(resolve => {
item.request.gen().end((err, res) => {
if (res && res.headers) {
this.requestLimit = res.headers['x-ratelimit-limit'];
this.requestLimit = Number(res.headers['x-ratelimit-limit']);
this.requestResetTime = Number(res.headers['x-ratelimit-reset']) * 1000;
this.requestRemaining = Number(res.headers['x-ratelimit-remaining']);
this.timeDifference = Date.now() - new Date(res.headers.date).getTime();
}
if (err) {
if (err.status === 429) {
this.queue.unshift(item);
this.restManager.client.setTimeout(() => {
this.waiting = false;
this.globalLimit = false;
resolve();
}, Number(res.headers['retry-after']) + 500);
if (res.headers['x-ratelimit-global']) {
this.globalLimit = true;
}
}, Number(res.headers['retry-after']) + this.restManager.client.options.restTimeOffset);
if (res.headers['x-ratelimit-global']) this.globalLimit = true;
} else if (err.status >= 500 && err.status < 600) {
this.queue.unshift(item);
this.restManager.client.setTimeout(resolve, 1e3 + this.restManager.client.options.restTimeOffset);
} else {
this.queue.shift();
this.waiting = false;
item.reject(err);
item.reject(err.status >= 400 && err.status < 500 ? new DiscordAPIError(res.request.path, res.body) : err);
resolve(err);
}
} else {
this.queue.shift();
this.globalLimit = false;
const data = res && res.body ? res.body : {};
item.resolve(data);
if (this.requestRemaining === 0) {
this.restManager.client.setTimeout(() => {
this.waiting = false;
resolve(data);
}, (this.requestResetTime - Date.now()) + this.timeDifference + 1000);
this.restManager.client.setTimeout(
() => resolve(data),
this.requestResetTime - Date.now() + this.timeDifference + this.restManager.client.options.restTimeOffset
);
} else {
this.waiting = false;
resolve(data);
}
}
@@ -91,12 +90,11 @@ class SequentialRequestHandler extends RequestHandler {
handle() {
super.handle();
if (this.waiting || this.queue.length === 0 || this.globalLimit) return;
this.waiting = true;
const item = this.queue[0];
this.execute(item).then(() => this.handle());
if (this.busy || this.remaining === 0 || this.queue.length === 0 || this.globalLimit) return;
this.execute(this.queue.shift()).then(() => {
this.busy = false;
this.handle();
});
}
}

View File

@@ -1,22 +1,25 @@
const Constants = require('../../util/Constants');
class UserAgentManager {
constructor(restManager) {
this.restManager = restManager;
this._userAgent = {
url: 'https://github.com/hydrabolt/discord.js',
version: Constants.Package.version,
};
constructor() {
this.build(this.constructor.DEFAULT);
}
set(info) {
this._userAgent.url = info.url || 'https://github.com/hydrabolt/discord.js';
this._userAgent.version = info.version || Constants.Package.version;
set({ url, version } = {}) {
this.build({
url: url || this.constructor.DFEAULT.url,
version: version || this.constructor.DEFAULT.version,
});
}
get userAgent() {
return `DiscordBot (${this._userAgent.url}, ${this._userAgent.version})`;
build(ua) {
this.userAgent = `DiscordBot (${ua.url}, ${ua.version}) Node.js/${process.version}`;
}
}
UserAgentManager.DEFAULT = {
url: Constants.Package.homepage.split('#')[0],
version: Constants.Package.version,
};
module.exports = UserAgentManager;

View File

@@ -1,11 +1,8 @@
const Collection = require('../../util/Collection');
const mergeDefault = require('../../util/MergeDefault');
const Constants = require('../../util/Constants');
const VoiceConnection = require('./VoiceConnection');
const EventEmitter = require('events').EventEmitter;
/**
* Manages all the voice stuff for the Client
* Manages all the voice stuff for the client.
* @private
*/
class ClientVoiceManager {
@@ -18,231 +15,67 @@ class ClientVoiceManager {
/**
* A collection mapping connection IDs to the Connection objects
* @type {Collection<string, VoiceConnection>}
* @type {Collection<Snowflake, VoiceConnection>}
*/
this.connections = new Collection();
/**
* Pending connection attempts, maps Guild ID to VoiceChannel
* @type {Collection<string, VoiceChannel>}
*/
this.pending = new Collection();
this.client.on('self.voiceServer', this.onVoiceServer.bind(this));
this.client.on('self.voiceStateUpdate', this.onVoiceStateUpdate.bind(this));
}
onVoiceServer(data) {
if (this.pending.has(data.guild_id)) this.pending.get(data.guild_id).setTokenAndEndpoint(data.token, data.endpoint);
onVoiceServer({ guild_id, token, endpoint }) {
const connection = this.connections.get(guild_id);
if (connection) connection.setTokenAndEndpoint(token, endpoint);
}
onVoiceStateUpdate(data) {
if (this.pending.has(data.guild_id)) this.pending.get(data.guild_id).setSessionID(data.session_id);
onVoiceStateUpdate({ guild_id, session_id, channel_id }) {
const connection = this.connections.get(guild_id);
if (connection) {
connection.channel = this.client.channels.get(channel_id);
connection.setSessionID(session_id);
}
}
/**
* Sends a request to the main gateway to join a voice channel
* @param {VoiceChannel} channel The channel to join
* @param {Object} [options] The options to provide
*/
sendVoiceStateUpdate(channel, options = {}) {
if (!this.client.user) throw new Error('Unable to join because there is no client user.');
if (!channel.permissionsFor) {
throw new Error('Channel does not support permissionsFor; is it really a voice channel?');
}
const permissions = channel.permissionsFor(this.client.user);
if (!permissions) {
throw new Error('There is no permission set for the client user in this channel - are they part of the guild?');
}
if (!permissions.hasPermission('CONNECT')) {
throw new Error('You do not have permission to connect to this voice channel.');
}
options = mergeDefault({
guild_id: channel.guild.id,
channel_id: channel.id,
self_mute: false,
self_deaf: false,
}, options);
this.client.ws.send({
op: Constants.OPCodes.VOICE_STATE_UPDATE,
d: options,
});
}
/**
* Sets up a request to join a voice channel
* Sets up a request to join a voice channel.
* @param {VoiceChannel} channel The voice channel to join
* @returns {Promise<VoiceConnection>}
*/
joinChannel(channel) {
return new Promise((resolve, reject) => {
if (this.pending.get(channel.guild.id)) throw new Error('Already connecting to this guild\'s voice server.');
if (!channel.joinable) {
throw new Error('You do not have permission to join this voice channel');
}
const existingConnection = this.connections.get(channel.guild.id);
if (existingConnection) {
if (existingConnection.channel.id !== channel.id) {
this.sendVoiceStateUpdate(channel);
this.connections.get(channel.guild.id).channel = channel;
if (channel.full) {
throw new Error('You do not have permission to join this voice channel; it is full.');
} else {
throw new Error('You do not have permission to join this voice channel.');
}
resolve(existingConnection);
return;
}
const pendingConnection = new PendingVoiceConnection(this, channel);
this.pending.set(channel.guild.id, pendingConnection);
let connection = this.connections.get(channel.guild.id);
pendingConnection.on('fail', reason => {
this.pending.delete(channel.guild.id);
if (connection) {
if (connection.channel.id !== channel.id) {
this.connections.get(channel.guild.id).updateChannel(channel);
}
resolve(connection);
return;
} else {
connection = new VoiceConnection(this, channel);
this.connections.set(channel.guild.id, connection);
}
connection.once('failed', reason => {
this.connections.delete(channel.guild.id);
reject(reason);
});
pendingConnection.on('pass', voiceConnection => {
this.pending.delete(channel.guild.id);
this.connections.set(channel.guild.id, voiceConnection);
voiceConnection.once('ready', () => resolve(voiceConnection));
voiceConnection.once('error', reject);
voiceConnection.once('disconnect', () => this.connections.delete(channel.guild.id));
connection.once('authenticated', () => {
connection.once('ready', () => resolve(connection));
connection.once('error', reject);
connection.once('disconnect', () => this.connections.delete(channel.guild.id));
});
});
}
}
/**
* Represents a Pending Voice Connection
* @private
*/
class PendingVoiceConnection extends EventEmitter {
constructor(voiceManager, channel) {
super();
/**
* The ClientVoiceManager that instantiated this pending connection
* @type {ClientVoiceManager}
*/
this.voiceManager = voiceManager;
/**
* The channel that this pending voice connection will attempt to join
* @type {VoiceChannel}
*/
this.channel = channel;
/**
* The timeout that will be invoked after 15 seconds signifying a failure to connect
* @type {Timeout}
*/
this.deathTimer = this.voiceManager.client.setTimeout(
() => this.fail(new Error('Connection not established within 15 seconds.')), 15000);
/**
* An object containing data required to connect to the voice servers with
* @type {object}
*/
this.data = {};
this.sendVoiceStateUpdate();
}
checkReady() {
if (this.data.token && this.data.endpoint && this.data.session_id) {
this.pass();
return true;
} else {
return false;
}
}
/**
* Set the token and endpoint required to connect to the the voice servers
* @param {string} token the token
* @param {string} endpoint the endpoint
* @returns {void}
*/
setTokenAndEndpoint(token, endpoint) {
if (!token) {
this.fail(new Error('Token not provided from voice server packet.'));
return;
}
if (!endpoint) {
this.fail(new Error('Endpoint not provided from voice server packet.'));
return;
}
if (this.data.token) {
this.fail(new Error('There is already a registered token for this connection.'));
return;
}
if (this.data.endpoint) {
this.fail(new Error('There is already a registered endpoint for this connection.'));
return;
}
endpoint = endpoint.match(/([^:]*)/)[0];
if (!endpoint) {
this.fail(new Error('Failed to find an endpoint.'));
return;
}
this.data.token = token;
this.data.endpoint = endpoint;
this.checkReady();
}
/**
* Sets the Session ID for the connection
* @param {string} sessionID the session ID
*/
setSessionID(sessionID) {
if (!sessionID) {
this.fail(new Error('Session ID not supplied.'));
return;
}
if (this.data.session_id) {
this.fail(new Error('There is already a registered session ID for this connection.'));
return;
}
this.data.session_id = sessionID;
this.checkReady();
}
clean() {
clearInterval(this.deathTimer);
this.emit('fail', new Error('Clean-up triggered :fourTriggered:'));
}
pass() {
clearInterval(this.deathTimer);
this.emit('pass', this.upgrade());
}
fail(reason) {
this.emit('fail', reason);
this.clean();
}
sendVoiceStateUpdate() {
try {
this.voiceManager.sendVoiceStateUpdate(this.channel);
} catch (error) {
this.fail(error);
}
}
/**
* Upgrades this Pending Connection to a full Voice Connection
* @returns {VoiceConnection}
*/
upgrade() {
return new VoiceConnection(this);
}
}
module.exports = ClientVoiceManager;

View File

@@ -0,0 +1,366 @@
const VolumeInterface = require('./util/VolumeInterface');
const Prism = require('prism-media');
const OpusEncoders = require('./opus/OpusEngineList');
const Collection = require('../../util/Collection');
const ffmpegArguments = [
'-analyzeduration', '0',
'-loglevel', '0',
'-f', 's16le',
'-ar', '48000',
'-ac', '2',
];
/**
* A voice broadcast can be played across multiple voice connections for improved shared-stream efficiency.
*
* Example usage:
* ```js
* const broadcast = client.createVoiceBroadcast();
* broadcast.playFile('./music.mp3');
* // Play "music.mp3" in all voice connections that the client is in
* for (const connection of client.voiceConnections.values()) {
* connection.playBroadcast(broadcast);
* }
* ```
* @implements {VolumeInterface}
*/
class VoiceBroadcast extends VolumeInterface {
constructor(client) {
super();
/**
* The client that created the broadcast
* @type {Client}
*/
this.client = client;
this._dispatchers = new Collection();
this._encoders = new Collection();
/**
* The audio transcoder that this broadcast uses
* @type {Prism}
*/
this.prism = new Prism();
/**
* The current audio transcoder that is being used
* @type {Object}
*/
this.currentTranscoder = null;
this.tickInterval = null;
this._volume = 1;
}
/**
* An array of subscribed dispatchers
* @type {StreamDispatcher[]}
* @readonly
*/
get dispatchers() {
let d = [];
for (const container of this._dispatchers.values()) {
d = d.concat(Array.from(container.values()));
}
return d;
}
get _playableStream() {
const currentTranscoder = this.currentTranscoder;
if (!currentTranscoder) return null;
const transcoder = currentTranscoder.transcoder;
const options = currentTranscoder.options;
return (transcoder && transcoder.output) || options.stream;
}
unregisterDispatcher(dispatcher, old) {
const volume = old || dispatcher.volume;
/**
* Emitted whenever a stream dispatcher unsubscribes from the broadcast.
* @event VoiceBroadcast#unsubscribe
* @param {StreamDispatcher} dispatcher The unsubscribed dispatcher
*/
this.emit('unsubscribe', dispatcher);
for (const container of this._dispatchers.values()) {
container.delete(dispatcher);
if (!container.size) {
this._encoders.get(volume).destroy();
this._dispatchers.delete(volume);
this._encoders.delete(volume);
}
}
}
registerDispatcher(dispatcher) {
if (!this._dispatchers.has(dispatcher.volume)) {
this._dispatchers.set(dispatcher.volume, new Set());
this._encoders.set(dispatcher.volume, OpusEncoders.fetch());
}
const container = this._dispatchers.get(dispatcher.volume);
if (!container.has(dispatcher)) {
container.add(dispatcher);
dispatcher.once('end', () => this.unregisterDispatcher(dispatcher));
dispatcher.on('volumeChange', (o, n) => {
this.unregisterDispatcher(dispatcher, o);
if (!this._dispatchers.has(n)) {
this._dispatchers.set(n, new Set());
this._encoders.set(n, OpusEncoders.fetch());
}
this._dispatchers.get(n).add(dispatcher);
});
/**
* Emitted whenever a stream dispatcher subscribes to the broadcast.
* @event VoiceBroadcast#subscribe
* @param {StreamDispatcher} dispatcher The subscribed dispatcher
*/
this.emit('subscribe', dispatcher);
}
}
killCurrentTranscoder() {
if (this.currentTranscoder) {
if (this.currentTranscoder.transcoder) this.currentTranscoder.transcoder.kill();
this.currentTranscoder = null;
this.emit('end');
}
}
/**
* Plays any audio stream across the broadcast.
* @param {ReadableStream} stream The audio stream to play
* @param {StreamOptions} [options] Options for playing the stream
* @returns {VoiceBroadcast}
* @example
* // Play streams using ytdl-core
* const ytdl = require('ytdl-core');
* const streamOptions = { seek: 0, volume: 1 };
* const broadcast = client.createVoiceBroadcast();
*
* voiceChannel.join()
* .then(connection => {
* const stream = ytdl('https://www.youtube.com/watch?v=XAWgeLF9EVQ', { filter : 'audioonly' });
* broadcast.playStream(stream);
* const dispatcher = connection.playBroadcast(broadcast);
* })
* .catch(console.error);
*/
playStream(stream, options = {}) {
this.setVolume(options.volume || 1);
return this._playTranscodable(stream, options);
}
/**
* Play the given file in the voice connection.
* @param {string} file The absolute path to the file
* @param {StreamOptions} [options] Options for playing the stream
* @returns {StreamDispatcher}
* @example
* // Play files natively
* const broadcast = client.createVoiceBroadcast();
*
* voiceChannel.join()
* .then(connection => {
* broadcast.playFile('C:/Users/Discord/Desktop/music.mp3');
* const dispatcher = connection.playBroadcast(broadcast);
* })
* .catch(console.error);
*/
playFile(file, options = {}) {
this.setVolume(options.volume || 1);
return this._playTranscodable(`file:${file}`, options);
}
_playTranscodable(media, options) {
this.killCurrentTranscoder();
const transcoder = this.prism.transcode({
type: 'ffmpeg',
media,
ffmpegArguments: ffmpegArguments.concat(['-ss', String(options.seek || 0)]),
});
/**
* Emitted whenever an error occurs.
* @event VoiceBroadcast#error
* @param {Error} error The error that occurred
*/
transcoder.once('error', e => {
if (this.listenerCount('error') > 0) this.emit('error', e);
/**
* Emitted whenever the VoiceBroadcast has any warnings.
* @event VoiceBroadcast#warn
* @param {string|Error} warning The warning that was raised
*/
else this.emit('warn', e);
});
/**
* Emitted once the broadcast (the audio stream) ends.
* @event VoiceBroadcast#end
*/
transcoder.once('end', () => this.killCurrentTranscoder());
this.currentTranscoder = {
transcoder,
options,
};
transcoder.output.once('readable', () => this._startPlaying());
return this;
}
/**
* Plays a stream of 16-bit signed stereo PCM.
* @param {ReadableStream} stream The audio stream to play
* @param {StreamOptions} [options] Options for playing the stream
* @returns {VoiceBroadcast}
*/
playConvertedStream(stream, options = {}) {
this.killCurrentTranscoder();
this.setVolume(options.volume || 1);
this.currentTranscoder = { options: { stream } };
stream.once('readable', () => this._startPlaying());
return this;
}
/**
* Plays an Opus encoded stream.
* <warn>Note that inline volume is not compatible with this method.</warn>
* @param {ReadableStream} stream The Opus audio stream to play
* @param {StreamOptions} [options] Options for playing the stream
* @returns {StreamDispatcher}
*/
playOpusStream(stream) {
this.currentTranscoder = { options: { stream }, opus: true };
stream.once('readable', () => this._startPlaying());
return this;
}
/**
* Play an arbitrary input that can be [handled by ffmpeg](https://ffmpeg.org/ffmpeg-protocols.html#Description)
* @param {string} input The arbitrary input
* @param {StreamOptions} [options] Options for playing the stream
* @returns {VoiceBroadcast}
*/
playArbitraryInput(input, options = {}) {
this.setVolume(options.volume || 1);
options.input = input;
return this._playTranscodable(input, options);
}
/**
* Pauses the entire broadcast - all dispatchers also pause.
*/
pause() {
this.paused = true;
for (const container of this._dispatchers.values()) {
for (const dispatcher of container.values()) {
dispatcher.pause();
}
}
}
/**
* Resumes the entire broadcast - all dispatchers also resume.
*/
resume() {
this.paused = false;
for (const container of this._dispatchers.values()) {
for (const dispatcher of container.values()) {
dispatcher.resume();
}
}
}
_startPlaying() {
if (this.tickInterval) clearInterval(this.tickInterval);
// Old code?
// this.tickInterval = this.client.setInterval(this.tick.bind(this), 20);
this._startTime = Date.now();
this._count = 0;
this._pausedTime = 0;
this._missed = 0;
this.tick();
}
tick() {
if (!this._playableStream) return;
if (this.paused) {
this._pausedTime += 20;
setTimeout(() => this.tick(), 20);
return;
}
const opus = this.currentTranscoder.opus;
const buffer = this.readStreamBuffer();
if (!buffer) {
this._missed++;
if (this._missed < 5) {
this._pausedTime += 200;
setTimeout(() => this.tick(), 200);
} else {
this.killCurrentTranscoder();
}
return;
}
this._missed = 0;
let packetMatrix = {};
const getOpusPacket = volume => {
if (packetMatrix[volume]) return packetMatrix[volume];
const opusEncoder = this._encoders.get(volume);
const opusPacket = opusEncoder.encode(this.applyVolume(buffer, this._volume * volume));
packetMatrix[volume] = opusPacket;
return opusPacket;
};
for (const dispatcher of this.dispatchers) {
if (opus) {
dispatcher.processPacket(buffer);
continue;
}
const volume = dispatcher.volume;
dispatcher.processPacket(getOpusPacket(volume));
}
const next = 20 + (this._startTime + this._pausedTime + (this._count * 20) - Date.now());
this._count++;
setTimeout(() => this.tick(), next);
}
readStreamBuffer() {
const opus = this.currentTranscoder.opus;
const bufferLength = (opus ? 80 : 1920) * 2;
let buffer = this._playableStream.read(bufferLength);
if (opus) return buffer;
if (!buffer) return null;
if (buffer.length !== bufferLength) {
const newBuffer = Buffer.alloc(bufferLength).fill(0);
buffer.copy(newBuffer);
buffer = newBuffer;
}
return buffer;
}
/**
* Stop the current stream from playing without unsubscribing dispatchers.
*/
end() {
this.killCurrentTranscoder();
}
/**
* End the current broadcast, all subscribed dispatchers will also end.
*/
destroy() {
this.end();
for (const container of this._dispatchers.values()) {
for (const dispatcher of container.values()) {
dispatcher.destroy('end', 'broadcast ended');
}
}
}
}
module.exports = VoiceBroadcast;

View File

@@ -1,36 +1,67 @@
const VoiceWebSocket = require('./VoiceWebSocket');
const VoiceUDP = require('./VoiceUDPClient');
const Util = require('../../util/Util');
const Constants = require('../../util/Constants');
const AudioPlayer = require('./player/AudioPlayer');
const VoiceReceiver = require('./receiver/VoiceReceiver');
const EventEmitter = require('events').EventEmitter;
const fs = require('fs');
const Prism = require('prism-media');
/**
* Represents a connection to a Voice Channel in Discord.
* Represents a connection to a guild's voice server.
* ```js
* // obtained using:
* voiceChannel.join().then(connection => {
* // Obtained using:
* voiceChannel.join()
* .then(connection => {
*
* });
* });
* ```
* @extends {EventEmitter}
*/
class VoiceConnection extends EventEmitter {
constructor(pendingConnection) {
constructor(voiceManager, channel) {
super();
/**
* The Voice Manager that instantiated this connection
* The voice manager that instantiated this connection
* @type {ClientVoiceManager}
*/
this.voiceManager = pendingConnection.voiceManager;
this.voiceManager = voiceManager;
/**
* The client that instantiated this connection
* @type {Client}
*/
this.client = voiceManager.client;
/**
* @external Prism
* @see {@link https://github.com/hydrabolt/prism-media}
*/
/**
* The audio transcoder for this connection
* @type {Prism}
*/
this.prism = new Prism();
/**
* The voice channel this connection is currently serving
* @type {VoiceChannel}
*/
this.channel = pendingConnection.channel;
this.channel = channel;
/**
* The current status of the voice connection
* @type {number}
*/
this.status = Constants.VoiceStatus.AUTHENTICATING;
/**
* Whether we're currently transmitting audio
* @type {boolean}
*/
this.speaking = false;
/**
* An array of Voice Receivers that have been created for this connection
@@ -40,10 +71,10 @@ class VoiceConnection extends EventEmitter {
/**
* The authentication data needed to connect to the voice server
* @type {object}
* @type {Object}
* @private
*/
this.authentication = pendingConnection.data;
this.authentication = {};
/**
* The audio player for this voice connection
@@ -53,21 +84,20 @@ class VoiceConnection extends EventEmitter {
this.player.on('debug', m => {
/**
* Debug info from the connection
* Debug info from the connection.
* @event VoiceConnection#debug
* @param {string} message the debug message
* @param {string} message The debug message
*/
this.emit('debug', `audio player - ${m}`);
});
this.player.on('error', e => {
/**
* Warning info from the connection
* Warning info from the connection.
* @event VoiceConnection#warn
* @param {string|error} warning the warning
* @param {string|Error} warning The warning
*/
this.emit('warn', e);
this.player.cleanup();
});
/**
@@ -79,20 +109,31 @@ class VoiceConnection extends EventEmitter {
/**
* Object that wraps contains the `ws` and `udp` sockets of this voice connection
* @type {object}
* @type {Object}
* @private
*/
this.sockets = {};
this.connect();
this.authenticate();
}
/**
* Sets whether the voice connection should display as "speaking" or not
* @param {boolean} value whether or not to speak
* The current stream dispatcher (if any)
* @type {?StreamDispatcher}
* @readonly
*/
get dispatcher() {
return this.player.dispatcher;
}
/**
* Sets whether the voice connection should display as "speaking" or not.
* @param {boolean} value Whether or not to speak
* @private
*/
setSpeaking(value) {
if (this.speaking === value) return;
if (this.status !== Constants.VoiceStatus.CONNECTED) return;
this.speaking = value;
this.sockets.ws.sendPacket({
op: Constants.VoiceOPCodes.SPEAKING,
@@ -100,97 +141,289 @@ class VoiceConnection extends EventEmitter {
speaking: true,
delay: 0,
},
})
.catch(e => {
}).catch(e => {
this.emit('debug', e);
});
}
/**
* Sends a request to the main gateway to join a voice channel.
* @param {Object} [options] The options to provide
*/
sendVoiceStateUpdate(options = {}) {
options = Util.mergeDefault({
guild_id: this.channel.guild.id,
channel_id: this.channel.id,
self_mute: false,
self_deaf: false,
}, options);
this.client.ws.send({
op: Constants.OPCodes.VOICE_STATE_UPDATE,
d: options,
});
}
/**
* Set the token and endpoint required to connect to the voice servers.
* @param {string} token The voice token
* @param {string} endpoint The voice endpoint
* @returns {void}
*/
setTokenAndEndpoint(token, endpoint) {
if (!endpoint) {
// Signifies awaiting endpoint stage
return;
}
if (!token) {
this.authenticateFailed('Token not provided from voice server packet.');
return;
}
endpoint = endpoint.match(/([^:]*)/)[0];
if (!endpoint) {
this.authenticateFailed('Invalid endpoint received.');
return;
}
if (this.status === Constants.VoiceStatus.AUTHENTICATING) {
this.authentication.token = token;
this.authentication.endpoint = endpoint;
this.checkAuthenticated();
} else if (token !== this.authentication.token || endpoint !== this.authentication.endpoint) {
this.reconnect(token, endpoint);
}
}
/**
* Sets the Session ID for the connection.
* @param {string} sessionID The voice session ID
*/
setSessionID(sessionID) {
if (!sessionID) {
this.authenticateFailed('Session ID not supplied.');
return;
}
if (this.status === Constants.VoiceStatus.AUTHENTICATING) {
this.authentication.sessionID = sessionID;
this.checkAuthenticated();
} else if (sessionID !== this.authentication.sessionID) {
this.authentication.sessionID = sessionID;
/**
* Emitted when a new session ID is received.
* @event VoiceConnection#newSession
* @private
*/
this.emit('newSession', sessionID);
}
}
/**
* Checks whether the voice connection is authenticated.
* @private
*/
checkAuthenticated() {
const { token, endpoint, sessionID } = this.authentication;
if (token && endpoint && sessionID) {
clearTimeout(this.connectTimeout);
this.status = Constants.VoiceStatus.CONNECTING;
/**
* Emitted when we successfully initiate a voice connection.
* @event VoiceConnection#authenticated
*/
this.emit('authenticated');
this.connect();
}
}
/**
* Invoked when we fail to initiate a voice connection.
* @param {string} reason The reason for failure
* @private
*/
authenticateFailed(reason) {
clearTimeout(this.connectTimeout);
if (this.status === Constants.VoiceStatus.AUTHENTICATING) {
/**
* Emitted when we fail to initiate a voice connection.
* @event VoiceConnection#failed
* @param {Error} error The encountered error
*/
this.emit('failed', new Error(reason));
} else {
this.emit('error', new Error(reason));
}
this.status = Constants.VoiceStatus.DISCONNECTED;
}
/**
* Move to a different voice channel in the same guild.
* @param {VoiceChannel} channel The channel to move to
* @private
*/
updateChannel(channel) {
this.channel = channel;
this.sendVoiceStateUpdate();
}
/**
* Attempts to authenticate to the voice server.
* @private
*/
authenticate() {
this.sendVoiceStateUpdate();
this.connectTimeout = this.client.setTimeout(
() => this.authenticateFailed(new Error('Connection not established within 15 seconds.')), 15000);
}
/**
* Attempts to reconnect to the voice server (typically after a region change).
* @param {string} token The voice token
* @param {string} endpoint The voice endpoint
* @private
*/
reconnect(token, endpoint) {
this.authentication.token = token;
this.authentication.endpoint = endpoint;
this.status = Constants.VoiceStatus.RECONNECTING;
/**
* Emitted when the voice connection is reconnecting (typically after a region change).
* @event VoiceConnection#reconnecting
*/
this.emit('reconnecting');
this.connect();
}
/**
* Disconnect the voice connection, causing a disconnect and closing event to be emitted.
*/
disconnect() {
this.emit('closing');
this.voiceManager.client.ws.send({
op: Constants.OPCodes.VOICE_STATE_UPDATE,
d: {
guild_id: this.channel.guild.id,
channel_id: null,
self_mute: false,
self_deaf: false,
},
this.sendVoiceStateUpdate({
channel_id: null,
});
this.player.destroy();
this.cleanup();
this.status = Constants.VoiceStatus.DISCONNECTED;
/**
* Emitted when the voice connection disconnects
* Emitted when the voice connection disconnects.
* @event VoiceConnection#disconnect
*/
this.emit('disconnect');
}
/**
* Connect the voice connection
* Cleans up after disconnect.
* @private
*/
cleanup() {
const { ws, udp } = this.sockets;
if (ws) {
ws.removeAllListeners('error');
ws.removeAllListeners('ready');
ws.removeAllListeners('sessionDescription');
ws.removeAllListeners('speaking');
}
if (udp) udp.removeAllListeners('error');
this.sockets.ws = null;
this.sockets.udp = null;
}
/**
* Connect the voice connection.
* @private
*/
connect() {
if (this.sockets.ws) throw new Error('There is already an existing WebSocket connection.');
if (this.sockets.udp) throw new Error('There is already an existing UDP connection.');
if (this.status !== Constants.VoiceStatus.RECONNECTING) {
if (this.sockets.ws) throw new Error('There is already an existing WebSocket connection.');
if (this.sockets.udp) throw new Error('There is already an existing UDP connection.');
}
if (this.sockets.ws) this.sockets.ws.shutdown();
if (this.sockets.udp) this.sockets.udp.shutdown();
this.sockets.ws = new VoiceWebSocket(this);
this.sockets.udp = new VoiceUDP(this);
this.sockets.ws.on('error', e => this.emit('error', e));
this.sockets.udp.on('error', e => this.emit('error', e));
this.sockets.ws.once('ready', d => {
this.authentication.port = d.port;
this.authentication.ssrc = d.ssrc;
/**
* Emitted whenever the connection encounters an error.
* @event VoiceConnection#error
* @param {Error} error the encountered error
*/
this.sockets.udp.findEndpointAddress()
.then(address => {
this.sockets.udp.createUDPSocket(address);
})
.catch(e => this.emit('error', e));
});
this.sockets.ws.once('sessionDescription', (mode, secret) => {
this.authentication.encryptionMode = mode;
this.authentication.secretKey = secret;
/**
* Emitted once the connection is ready, when a promise to join a voice channel resolves,
* the connection will already be ready.
* @event VoiceConnection#ready
*/
this.emit('ready');
});
this.sockets.ws.on('speaking', data => {
const guild = this.channel.guild;
const user = this.voiceManager.client.users.get(data.user_id);
this.ssrcMap.set(+data.ssrc, user);
if (!data.speaking) {
for (const receiver of this.receivers) {
const opusStream = receiver.opusStreams.get(user.id);
const pcmStream = receiver.pcmStreams.get(user.id);
if (opusStream) {
opusStream.push(null);
opusStream.open = false;
receiver.opusStreams.delete(user.id);
}
if (pcmStream) {
pcmStream.push(null);
pcmStream.open = false;
receiver.pcmStreams.delete(user.id);
}
}
const { ws, udp } = this.sockets;
ws.on('error', err => this.emit('error', err));
udp.on('error', err => this.emit('error', err));
ws.on('ready', this.onReady.bind(this));
ws.on('sessionDescription', this.onSessionDescription.bind(this));
ws.on('speaking', this.onSpeaking.bind(this));
}
/**
* Invoked when the voice websocket is ready.
* @param {Object} data The received data
* @private
*/
onReady({ port, ssrc }) {
this.authentication.port = port;
this.authentication.ssrc = ssrc;
const udp = this.sockets.udp;
/**
* Emitted whenever the connection encounters an error.
* @event VoiceConnection#error
* @param {Error} error The encountered error
*/
udp.findEndpointAddress()
.then(address => {
udp.createUDPSocket(address);
}, e => this.emit('error', e));
}
/**
* Invoked when a session description is received.
* @param {string} mode The encryption mode
* @param {string} secret The secret key
* @private
*/
onSessionDescription(mode, secret) {
this.authentication.encryptionMode = mode;
this.authentication.secretKey = secret;
this.status = Constants.VoiceStatus.CONNECTED;
/**
* Emitted once the connection is ready, when a promise to join a voice channel resolves,
* the connection will already be ready.
* @event VoiceConnection#ready
*/
this.emit('ready');
}
/**
* Invoked when a speaking event is received.
* @param {Object} data The received data
* @private
*/
onSpeaking({ user_id, ssrc, speaking }) {
const guild = this.channel.guild;
const user = this.client.users.get(user_id);
this.ssrcMap.set(+ssrc, user);
if (!speaking) {
for (const receiver of this.receivers) {
receiver.stoppedSpeaking(user);
}
/**
* Emitted whenever a user starts/stops speaking
* @event VoiceConnection#speaking
* @param {User} user The user that has started/stopped speaking
* @param {boolean} speaking Whether or not the user is speaking
*/
if (this.ready) this.emit('speaking', user, data.speaking);
guild._memberSpeakUpdate(data.user_id, data.speaking);
});
}
/**
* Emitted whenever a user starts/stops speaking.
* @event VoiceConnection#speaking
* @param {User} user The user that has started/stopped speaking
* @param {boolean} speaking Whether or not the user is speaking
*/
if (this.status === Constants.VoiceStatus.CONNECTED) this.emit('speaking', user, speaking);
guild._memberSpeakUpdate(user_id, speaking);
}
/**
@@ -199,23 +432,35 @@ class VoiceConnection extends EventEmitter {
* @property {number} [seek=0] The time to seek to
* @property {number} [volume=1] The volume to play at
* @property {number} [passes=1] How many times to send the voice packet to reduce packet loss
* @property {number|string} [bitrate=48000] The bitrate (quality) of the audio.
* If set to 'auto', the voice channel's bitrate will be used
*/
/**
* Play the given file in the voice connection.
* @param {string} file The path to the file
* @param {string} file The absolute path to the file
* @param {StreamOptions} [options] Options for playing the stream
* @returns {StreamDispatcher}
* @example
* // play files natively
* // Play files natively
* voiceChannel.join()
* .then(connection => {
* const dispatcher = connection.playFile('C:/Users/Discord/Desktop/music.mp3');
* })
* .catch(console.error);
* .then(connection => {
* const dispatcher = connection.playFile('C:/Users/Discord/Desktop/music.mp3');
* })
* .catch(console.error);
*/
playFile(file, options) {
return this.playStream(fs.createReadStream(file), options);
return this.player.playUnknownStream(`file:${file}`, options);
}
/**
* Play an arbitrary input that can be [handled by ffmpeg](https://ffmpeg.org/ffmpeg-protocols.html#Description)
* @param {string} input the arbitrary input
* @param {StreamOptions} [options] Options for playing the stream
* @returns {StreamDispatcher}
*/
playArbitraryInput(input, options) {
return this.player.playUnknownStream(input, options);
}
/**
@@ -224,34 +469,60 @@ class VoiceConnection extends EventEmitter {
* @param {StreamOptions} [options] Options for playing the stream
* @returns {StreamDispatcher}
* @example
* // play streams using ytdl-core
* // Play streams using ytdl-core
* const ytdl = require('ytdl-core');
* const streamOptions = { seek: 0, volume: 1 };
* voiceChannel.join()
* .then(connection => {
* const stream = ytdl('https://www.youtube.com/watch?v=XAWgeLF9EVQ', {filter : 'audioonly'});
* const dispatcher = connection.playStream(stream, streamOptions);
* })
* .catch(console.error);
* .then(connection => {
* const stream = ytdl('https://www.youtube.com/watch?v=XAWgeLF9EVQ', { filter : 'audioonly' });
* const dispatcher = connection.playStream(stream, streamOptions);
* })
* .catch(console.error);
*/
playStream(stream, { seek = 0, volume = 1, passes = 1 } = {}) {
const options = { seek, volume, passes };
playStream(stream, options) {
return this.player.playUnknownStream(stream, options);
}
/**
* Plays a stream of 16-bit signed stereo PCM at 48KHz.
* @param {ReadableStream} stream The audio stream to play.
* Plays a stream of 16-bit signed stereo PCM.
* @param {ReadableStream} stream The audio stream to play
* @param {StreamOptions} [options] Options for playing the stream
* @returns {StreamDispatcher}
*/
playConvertedStream(stream, { seek = 0, volume = 1, passes = 1 } = {}) {
const options = { seek, volume, passes };
playConvertedStream(stream, options) {
return this.player.playPCMStream(stream, options);
}
/**
* Creates a VoiceReceiver so you can start listening to voice data. It's recommended to only create one of these.
* Plays an Opus encoded stream.
* <warn>Note that inline volume is not compatible with this method.</warn>
* @param {ReadableStream} stream The Opus audio stream to play
* @param {StreamOptions} [options] Options for playing the stream
* @returns {StreamDispatcher}
*/
playOpusStream(stream, options) {
return this.player.playOpusStream(stream, options);
}
/**
* Plays a voice broadcast.
* @param {VoiceBroadcast} broadcast The broadcast to play
* @param {StreamOptions} [options] Options for playing the stream
* @returns {StreamDispatcher}
* @example
* // Play a broadcast
* const broadcast = client
* .createVoiceBroadcast()
* .playFile('./test.mp3');
* const dispatcher = voiceConnection.playBroadcast(broadcast);
*/
playBroadcast(broadcast, options) {
return this.player.playBroadcast(broadcast, options);
}
/**
* Creates a VoiceReceiver so you can start listening to voice data.
* It's recommended to only create one of these.
* @returns {VoiceReceiver}
*/
createReceiver() {

View File

@@ -3,20 +3,8 @@ const dns = require('dns');
const Constants = require('../../util/Constants');
const EventEmitter = require('events').EventEmitter;
function parseLocalPacket(message) {
try {
const packet = new Buffer(message);
let address = '';
for (let i = 4; i < packet.indexOf(0, i); i++) address += String.fromCharCode(packet[i]);
const port = parseInt(packet.readUIntLE(packet.length - 2, 2).toString(10), 10);
return { address, port };
} catch (error) {
return { error };
}
}
/**
* Represents a UDP Client for a Voice Connection
* Represents a UDP client for a Voice Connection.
* @extends {EventEmitter}
* @private
*/
@@ -37,7 +25,7 @@ class VoiceConnectionUDPClient extends EventEmitter {
this.socket = null;
/**
* The address of the discord voice server
* The address of the Discord voice server
* @type {?string}
*/
this.discordAddress = null;
@@ -59,17 +47,17 @@ class VoiceConnectionUDPClient extends EventEmitter {
shutdown() {
if (this.socket) {
this.socket.removeAllListeners('message');
try {
this.socket.close();
} catch (e) {
return;
} finally {
this.socket = null;
}
this.socket = null;
}
}
/**
* The port of the discord voice server
* The port of the Discord voice server
* @type {number}
* @readonly
*/
@@ -78,7 +66,7 @@ class VoiceConnectionUDPClient extends EventEmitter {
}
/**
* Tries to resolve the voice server endpoint to an address
* Tries to resolve the voice server endpoint to an address.
* @returns {Promise<string>}
*/
findEndpointAddress() {
@@ -95,8 +83,8 @@ class VoiceConnectionUDPClient extends EventEmitter {
}
/**
* Send a packet to the UDP client
* @param {Object} packet the packet to send
* Send a packet to the UDP client.
* @param {Object} packet The packet to send
* @returns {Promise<Object>}
*/
send(packet) {
@@ -136,10 +124,22 @@ class VoiceConnectionUDPClient extends EventEmitter {
});
});
const blankMessage = new Buffer(70);
const blankMessage = Buffer.alloc(70);
blankMessage.writeUIntBE(this.voiceConnection.authentication.ssrc, 0, 4);
this.send(blankMessage);
}
}
function parseLocalPacket(message) {
try {
const packet = Buffer.from(message);
let address = '';
for (let i = 4; i < packet.indexOf(0, i); i++) address += String.fromCharCode(packet[i]);
const port = parseInt(packet.readUIntLE(packet.length - 2, 2).toString(10), 10);
return { address, port };
} catch (error) {
return { error };
}
}
module.exports = VoiceConnectionUDPClient;

Some files were not shown because too many files have changed in this diff Show More