@glade-chat/glade.js 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +52 -0
  3. package/package.json +57 -0
  4. package/src/client/Client.js +355 -0
  5. package/src/errors/index.js +59 -0
  6. package/src/gateway/Gateway.js +172 -0
  7. package/src/gateway/handleDispatch.js +259 -0
  8. package/src/index.js +66 -0
  9. package/src/managers/CachedManager.js +95 -0
  10. package/src/managers/ChannelManager.js +40 -0
  11. package/src/managers/DMManager.js +43 -0
  12. package/src/managers/FriendManager.js +89 -0
  13. package/src/managers/HouseManager.js +44 -0
  14. package/src/managers/InviteManager.js +46 -0
  15. package/src/managers/MemberManager.js +41 -0
  16. package/src/managers/MessageManager.js +69 -0
  17. package/src/managers/RoleManager.js +65 -0
  18. package/src/managers/RoomManager.js +80 -0
  19. package/src/managers/UserManager.js +41 -0
  20. package/src/rest/REST.js +250 -0
  21. package/src/rest/Routes.js +85 -0
  22. package/src/structures/Base.js +70 -0
  23. package/src/structures/ClientUser.js +142 -0
  24. package/src/structures/DMChannel.js +85 -0
  25. package/src/structures/House.js +171 -0
  26. package/src/structures/Invite.js +83 -0
  27. package/src/structures/Member.js +148 -0
  28. package/src/structures/Message.js +176 -0
  29. package/src/structures/ReactionGroup.js +42 -0
  30. package/src/structures/Role.js +107 -0
  31. package/src/structures/Room.js +264 -0
  32. package/src/structures/User.js +113 -0
  33. package/src/structures/VoiceState.js +42 -0
  34. package/src/util/Collection.js +167 -0
  35. package/src/util/Constants.js +183 -0
  36. package/src/util/Permissions.js +148 -0
  37. package/src/util/Util.js +62 -0
  38. package/types/index.d.ts +784 -0
