district.js 0.2.0 → 0.3.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.
@@ -0,0 +1,15 @@
1
+ export declare const ChannelType: {
2
+ readonly Text: "text";
3
+ readonly Voice: "voice";
4
+ readonly Stage: "stage";
5
+ readonly Event: "event";
6
+ readonly Lobby: "lobby";
7
+ readonly Announcement: "announcement";
8
+ readonly Rules: "rules";
9
+ readonly Calendar: "calendar";
10
+ readonly Media: "media";
11
+ readonly DM: "dm";
12
+ readonly Overview: "overview";
13
+ readonly AuditLog: "audit-log";
14
+ };
15
+ export type ChannelTypeValue = (typeof ChannelType)[keyof typeof ChannelType];
package/dist/enums.js ADDED
@@ -0,0 +1,15 @@
1
+ // District channel kinds (mirrors the channels.kind check constraint).
2
+ export const ChannelType = {
3
+ Text: "text",
4
+ Voice: "voice",
5
+ Stage: "stage",
6
+ Event: "event",
7
+ Lobby: "lobby",
8
+ Announcement: "announcement",
9
+ Rules: "rules",
10
+ Calendar: "calendar",
11
+ Media: "media",
12
+ DM: "dm",
13
+ Overview: "overview",
14
+ AuditLog: "audit-log",
15
+ };
package/dist/index.d.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  export { Client, type ClientEvents } from "./client.js";
2
2
  export { REST, DistrictAPIError, DEFAULT_API_BASE } from "./rest.js";
3
- export { ClientUser, Message, TextChannel, Interaction, User, Space, SpaceMember, Role, type ClientContext, } from "./structures.js";
3
+ export { ClientUser, Message, TextChannel, Interaction, User, Space, SpaceMember, Role, Attachment, Ban, Invite, Webhook, type ClientContext, } from "./structures.js";
4
4
  export { Collection } from "./collection.js";
5
5
  export { PermissionsBitField, type PermissionResolvable, type PermissionFlagName } from "./permissions.js";
6
6
  export { Formatters } from "./formatters.js";
7
+ export { ChannelType, type ChannelTypeValue } from "./enums.js";
7
8
  export { verifySignature, parseEvent, verifyAndParse, type EventHeaders } from "./webhook.js";
8
- export type { ClientOptions, RawMessage, RawSelfUser, RawUser, RawSpace, RawChannel, RawRole, RawMember, DistrictEvent, ReactionEvent, DeletedMessage, } from "./types.js";
9
+ export type { ClientOptions, RawMessage, RawSelfUser, RawUser, RawSpace, RawChannel, RawRole, RawMember, RawAttachment, RawBan, RawInvite, RawWebhook, DistrictEvent, ReactionEvent, DeletedMessage, } from "./types.js";
package/dist/index.js CHANGED
@@ -6,8 +6,9 @@
6
6
  // client.listen(3000);
7
7
  export { Client } from "./client.js";
8
8
  export { REST, DistrictAPIError, DEFAULT_API_BASE } from "./rest.js";
9
- export { ClientUser, Message, TextChannel, Interaction, User, Space, SpaceMember, Role, } from "./structures.js";
9
+ export { ClientUser, Message, TextChannel, Interaction, User, Space, SpaceMember, Role, Attachment, Ban, Invite, Webhook, } from "./structures.js";
10
10
  export { Collection } from "./collection.js";
11
11
  export { PermissionsBitField } from "./permissions.js";
12
12
  export { Formatters } from "./formatters.js";
13
+ export { ChannelType } from "./enums.js";
13
14
  export { verifySignature, parseEvent, verifyAndParse } from "./webhook.js";
@@ -1,5 +1,5 @@
1
1
  import type { REST } from "./rest.js";
2
- import type { RawMessage, RawSelfUser, RawUser, RawSpace, RawChannel, RawRole, RawMember } from "./types.js";
2
+ import type { RawMessage, RawSelfUser, RawUser, RawSpace, RawChannel, RawRole, RawMember, RawAttachment, RawBan, RawInvite, RawWebhook } from "./types.js";
3
3
  import { Collection } from "./collection.js";
4
4
  import { PermissionsBitField } from "./permissions.js";
5
5
  /** Minimal client surface the structures need (avoids a circular import). */
