@metagames/channel-bot-sdk 1.0.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/LICENSE +201 -0
- package/README.md +75 -0
- package/dist/client.d.ts +49 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +159 -0
- package/dist/commands.d.ts +52 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +80 -0
- package/dist/events.d.ts +9 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +27 -0
- package/dist/examples/echo-bot.d.ts +2 -0
- package/dist/examples/echo-bot.d.ts.map +1 -0
- package/dist/examples/echo-bot.js +24 -0
- package/dist/examples/moderation-helper-bot.d.ts +2 -0
- package/dist/examples/moderation-helper-bot.d.ts.map +1 -0
- package/dist/examples/moderation-helper-bot.js +31 -0
- package/dist/http.d.ts +12 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +28 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/src/client.d.ts +49 -0
- package/dist/src/client.d.ts.map +1 -0
- package/dist/src/client.js +159 -0
- package/dist/src/commands.d.ts +52 -0
- package/dist/src/commands.d.ts.map +1 -0
- package/dist/src/commands.js +80 -0
- package/dist/src/events.d.ts +9 -0
- package/dist/src/events.d.ts.map +1 -0
- package/dist/src/events.js +27 -0
- package/dist/src/http.d.ts +12 -0
- package/dist/src/http.d.ts.map +1 -0
- package/dist/src/http.js +28 -0
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +3 -0
- package/dist/src/types.d.ts +119 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +2 -0
- package/dist/types.d.ts +119 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/docs/command-handling.md +44 -0
- package/docs/events.md +27 -0
- package/docs/quick-start.md +44 -0
- package/package.json +46 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// (c) 2026 Meta Games LLC. All rights reserved.
|
|
2
|
+
import { ChannelBotClient, CommandRouter } from "../src/index";
|
|
3
|
+
async function main() {
|
|
4
|
+
const token = process.env.BOT_TOKEN;
|
|
5
|
+
if (!token)
|
|
6
|
+
throw new Error("BOT_TOKEN is required");
|
|
7
|
+
const client = new ChannelBotClient({
|
|
8
|
+
token,
|
|
9
|
+
apiBaseUrl: process.env.BOT_API_BASE_URL || "https://api.brickverse.gg",
|
|
10
|
+
});
|
|
11
|
+
const router = new CommandRouter()
|
|
12
|
+
.command("ping", async (ctx) => {
|
|
13
|
+
await ctx.replyMention("pong");
|
|
14
|
+
})
|
|
15
|
+
.command("echo", async (ctx) => {
|
|
16
|
+
await ctx.reply(ctx.args.join(" ") || "Nothing to echo.");
|
|
17
|
+
});
|
|
18
|
+
client.useCommandRouter(router);
|
|
19
|
+
client.on("guildBot.ready", (event) => {
|
|
20
|
+
console.log(`ready as ${event.bot.username}`);
|
|
21
|
+
});
|
|
22
|
+
await client.connect();
|
|
23
|
+
}
|
|
24
|
+
void main();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"moderation-helper-bot.d.ts","sourceRoot":"","sources":["../../examples/moderation-helper-bot.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// (c) 2026 Meta Games LLC. All rights reserved.
|
|
2
|
+
import { ChannelBotClient, CommandRouter } from "../src/index";
|
|
3
|
+
async function main() {
|
|
4
|
+
const token = process.env.BOT_TOKEN;
|
|
5
|
+
if (!token)
|
|
6
|
+
throw new Error("BOT_TOKEN is required");
|
|
7
|
+
const client = new ChannelBotClient({
|
|
8
|
+
token,
|
|
9
|
+
apiBaseUrl: process.env.BOT_API_BASE_URL || "https://api.brickverse.gg",
|
|
10
|
+
});
|
|
11
|
+
const router = new CommandRouter().command("warn", async (ctx) => {
|
|
12
|
+
const target = ctx.args[0];
|
|
13
|
+
const reason = ctx.args.slice(1).join(" ") || "No reason provided";
|
|
14
|
+
if (!target) {
|
|
15
|
+
await ctx.reply("Usage: /warn <username> <reason>");
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
await ctx.replyEmbed({
|
|
19
|
+
title: "Moderator Warning",
|
|
20
|
+
description: `@${target} was warned by @${ctx.user.username}.`,
|
|
21
|
+
fields: [{ name: "Reason", value: reason }],
|
|
22
|
+
color: "#ff6b6b",
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
client.useCommandRouter(router);
|
|
26
|
+
client.on("guildBot.ready", (event) => {
|
|
27
|
+
console.log(`ready as ${event.bot.username}`);
|
|
28
|
+
});
|
|
29
|
+
await client.connect();
|
|
30
|
+
}
|
|
31
|
+
void main();
|
package/dist/http.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare function normalizeApiBaseUrl(input: string): string;
|
|
2
|
+
export declare function createWebSocketUrl(apiBaseUrl: string): string;
|
|
3
|
+
export declare function requestWithBotToken<T>(args: {
|
|
4
|
+
fetchImpl: typeof fetch;
|
|
5
|
+
apiBaseUrl: string;
|
|
6
|
+
token: string;
|
|
7
|
+
path: string;
|
|
8
|
+
options?: Omit<RequestInit, "body"> & {
|
|
9
|
+
body?: unknown;
|
|
10
|
+
};
|
|
11
|
+
}): Promise<T>;
|
|
12
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAEA,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAK7D;AAED,wBAAsB,mBAAmB,CAAC,CAAC,EAAE,IAAI,EAAE;IAClD,SAAS,EAAE,OAAO,KAAK,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,GAAG;QAAE,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CACzD,GAAG,OAAO,CAAC,CAAC,CAAC,CAqBb"}
|
package/dist/http.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// (c) 2026 Meta Games LLC. All rights reserved.
|
|
2
|
+
export function normalizeApiBaseUrl(input) {
|
|
3
|
+
return input.replace(/\/+$/, "");
|
|
4
|
+
}
|
|
5
|
+
export function createWebSocketUrl(apiBaseUrl) {
|
|
6
|
+
const normalized = normalizeApiBaseUrl(apiBaseUrl);
|
|
7
|
+
const resolved = new URL("/v3/social/guild-bots/ws", `${normalized}/`);
|
|
8
|
+
resolved.protocol = resolved.protocol === "https:" ? "wss:" : "ws:";
|
|
9
|
+
return resolved.toString();
|
|
10
|
+
}
|
|
11
|
+
export async function requestWithBotToken(args) {
|
|
12
|
+
const endpoint = args.path.startsWith("/") ? args.path : `/${args.path}`;
|
|
13
|
+
const options = args.options || {};
|
|
14
|
+
const response = await args.fetchImpl(`${args.apiBaseUrl}${endpoint}`, {
|
|
15
|
+
method: options.method || "GET",
|
|
16
|
+
headers: {
|
|
17
|
+
"Content-Type": "application/json",
|
|
18
|
+
Authorization: `Bot ${args.token}`,
|
|
19
|
+
...(options.headers || {}),
|
|
20
|
+
},
|
|
21
|
+
body: options.body === undefined ? undefined : JSON.stringify(options.body),
|
|
22
|
+
});
|
|
23
|
+
const data = (await response.json().catch(() => ({})));
|
|
24
|
+
if (!response.ok || data.success === false) {
|
|
25
|
+
throw new Error(data.message || `Channel bot request failed (${response.status})`);
|
|
26
|
+
}
|
|
27
|
+
return data;
|
|
28
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { ChannelBotClient } from "./client";
|
|
2
|
+
export { CommandContext, CommandRouter, messageInputFromInteraction, } from "./commands";
|
|
3
|
+
export type { ChannelBotClientOptions, ChannelBotEmbed, ChannelBotEmbedField, ChannelBotMessage, ChannelBotMessageInput, GuildBotInteraction, GuildBotMeResponse, GuildBotSocketEvent, } from "./types";
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EACN,cAAc,EACd,aAAa,EACb,2BAA2B,GAC3B,MAAM,YAAY,CAAC;AACpB,YAAY,EACX,uBAAuB,EACvB,eAAe,EACf,oBAAoB,EACpB,iBAAiB,EACjB,sBAAsB,EACtB,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,GACnB,MAAM,SAAS,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { CommandRouter } from "./commands";
|
|
2
|
+
import type { ChannelBotClientOptions, ChannelBotEmbed, ChannelBotMessage, ChannelBotMessageInput, GuildBotMeResponse, GuildBotSocketEvent } from "./types";
|
|
3
|
+
export declare class ChannelBotClient {
|
|
4
|
+
private readonly token;
|
|
5
|
+
private readonly apiBaseUrl;
|
|
6
|
+
private readonly websocketUrl;
|
|
7
|
+
private readonly fetchImpl;
|
|
8
|
+
private readonly WebSocketImpl;
|
|
9
|
+
private readonly autoReconnect;
|
|
10
|
+
private readonly reconnectDelayMs;
|
|
11
|
+
private reconnectTimer;
|
|
12
|
+
private manuallyClosed;
|
|
13
|
+
private ws;
|
|
14
|
+
private readonly events;
|
|
15
|
+
private commandRouter;
|
|
16
|
+
constructor(options: ChannelBotClientOptions);
|
|
17
|
+
on<TEvent = GuildBotSocketEvent>(eventName: string, listener: (event: TEvent) => void): () => void;
|
|
18
|
+
off<TEvent = GuildBotSocketEvent>(eventName: string, listener: (event: TEvent) => void): void;
|
|
19
|
+
useCommandRouter(router: CommandRouter): this;
|
|
20
|
+
connect(): Promise<void>;
|
|
21
|
+
private scheduleReconnect;
|
|
22
|
+
disconnect(): void;
|
|
23
|
+
private request;
|
|
24
|
+
getMe(): Promise<GuildBotMeResponse>;
|
|
25
|
+
sendMessage(input: ChannelBotMessageInput): Promise<{
|
|
26
|
+
success: boolean;
|
|
27
|
+
message: ChannelBotMessage;
|
|
28
|
+
}>;
|
|
29
|
+
sendEmbed(input: {
|
|
30
|
+
guildId: string;
|
|
31
|
+
channelId: string;
|
|
32
|
+
embed: ChannelBotEmbed;
|
|
33
|
+
content?: string;
|
|
34
|
+
}): Promise<{
|
|
35
|
+
success: boolean;
|
|
36
|
+
message: ChannelBotMessage;
|
|
37
|
+
}>;
|
|
38
|
+
editMessage(messageId: string, input: {
|
|
39
|
+
content?: string;
|
|
40
|
+
embeds?: ChannelBotEmbed[];
|
|
41
|
+
}): Promise<{
|
|
42
|
+
success: boolean;
|
|
43
|
+
message: ChannelBotMessage;
|
|
44
|
+
}>;
|
|
45
|
+
deleteMessage(messageId: string): Promise<{
|
|
46
|
+
success: boolean;
|
|
47
|
+
}>;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EACX,uBAAuB,EACvB,eAAe,EACf,iBAAiB,EACjB,sBAAsB,EACtB,kBAAkB,EAClB,mBAAmB,EACnB,MAAM,SAAS,CAAC;AAEjB,qBAAa,gBAAgB;IAC5B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAmB;IACjD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAU;IACxC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,cAAc,CAA8C;IACpE,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsB;IAC7C,OAAO,CAAC,aAAa,CAA8B;gBAEvC,OAAO,EAAE,uBAAuB;IAkB5C,EAAE,CAAC,MAAM,GAAG,mBAAmB,EAC9B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI;IAKlC,GAAG,CAAC,MAAM,GAAG,mBAAmB,EAC/B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI;IAKlC,gBAAgB,CAAC,MAAM,EAAE,aAAa;IAKhC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAyE9B,OAAO,CAAC,iBAAiB;IAQzB,UAAU;IAYV,OAAO,CAAC,OAAO;IAaf,KAAK;IAIL,WAAW,CAAC,KAAK,EAAE,sBAAsB;iBACT,OAAO;iBAAW,iBAAiB;;IASnE,SAAS,CAAC,KAAK,EAAE;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE,eAAe,CAAC;QACvB,OAAO,CAAC,EAAE,MAAM,CAAC;KACjB;iBAd+B,OAAO;iBAAW,iBAAiB;;IAuBnE,WAAW,CACV,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,eAAe,EAAE,CAAA;KAAE;iBAExB,OAAO;iBAAW,iBAAiB;;IASnE,aAAa,CAAC,SAAS,EAAE,MAAM;iBACC,OAAO;;CAKvC"}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
// (c) 2026 Meta Games LLC. All rights reserved.
|
|
2
|
+
import { EventEmitter } from "./events";
|
|
3
|
+
import { createWebSocketUrl, normalizeApiBaseUrl, requestWithBotToken, } from "./http";
|
|
4
|
+
export class ChannelBotClient {
|
|
5
|
+
token;
|
|
6
|
+
apiBaseUrl;
|
|
7
|
+
websocketUrl;
|
|
8
|
+
fetchImpl;
|
|
9
|
+
WebSocketImpl;
|
|
10
|
+
autoReconnect;
|
|
11
|
+
reconnectDelayMs;
|
|
12
|
+
reconnectTimer = null;
|
|
13
|
+
manuallyClosed = false;
|
|
14
|
+
ws = null;
|
|
15
|
+
events = new EventEmitter();
|
|
16
|
+
commandRouter = null;
|
|
17
|
+
constructor(options) {
|
|
18
|
+
if (!options.token?.trim()) {
|
|
19
|
+
throw new Error("ChannelBotClient requires a bot token");
|
|
20
|
+
}
|
|
21
|
+
if (!options.apiBaseUrl?.trim()) {
|
|
22
|
+
throw new Error("ChannelBotClient requires apiBaseUrl");
|
|
23
|
+
}
|
|
24
|
+
this.token = options.token.trim();
|
|
25
|
+
this.apiBaseUrl = normalizeApiBaseUrl(options.apiBaseUrl);
|
|
26
|
+
this.websocketUrl =
|
|
27
|
+
options.websocketUrl || createWebSocketUrl(this.apiBaseUrl);
|
|
28
|
+
this.fetchImpl = options.fetchImpl || fetch;
|
|
29
|
+
this.WebSocketImpl = options.WebSocketImpl || WebSocket;
|
|
30
|
+
this.autoReconnect = options.autoReconnect !== false;
|
|
31
|
+
this.reconnectDelayMs = Math.max(250, options.reconnectDelayMs || 2500);
|
|
32
|
+
}
|
|
33
|
+
on(eventName, listener) {
|
|
34
|
+
return this.events.on(eventName, listener);
|
|
35
|
+
}
|
|
36
|
+
off(eventName, listener) {
|
|
37
|
+
return this.events.off(eventName, listener);
|
|
38
|
+
}
|
|
39
|
+
useCommandRouter(router) {
|
|
40
|
+
this.commandRouter = router;
|
|
41
|
+
return this;
|
|
42
|
+
}
|
|
43
|
+
async connect() {
|
|
44
|
+
this.manuallyClosed = false;
|
|
45
|
+
if (this.ws && this.ws.readyState <= 1)
|
|
46
|
+
return;
|
|
47
|
+
await new Promise((resolve, reject) => {
|
|
48
|
+
const ws = new this.WebSocketImpl(this.websocketUrl);
|
|
49
|
+
this.ws = ws;
|
|
50
|
+
let resolved = false;
|
|
51
|
+
ws.onopen = () => {
|
|
52
|
+
ws.send(JSON.stringify({ type: "guildBot.authenticate", token: this.token }));
|
|
53
|
+
};
|
|
54
|
+
ws.onmessage = (rawEvent) => {
|
|
55
|
+
if (!rawEvent.data)
|
|
56
|
+
return;
|
|
57
|
+
let parsed;
|
|
58
|
+
try {
|
|
59
|
+
parsed = JSON.parse(String(rawEvent.data));
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
this.events.emit("raw", parsed);
|
|
65
|
+
this.events.emit(parsed.type, parsed);
|
|
66
|
+
if (parsed.type === "guildBot.interactionCreate" &&
|
|
67
|
+
this.commandRouter) {
|
|
68
|
+
void this.commandRouter.handle(parsed, this).catch((error) => {
|
|
69
|
+
this.events.emit("guildBot.commandError", {
|
|
70
|
+
type: "guildBot.commandError",
|
|
71
|
+
error,
|
|
72
|
+
event: parsed,
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
if (parsed.type === "guildBot.ready" && !resolved) {
|
|
77
|
+
resolved = true;
|
|
78
|
+
resolve();
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (parsed.type === "guildBot.error" && !resolved) {
|
|
82
|
+
resolved = true;
|
|
83
|
+
reject(new Error(parsed.message || "Guild bot websocket error"));
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
ws.onerror = () => {
|
|
87
|
+
if (resolved)
|
|
88
|
+
return;
|
|
89
|
+
resolved = true;
|
|
90
|
+
reject(new Error("Guild bot websocket connection failed"));
|
|
91
|
+
};
|
|
92
|
+
ws.onclose = () => {
|
|
93
|
+
this.ws = null;
|
|
94
|
+
this.events.emit("disconnected", { type: "disconnected" });
|
|
95
|
+
if (!this.manuallyClosed && this.autoReconnect) {
|
|
96
|
+
this.scheduleReconnect();
|
|
97
|
+
}
|
|
98
|
+
if (!resolved) {
|
|
99
|
+
resolved = true;
|
|
100
|
+
reject(new Error("Guild bot websocket closed before ready"));
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
scheduleReconnect() {
|
|
106
|
+
if (this.reconnectTimer)
|
|
107
|
+
return;
|
|
108
|
+
this.reconnectTimer = setTimeout(() => {
|
|
109
|
+
this.reconnectTimer = null;
|
|
110
|
+
void this.connect().catch(() => { });
|
|
111
|
+
}, this.reconnectDelayMs);
|
|
112
|
+
}
|
|
113
|
+
disconnect() {
|
|
114
|
+
this.manuallyClosed = true;
|
|
115
|
+
if (this.reconnectTimer) {
|
|
116
|
+
clearTimeout(this.reconnectTimer);
|
|
117
|
+
this.reconnectTimer = null;
|
|
118
|
+
}
|
|
119
|
+
if (this.ws) {
|
|
120
|
+
this.ws.close();
|
|
121
|
+
this.ws = null;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
request(path, options = {}) {
|
|
125
|
+
return requestWithBotToken({
|
|
126
|
+
fetchImpl: this.fetchImpl,
|
|
127
|
+
apiBaseUrl: this.apiBaseUrl,
|
|
128
|
+
token: this.token,
|
|
129
|
+
path,
|
|
130
|
+
options,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
getMe() {
|
|
134
|
+
return this.request("/v3/social/guild-bots/@me");
|
|
135
|
+
}
|
|
136
|
+
sendMessage(input) {
|
|
137
|
+
return this.request("/v3/social/guild-bots/@me/messages", {
|
|
138
|
+
method: "POST",
|
|
139
|
+
body: input,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
sendEmbed(input) {
|
|
143
|
+
return this.sendMessage({
|
|
144
|
+
guildId: input.guildId,
|
|
145
|
+
channelId: input.channelId,
|
|
146
|
+
content: input.content,
|
|
147
|
+
embeds: [input.embed],
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
editMessage(messageId, input) {
|
|
151
|
+
return this.request(`/v3/social/guild-bots/@me/messages/${messageId}`, {
|
|
152
|
+
method: "PUT",
|
|
153
|
+
body: input,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
deleteMessage(messageId) {
|
|
157
|
+
return this.request(`/v3/social/guild-bots/@me/messages/${messageId}`, { method: "DELETE" });
|
|
158
|
+
}
|
|
159
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { ChannelBotEmbed, ChannelBotMessage, ChannelBotMessageInput, GuildBotInteraction, GuildBotSocketEvent } from "./types";
|
|
2
|
+
import type { ChannelBotClient } from "./client";
|
|
3
|
+
export declare class CommandContext {
|
|
4
|
+
readonly client: ChannelBotClient;
|
|
5
|
+
readonly guildId: string;
|
|
6
|
+
readonly channelId: string;
|
|
7
|
+
readonly interaction: GuildBotInteraction;
|
|
8
|
+
constructor(args: {
|
|
9
|
+
client: ChannelBotClient;
|
|
10
|
+
guildId: string;
|
|
11
|
+
channelId: string;
|
|
12
|
+
interaction: GuildBotInteraction;
|
|
13
|
+
});
|
|
14
|
+
get commandName(): string;
|
|
15
|
+
get args(): string[];
|
|
16
|
+
get user(): {
|
|
17
|
+
id: string;
|
|
18
|
+
username: string;
|
|
19
|
+
};
|
|
20
|
+
reply(input: string | {
|
|
21
|
+
content?: string;
|
|
22
|
+
embeds?: ChannelBotEmbed[];
|
|
23
|
+
}): Promise<{
|
|
24
|
+
success: boolean;
|
|
25
|
+
message: ChannelBotMessage;
|
|
26
|
+
}>;
|
|
27
|
+
replyMention(content: string): Promise<{
|
|
28
|
+
success: boolean;
|
|
29
|
+
message: ChannelBotMessage;
|
|
30
|
+
}>;
|
|
31
|
+
replyEmbed(embed: ChannelBotEmbed, content?: string): Promise<{
|
|
32
|
+
success: boolean;
|
|
33
|
+
message: ChannelBotMessage;
|
|
34
|
+
}>;
|
|
35
|
+
}
|
|
36
|
+
export type CommandHandler = (ctx: CommandContext) => Promise<unknown> | unknown;
|
|
37
|
+
export declare class CommandRouter {
|
|
38
|
+
private readonly handlers;
|
|
39
|
+
command(name: string, handler: CommandHandler): this;
|
|
40
|
+
remove(name: string): this;
|
|
41
|
+
handle(event: Extract<GuildBotSocketEvent, {
|
|
42
|
+
type: "guildBot.interactionCreate";
|
|
43
|
+
}>, client: ChannelBotClient): Promise<boolean>;
|
|
44
|
+
}
|
|
45
|
+
export type ChannelBotMessageResponse = {
|
|
46
|
+
success: boolean;
|
|
47
|
+
message: ChannelBotMessage;
|
|
48
|
+
};
|
|
49
|
+
export declare function messageInputFromInteraction(event: Extract<GuildBotSocketEvent, {
|
|
50
|
+
type: "guildBot.interactionCreate";
|
|
51
|
+
}>, input: Omit<ChannelBotMessageInput, "guildId" | "channelId">): ChannelBotMessageInput;
|
|
52
|
+
//# sourceMappingURL=commands.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/commands.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACX,eAAe,EACf,iBAAiB,EACjB,sBAAsB,EACtB,mBAAmB,EACnB,mBAAmB,EACnB,MAAM,SAAS,CAAC;AACjB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAEjD,qBAAa,cAAc;IAC1B,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,mBAAmB,CAAC;gBAE9B,IAAI,EAAE;QACjB,MAAM,EAAE,gBAAgB,CAAC;QACzB,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,EAAE,mBAAmB,CAAC;KACjC;IAOD,IAAI,WAAW,WAEd;IAED,IAAI,IAAI,aAEP;IAED,IAAI,IAAI;;;MAEP;IAED,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,eAAe,EAAE,CAAA;KAAE;;;;IAiBtE,YAAY,CAAC,OAAO,EAAE,MAAM;;;;IAI5B,UAAU,CAAC,KAAK,EAAE,eAAe,EAAE,OAAO,CAAC,EAAE,MAAM;;;;CAMnD;AAED,MAAM,MAAM,cAAc,GAAG,CAC5B,GAAG,EAAE,cAAc,KACf,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;AAEhC,qBAAa,aAAa;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqC;IAE9D,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,IAAI;IAOpD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAKpB,MAAM,CACX,KAAK,EAAE,OAAO,CAAC,mBAAmB,EAAE;QAAE,IAAI,EAAE,4BAA4B,CAAA;KAAE,CAAC,EAC3E,MAAM,EAAE,gBAAgB;CAiBzB;AAED,MAAM,MAAM,yBAAyB,GAAG;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,iBAAiB,CAAC;CAC3B,CAAC;AAEF,wBAAgB,2BAA2B,CAC1C,KAAK,EAAE,OAAO,CAAC,mBAAmB,EAAE;IAAE,IAAI,EAAE,4BAA4B,CAAA;CAAE,CAAC,EAC3E,KAAK,EAAE,IAAI,CAAC,sBAAsB,EAAE,SAAS,GAAG,WAAW,CAAC,GAC1D,sBAAsB,CAOxB"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// (c) 2026 Meta Games LLC. All rights reserved.
|
|
2
|
+
export class CommandContext {
|
|
3
|
+
client;
|
|
4
|
+
guildId;
|
|
5
|
+
channelId;
|
|
6
|
+
interaction;
|
|
7
|
+
constructor(args) {
|
|
8
|
+
this.client = args.client;
|
|
9
|
+
this.guildId = args.guildId;
|
|
10
|
+
this.channelId = args.channelId;
|
|
11
|
+
this.interaction = args.interaction;
|
|
12
|
+
}
|
|
13
|
+
get commandName() {
|
|
14
|
+
return this.interaction.commandName;
|
|
15
|
+
}
|
|
16
|
+
get args() {
|
|
17
|
+
return this.interaction.arguments;
|
|
18
|
+
}
|
|
19
|
+
get user() {
|
|
20
|
+
return this.interaction.user;
|
|
21
|
+
}
|
|
22
|
+
reply(input) {
|
|
23
|
+
if (typeof input === "string") {
|
|
24
|
+
return this.client.sendMessage({
|
|
25
|
+
guildId: this.guildId,
|
|
26
|
+
channelId: this.channelId,
|
|
27
|
+
content: input,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
return this.client.sendMessage({
|
|
31
|
+
guildId: this.guildId,
|
|
32
|
+
channelId: this.channelId,
|
|
33
|
+
content: input.content,
|
|
34
|
+
embeds: input.embeds,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
replyMention(content) {
|
|
38
|
+
return this.reply(`@${this.user.username} ${content}`);
|
|
39
|
+
}
|
|
40
|
+
replyEmbed(embed, content) {
|
|
41
|
+
return this.reply({
|
|
42
|
+
content,
|
|
43
|
+
embeds: [embed],
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export class CommandRouter {
|
|
48
|
+
handlers = new Map();
|
|
49
|
+
command(name, handler) {
|
|
50
|
+
const normalized = name.trim().toLowerCase();
|
|
51
|
+
if (!normalized)
|
|
52
|
+
throw new Error("Command name is required");
|
|
53
|
+
this.handlers.set(normalized, handler);
|
|
54
|
+
return this;
|
|
55
|
+
}
|
|
56
|
+
remove(name) {
|
|
57
|
+
this.handlers.delete(name.trim().toLowerCase());
|
|
58
|
+
return this;
|
|
59
|
+
}
|
|
60
|
+
async handle(event, client) {
|
|
61
|
+
const handler = this.handlers.get(event.interaction.commandName.toLowerCase());
|
|
62
|
+
if (!handler)
|
|
63
|
+
return false;
|
|
64
|
+
await handler(new CommandContext({
|
|
65
|
+
client,
|
|
66
|
+
guildId: event.guildId,
|
|
67
|
+
channelId: event.channelId,
|
|
68
|
+
interaction: event.interaction,
|
|
69
|
+
}));
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
export function messageInputFromInteraction(event, input) {
|
|
74
|
+
return {
|
|
75
|
+
guildId: event.guildId,
|
|
76
|
+
channelId: event.channelId,
|
|
77
|
+
content: input.content,
|
|
78
|
+
embeds: input.embeds,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { GuildBotSocketEvent } from "./types";
|
|
2
|
+
export type Listener<TEvent> = (event: TEvent) => void;
|
|
3
|
+
export declare class EventEmitter {
|
|
4
|
+
private listeners;
|
|
5
|
+
on<TEvent = GuildBotSocketEvent>(eventName: string, listener: Listener<TEvent>): () => void;
|
|
6
|
+
off<TEvent = GuildBotSocketEvent>(eventName: string, listener: Listener<TEvent>): void;
|
|
7
|
+
emit<TEvent = GuildBotSocketEvent>(eventName: string, event: TEvent): void;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/events.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAEnD,MAAM,MAAM,QAAQ,CAAC,MAAM,IAAI,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;AAEvD,qBAAa,YAAY;IACxB,OAAO,CAAC,SAAS,CAAyC;IAE1D,EAAE,CAAC,MAAM,GAAG,mBAAmB,EAC9B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC;IAQ3B,GAAG,CAAC,MAAM,GAAG,mBAAmB,EAC/B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC;IAU3B,IAAI,CAAC,MAAM,GAAG,mBAAmB,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;CAOnE"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// (c) 2026 Meta Games LLC. All rights reserved.
|
|
2
|
+
export class EventEmitter {
|
|
3
|
+
listeners = new Map();
|
|
4
|
+
on(eventName, listener) {
|
|
5
|
+
const listeners = this.listeners.get(eventName) || new Set();
|
|
6
|
+
listeners.add(listener);
|
|
7
|
+
this.listeners.set(eventName, listeners);
|
|
8
|
+
return () => this.off(eventName, listener);
|
|
9
|
+
}
|
|
10
|
+
off(eventName, listener) {
|
|
11
|
+
const listeners = this.listeners.get(eventName);
|
|
12
|
+
if (!listeners)
|
|
13
|
+
return;
|
|
14
|
+
listeners.delete(listener);
|
|
15
|
+
if (listeners.size === 0) {
|
|
16
|
+
this.listeners.delete(eventName);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
emit(eventName, event) {
|
|
20
|
+
const listeners = this.listeners.get(eventName);
|
|
21
|
+
if (!listeners)
|
|
22
|
+
return;
|
|
23
|
+
for (const listener of listeners) {
|
|
24
|
+
listener(event);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare function normalizeApiBaseUrl(input: string): string;
|
|
2
|
+
export declare function createWebSocketUrl(apiBaseUrl: string): string;
|
|
3
|
+
export declare function requestWithBotToken<T>(args: {
|
|
4
|
+
fetchImpl: typeof fetch;
|
|
5
|
+
apiBaseUrl: string;
|
|
6
|
+
token: string;
|
|
7
|
+
path: string;
|
|
8
|
+
options?: Omit<RequestInit, "body"> & {
|
|
9
|
+
body?: unknown;
|
|
10
|
+
};
|
|
11
|
+
}): Promise<T>;
|
|
12
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/http.ts"],"names":[],"mappings":"AAEA,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAK7D;AAED,wBAAsB,mBAAmB,CAAC,CAAC,EAAE,IAAI,EAAE;IAClD,SAAS,EAAE,OAAO,KAAK,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,GAAG;QAAE,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CACzD,GAAG,OAAO,CAAC,CAAC,CAAC,CAqBb"}
|
package/dist/src/http.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// (c) 2026 Meta Games LLC. All rights reserved.
|
|
2
|
+
export function normalizeApiBaseUrl(input) {
|
|
3
|
+
return input.replace(/\/+$/, "");
|
|
4
|
+
}
|
|
5
|
+
export function createWebSocketUrl(apiBaseUrl) {
|
|
6
|
+
const normalized = normalizeApiBaseUrl(apiBaseUrl);
|
|
7
|
+
const resolved = new URL("/v3/social/guild-bots/ws", `${normalized}/`);
|
|
8
|
+
resolved.protocol = resolved.protocol === "https:" ? "wss:" : "ws:";
|
|
9
|
+
return resolved.toString();
|
|
10
|
+
}
|
|
11
|
+
export async function requestWithBotToken(args) {
|
|
12
|
+
const endpoint = args.path.startsWith("/") ? args.path : `/${args.path}`;
|
|
13
|
+
const options = args.options || {};
|
|
14
|
+
const response = await args.fetchImpl(`${args.apiBaseUrl}${endpoint}`, {
|
|
15
|
+
method: options.method || "GET",
|
|
16
|
+
headers: {
|
|
17
|
+
"Content-Type": "application/json",
|
|
18
|
+
Authorization: `Bot ${args.token}`,
|
|
19
|
+
...(options.headers || {}),
|
|
20
|
+
},
|
|
21
|
+
body: options.body === undefined ? undefined : JSON.stringify(options.body),
|
|
22
|
+
});
|
|
23
|
+
const data = (await response.json().catch(() => ({})));
|
|
24
|
+
if (!response.ok || data.success === false) {
|
|
25
|
+
throw new Error(data.message || `Channel bot request failed (${response.status})`);
|
|
26
|
+
}
|
|
27
|
+
return data;
|
|
28
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { ChannelBotClient } from "./client";
|
|
2
|
+
export { CommandContext, CommandRouter, messageInputFromInteraction, } from "./commands";
|
|
3
|
+
export type { ChannelBotClientOptions, ChannelBotEmbed, ChannelBotEmbedField, ChannelBotMessage, ChannelBotMessageInput, GuildBotInteraction, GuildBotMeResponse, GuildBotSocketEvent, } from "./types";
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EACN,cAAc,EACd,aAAa,EACb,2BAA2B,GAC3B,MAAM,YAAY,CAAC;AACpB,YAAY,EACX,uBAAuB,EACvB,eAAe,EACf,oBAAoB,EACpB,iBAAiB,EACjB,sBAAsB,EACtB,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,GACnB,MAAM,SAAS,CAAC"}
|