@@ -0,0 +1,89 @@
1
+ 'use strict';
2
+
3
+ import { Collection } from '../util/Collection.js';
4
+ import { Routes } from '../rest/Routes.js';
5
+ import { resolveId } from '../util/Util.js';
6
+
7
+ /**
8
+ * Manages the client's friends and friend requests. Friends are {@link User}s,
9
+ * cached both here (accepted friends) and in the global user cache.
10
+ */
11
+ export class FriendManager {
12
+ constructor(client) {
13
+ Object.defineProperty(this, 'client', { value: client });
14
+ /**
15
+ * Accepted friends, keyed by user id.
16
+ * @type {Collection<string, import('../structures/User.js').User>}
17
+ */
18
+ this.cache = new Collection();
19
+ }
20
+
21
+ /**
22
+ * Fetches the client's accepted friends.
23
+ * @returns {Promise<Collection<string, import('../structures/User.js').User>>}
24
+ */
25
+ async fetch() {
26
+ const { friends } = await this.client.rest.get(Routes.friends());
27
+ this.cache.clear();
28
+ for (const friend of friends) {
29
+ const user = this.client.users._add(friend);
30
+ this.cache.set(user.id, user);
31
+ }
32
+ return this.cache;
33
+ }
34
+
35
+ /**
36
+ * Fetches incoming and outgoing pending friend requests.
37
+ * @returns {Promise<{ incoming: Array<{ id: string, user: import('../structures/User.js').User }>, outgoing: Array<{ id: string, user: import('../structures/User.js').User }> }>}
38
+ */
39
+ async fetchPending() {
40
+ const data = await this.client.rest.get(Routes.friendsPending());
41
+ const map = (list) =>
42
+ list.map((entry) => ({ id: entry.id, user: this.client.users._add(entry.user) }));
43
+ return { incoming: map(data.incoming ?? []), outgoing: map(data.outgoing ?? []) };
44
+ }
45
+
46
+ /**
47
+ * Sends a friend request by handle. If the target had already requested you, the
48
+ * friendship is accepted instead (`accepted: true`).
49
+ * @param {string} handle
50
+ * @returns {Promise<{ accepted: boolean, user: import('../structures/User.js').User }>}
51
+ */
52
+ async add(handle) {
53
+ const { accepted, target } = await this.client.rest.post(Routes.friends(), { handle });
54
+ const user = this.client.users._add(target);
55
+ if (accepted) this.cache.set(user.id, user);
56
+ return { accepted, user };
57
+ }
58
+
59
+ /**
60
+ * Accepts an incoming friend request.
61
+ * @param {string} requestId The friendship id from {@link FriendManager#fetchPending}.
62
+ * @returns {Promise<void>}
63
+ */
64
+ async accept(requestId) {
65
+ await this.client.rest.post(Routes.friendAccept(requestId));
66
+ }
67
+
68
+ /**
69
+ * Declines an incoming request, or cancels an outgoing one.
70
+ * @param {string} requestId
71
+ * @returns {Promise<void>}
72
+ */
73
+ async decline(requestId) {
74
+ await this.client.rest.post(Routes.friendDecline(requestId));
75
+ }
76
+
77
+ /**
78
+ * Removes an existing friend.
79
+ * @param {string | import('../structures/User.js').User} user
80
+ * @returns {Promise<void>}
81
+ */
82
+ async remove(user) {
83
+ const id = resolveId(user);
84
+ await this.client.rest.delete(Routes.friendRemove(id));
85
+ this.cache.delete(id);
86
+ }
87
+ }
88
+
89
+ export default FriendManager;
@@ -0,0 +1,44 @@
1
+ 'use strict';
2
+
3
+ import { CachedManager } from './CachedManager.js';
4
+ import { House } from '../structures/House.js';
5
+ import { Routes } from '../rest/Routes.js';
6
+
7
+ /**
8
+ * Manages the Houses the client is a member of.
9
+ * @extends {CachedManager}
10
+ */
11
+ export class HouseManager extends CachedManager {
12
+ constructor(client) {
13
+ super(client, House);
14
+ }
15
+
16
+ /**
17
+ * Fetches all of the client's Houses, or a single House by id.
18
+ * @param {string} [id] If provided, fetches just that House (with its rooms).
19
+ * @param {{ force?: boolean }} [options]
20
+ * @returns {Promise<import('../util/Collection.js').Collection<string, House> | House>}
21
+ */
22
+ async fetch(id, { force = false } = {}) {
23
+ if (id) {
24
+ if (!force && this.cache.has(id)) return this.cache.get(id);
25
+ const { house } = await this.client.rest.get(Routes.house(id));
26
+ return this._add(house);
27
+ }
28
+ const { houses } = await this.client.rest.get(Routes.houses());
29
+ for (const house of houses) this._add(house);
30
+ return this.cache;
31
+ }
32
+
33
+ /**
34
+ * Creates a new House owned by the client.
35
+ * @param {string} name
36
+ * @returns {Promise<House>}
37
+ */
38
+ async create(name) {
39
+ const { house } = await this.client.rest.post(Routes.houses(), { name });
40
+ return this._add(house);
41
+ }
42
+ }
43
+
44
+ export default HouseManager;
@@ -0,0 +1,46 @@
1
+ 'use strict';
2
+
3
+ import { CachedManager } from './CachedManager.js';
4
+ import { Invite } from '../structures/Invite.js';
5
+ import { Routes } from '../rest/Routes.js';
6
+
7
+ /**
8
+ * Manages the invites of a single House.
9
+ * @extends {CachedManager}
10
+ */
11
+ export class InviteManager extends CachedManager {
12
+ /**
13
+ * @param {import('../client/Client.js').Client} client
14
+ * @param {import('../structures/House.js').House} house
15
+ */
16
+ constructor(client, house) {
17
+ super(client, Invite);
18
+ /** @type {import('../structures/House.js').House} */
19
+ Object.defineProperty(this, 'house', { value: house });
20
+ }
21
+
22
+ /**
23
+ * Fetches all active invites for this House.
24
+ * @returns {Promise<import('../util/Collection.js').Collection<string, Invite>>}
25
+ */
26
+ async fetch() {
27
+ const { invites } = await this.client.rest.get(Routes.houseInvites(this.house.id));
28
+ for (const invite of invites) this._add(invite);
29
+ return this.cache;
30
+ }
31
+
32
+ /**
33
+ * Creates an invite to this House.
34
+ * @param {{ expiresInMinutes?: number | null, maxUses?: number | null }} [options]
35
+ * @returns {Promise<Invite>}
36
+ */
37
+ async create({ expiresInMinutes, maxUses } = {}) {
38
+ const { invite } = await this.client.rest.post(Routes.houseInvites(this.house.id), {
39
+ expiresInMinutes,
40
+ maxUses,
41
+ });
42
+ return this._add({ ...invite, house: { id: this.house.id, name: this.house.name } });
43
+ }
44
+ }
45
+
46
+ export default InviteManager;
@@ -0,0 +1,41 @@
1
+ 'use strict';
2
+
3
+ import { CachedManager } from './CachedManager.js';
4
+ import { Member } from '../structures/Member.js';
5
+ import { Routes } from '../rest/Routes.js';
6
+
7
+ /**
8
+ * Manages the members of a single House.
9
+ * @extends {CachedManager}
10
+ */
11
+ export class MemberManager extends CachedManager {
12
+ /**
13
+ * @param {import('../client/Client.js').Client} client
14
+ * @param {import('../structures/House.js').House} house
15
+ */
16
+ constructor(client, house) {
17
+ super(client, Member);
18
+ /** @type {import('../structures/House.js').House} */
19
+ Object.defineProperty(this, 'house', { value: house });
20
+ }
21
+
22
+ _create(data) {
23
+ return new Member(this.client, this.house, data);
24
+ }
25
+
26
+ /**
27
+ * Fetches all members, or returns a single member by id (fetching all first if
28
+ * necessary, since there is no single-member endpoint).
29
+ * @param {string} [id]
30
+ * @param {{ force?: boolean }} [options]
31
+ * @returns {Promise<import('../util/Collection.js').Collection<string, Member> | Member | null>}
32
+ */
33
+ async fetch(id, { force = false } = {}) {
34
+ if (id && !force && this.cache.has(id)) return this.cache.get(id);
35
+ const { members } = await this.client.rest.get(Routes.houseMembers(this.house.id));
36
+ for (const member of members) this._add(member);
37
+ return id ? (this.cache.get(id) ?? null) : this.cache;
38
+ }
39
+ }
40
+
41
+ export default MemberManager;
@@ -0,0 +1,69 @@
1
+ 'use strict';
2
+
3
+ import { CachedManager } from './CachedManager.js';
4
+ import { Message } from '../structures/Message.js';
5
+ import { Routes } from '../rest/Routes.js';
6
+
7
+ /**
8
+ * Manages the cached messages of a single room or DM channel.
9
+ * @extends {CachedManager}
10
+ *
11
+ * @typedef {object} MessagePayload
12
+ * @property {string} content The message content (may be E2E ciphertext).
13
+ * @property {string} [nonce] A client nonce echoed back for optimistic dedupe.
14
+ * @property {string[]} [mentions] Explicit mentioned user ids (needed when content is encrypted).
15
+ */
16
+ export class MessageManager extends CachedManager {
17
+ /**
18
+ * @param {import('../client/Client.js').Client} client
19
+ * @param {import('../structures/Room.js').Room | import('../structures/DMChannel.js').DMChannel} channel
20
+ */
21
+ constructor(client, channel) {
22
+ super(client, Message);
23
+ /** @type {import('../structures/Room.js').Room | import('../structures/DMChannel.js').DMChannel} */
24
+ Object.defineProperty(this, 'channel', { value: channel });
25
+ }
26
+
27
+ /** Whether the owning channel is a DM. */
28
+ get isDM() {
29
+ return typeof this.channel.isDM === 'function' && this.channel.isDM();
30
+ }
31
+
32
+ /**
33
+ * Fetches a page of messages (ascending order), newest page first via `cursor`.
34
+ * @param {{ cursor?: string, limit?: number }} [options]
35
+ * @returns {Promise<{ messages: Message[], nextCursor: string | null }>}
36
+ */
37
+ async fetch({ cursor, limit } = {}) {
38
+ const path = this.isDM
39
+ ? Routes.dmMessages(this.channel.id)
40
+ : Routes.roomMessages(this.channel.id);
41
+ const data = await this.client.rest.get(path, { query: { cursor, limit } });
42
+ const messages = data.messages.map((m) => this._add(m));
43
+ return { messages, nextCursor: data.nextCursor ?? null };
44
+ }
45
+
46
+ /**
47
+ * Fetches the room's pinned messages (rooms only).
48
+ * @returns {Promise<Message[]>}
49
+ */
50
+ async fetchPins() {
51
+ if (this.isDM) throw new Error('DM channels do not support pinned messages');
52
+ const { messages } = await this.client.rest.get(Routes.roomPins(this.channel.id));
53
+ return messages.map((m) => this._add(m));
54
+ }
55
+
56
+ /**
57
+ * Sends a message to this channel.
58
+ * @param {string | MessagePayload} content
59
+ * @returns {Promise<Message>}
60
+ */
61
+ send(content) {
62
+ const target = this.isDM
63
+ ? { dmChannelId: this.channel.id }
64
+ : { roomId: this.channel.id };
65
+ return this.client._sendMessage(target, content);
66
+ }
67
+ }
68
+
69
+ export default MessageManager;
@@ -0,0 +1,65 @@
1
+ 'use strict';
2
+
3
+ import { CachedManager } from './CachedManager.js';
4
+ import { Role } from '../structures/Role.js';
5
+ import { Routes } from '../rest/Routes.js';
6
+ import { PermissionsBitField } from '../util/Permissions.js';
7
+
8
+ /**
9
+ * Manages the roles of a single House.
10
+ * @extends {CachedManager}
11
+ */
12
+ export class RoleManager extends CachedManager {
13
+ /**
14
+ * @param {import('../client/Client.js').Client} client
15
+ * @param {import('../structures/House.js').House} house
16
+ */
17
+ constructor(client, house) {
18
+ super(client, Role);
19
+ /** @type {import('../structures/House.js').House} */
20
+ Object.defineProperty(this, 'house', { value: house });
21
+ }
22
+
23
+ /** The default `@everyone` role, if cached. */
24
+ get everyone() {
25
+ return this.cache.find((r) => r.isDefault) ?? null;
26
+ }
27
+
28
+ /**
29
+ * Fetches all roles in this House.
30
+ * @returns {Promise<import('../util/Collection.js').Collection<string, Role>>}
31
+ */
32
+ async fetch() {
33
+ const { roles } = await this.client.rest.get(Routes.houseRoles(this.house.id));
34
+ for (const role of roles) this._add(role);
35
+ return this.cache;
36
+ }
37
+
38
+ /**
39
+ * Creates a role. Requires `ManageRoles`.
40
+ * @param {{ name: string, color?: string | null, permissions?: import('../util/Permissions.js').PermissionResolvable }} data
41
+ * @returns {Promise<Role>}
42
+ */
43
+ async create({ name, color, permissions } = {}) {
44
+ const body = { name, color };
45
+ if (permissions !== undefined) body.permissions = PermissionsBitField.resolve(permissions);
46
+ const { role } = await this.client.rest.post(Routes.houseRoles(this.house.id), body);
47
+ return this._add(role);
48
+ }
49
+
50
+ /**
51
+ * Reorders the House's roles.
52
+ * @param {Array<string | Role>} orderedIds
53
+ * @returns {Promise<import('../util/Collection.js').Collection<string, Role>>}
54
+ */
55
+ async reorder(orderedIds) {
56
+ const ids = orderedIds.map((r) => this.resolveId(r));
57
+ const { roles } = await this.client.rest.post(Routes.houseRolesReorder(this.house.id), {
58
+ orderedIds: ids,
59
+ });
60
+ for (const role of roles) this._add(role);
61
+ return this.cache;
62
+ }
63
+ }
64
+
65
+ export default RoleManager;
@@ -0,0 +1,80 @@
1
+ 'use strict';
2
+
3
+ import { CachedManager } from './CachedManager.js';
4
+ import { Room, createRoom } from '../structures/Room.js';
5
+ import { Routes } from '../rest/Routes.js';
6
+
7
+ /**
8
+ * Manages the rooms (channels) of a single House. Entries are also mirrored into
9
+ * the client-wide {@link ChannelManager} cache.
10
+ * @extends {CachedManager}
11
+ */
12
+ export class RoomManager extends CachedManager {
13
+ /**
14
+ * @param {import('../client/Client.js').Client} client
15
+ * @param {import('../structures/House.js').House} house
16
+ */
17
+ constructor(client, house) {
18
+ super(client, Room);
19
+ /** @type {import('../structures/House.js').House} */
20
+ Object.defineProperty(this, 'house', { value: house });
21
+ }
22
+
23
+ _create(data) {
24
+ return createRoom(this.client, { ...data, houseId: data.houseId ?? this.house.id });
25
+ }
26
+
27
+ _add(data, cache = true) {
28
+ const isNew = data.id != null && !this.cache.has(data.id);
29
+ const room = super._add(data, cache);
30
+ // Mirror into the global channel cache so rooms resolve by id anywhere.
31
+ if (cache && this.client.options.cache) this.client.channels.cache.set(room.id, room);
32
+ // Subscribe to realtime events for rooms discovered while already connected
33
+ // (rooms cached before connect are handled by Client on the `ready` event).
34
+ if (
35
+ isNew &&
36
+ this.client.options.autoSubscribeRooms !== false &&
37
+ this.client.gateway?.connected
38
+ ) {
39
+ this.client.gateway.send('room:join', room.id);
40
+ }
41
+ return room;
42
+ }
43
+
44
+ /**
45
+ * Fetches all rooms in this House (filtered to those the client can view).
46
+ * @returns {Promise<import('../util/Collection.js').Collection<string, Room>>}
47
+ */
48
+ async fetch() {
49
+ const { rooms } = await this.client.rest.get(Routes.houseRooms(this.house.id));
50
+ for (const room of rooms) this._add(room);
51
+ return this.cache;
52
+ }
53
+
54
+ /**
55
+ * Creates a room in this House. Requires `ManageChannels`.
56
+ * @param {string} name
57
+ * @param {{ type?: 'text' | 'voice' | 'portal' }} [options]
58
+ * @returns {Promise<Room>}
59
+ */
60
+ async create(name, { type } = {}) {
61
+ const { room } = await this.client.rest.post(Routes.houseRooms(this.house.id), { name, type });
62
+ return this._add(room);
63
+ }
64
+
65
+ /**
66
+ * Reorders the House's rooms.
67
+ * @param {Array<string | Room>} orderedIds
68
+ * @returns {Promise<import('../util/Collection.js').Collection<string, Room>>}
69
+ */
70
+ async reorder(orderedIds) {
71
+ const ids = orderedIds.map((r) => this.resolveId(r));
72
+ const { rooms } = await this.client.rest.post(Routes.houseRoomsReorder(this.house.id), {
73
+ orderedIds: ids,
74
+ });
75
+ for (const room of rooms) this._add(room);
76
+ return this.cache;
77
+ }
78
+ }
79
+
80
+ export default RoomManager;
@@ -0,0 +1,41 @@
1
+ 'use strict';
2
+
3
+ import { CachedManager } from './CachedManager.js';
4
+ import { User } from '../structures/User.js';
5
+ import { Routes } from '../rest/Routes.js';
6
+
7
+ /**
8
+ * The client-wide cache of known users.
9
+ * @extends {CachedManager}
10
+ */
11
+ export class UserManager extends CachedManager {
12
+ constructor(client) {
13
+ super(client, User);
14
+ }
15
+
16
+ /**
17
+ * Fetches a user's full profile by id.
18
+ * @param {string} id
19
+ * @param {{ force?: boolean }} [options]
20
+ * @returns {Promise<User>}
21
+ */
22
+ async fetch(id, { force = false } = {}) {
23
+ if (!force && this.cache.has(id)) return this.cache.get(id);
24
+ const { profile } = await this.client.rest.get(Routes.user(id));
25
+ return this._add(profile);
26
+ }
27
+
28
+ /**
29
+ * Searches the client's contacts (accepted friends + existing DM partners).
30
+ * @param {string} [query]
31
+ * @returns {Promise<User[]>}
32
+ */
33
+ async search(query = '') {
34
+ const { users } = await this.client.rest.get(Routes.userSearch(), {
35
+ query: { q: query },
36
+ });
37
+ return users.map((u) => this._add(u));
38
+ }
39
+ }
40
+
41
+ export default UserManager;