@@ -25,6 +25,8 @@ export declare class Message {
25
25
  createdAt: string | null;
26
26
  editedAt: string | null;
27
27
  parentId: string | null;
28
+ /** Media/files on the message (populated from fetchMessages / fetchAttachments). */
29
+ attachments: Attachment[];
28
30
  constructor(client: ClientContext, raw: RawMessage);
29
31
  /** True if this message was sent by the current bot. */
30
32
  get authoredByMe(): boolean;
@@ -42,6 +44,8 @@ export declare class Message {
42
44
  fetchAuthor(): Promise<User>;
43
45
  /** A handle to the channel this message is in. */
44
46
  get channel(): TextChannel;
47
+ /** Fetch this message's attachments (useful for messages from events). */
48
+ fetchAttachments(): Promise<Attachment[]>;
45
49
  }
46
50
  export declare class TextChannel {
47
51
  private client;
@@ -61,6 +65,12 @@ export declare class TextChannel {
61
65
  send(content: string): Promise<Message>;
62
66
  /** Fetch recent messages (oldest → newest), up to 100. */
63
67
  fetchMessages(limit?: number): Promise<Collection<string, Message>>;
68
+ /** The channel's webhooks. */
69
+ webhooks(): Promise<Collection<string, Webhook>>;
70
+ /** Create a webhook. The returned Webhook has a one-time url + token you can send() with. */
71
+ createWebhook(name: string, options?: {
72
+ avatarUrl?: string;
73
+ }): Promise<Webhook>;
64
74
  }
65
75
  /** A District user's public profile. */
66
76
  export declare class User {
@@ -94,6 +104,76 @@ export declare class Space {
94
104
  roles(): Promise<Collection<string, Role>>;
95
105
  /** The space owner's profile. */
96
106
  fetchOwner(): Promise<User>;
107
+ /** Banned users. */
108
+ bans(): Promise<Collection<string, Ban>>;
109
+ /** Ban a user (needs the Ban Members permission). Removes their membership. */
110
+ ban(userId: string, options?: {
111
+ reason?: string;
112
+ until?: string | Date;
113
+ }): Promise<void>;
114
+ /** Lift a user's ban. */
115
+ unban(userId: string): Promise<void>;
116
+ /** Active invites. */
117
+ invites(): Promise<Collection<string, Invite>>;
118
+ /** Create an invite (needs Create Invites). `expiresIn` is seconds. */
119
+ createInvite(options?: {
120
+ maxUses?: number;
121
+ expiresIn?: number;
122
+ }): Promise<Invite>;
123
+ }
124
+ /** A file or media on a message. */
125
+ export declare class Attachment {
126
+ id: string;
127
+ kind: string;
128
+ url: string;
129
+ thumbUrl: string | null;
130
+ filename: string | null;
131
+ mimeType: string | null;
132
+ size: number | null;
133
+ width: number | null;
134
+ height: number | null;
135
+ spoiler: boolean;
136
+ constructor(raw: RawAttachment);
137
+ get isImage(): boolean;
138
+ get isVideo(): boolean;
139
+ }
140
+ /** A space ban. */
141
+ export declare class Ban {
142
+ userId: string;
143
+ reason: string | null;
144
+ until: string | null;
145
+ by: string;
146
+ constructor(raw: RawBan);
147
+ /** True if the ban is permanent (no expiry). */
148
+ get permanent(): boolean;
149
+ }
150
+ /** A space invite. */
151
+ export declare class Invite {
152
+ id: string;
153
+ code: string;
154
+ url: string;
155
+ maxUses: number | null;
156
+ useCount: number;
157
+ expiresAt: string | null;
158
+ constructor(raw: RawInvite);
159
+ }
160
+ /** A channel webhook. `url`/`token` are only present on the one you just created. */
161
+ export declare class Webhook {
162
+ private client;
163
+ id: string;
164
+ name: string;
165
+ channelId: string;
166
+ avatarUrl: string | null;
167
+ url: string | null;
168
+ token: string | null;
169
+ constructor(client: ClientContext, raw: RawWebhook);
170
+ /** Post via this webhook. Only works on a webhook you just created (has a url). */
171
+ send(content: string, options?: {
172
+ username?: string;
173
+ avatarUrl?: string;
174
+ }): Promise<void>;
175
+ /** Delete this webhook. */
176
+ delete(): Promise<void>;
97
177
  }
98
178
  /** A user's membership in a space. */
99
179
  export declare class SpaceMember {
@@ -25,6 +25,8 @@ export class Message {
25
25
  createdAt;
26
26
  editedAt;
27
27
  parentId;
28
+ /** Media/files on the message (populated from fetchMessages / fetchAttachments). */
29
+ attachments;
28
30
  constructor(client, raw) {
29
31
  this.client = client;
30
32
  this.id = raw.id;
@@ -34,6 +36,7 @@ export class Message {
34
36
  this.createdAt = raw.created_at ?? null;
35
37
  this.editedAt = raw.edited_at ?? null;
36
38
  this.parentId = raw.parent_id ?? null;
39
+ this.attachments = (raw.attachments ?? []).map((a) => new Attachment(a));
37
40
  }
38
41
  /** True if this message was sent by the current bot. */
39
42
  get authoredByMe() { return !!this.client.user && this.authorId === this.client.user.id; }
@@ -69,6 +72,11 @@ export class Message {
69
72
  get channel() {
70
73
  return new TextChannel(this.client, this.channelId);
71
74
  }
75
+ /** Fetch this message's attachments (useful for messages from events). */
76
+ async fetchAttachments() {
77
+ const { attachments } = await this.client.rest.get(`/channels/${this.channelId}/messages/${this.id}/attachments`);
78
+ return (attachments ?? []).map((a) => new Attachment(a));
79
+ }
72
80
  }
73
81
  export class TextChannel {
74
82
  client;
@@ -110,6 +118,19 @@ export class TextChannel {
110
118
  out.set(m.id, new Message(this.client, m));
111
119
  return out;
112
120
  }
121
+ /** The channel's webhooks. */
122
+ async webhooks() {
123
+ const { webhooks } = await this.client.rest.get(`/channels/${this.id}/webhooks`);
124
+ const out = new Collection();
125
+ for (const w of (webhooks ?? []))
126
+ out.set(w.id, new Webhook(this.client, w));
127
+ return out;
128
+ }
129
+ /** Create a webhook. The returned Webhook has a one-time url + token you can send() with. */
130
+ async createWebhook(name, options) {
131
+ const { webhook } = await this.client.rest.post(`/channels/${this.id}/webhooks`, { name, avatarUrl: options?.avatarUrl });
132
+ return new Webhook(this.client, webhook);
133
+ }
113
134
  }
114
135
  /** A District user's public profile. */
115
136
  export class User {
@@ -182,6 +203,128 @@ export class Space {
182
203
  const { user } = await this.client.rest.get(`/users/${this.ownerId}`);
183
204
  return new User(user);
184
205
  }
206
+ /** Banned users. */
207
+ async bans() {
208
+ const { bans } = await this.client.rest.get(`/spaces/${this.id}/bans`);
209
+ const out = new Collection();
210
+ for (const b of (bans ?? []))
211
+ out.set(b.user_id, new Ban(b));
212
+ return out;
213
+ }
214
+ /** Ban a user (needs the Ban Members permission). Removes their membership. */
215
+ async ban(userId, options) {
216
+ const until = options?.until ? new Date(options.until).toISOString() : undefined;
217
+ await this.client.rest.put(`/spaces/${this.id}/bans/${userId}`, { reason: options?.reason, until });
218
+ }
219
+ /** Lift a user's ban. */
220
+ async unban(userId) {
221
+ await this.client.rest.delete(`/spaces/${this.id}/bans/${userId}`);
222
+ }
223
+ /** Active invites. */
224
+ async invites() {
225
+ const { invites } = await this.client.rest.get(`/spaces/${this.id}/invites`);
226
+ const out = new Collection();
227
+ for (const i of (invites ?? []))
228
+ out.set(i.code, new Invite(i));
229
+ return out;
230
+ }
231
+ /** Create an invite (needs Create Invites). `expiresIn` is seconds. */
232
+ async createInvite(options) {
233
+ const { invite } = await this.client.rest.post(`/spaces/${this.id}/invites`, { maxUses: options?.maxUses, expiresIn: options?.expiresIn });
234
+ return new Invite(invite);
235
+ }
236
+ }
237
+ /** A file or media on a message. */
238
+ export class Attachment {
239
+ id;
240
+ kind;
241
+ url;
242
+ thumbUrl;
243
+ filename;
244
+ mimeType;
245
+ size;
246
+ width;
247
+ height;
248
+ spoiler;
249
+ constructor(raw) {
250
+ this.id = raw.id;
251
+ this.kind = raw.kind;
252
+ this.url = raw.url;
253
+ this.thumbUrl = raw.thumb_url ?? null;
254
+ this.filename = raw.filename ?? null;
255
+ this.mimeType = raw.mime_type ?? null;
256
+ this.size = raw.size_bytes ?? null;
257
+ this.width = raw.width ?? null;
258
+ this.height = raw.height ?? null;
259
+ this.spoiler = raw.spoiler ?? false;
260
+ }
261
+ get isImage() { return this.kind === "image" || this.kind === "gifv"; }
262
+ get isVideo() { return this.kind === "video"; }
263
+ }
264
+ /** A space ban. */
265
+ export class Ban {
266
+ userId;
267
+ reason;
268
+ until;
269
+ by;
270
+ constructor(raw) {
271
+ this.userId = raw.user_id;
272
+ this.reason = raw.reason;
273
+ this.until = raw.until;
274
+ this.by = raw.by;
275
+ }
276
+ /** True if the ban is permanent (no expiry). */
277
+ get permanent() { return this.until === null; }
278
+ }
279
+ /** A space invite. */
280
+ export class Invite {
281
+ id;
282
+ code;
283
+ url;
284
+ maxUses;
285
+ useCount;
286
+ expiresAt;
287
+ constructor(raw) {
288
+ this.id = raw.id;
289
+ this.code = raw.code;
290
+ this.url = raw.url ?? `https://usedistrict.org/i/${raw.code}`;
291
+ this.maxUses = raw.max_uses;
292
+ this.useCount = raw.use_count ?? 0;
293
+ this.expiresAt = raw.expires_at;
294
+ }
295
+ }
296
+ /** A channel webhook. `url`/`token` are only present on the one you just created. */
297
+ export class Webhook {
298
+ client;
299
+ id;
300
+ name;
301
+ channelId;
302
+ avatarUrl;
303
+ url;
304
+ token;
305
+ constructor(client, raw) {
306
+ this.client = client;
307
+ this.id = raw.id;
308
+ this.name = raw.name;
309
+ this.channelId = raw.channel_id;
310
+ this.avatarUrl = raw.avatar_url ?? null;
311
+ this.url = raw.url ?? null;
312
+ this.token = raw.token ?? null;
313
+ }
314
+ /** Post via this webhook. Only works on a webhook you just created (has a url). */
315
+ async send(content, options) {
316
+ if (!this.url)
317
+ throw new Error("This webhook has no url/token — those are only returned when it's first created.");
318
+ await fetch(this.url, {
319
+ method: "POST",
320
+ headers: { "content-type": "application/json" },
321
+ body: JSON.stringify({ content, username: options?.username, avatar_url: options?.avatarUrl }),
322
+ });
323
+ }
324
+ /** Delete this webhook. */
325
+ async delete() {
326
+ await this.client.rest.delete(`/channels/${this.channelId}/webhooks/${this.id}`);
327
+ }
185
328
  }
186
329
  /** A user's membership in a space. */
187
330
  export class SpaceMember {
package/dist/types.d.ts CHANGED
@@ -19,6 +19,47 @@ export interface RawMessage {
19
19
  created_at?: string;
20
20
  edited_at?: string | null;
21
21
  parent_id?: string | null;
22
+ attachments?: RawAttachment[];
23
+ }
24
+ /** A message attachment (image/video/file/…). */
25
+ export interface RawAttachment {
26
+ id: string;
27
+ kind: string;
28
+ url: string;
29
+ thumb_url?: string | null;
30
+ filename?: string | null;
31
+ mime_type?: string | null;
32
+ size_bytes?: number | null;
33
+ width?: number | null;
34
+ height?: number | null;
35
+ spoiler?: boolean;
36
+ }
37
+ /** A space ban (`GET /spaces/:id/bans`). */
38
+ export interface RawBan {
39
+ user_id: string;
40
+ reason: string | null;
41
+ until: string | null;
42
+ by: string;
43
+ created_at?: string;
44
+ }
45
+ /** A space invite (`GET/POST /spaces/:id/invites`). */
46
+ export interface RawInvite {
47
+ id: string;
48
+ code: string;
49
+ url?: string;
50
+ max_uses: number | null;
51
+ use_count?: number;
52
+ expires_at: string | null;
53
+ revoked_at?: string | null;
54
+ }
55
+ /** A channel webhook (`GET/POST /channels/:id/webhooks`). */
56
+ export interface RawWebhook {
57
+ id: string;
58
+ name: string;
59
+ channel_id: string;
60
+ avatar_url?: string | null;
61
+ token?: string;
62
+ url?: string;
22
63
  }
23
64
  /** The bot's own identity (`GET /me`). */
24
65
  export interface RawSelfUser {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "district.js",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "A powerful Node.js module for building District bots — post, react, run slash commands, and receive events.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",