lunibee 0.2.0 → 0.2.1

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.
@@ -1,6 +1,6 @@
1
1
  import { Manager } from "./base.js";
2
2
  import { type REST } from "@lunibee/rest";
3
- import type { APIGuildScheduledEvent, APIStageInstance, UserData } from "@lunibee/types";
3
+ import type { APIOverwrite, APIGuildScheduledEvent, APIStageInstance, UserData } from "@lunibee/types";
4
4
  /** Raw Discord ban object. */
5
5
  export interface APIBan {
6
6
  user: UserData;
@@ -97,11 +97,31 @@ export interface PermissionOverwriteOptions {
97
97
  /** Denied permission bits. */
98
98
  deny?: bigint | number | string;
99
99
  }
100
+ /** Bits to change on one overwrite by {@link PermissionOverwriteManager.update}; bits in none of them keep their state. */
101
+ export interface PermissionOverwriteUpdate {
102
+ /** Bits to allow. */
103
+ allow?: bigint;
104
+ /** Bits to deny. */
105
+ deny?: bigint;
106
+ /** Bits to reset to inherited (neither allowed nor denied). */
107
+ inherit?: bigint;
108
+ }
100
109
  /** Manages a channel's permission overwrites. Discord.js-familiar. */
101
110
  export declare class PermissionOverwriteManager {
102
111
  #private;
103
112
  readonly channelId: string;
104
- constructor(rest: REST, channelId: string);
113
+ /** @param current The channel's cached overwrites, read by {@link update}; the channel is fetched when they are unknown. */
114
+ constructor(rest: REST, channelId: string, current?: () => readonly APIOverwrite[] | undefined);
115
+ /**
116
+ * Changes some bits of one overwrite and keeps the rest -- unlike
117
+ * {@link edit}, which replaces the whole overwrite. Creates the overwrite
118
+ * when there is none (then `options.type` is required).
119
+ * @throws {TypeError} When the overwrite does not exist and no type is given.
120
+ */
121
+ update(overwriteId: string, changes: PermissionOverwriteUpdate, options?: {
122
+ type?: PermissionOverwriteTargetType;
123
+ reason?: string;
124
+ }): Promise<void>;
105
125
  /** Creates or replaces the overwrite for a role or member. */
106
126
  edit(overwriteId: string, options: PermissionOverwriteOptions, reason?: string): Promise<void>;
107
127
  /** Deletes the overwrite for a role or member. */
@@ -4,7 +4,7 @@ import { GuildSoundboardManager, GuildStickerManager } from "./advanced.js";
4
4
  import { GuildMemberManager } from "./member.js";
5
5
  import { RoleManager } from "./role.js";
6
6
  import { GuildBanManager, GuildScheduledEventManager } from "./guild-resources.js";
7
- import { Guild, AutoModerationRule } from "@lunibee/structures";
7
+ import { Guild, AutoModerationRule, type ResourceContext } from "@lunibee/structures";
8
8
  import { type REST } from "@lunibee/rest";
9
9
  import { type APIChannel, type APIGuildPreview, type APIInvite, type APIWebhook, type APIAutoModerationRule, type APIInviteCreate, type APIVoiceState } from "@lunibee/types";
10
10
  type GuildData = ConstructorParameters<typeof Guild>[0];
@@ -15,6 +15,8 @@ export interface GuildEditOptions extends Record<string, unknown> {
15
15
  }
16
16
  export declare class GuildManager extends ResourceManager<string, Guild> {
17
17
  #private;
18
+ /** Lets members from this manager act through a client (`member.kick()`...). Called by the client. */
19
+ attachContext(context: ResourceContext): void;
18
20
  constructor(rest: REST);
19
21
  /** Ban manager for a guild (one instance per guild). */
20
22
  bans(guildId: string): GuildBanManager;
@@ -135,11 +135,11 @@ export declare class ChannelManager extends Manager<string, Channel> {
135
135
  clear(): void;
136
136
  }
137
137
  export type CreateMessageOptions = MessageCreateOptions;
138
- export { MessageManager, type MessageCacheOptions } from "./message.js";
138
+ export { MessageManager, toRequest, type MessageCacheOptions, type MessageFile, } from "./message.js";
139
139
  export { ThreadManager } from "./thread.js";
140
140
  export { RoleManager, type RoleCreateOptions, type RoleEditOptions, } from "./role.js";
141
141
  export { GuildMemberManager, type MemberEditOptions, type BanOptions, } from "./member.js";
142
142
  export { ApplicationCommandManager } from "./application.js";
143
- export { GuildBanManager, GuildScheduledEventManager, StageInstanceManager, PermissionOverwriteManager, type APIBan, type GuildBanCreateOptions, type GuildScheduledEventOptions, type StageInstanceCreateOptions, type PermissionOverwriteOptions, type PermissionOverwriteTargetType, } from "./guild-resources.js";
143
+ export { GuildBanManager, GuildScheduledEventManager, StageInstanceManager, PermissionOverwriteManager, type APIBan, type GuildBanCreateOptions, type GuildScheduledEventOptions, type StageInstanceCreateOptions, type PermissionOverwriteOptions, type PermissionOverwriteTargetType, type PermissionOverwriteUpdate, } from "./guild-resources.js";
144
144
  export { EmojiManager, type EmojiCreateOptions, type EmojiEditOptions, } from "./emoji.js";
145
145
  export { GuildStickerManager, GuildSoundboardManager, MonetizationManager, type APIEntitlement, type APISKU, type APISoundboardSound, type APISubscription, type EntitlementQuery, type GuildStickerCreateOptions, type SoundboardSoundCreateOptions, } from "./advanced.js";
@@ -651,7 +651,7 @@ class ResourceManager extends Manager {
651
651
  // packages/rest/package.json
652
652
  var package_default = {
653
653
  name: "@lunibee/rest",
654
- version: "0.2.0",
654
+ version: "0.2.1",
655
655
  type: "module",
656
656
  module: "./src/index.ts",
657
657
  types: "./src/index.ts",
@@ -865,6 +865,11 @@ class Channel extends BaseStructure {
865
865
  editTopic(topic) {
866
866
  return this.edit({ topic });
867
867
  }
868
+ editPermissionOverwrite(targetId, changes, options) {
869
+ if (!this.#context?.editPermissionOverwrite)
870
+ throw new Error("This channel is not attached to a client.");
871
+ return this.#context.editPermissionOverwrite(this.id, targetId, changes, options);
872
+ }
868
873
  editParent(parentId) {
869
874
  return this.edit({ parent_id: parentId });
870
875
  }
@@ -999,6 +1004,7 @@ class Guild extends BaseStructure {
999
1004
  return this.name;
1000
1005
  }
1001
1006
  }
1007
+
1002
1008
  // packages/core/src/permissions.ts
1003
1009
  var Permission = {
1004
1010
  createInstantInvite: 1n << 0n,
@@ -1391,6 +1397,11 @@ class PermissionSet {
1391
1397
  toArray() {
1392
1398
  return Object.keys(Permission).filter((permission) => this.has(permission));
1393
1399
  }
1400
+ static #renamed = {
1401
+ ManageEmojisAndStickers: "ManageGuildExpressions",
1402
+ ManageEmojis: "ManageGuildExpressions",
1403
+ UseSlashCommands: "UseApplicationCommands"
1404
+ };
1394
1405
  #resolve(permission) {
1395
1406
  if (typeof permission === "bigint")
1396
1407
  return permission;
@@ -1405,6 +1416,9 @@ class PermissionSet {
1405
1416
  return bit;
1406
1417
  }
1407
1418
  if (typeof permission === "string") {
1419
+ const renamed = PermissionSet.#renamed[permission];
1420
+ if (renamed)
1421
+ return this.#resolve(renamed);
1408
1422
  if (permission in Permission)
1409
1423
  return Permission[permission];
1410
1424
  if (permission in Permissions)
@@ -1526,13 +1540,15 @@ class GuildMember {
1526
1540
  roleIds;
1527
1541
  pending;
1528
1542
  joinedAt;
1529
- permissions;
1530
1543
  avatarHash;
1531
1544
  premiumSince;
1532
1545
  timedOutUntil;
1533
1546
  flags;
1534
- constructor(data) {
1547
+ #suppliedPermissions;
1548
+ #context;
1549
+ constructor(data, context) {
1535
1550
  this.user = new User(data.user);
1551
+ this.#context = context;
1536
1552
  if (!/^\d{1,20}$/.test(data.guild_id))
1537
1553
  throw new TypeError("Member guild_id must be a valid snowflake.");
1538
1554
  this.guildId = data.guild_id;
@@ -1542,12 +1558,53 @@ class GuildMember {
1542
1558
  this.joinedAt = data.joined_at ? new Date(data.joined_at) : undefined;
1543
1559
  if (this.joinedAt?.toString() === "Invalid Date")
1544
1560
  throw new RangeError("Member joined_at must be a valid date.");
1545
- this.permissions = new PermissionsBitField(data.permissions ?? 0n);
1561
+ if (data.permissions !== undefined)
1562
+ this.#suppliedPermissions = new PermissionsBitField(data.permissions);
1546
1563
  this.avatarHash = data.avatar ?? null;
1547
1564
  this.premiumSince = toDateOrNull(data.premium_since);
1548
1565
  this.timedOutUntil = toDateOrNull(data.communication_disabled_until);
1549
1566
  this.flags = data.flags ?? 0;
1550
1567
  }
1568
+ get permissions() {
1569
+ if (this.#suppliedPermissions)
1570
+ return this.#suppliedPermissions;
1571
+ return new PermissionsBitField(this.#context?.memberPermissions?.(this.guildId, this.user.id, this.roleIds) ?? 0n);
1572
+ }
1573
+ permissionsIn(channelId) {
1574
+ const computed = this.#context?.memberPermissions?.(this.guildId, this.user.id, this.roleIds, channelId);
1575
+ return computed ? new PermissionsBitField(computed) : null;
1576
+ }
1577
+ #action(name) {
1578
+ const action = this.#context?.[name];
1579
+ if (!action)
1580
+ throw new Error("This member is not attached to a client.");
1581
+ return action;
1582
+ }
1583
+ kick(reason) {
1584
+ return this.#action("kickMember")(this.guildId, this.user.id, reason);
1585
+ }
1586
+ ban(options = {}) {
1587
+ return this.#action("banMember")(this.guildId, this.user.id, options);
1588
+ }
1589
+ edit(options, reason) {
1590
+ return this.#action("editMember")(this.guildId, this.user.id, options, reason);
1591
+ }
1592
+ timeout(milliseconds, reason) {
1593
+ if (milliseconds !== null && !(milliseconds > 0))
1594
+ throw new RangeError("Timeout length must be a positive number of milliseconds.");
1595
+ return this.edit({
1596
+ communication_disabled_until: milliseconds === null ? null : new Date(Date.now() + milliseconds).toISOString()
1597
+ }, reason);
1598
+ }
1599
+ setNickname(nickname, reason) {
1600
+ return this.edit({ nick: nickname }, reason);
1601
+ }
1602
+ addRole(roleId, reason) {
1603
+ return this.#action("addMemberRole")(this.guildId, this.user.id, roleId, reason);
1604
+ }
1605
+ removeRole(roleId, reason) {
1606
+ return this.#action("removeMemberRole")(this.guildId, this.user.id, roleId, reason);
1607
+ }
1551
1608
  get displayName() {
1552
1609
  return this.nickname ?? this.user.displayName;
1553
1610
  }
@@ -1795,6 +1852,7 @@ function createChannel2(data, context) {
1795
1852
  var TOKEN_LIFETIME_MS = 15 * 60000;
1796
1853
 
1797
1854
  // packages/structures/src/index.ts
1855
+ var COMPONENT_COLLECTOR_DEFAULT_TIME = 15 * 60000;
1798
1856
  var NON_SYSTEM_MESSAGE_TYPES = new Set([0, 19, 20, 23]);
1799
1857
 
1800
1858
  class Message extends BaseStructure {
@@ -1815,6 +1873,7 @@ class Message extends BaseStructure {
1815
1873
  reference;
1816
1874
  components;
1817
1875
  referencedMessage;
1876
+ member;
1818
1877
  #context;
1819
1878
  constructor(data, context) {
1820
1879
  super(data.id);
@@ -1837,6 +1896,11 @@ class Message extends BaseStructure {
1837
1896
  this.reference = data.message_reference;
1838
1897
  this.referencedMessage = data.referenced_message ? new Message(data.referenced_message, context) : data.referenced_message === null ? null : undefined;
1839
1898
  this.#context = context;
1899
+ this.member = data.member && data.guild_id ? new GuildMember({
1900
+ ...data.member,
1901
+ user: data.member.user ?? data.author,
1902
+ guild_id: data.guild_id
1903
+ }, context) : null;
1840
1904
  this.channel = new Channel({ id: data.channel_id, type: 0, guild_id: data.guild_id }, context);
1841
1905
  }
1842
1906
  get embedsSuppressed() {
@@ -1877,6 +1941,20 @@ class Message extends BaseStructure {
1877
1941
  throw new Error("This message is not attached to a client.");
1878
1942
  return this.#context.crosspostMessage(this.channelId, this.id);
1879
1943
  }
1944
+ createComponentCollector(options = {}) {
1945
+ const collect = this.#context?.collectInteractions;
1946
+ if (!collect)
1947
+ throw new Error("This message is not attached to a client.");
1948
+ const { componentType, filter, ...rest } = options;
1949
+ return collect({
1950
+ ...rest,
1951
+ time: rest.time ?? (rest.idle ? undefined : COMPONENT_COLLECTOR_DEFAULT_TIME),
1952
+ filter: async (interaction) => interaction.isMessageComponent() && interaction.messageId === this.id && (componentType === undefined || interaction.componentType === componentType) && (filter ? await filter(interaction) : true)
1953
+ });
1954
+ }
1955
+ awaitComponent(options = {}) {
1956
+ return this.createComponentCollector({ ...options, max: 1 }).next();
1957
+ }
1880
1958
  react(emoji) {
1881
1959
  if (!this.#context?.addReaction)
1882
1960
  throw new Error("This message is not attached to a client.");
@@ -1950,6 +2028,20 @@ class Message extends BaseStructure {
1950
2028
  }
1951
2029
 
1952
2030
  // packages/managers/src/message.ts
2031
+ function toRequest(options) {
2032
+ const { files, ...body } = options;
2033
+ if (!files?.length)
2034
+ return body;
2035
+ return {
2036
+ body,
2037
+ files: files.map((file) => ({
2038
+ name: file.name,
2039
+ data: typeof file.data === "string" ? new TextEncoder().encode(file.data) : file.data,
2040
+ contentType: file.contentType
2041
+ }))
2042
+ };
2043
+ }
2044
+
1953
2045
  class MessageManager {
1954
2046
  cache;
1955
2047
  #caching;
@@ -1972,7 +2064,7 @@ class MessageManager {
1972
2064
  return this.cache.get(messageId) ?? this.fetch(messageId);
1973
2065
  }
1974
2066
  async send(options) {
1975
- const data = await this.#rest.post(Routes.channelMessages(this.#channelId), options);
2067
+ const data = await this.#rest.post(Routes.channelMessages(this.#channelId), toRequest(options));
1976
2068
  return this.upsert(data);
1977
2069
  }
1978
2070
  async fetch(messageId) {
@@ -2253,14 +2345,20 @@ class MonetizationManager extends Manager {
2253
2345
  // packages/managers/src/member.ts
2254
2346
  class GuildMemberManager extends ResourceManager {
2255
2347
  #rest;
2348
+ #context;
2256
2349
  guildId;
2257
- constructor(guildId, rest) {
2258
- super((id) => rest.get(Routes.guildMember(guildId, id)).then((data) => new GuildMember({ ...data, guild_id: guildId })), (member) => member.user.id);
2350
+ constructor(guildId, rest, context = () => {
2351
+ return;
2352
+ }) {
2353
+ super((id) => rest.get(Routes.guildMember(guildId, id)).then((data) => new GuildMember({ ...data, guild_id: guildId }, context())), (member) => member.user.id);
2259
2354
  this.guildId = guildId;
2260
2355
  this.#rest = rest;
2356
+ this.#context = context;
2261
2357
  }
2262
- async kick(userId) {
2263
- await this.#rest.delete(Routes.guildMember(this.guildId, userId));
2358
+ async kick(userId, reason) {
2359
+ await this.#rest.delete(Routes.guildMember(this.guildId, userId), {
2360
+ reason
2361
+ });
2264
2362
  this.delete(userId);
2265
2363
  }
2266
2364
  async ban(userId, options = {}) {
@@ -2270,21 +2368,21 @@ class GuildMemberManager extends ResourceManager {
2270
2368
  async unban(userId) {
2271
2369
  await this.#rest.delete(Routes.guildBan(this.guildId, userId));
2272
2370
  }
2273
- async edit(userId, options) {
2274
- const data = await this.#rest.patch(Routes.guildMember(this.guildId, userId), options);
2275
- const member = new GuildMember({ ...data, guild_id: this.guildId });
2371
+ async edit(userId, options, reason) {
2372
+ const data = await this.#rest.patch(Routes.guildMember(this.guildId, userId), options, { reason });
2373
+ const member = new GuildMember({ ...data, guild_id: this.guildId }, this.#context());
2276
2374
  this.set(member.user.id, member);
2277
2375
  return member;
2278
2376
  }
2279
- async addRole(userId, roleId) {
2280
- await this.#rest.put(Routes.guildMemberRole(this.guildId, userId, roleId));
2377
+ async addRole(userId, roleId, reason) {
2378
+ await this.#rest.put(Routes.guildMemberRole(this.guildId, userId, roleId), undefined, { reason });
2281
2379
  }
2282
- async removeRole(userId, roleId) {
2283
- await this.#rest.delete(Routes.guildMemberRole(this.guildId, userId, roleId));
2380
+ async removeRole(userId, roleId, reason) {
2381
+ await this.#rest.delete(Routes.guildMemberRole(this.guildId, userId, roleId), { reason });
2284
2382
  }
2285
- async timeout(userId, milliseconds) {
2383
+ async timeout(userId, milliseconds, reason) {
2286
2384
  const timeoutDate = milliseconds === null ? null : new Date(Date.now() + milliseconds).toISOString();
2287
- return this.edit(userId, { communication_disabled_until: timeoutDate });
2385
+ return this.edit(userId, { communication_disabled_until: timeoutDate }, reason);
2288
2386
  }
2289
2387
  }
2290
2388
 
@@ -2455,10 +2553,25 @@ class StageInstanceManager extends Manager {
2455
2553
 
2456
2554
  class PermissionOverwriteManager {
2457
2555
  #rest;
2556
+ #current;
2458
2557
  channelId;
2459
- constructor(rest, channelId) {
2558
+ constructor(rest, channelId, current = () => {
2559
+ return;
2560
+ }) {
2460
2561
  this.#rest = rest;
2461
2562
  this.channelId = channelId;
2563
+ this.#current = current;
2564
+ }
2565
+ async update(overwriteId, changes, options = {}) {
2566
+ const overwrites = this.#current() ?? (await this.#rest.get(Routes.channel(this.channelId))).permission_overwrites ?? [];
2567
+ const existing = overwrites.find((entry) => entry.id === overwriteId);
2568
+ const type = existing?.type ?? options.type;
2569
+ if (type !== 0 && type !== 1)
2570
+ throw new TypeError("A new permission overwrite needs a type: 0 for a role, 1 for a member.");
2571
+ const touched = (changes.allow ?? 0n) | (changes.deny ?? 0n) | (changes.inherit ?? 0n);
2572
+ const allow = BigInt(existing?.allow ?? 0) & ~touched | (changes.allow ?? 0n);
2573
+ const deny = BigInt(existing?.deny ?? 0) & ~touched | (changes.deny ?? 0n);
2574
+ await this.edit(overwriteId, { type, allow, deny }, options.reason);
2462
2575
  }
2463
2576
  async edit(overwriteId, options, reason) {
2464
2577
  await this.#rest.put(Routes.channelPermission(this.channelId, overwriteId), {
@@ -2490,6 +2603,10 @@ class GuildManager extends ResourceManager {
2490
2603
  #voiceStates = new Map;
2491
2604
  #autoModerationRules = new Map;
2492
2605
  #invites = new Map;
2606
+ #context;
2607
+ attachContext(context) {
2608
+ this.#context = context;
2609
+ }
2493
2610
  #perGuild(map, guildId, create) {
2494
2611
  let manager = map.get(guildId);
2495
2612
  if (!manager)
@@ -2515,7 +2632,7 @@ class GuildManager extends ResourceManager {
2515
2632
  members(guildId) {
2516
2633
  let manager = this.#members.get(guildId);
2517
2634
  if (!manager)
2518
- this.#members.set(guildId, manager = new GuildMemberManager(guildId, this.#rest));
2635
+ this.#members.set(guildId, manager = new GuildMemberManager(guildId, this.#rest, () => this.#context));
2519
2636
  return manager;
2520
2637
  }
2521
2638
  roles(guildId) {
@@ -2771,7 +2888,7 @@ class ChannelManager extends Manager {
2771
2888
  return manager;
2772
2889
  }
2773
2890
  permissionOverwrites(channelId) {
2774
- return new PermissionOverwriteManager(this.#rest, channelId);
2891
+ return new PermissionOverwriteManager(this.#rest, channelId, () => this.get(channelId)?.permissionOverwrites);
2775
2892
  }
2776
2893
  threads(channelId) {
2777
2894
  return new ThreadManager(this.#rest, this.#context, channelId);
@@ -2871,7 +2988,7 @@ class ChannelManager extends Manager {
2871
2988
  return this.messages(data.channel_id).upsert(data);
2872
2989
  }
2873
2990
  async editMessage(channelId, messageId, options) {
2874
- return this.messages(channelId).upsert(await this.#rest.patch(Routes.message(channelId, messageId), options));
2991
+ return this.messages(channelId).upsert(await this.#rest.patch(Routes.message(channelId, messageId), toRequest(options)));
2875
2992
  }
2876
2993
  async deleteMessage(channelId, messageId) {
2877
2994
  await this.#rest.delete(Routes.message(channelId, messageId));
@@ -2994,7 +3111,8 @@ export {
2994
3111
  RoleManager,
2995
3112
  StageInstanceManager,
2996
3113
  ThreadManager,
2997
- UserManager
3114
+ UserManager,
3115
+ toRequest
2998
3116
  };
2999
3117
 
3000
- //# debugId=DAACDF8C2AA7984564756E2164756E21
3118
+ //# debugId=6C6CF1CF95B4E3CA64756E2164756E21