@stoatx/client 0.7.0 → 0.9.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.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/client/Client.ts
2
- import { EventEmitter as EventEmitter2 } from "events";
2
+ import { EventEmitter as EventEmitter4 } from "events";
3
3
 
4
4
  // src/gateway/GatewayManager.ts
5
5
  import WebSocket from "ws";
@@ -449,7 +449,7 @@ var MessageMentions = class _MessageMentions {
449
449
  static parseChannelIds(content) {
450
450
  return [...content.matchAll(/<#([A-Z0-9]+)>/g)].flatMap((m) => m[1] ? [m[1]] : []);
451
451
  }
452
- [util2.inspect.custom](depth, options, inspect12) {
452
+ [util2.inspect.custom](depth, options, inspect13) {
453
453
  const { client, message, ...props } = this;
454
454
  const data = {
455
455
  ...props,
@@ -458,7 +458,7 @@ var MessageMentions = class _MessageMentions {
458
458
  users: this.users,
459
459
  roles: this.roles
460
460
  };
461
- return `${this.constructor.name} ${inspect12(data, { ...options, depth: depth ?? options.depth })}`;
461
+ return `${this.constructor.name} ${inspect13(data, { ...options, depth: depth ?? options.depth })}`;
462
462
  }
463
463
  };
464
464
 
@@ -470,7 +470,8 @@ var Message = class extends Base {
470
470
  embeds = [];
471
471
  attachments = [];
472
472
  editedAt = null;
473
- createdAt = null;
473
+ createdAt;
474
+ createdTimestamp;
474
475
  flags = 0;
475
476
  interactions = null;
476
477
  masquerade = null;
@@ -482,10 +483,9 @@ var Message = class extends Base {
482
483
  super(client, data);
483
484
  this.authorId = data.author;
484
485
  this.channelId = data.channel;
485
- const timestamp = decodeTime(this.id);
486
- if (timestamp) {
487
- this.createdAt = new Date(timestamp);
488
- }
486
+ const timestamp = decodeTime(data._id);
487
+ this.createdAt = new Date(timestamp);
488
+ this.createdTimestamp = timestamp;
489
489
  if (data.attachments) {
490
490
  this.attachments = data.attachments.map((fileData) => new Attachment(this.client, fileData));
491
491
  }
@@ -666,7 +666,7 @@ var Message = class extends Base {
666
666
  }
667
667
  }
668
668
  // This tells Node.js exactly how to print this object in console.log()
669
- [util3.inspect.custom](depth, options, inspect12) {
669
+ [util3.inspect.custom](depth, options, inspect13) {
670
670
  const { client, authorId, channelId, ...props } = this;
671
671
  const data = {
672
672
  ...props,
@@ -675,11 +675,12 @@ var Message = class extends Base {
675
675
  server: this.server,
676
676
  member: this.member
677
677
  };
678
- return `${this.constructor.name} ${inspect12(data, { ...options, depth: depth ?? options.depth })}`;
678
+ return `${this.constructor.name} ${inspect13(data, { ...options, depth: depth ?? options.depth })}`;
679
679
  }
680
680
  };
681
681
 
682
682
  // src/structures/User.ts
683
+ import { decodeTime as decodeTime2 } from "ulid";
683
684
  var User = class extends Base {
684
685
  discriminator;
685
686
  online;
@@ -692,11 +693,17 @@ var User = class extends Base {
692
693
  flags;
693
694
  privileged;
694
695
  status;
696
+ createdAt;
697
+ createdTimestamp;
698
+ pronouns = null;
695
699
  constructor(client, data) {
696
700
  super(client, data);
697
701
  this.bot = false;
698
702
  this.privileged = false;
699
703
  this.flags = 0;
704
+ const timestamp = decodeTime2(data._id);
705
+ this.createdAt = new Date(timestamp);
706
+ this.createdTimestamp = timestamp;
700
707
  this._patch(data);
701
708
  }
702
709
  _patch(data, clear) {
@@ -704,6 +711,7 @@ var User = class extends Base {
704
711
  if (data.discriminator !== void 0) this.discriminator = data.discriminator;
705
712
  if (data.online !== void 0) this.online = data.online;
706
713
  if (data.relationship !== void 0) this.relationship = data.relationship;
714
+ if (data.pronouns !== void 0) this.pronouns = data.pronouns;
707
715
  if (data.display_name !== void 0) this.displayName = data.display_name;
708
716
  if (data.badges !== void 0) this.badges = data.badges;
709
717
  if (data.flags !== void 0) this.flags = data.flags;
@@ -717,9 +725,23 @@ var User = class extends Base {
717
725
  }
718
726
  if (clear && Array.isArray(clear)) {
719
727
  for (const field of clear) {
720
- if (field === "Avatar") this.avatar = null;
721
- if (field === "StatusText" && this.status) this.status.text = null;
722
- if (field === "DisplayName") this.displayName = null;
728
+ switch (field) {
729
+ case "Avatar":
730
+ this.avatar = null;
731
+ break;
732
+ case "DisplayName":
733
+ this.displayName = null;
734
+ break;
735
+ case "Pronouns":
736
+ this.pronouns = null;
737
+ break;
738
+ case "StatusText":
739
+ this.status.text = null;
740
+ break;
741
+ case "StatusPresence":
742
+ this.status.presence = null;
743
+ break;
744
+ }
723
745
  }
724
746
  }
725
747
  }
@@ -835,11 +857,10 @@ var GatewayManager = class {
835
857
  }
836
858
  if (payload.emojis) {
837
859
  for (const rawEmoji of payload.emojis) {
838
- const emoji = this.client.emojis._add(rawEmoji);
839
860
  if (rawEmoji.parent && rawEmoji.parent.type === "Server") {
840
861
  const server = this.client.servers.cache.get(rawEmoji.parent.id);
841
862
  if (server) {
842
- server.emojis._add(emoji);
863
+ server.emojis._add(rawEmoji);
843
864
  }
844
865
  }
845
866
  }
@@ -1017,7 +1038,7 @@ var GatewayManager = class {
1017
1038
  case "ServerMemberJoin": {
1018
1039
  const server = this.client.servers.cache.get(payload.id);
1019
1040
  if (server) {
1020
- const member = server.members._add({ user: payload.user });
1041
+ const member = server.members._add(payload.member);
1021
1042
  this.client.emit("serverMemberJoin", member);
1022
1043
  }
1023
1044
  break;
@@ -1035,7 +1056,7 @@ var GatewayManager = class {
1035
1056
  if (payload.reason === "Ban") {
1036
1057
  if (server) {
1037
1058
  const dummyBanPayload = {
1038
- id: payload.user,
1059
+ _id: payload.user,
1039
1060
  reason: null
1040
1061
  };
1041
1062
  server.bans._add(dummyBanPayload);
@@ -1049,11 +1070,10 @@ var GatewayManager = class {
1049
1070
  break;
1050
1071
  }
1051
1072
  case "EmojiCreate": {
1052
- const emoji = this.client.emojis._add(payload);
1053
1073
  if (payload.parent?.type === "Server") {
1054
1074
  const server = this.client.servers.cache.get(payload.parent.id);
1055
1075
  if (server) {
1056
- server.emojis._add(emoji);
1076
+ server.emojis._add(payload);
1057
1077
  }
1058
1078
  }
1059
1079
  break;
@@ -1355,13 +1375,14 @@ var RESTManager = class {
1355
1375
  this.client.emit("debug", `Bucket [${method}:${endpoint}] exhausted. Waiting ${waitTime}ms proactively...`);
1356
1376
  await sleep(waitTime);
1357
1377
  }
1378
+ const isBodyMethod = ["post", "patch", "put"].includes(method.toLowerCase());
1358
1379
  const response = await fetch(url, {
1359
1380
  method: method.toUpperCase(),
1360
1381
  headers: {
1361
1382
  "X-Bot-Token": this.token,
1362
1383
  "Content-Type": "application/json"
1363
1384
  },
1364
- ...body !== void 0 ? { body: JSON.stringify(body) } : {}
1385
+ ...isBodyMethod ? { body: JSON.stringify(body ?? {}) } : {}
1365
1386
  });
1366
1387
  const remainingHeader = response.headers.get("x-ratelimit-remaining");
1367
1388
  const resetAfterHeader = response.headers.get("x-ratelimit-reset-after");
@@ -1452,7 +1473,7 @@ var BaseManager = class {
1452
1473
  _add(data) {
1453
1474
  const id = this.extractId(data);
1454
1475
  const existing = this.cache.get(id);
1455
- if (existing && typeof existing._patch === "function") {
1476
+ if (existing && this.isPatchable(existing)) {
1456
1477
  existing._patch(data);
1457
1478
  return existing;
1458
1479
  }
@@ -1460,6 +1481,9 @@ var BaseManager = class {
1460
1481
  this.cache.set(id, structure);
1461
1482
  return structure;
1462
1483
  }
1484
+ isPatchable(value) {
1485
+ return typeof value._patch === "function";
1486
+ }
1463
1487
  };
1464
1488
 
1465
1489
  // src/builders/AttachmentBuilder.ts
@@ -1897,9 +1921,13 @@ var BaseChannel = class extends Base {
1897
1921
  isGroup() {
1898
1922
  return this.type === "Group";
1899
1923
  }
1924
+ isVoice() {
1925
+ return this.isText() && this.voice !== null;
1926
+ }
1900
1927
  };
1901
1928
 
1902
1929
  // src/structures/TextChannel.ts
1930
+ import { decodeTime as decodeTime3 } from "ulid";
1903
1931
  var TextChannel = class extends BaseChannel {
1904
1932
  name;
1905
1933
  serverId;
@@ -1910,9 +1938,14 @@ var TextChannel = class extends BaseChannel {
1910
1938
  nsfw;
1911
1939
  slowmode;
1912
1940
  voice;
1941
+ createdAt;
1942
+ createdTimestamp;
1913
1943
  constructor(client, data) {
1914
1944
  super(client, data);
1915
1945
  this.serverId = data.server;
1946
+ const timestamp = decodeTime3(data._id);
1947
+ this.createdTimestamp = timestamp;
1948
+ this.createdAt = new Date(timestamp);
1916
1949
  this.defaultPermissions = data.default_permissions;
1917
1950
  this.description = data.description;
1918
1951
  if (data.icon !== void 0) {
@@ -1937,10 +1970,10 @@ var TextChannel = class extends BaseChannel {
1937
1970
  this.icon = null;
1938
1971
  break;
1939
1972
  case "DefaultPermissions":
1940
- this.defaultPermissions = data.default_permissions;
1973
+ this.defaultPermissions = null;
1941
1974
  break;
1942
1975
  case "Voice":
1943
- this.voice = data.voice;
1976
+ this.voice = null;
1944
1977
  break;
1945
1978
  }
1946
1979
  }
@@ -2135,6 +2168,7 @@ var DMChannel = class extends BaseChannel {
2135
2168
 
2136
2169
  // src/structures/GroupChannel.ts
2137
2170
  import * as util5 from "util";
2171
+ import { decodeTime as decodeTime4 } from "ulid";
2138
2172
  var GroupChannel = class extends BaseChannel {
2139
2173
  name;
2140
2174
  ownerId;
@@ -2143,8 +2177,13 @@ var GroupChannel = class extends BaseChannel {
2143
2177
  icon = null;
2144
2178
  lastMessageId = null;
2145
2179
  nsfw = false;
2180
+ createdAt;
2181
+ createdTimestamp;
2146
2182
  constructor(client, data) {
2147
2183
  super(client, data);
2184
+ const timestamp = decodeTime4(data._id);
2185
+ this.createdAt = new Date(timestamp);
2186
+ this.createdTimestamp = timestamp;
2148
2187
  this._patch(data);
2149
2188
  }
2150
2189
  _patch(data, clear) {
@@ -2160,9 +2199,6 @@ var GroupChannel = class extends BaseChannel {
2160
2199
  if (clear && Array.isArray(clear)) {
2161
2200
  for (const field of clear) {
2162
2201
  switch (field) {
2163
- case "Name":
2164
- this.name = "";
2165
- break;
2166
2202
  case "Description":
2167
2203
  this.description = null;
2168
2204
  break;
@@ -2194,9 +2230,9 @@ var GroupChannel = class extends BaseChannel {
2194
2230
  async setDefaultPermissions(permissions) {
2195
2231
  return await this.client.channels.setDefaultPermissions(this.id, permissions);
2196
2232
  }
2197
- [util5.inspect.custom](_depth, options, inspect12) {
2233
+ [util5.inspect.custom](_depth, options, inspect13) {
2198
2234
  const { client, ...props } = this;
2199
- return `${this.constructor.name} ${inspect12(
2235
+ return `${this.constructor.name} ${inspect13(
2200
2236
  {
2201
2237
  ...props,
2202
2238
  owner: this.owner,
@@ -2207,10 +2243,21 @@ var GroupChannel = class extends BaseChannel {
2207
2243
  }
2208
2244
  };
2209
2245
 
2246
+ // src/structures/VoiceChannel.ts
2247
+ var VoiceChannel = class extends TextChannel {
2248
+ async join() {
2249
+ return this.client.voice.join(this.id, this.serverId);
2250
+ }
2251
+ async leave() {
2252
+ return this.client.voice.leave(this.id);
2253
+ }
2254
+ };
2255
+
2210
2256
  // src/utils/ChannelFactory.ts
2211
2257
  function createChannel(client, data) {
2212
2258
  switch (data.channel_type) {
2213
2259
  case "TextChannel":
2260
+ if (data.voice) return new VoiceChannel(client, data);
2214
2261
  return new TextChannel(client, data);
2215
2262
  case "DirectMessage":
2216
2263
  return new DMChannel(client, data);
@@ -2832,6 +2879,8 @@ var Member = class extends Base {
2832
2879
  _roles = [];
2833
2880
  // The date the member joined the server
2834
2881
  joinedAt;
2882
+ // The timestamp the member joined the server
2883
+ joinedTimestamp;
2835
2884
  // The date the member's timeout expires, or null if not timed out
2836
2885
  timeout = null;
2837
2886
  // Whatever the user can talk in Voice Chat
@@ -2840,20 +2889,53 @@ var Member = class extends Base {
2840
2889
  canRecieve = false;
2841
2890
  // Member roles manager
2842
2891
  roles;
2892
+ // Member pronouns
2893
+ pronouns = null;
2843
2894
  constructor(client, data, serverId) {
2844
2895
  super(client, { _id: data._id?.user });
2845
2896
  this.serverId = data._id?.server ?? serverId;
2846
- this.joinedAt = new Date(data.joined_at);
2897
+ const timestamp = new Date(data.joined_at);
2898
+ this.joinedAt = timestamp;
2899
+ this.joinedTimestamp = timestamp.getTime();
2847
2900
  this.roles = new MemberRoleManager(this);
2848
2901
  this._patch(data);
2849
2902
  }
2850
- _patch(data) {
2903
+ _patch(data, clear) {
2851
2904
  if (data.nickname !== void 0) this.nickname = data.nickname;
2852
2905
  if (data.avatar !== void 0) this.avatar = data.avatar ? new Attachment(this.client, data.avatar) : null;
2853
2906
  if (data.roles !== void 0) this._roles = data.roles;
2854
2907
  if (data.timeout !== void 0) this.timeout = data.timeout ? new Date(data.timeout) : null;
2855
2908
  if (data.can_publish !== void 0) this.canPublish = data.can_publish;
2856
2909
  if (data.can_receive !== void 0) this.canRecieve = data.can_receive;
2910
+ if (data.pronouns !== void 0) this.pronouns = data.pronouns;
2911
+ if (clear && Array.isArray(clear)) {
2912
+ for (const field of clear) {
2913
+ switch (field) {
2914
+ case "Avatar":
2915
+ this.avatar = null;
2916
+ break;
2917
+ case "Nickname":
2918
+ this.nickname = null;
2919
+ break;
2920
+ case "Pronouns":
2921
+ this.pronouns = null;
2922
+ break;
2923
+ case "CanPublish":
2924
+ this.canPublish = false;
2925
+ break;
2926
+ case "Timeout":
2927
+ this.timeout = null;
2928
+ break;
2929
+ case "Roles":
2930
+ this._roles = [];
2931
+ this.roles = new MemberRoleManager(this);
2932
+ break;
2933
+ case "CanReceive":
2934
+ this.canRecieve = false;
2935
+ break;
2936
+ }
2937
+ }
2938
+ }
2857
2939
  }
2858
2940
  /**
2859
2941
  * Get member role IDs
@@ -2973,9 +3055,9 @@ var Member = class extends Base {
2973
3055
  await server.members.kick(this.id);
2974
3056
  }
2975
3057
  /** @internal */
2976
- [util6.inspect.custom](depth, options, inspect12) {
3058
+ [util6.inspect.custom](depth, options, inspect13) {
2977
3059
  const { client, serverId, ...props } = this;
2978
- return `${this.constructor.name} ${inspect12(
3060
+ return `${this.constructor.name} ${inspect13(
2979
3061
  {
2980
3062
  ...props,
2981
3063
  user: this.user,
@@ -3206,6 +3288,7 @@ var ServerChannelManager = class {
3206
3288
 
3207
3289
  // src/structures/Role.ts
3208
3290
  import * as util9 from "util";
3291
+ import { decodeTime as decodeTime5 } from "ulid";
3209
3292
  var Role = class extends Base {
3210
3293
  serverId;
3211
3294
  name;
@@ -3214,16 +3297,21 @@ var Role = class extends Base {
3214
3297
  rank = 0;
3215
3298
  icon = null;
3216
3299
  _permissions = 0n;
3300
+ createdAt;
3301
+ createdTimestamp;
3217
3302
  constructor(client, data, serverId) {
3218
3303
  super(client, data);
3219
3304
  this.serverId = serverId;
3305
+ const timestamp = decodeTime5(data._id);
3306
+ this.createdAt = new Date(timestamp);
3307
+ this.createdTimestamp = timestamp;
3220
3308
  this._patch(data);
3221
3309
  }
3222
3310
  /**
3223
3311
  * Updates the role instance with new data without losing the object reference.
3224
3312
  * @internal
3225
3313
  */
3226
- _patch(data) {
3314
+ _patch(data, clear) {
3227
3315
  if (data.name !== void 0) this.name = data.name;
3228
3316
  if (data.colour !== void 0) this.color = data.colour;
3229
3317
  if (data.hoist !== void 0) this.hoist = data.hoist;
@@ -3241,6 +3329,18 @@ var Role = class extends Base {
3241
3329
  this._permissions = 0n;
3242
3330
  }
3243
3331
  }
3332
+ if (clear && Array.isArray(clear)) {
3333
+ for (const field of clear) {
3334
+ switch (field) {
3335
+ case "Colour":
3336
+ this.color = null;
3337
+ break;
3338
+ case "Icon":
3339
+ this.icon = null;
3340
+ break;
3341
+ }
3342
+ }
3343
+ }
3244
3344
  }
3245
3345
  /**
3246
3346
  * The server this role belongs to.
@@ -3736,7 +3836,7 @@ var ServerBanManager = class extends BaseManager {
3736
3836
  this.server = server;
3737
3837
  }
3738
3838
  extractId(data) {
3739
- return data._id?.user ?? data.id;
3839
+ return data._id?.user;
3740
3840
  }
3741
3841
  construct(data) {
3742
3842
  return new ServerBan(data);
@@ -3748,11 +3848,6 @@ var ServerBanManager = class extends BaseManager {
3748
3848
  */
3749
3849
  async fetch() {
3750
3850
  const data = await this.client.rest.get(`/servers/${this.server.id}/bans`);
3751
- if (data.users && Array.isArray(data.users)) {
3752
- for (const userData of data.users) {
3753
- this.client.users._add(userData);
3754
- }
3755
- }
3756
3851
  const rawBans = data.bans || (Array.isArray(data) ? data : []);
3757
3852
  const fetched = new Collection();
3758
3853
  for (const rawBan of rawBans) {
@@ -3773,14 +3868,20 @@ var ServerBanManager = class extends BaseManager {
3773
3868
  };
3774
3869
 
3775
3870
  // src/structures/Emoji.ts
3871
+ import { decodeTime as decodeTime6 } from "ulid";
3776
3872
  var Emoji = class extends Base {
3777
3873
  creatorId;
3778
3874
  name;
3779
3875
  parent;
3876
+ createdAt;
3877
+ createdTimestamp;
3780
3878
  animated = false;
3781
3879
  nsfw = false;
3782
3880
  constructor(client, data) {
3783
3881
  super(client, data);
3882
+ const timestamp = decodeTime6(data._id);
3883
+ this.createdAt = new Date(timestamp);
3884
+ this.createdTimestamp = timestamp;
3784
3885
  this._patch(data);
3785
3886
  }
3786
3887
  _patch(data) {
@@ -3958,6 +4059,114 @@ var EmojiManager = class extends BaseManager {
3958
4059
  }
3959
4060
  };
3960
4061
 
4062
+ // src/structures/Server.ts
4063
+ import { decodeTime as decodeTime7 } from "ulid";
4064
+
4065
+ // src/structures/AuditLogEntry.ts
4066
+ import * as util12 from "util";
4067
+ function resolveTargetId(data) {
4068
+ if (data.target) return data.target;
4069
+ const action = data.action;
4070
+ switch (action.type) {
4071
+ case "ChannelCreate":
4072
+ case "ChannelEdit":
4073
+ case "ChannelDelete":
4074
+ case "ChannelRolePermissionsEdit":
4075
+ return action.channel;
4076
+ case "RoleCreate":
4077
+ case "RoleEdit":
4078
+ case "RoleDelete":
4079
+ return action.role;
4080
+ case "InviteCreate":
4081
+ case "InviteDelete":
4082
+ return action.invite;
4083
+ case "WebhookCreate":
4084
+ case "WebhookDelete":
4085
+ return action.webhook;
4086
+ case "EmojiCreate":
4087
+ case "EmojiUpdate":
4088
+ case "EmojiDelete":
4089
+ return action.emoji;
4090
+ case "MessageDelete":
4091
+ return action.channel;
4092
+ case "MessagePin":
4093
+ case "MessageUnpin":
4094
+ return action.message;
4095
+ case "MessageBulkDelete":
4096
+ return action.channel;
4097
+ default:
4098
+ return null;
4099
+ }
4100
+ }
4101
+ var AuditLogEntry = class extends Base {
4102
+ serverId;
4103
+ userId;
4104
+ targetId;
4105
+ reason;
4106
+ action;
4107
+ constructor(client, data) {
4108
+ super(client, data);
4109
+ this.serverId = data.server;
4110
+ this.userId = data.user;
4111
+ this.targetId = resolveTargetId(data);
4112
+ this.reason = data.reason ?? null;
4113
+ this.action = data.action;
4114
+ }
4115
+ /** The action tag, e.g. "ChannelEdit" — narrow `entry.action` on this. */
4116
+ get type() {
4117
+ return this.action.type;
4118
+ }
4119
+ get server() {
4120
+ return this.client.servers.cache.get(this.serverId);
4121
+ }
4122
+ /** The user who performed this action, if cached. */
4123
+ get executor() {
4124
+ return this.client.users.cache.get(this.userId);
4125
+ }
4126
+ /**
4127
+ * Best-effort resolve of `targetId` into the actual entity, picked by
4128
+ * `action.type` since a bare id alone doesn't say whether it's a member,
4129
+ * a role, a channel, etc.
4130
+ */
4131
+ get target() {
4132
+ if (!this.targetId) return void 0;
4133
+ const server = this.server;
4134
+ switch (this.action.type) {
4135
+ case "MemberEdit":
4136
+ case "MemberKick":
4137
+ return server?.members.cache.get(this.targetId);
4138
+ case "BanCreate":
4139
+ case "BanDelete":
4140
+ return this.client.users.cache.get(this.targetId);
4141
+ case "ChannelCreate":
4142
+ case "ChannelEdit":
4143
+ case "ChannelDelete":
4144
+ case "ChannelRolePermissionsEdit":
4145
+ return server?.channels.cache.get(this.targetId);
4146
+ case "RoleCreate":
4147
+ case "RoleEdit":
4148
+ case "RoleDelete":
4149
+ return server?.roles.cache.get(this.targetId);
4150
+ default:
4151
+ return this.targetId;
4152
+ }
4153
+ }
4154
+ /**
4155
+ * Customizer for Node.js `console.log` and `util.inspect`.
4156
+ * Hides the cyclic client reference for a cleaner output.
4157
+ * @internal
4158
+ */
4159
+ [util12.inspect.custom]() {
4160
+ const { client, ...props } = this;
4161
+ return `${this.constructor.name} ${util12.inspect({
4162
+ ...props,
4163
+ type: this.type,
4164
+ executor: this.executor,
4165
+ target: this.target
4166
+ })}`;
4167
+ }
4168
+ };
4169
+
3961
4170
  // src/structures/Server.ts
3962
4171
  var Server = class extends Base {
3963
4172
  channelIds = [];
@@ -3978,6 +4187,8 @@ var Server = class extends Base {
3978
4187
  bans;
3979
4188
  invites;
3980
4189
  emojis;
4190
+ createdAt;
4191
+ createdTimestamp;
3981
4192
  constructor(client, data) {
3982
4193
  super(client, data);
3983
4194
  this.channels = new ServerChannelManager(client, this);
@@ -3986,6 +4197,9 @@ var Server = class extends Base {
3986
4197
  this.bans = new ServerBanManager(this.client, this);
3987
4198
  this.invites = new ServerInviteManager(this.client, this);
3988
4199
  this.emojis = new EmojiManager(this.client, this);
4200
+ const timestamp = decodeTime7(data._id);
4201
+ this.createdAt = new Date(timestamp);
4202
+ this.createdTimestamp = timestamp;
3989
4203
  this._patch(data);
3990
4204
  }
3991
4205
  /**
@@ -4028,6 +4242,12 @@ var Server = class extends Base {
4028
4242
  case "Icon":
4029
4243
  this.icon = null;
4030
4244
  break;
4245
+ case "Banner":
4246
+ this.banner = null;
4247
+ break;
4248
+ case "Categories":
4249
+ this.categories = null;
4250
+ break;
4031
4251
  }
4032
4252
  }
4033
4253
  }
@@ -4046,6 +4266,27 @@ var Server = class extends Base {
4046
4266
  async leave() {
4047
4267
  return this.client.rest.delete(`/servers/${this.id}/leave`);
4048
4268
  }
4269
+ /**
4270
+ * Fetch audit logs for this server
4271
+ * @param options Query params
4272
+ */
4273
+ async fetchAuditLogs(options) {
4274
+ const query = {};
4275
+ if (options?.user) query.user = options.user;
4276
+ if (options?.target) query.target = options.target;
4277
+ if (options?.type) query.type = options.type;
4278
+ if (options?.before) query.before = options.before;
4279
+ if (options?.after) query.after = options.after;
4280
+ if (options?.limit) query.limit = options.limit;
4281
+ const data = await this.client.rest.get(`/servers/${this.id}/audit_logs`, query);
4282
+ for (const user of data.users) this.client.users._add(user);
4283
+ for (const member of data.members) this.members._add(member);
4284
+ const entries = new Collection();
4285
+ for (const raw of data.audit_logs) {
4286
+ entries.set(raw._id, new AuditLogEntry(this.client, raw));
4287
+ }
4288
+ return entries;
4289
+ }
4049
4290
  /**
4050
4291
  * Fetches multiple members from this server.
4051
4292
  * @param options Filter options for the fetch request.
@@ -4057,7 +4298,7 @@ var Server = class extends Base {
4057
4298
  };
4058
4299
 
4059
4300
  // src/managers/ServerManager.ts
4060
- import * as util12 from "util";
4301
+ import * as util13 from "util";
4061
4302
  var ServerManager = class extends BaseManager {
4062
4303
  constructor(client, limit = Infinity) {
4063
4304
  super(client, limit);
@@ -4128,7 +4369,7 @@ var ServerManager = class extends BaseManager {
4128
4369
  const data = await this.client.rest.patch(`/servers/${serverId}`, payload);
4129
4370
  return this._add(data);
4130
4371
  }
4131
- [util12.inspect.custom]() {
4372
+ [util13.inspect.custom]() {
4132
4373
  return this.cache;
4133
4374
  }
4134
4375
  };
@@ -4356,8 +4597,247 @@ var SweeperManager = class {
4356
4597
  }
4357
4598
  };
4358
4599
 
4600
+ // src/voice/VoiceConnection.ts
4601
+ import {
4602
+ Room,
4603
+ RoomEvent,
4604
+ TrackSource,
4605
+ LocalAudioTrack,
4606
+ TrackPublishOptions,
4607
+ AudioFrame,
4608
+ AudioSource
4609
+ } from "@livekit/rtc-node";
4610
+ import { EventEmitter as EventEmitter2 } from "events";
4611
+ var SAMPLE_RATE = 48e3;
4612
+ var CHANNELS = 2;
4613
+ var SAMPLES_PER_FRAME = 960;
4614
+ var BYTES_PER_FRAME = SAMPLES_PER_FRAME * CHANNELS * 2;
4615
+ var VoiceConnection = class extends EventEmitter2 {
4616
+ channelId;
4617
+ guildId;
4618
+ room;
4619
+ _status = "connecting";
4620
+ _player = null;
4621
+ _audioSource = null;
4622
+ _audioTrack = null;
4623
+ constructor(channelId, guildId) {
4624
+ super();
4625
+ this.channelId = channelId;
4626
+ this.guildId = guildId;
4627
+ this.room = new Room();
4628
+ this.room.on(RoomEvent.Disconnected, () => {
4629
+ this._status = "disconnected";
4630
+ this._player?.removeSubscriber(this);
4631
+ this.emit("disconnect");
4632
+ });
4633
+ }
4634
+ get status() {
4635
+ return this._status;
4636
+ }
4637
+ get player() {
4638
+ return this._player;
4639
+ }
4640
+ /** @internal */
4641
+ async connect(url, token) {
4642
+ await this.room.connect(url, token);
4643
+ this._status = "ready";
4644
+ this.emit("ready");
4645
+ }
4646
+ subscribe(player) {
4647
+ if (this._player) this._player.removeSubscriber(this);
4648
+ this._player = player;
4649
+ player.addSubscriber(this);
4650
+ }
4651
+ unsubscribe() {
4652
+ this._player?.removeSubscriber(this);
4653
+ this._player = null;
4654
+ }
4655
+ async _feedStream(stream) {
4656
+ if (!this._audioSource) {
4657
+ this._audioSource = new AudioSource(SAMPLE_RATE, CHANNELS, SAMPLES_PER_FRAME);
4658
+ this._audioTrack = LocalAudioTrack.createAudioTrack("audio", this._audioSource);
4659
+ const options = new TrackPublishOptions();
4660
+ options.source = TrackSource.SOURCE_MICROPHONE;
4661
+ if (!this.room.localParticipant) {
4662
+ throw new Error("Not connected to a room");
4663
+ }
4664
+ await this.room.localParticipant.publishTrack(this._audioTrack, options);
4665
+ }
4666
+ let leftover = Buffer.alloc(0);
4667
+ for await (const chunk of stream) {
4668
+ const buf = Buffer.concat([leftover, chunk]);
4669
+ let offset = 0;
4670
+ while (offset + BYTES_PER_FRAME <= buf.length) {
4671
+ const slice = buf.subarray(offset, offset + BYTES_PER_FRAME);
4672
+ const tmp = slice.buffer.slice(slice.byteOffset, slice.byteOffset + slice.byteLength);
4673
+ const pcm = new Int16Array(tmp);
4674
+ await this._audioSource.captureFrame(new AudioFrame(pcm, SAMPLE_RATE, CHANNELS, SAMPLES_PER_FRAME));
4675
+ offset += BYTES_PER_FRAME;
4676
+ }
4677
+ leftover = buf.subarray(offset);
4678
+ }
4679
+ }
4680
+ async disconnect() {
4681
+ this._status = "disconnecting";
4682
+ this.unsubscribe();
4683
+ if (this._audioTrack) {
4684
+ await this._audioTrack.close();
4685
+ this._audioTrack = null;
4686
+ this._audioSource = null;
4687
+ }
4688
+ await this.room.disconnect();
4689
+ }
4690
+ };
4691
+
4692
+ // src/voice/VoiceManager.ts
4693
+ var VoiceManager = class {
4694
+ connections = /* @__PURE__ */ new Map();
4695
+ client;
4696
+ constructor(client) {
4697
+ this.client = client;
4698
+ }
4699
+ async join(channelId, guildId) {
4700
+ const existing = this.connections.get(channelId);
4701
+ if (existing?.status === "ready") return existing;
4702
+ const { token, url } = await this.client.rest.post(`/channels/${channelId}/join_call`);
4703
+ const connection = new VoiceConnection(channelId, guildId);
4704
+ this.connections.set(channelId, connection);
4705
+ connection.once("disconnect", () => this.connections.delete(channelId));
4706
+ await connection.connect(url, token);
4707
+ return connection;
4708
+ }
4709
+ get(channelId) {
4710
+ return this.connections.get(channelId);
4711
+ }
4712
+ async leave(channelId) {
4713
+ await this.connections.get(channelId)?.disconnect();
4714
+ }
4715
+ async leaveAll() {
4716
+ await Promise.all([...this.connections.keys()].map((id) => this.leave(id)));
4717
+ }
4718
+ };
4719
+
4720
+ // src/voice/AudioPlayer.ts
4721
+ import { EventEmitter as EventEmitter3 } from "events";
4722
+ var AudioPlayer = class extends EventEmitter3 {
4723
+ _status = "idle";
4724
+ _resource = null;
4725
+ /** Registered VoiceConnections subscribed to this player */
4726
+ subscribers = /* @__PURE__ */ new Set();
4727
+ get status() {
4728
+ return this._status;
4729
+ }
4730
+ get resource() {
4731
+ return this._resource;
4732
+ }
4733
+ play(resource) {
4734
+ this.transition("buffering");
4735
+ this._resource = resource;
4736
+ resource.stream.once("readable", () => {
4737
+ this.transition("playing");
4738
+ this.feed();
4739
+ });
4740
+ resource.stream.once("end", () => {
4741
+ this._resource = null;
4742
+ this.transition("idle");
4743
+ this.emit("idle");
4744
+ });
4745
+ resource.stream.once("error", (err) => {
4746
+ this._resource = null;
4747
+ this.transition("idle");
4748
+ this.emit("error", err);
4749
+ });
4750
+ }
4751
+ pause() {
4752
+ if (this._status !== "playing") return;
4753
+ this._resource?.stream.pause();
4754
+ this.transition("paused");
4755
+ }
4756
+ resume() {
4757
+ if (this._status !== "paused") return;
4758
+ this._resource?.stream.resume();
4759
+ this.transition("playing");
4760
+ }
4761
+ stop() {
4762
+ this._resource?.stream.destroy();
4763
+ this._resource = null;
4764
+ this.transition("stopped");
4765
+ this.transition("idle");
4766
+ this.emit("idle");
4767
+ }
4768
+ /** @internal */
4769
+ addSubscriber(conn) {
4770
+ this.subscribers.add(conn);
4771
+ }
4772
+ /** @internal */
4773
+ removeSubscriber(conn) {
4774
+ this.subscribers.delete(conn);
4775
+ }
4776
+ feed() {
4777
+ if (!this._resource) return;
4778
+ for (const conn of this.subscribers) {
4779
+ conn._feedStream(this._resource.stream);
4780
+ }
4781
+ }
4782
+ transition(next) {
4783
+ const prev = this._status;
4784
+ this._status = next;
4785
+ this.emit("stateChange", prev, next);
4786
+ }
4787
+ };
4788
+
4789
+ // src/voice/AudioResource.ts
4790
+ import { Readable } from "stream";
4791
+ import { spawn } from "child_process";
4792
+ import { PassThrough } from "stream";
4793
+ import { existsSync } from "fs";
4794
+ var AudioResource = class _AudioResource {
4795
+ stream;
4796
+ constructor(stream) {
4797
+ this.stream = stream;
4798
+ }
4799
+ static from(source, options = {}) {
4800
+ if (typeof source === "string" && !source.startsWith("http") && !existsSync(source)) {
4801
+ throw new Error(`Audio file not found: ${source}`);
4802
+ }
4803
+ const { volume = 1, inputType } = options;
4804
+ const args = [
4805
+ ...inputType ? ["-f", inputType] : [],
4806
+ "-i",
4807
+ typeof source === "string" ? source : "pipe:0",
4808
+ "-af",
4809
+ `volume=${volume}`,
4810
+ "-ar",
4811
+ "48000",
4812
+ "-ac",
4813
+ "2",
4814
+ "-f",
4815
+ "s16le",
4816
+ "-acodec",
4817
+ "pcm_s16le",
4818
+ "pipe:1"
4819
+ ];
4820
+ const ffmpeg = spawn("ffmpeg", args, { stdio: ["pipe", "pipe", "ignore"] });
4821
+ if (source instanceof Readable) {
4822
+ source.pipe(ffmpeg.stdin);
4823
+ source.once("error", () => ffmpeg.kill());
4824
+ }
4825
+ ffmpeg.stdin?.on("error", () => {
4826
+ });
4827
+ const pass = new PassThrough();
4828
+ ffmpeg.stdout.pipe(pass);
4829
+ ffmpeg.once("error", (err) => pass.destroy(err));
4830
+ ffmpeg.once("close", (code) => {
4831
+ if (code !== 0 && code !== null) {
4832
+ pass.destroy(new Error(`ffmpeg exited with code ${code}`));
4833
+ }
4834
+ });
4835
+ return new _AudioResource(pass);
4836
+ }
4837
+ };
4838
+
4359
4839
  // src/client/Client.ts
4360
- var Client = class extends EventEmitter2 {
4840
+ var Client = class extends EventEmitter4 {
4361
4841
  rest;
4362
4842
  gateway;
4363
4843
  channels;
@@ -4367,6 +4847,7 @@ var Client = class extends EventEmitter2 {
4367
4847
  emojis;
4368
4848
  user = null;
4369
4849
  options;
4850
+ voice;
4370
4851
  constructor(options = {}) {
4371
4852
  super({ captureRejections: true });
4372
4853
  this.options = options;
@@ -4376,6 +4857,7 @@ var Client = class extends EventEmitter2 {
4376
4857
  this.servers = new ServerManager(this, options.cacheLimits?.servers);
4377
4858
  this.users = new UserManager(this, options.cacheLimits?.users);
4378
4859
  this.emojis = new EmojiManager(this, void 0, options.cacheLimits?.emojis);
4860
+ this.voice = new VoiceManager(this);
4379
4861
  this.sweepers = new SweeperManager(this, options.sweepers ?? {});
4380
4862
  }
4381
4863
  /**
@@ -4465,11 +4947,14 @@ var EmbedBuilder = class {
4465
4947
  };
4466
4948
 
4467
4949
  // src/index.ts
4950
+ import { decodeTime as decodeTime8 } from "ulid";
4468
4951
  import * as API from "stoat-api";
4469
4952
  export {
4470
4953
  API,
4471
4954
  Attachment,
4472
4955
  AttachmentBuilder,
4956
+ AudioPlayer,
4957
+ AudioResource,
4473
4958
  Base,
4474
4959
  BaseChannel,
4475
4960
  BitField,
@@ -4504,6 +4989,9 @@ export {
4504
4989
  TextChannel,
4505
4990
  UnknownChannel,
4506
4991
  User,
4507
- UserManager
4992
+ UserManager,
4993
+ VoiceConnection,
4994
+ VoiceManager,
4995
+ decodeTime8 as decodeTime
4508
4996
  };
4509
4997
  //# sourceMappingURL=index.js.map