@theholocron/holocron-plugin-discord 5.0.0-alpha.18 → 5.0.0-alpha.181
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/README.md +20 -0
- package/dist/index.d.mts +47 -37
- package/dist/index.mjs +123 -38
- package/package.json +15 -11
package/README.md
CHANGED
|
@@ -61,3 +61,23 @@ server membership is required — generate one in Discord under
|
|
|
61
61
|
The webhook id and token are embedded in the URL path — no `Authorization`
|
|
62
62
|
header is sent. `parseWebhookUrl` is exported as a utility for callers
|
|
63
63
|
that need to split the URL into its parts.
|
|
64
|
+
|
|
65
|
+
## Provisioning
|
|
66
|
+
|
|
67
|
+
`holocron notifications setup` and `holocron notifications channel add` create
|
|
68
|
+
channels, roles and webhooks through the `notifications` capability's `ensure*`
|
|
69
|
+
methods. They use the **Holonet bot**, not a webhook URL:
|
|
70
|
+
|
|
71
|
+
- `HOLOCRON_DISCORD_BOT_TOKEN` (or `DISCORD_BOT_TOKEN`) — the bot token from
|
|
72
|
+
the Developer Portal. Only read when a provisioning command runs.
|
|
73
|
+
- `guildId` — the server id (not a secret), set in the provider options.
|
|
74
|
+
- `layout` — categories, channels (each optionally with a `webhook`) and roles.
|
|
75
|
+
|
|
76
|
+
The bot needs Manage Channels, Manage Roles, Manage Webhooks and View Channels.
|
|
77
|
+
Everything is idempotent (looked up by name). Discord returns a webhook's URL
|
|
78
|
+
only to the application that created it, so a webhook the bot did not create
|
|
79
|
+
cannot have its URL read back.
|
|
80
|
+
|
|
81
|
+
`baseUrl` only redirects posting. The bot token is always sent to the real
|
|
82
|
+
Discord API unless `unsafeBotBaseUrl: true` is also set (meant for tests that
|
|
83
|
+
stub the API), so a changed config cannot redirect the token to another host.
|
package/dist/index.d.mts
CHANGED
|
@@ -1,30 +1,8 @@
|
|
|
1
|
-
import { AuthError, Notifications, ResolveTokenInput } from "@theholocron/cli";
|
|
1
|
+
import { AuthError, EnsureWebhookResult, EnsuredResource, Notifications, NotificationsLayout, ResolveTokenInput, VerifyTokenFailure, VerifyTokenResult, VerifyTokenResult as VerifyTokenResult$1, VerifyTokenSuccess } from "@theholocron/cli";
|
|
2
|
+
import { DiscordBotClient, DiscordClient, DiscordClientOptions, DiscordWebhookInfo, createDiscordClient, parseWebhookUrl } from "@theholocron/discord-client";
|
|
2
3
|
//#region src/auth.d.ts
|
|
3
4
|
declare const resolveToken: (input?: ResolveTokenInput) => string;
|
|
4
|
-
|
|
5
|
-
//#region src/rest.d.ts
|
|
6
|
-
interface DiscordClientOptions {
|
|
7
|
-
/** Override base URL for tests. Default: https://discord.com/api/v10 */
|
|
8
|
-
baseUrl?: string;
|
|
9
|
-
fetch?: typeof fetch;
|
|
10
|
-
}
|
|
11
|
-
interface DiscordWebhookInfo {
|
|
12
|
-
id: string;
|
|
13
|
-
name: string;
|
|
14
|
-
guild_id?: string;
|
|
15
|
-
}
|
|
16
|
-
interface DiscordClient {
|
|
17
|
-
webhooks: {
|
|
18
|
-
get(id: string, token: string): Promise<DiscordWebhookInfo>;
|
|
19
|
-
execute(id: string, token: string, content: string): Promise<void>;
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
declare function createDiscordClient({ baseUrl, fetch: fetchImpl }?: DiscordClientOptions): DiscordClient;
|
|
23
|
-
/** Parse a Discord webhook URL into its id and token parts. */
|
|
24
|
-
declare function parseWebhookUrl(url: string): {
|
|
25
|
-
id: string;
|
|
26
|
-
token: string;
|
|
27
|
-
};
|
|
5
|
+
declare const resolveBotToken: (input?: ResolveTokenInput) => string;
|
|
28
6
|
//#endregion
|
|
29
7
|
//#region src/capabilities/notifications.d.ts
|
|
30
8
|
interface DiscordNotificationsOptions {
|
|
@@ -38,42 +16,72 @@ interface DiscordNotificationsOptions {
|
|
|
38
16
|
* without an explicit channel, or with an empty string.
|
|
39
17
|
*/
|
|
40
18
|
defaultChannel?: string;
|
|
19
|
+
/** Discord server (guild) id that `notifications setup` provisions. Not a secret. */
|
|
20
|
+
guildId?: string;
|
|
21
|
+
/** The server layout `notifications setup` applies. */
|
|
22
|
+
layout?: NotificationsLayout;
|
|
41
23
|
}
|
|
42
24
|
declare class DiscordNotifications implements Notifications {
|
|
43
25
|
private readonly client;
|
|
44
26
|
private readonly opts;
|
|
45
27
|
/** Memoized thunk — resolves the default webhook URL on the first `send()`. */
|
|
46
28
|
private readonly defaultWebhookUrl;
|
|
29
|
+
/** Memoized thunk — resolves the bot token on first provisioning call, never on `send()`. */
|
|
30
|
+
private readonly bot;
|
|
47
31
|
readonly key: "notifications";
|
|
48
32
|
readonly providerName = "discord";
|
|
49
33
|
constructor(client: DiscordClient, opts: DiscordNotificationsOptions,
|
|
50
34
|
/** Memoized thunk — resolves the default webhook URL on the first `send()`. */
|
|
51
|
-
defaultWebhookUrl: () => string
|
|
35
|
+
defaultWebhookUrl: () => string,
|
|
36
|
+
/** Memoized thunk — resolves the bot token on first provisioning call, never on `send()`. */
|
|
37
|
+
bot?: () => DiscordBotClient);
|
|
38
|
+
get layout(): NotificationsLayout | undefined;
|
|
52
39
|
send(channel: string, message: string): Promise<void>;
|
|
40
|
+
private guild;
|
|
41
|
+
private static same;
|
|
42
|
+
ensureCategory(name: string): Promise<EnsuredResource>;
|
|
43
|
+
/**
|
|
44
|
+
* Find a channel by name. A name can exist in several categories, so with a
|
|
45
|
+
* parent: prefer the one already there, then an uncategorised one (moved in);
|
|
46
|
+
* never grab a channel that lives under a different category. Announcement
|
|
47
|
+
* channels count: reusing one beats creating a same-named text channel.
|
|
48
|
+
*/
|
|
49
|
+
private static findChannel;
|
|
50
|
+
ensureChannel(input: {
|
|
51
|
+
name: string;
|
|
52
|
+
category?: string;
|
|
53
|
+
topic?: string;
|
|
54
|
+
}): Promise<EnsuredResource>;
|
|
55
|
+
ensureWebhook(input: {
|
|
56
|
+
channel: string;
|
|
57
|
+
name: string;
|
|
58
|
+
category?: string;
|
|
59
|
+
}): Promise<EnsureWebhookResult>;
|
|
60
|
+
ensureRole(input: {
|
|
61
|
+
name: string;
|
|
62
|
+
mentionable?: boolean;
|
|
63
|
+
}): Promise<EnsuredResource>;
|
|
53
64
|
private resolve;
|
|
54
65
|
}
|
|
55
66
|
//#endregion
|
|
56
67
|
//#region src/verify-token.d.ts
|
|
57
|
-
interface VerifyTokenSuccess {
|
|
58
|
-
ok: true;
|
|
59
|
-
subject: string;
|
|
60
|
-
}
|
|
61
|
-
interface VerifyTokenFailure {
|
|
62
|
-
ok: false;
|
|
63
|
-
message: string;
|
|
64
|
-
}
|
|
65
|
-
type VerifyTokenResult = VerifyTokenSuccess | VerifyTokenFailure;
|
|
66
68
|
interface VerifyTokenOptions {
|
|
67
69
|
baseUrl?: string;
|
|
68
70
|
fetch?: typeof fetch;
|
|
69
71
|
}
|
|
70
|
-
declare function verifyToken(webhookUrl: string, opts?: VerifyTokenOptions): Promise<VerifyTokenResult>;
|
|
72
|
+
declare function verifyToken(webhookUrl: string, opts?: VerifyTokenOptions): Promise<VerifyTokenResult$1>;
|
|
71
73
|
//#endregion
|
|
72
74
|
//#region src/index.d.ts
|
|
73
75
|
interface DiscordPluginOptions extends ResolveTokenInput, DiscordNotificationsOptions {
|
|
74
76
|
/** Override base URL for tests. Default: https://discord.com/api/v10 */
|
|
75
77
|
baseUrl?: string;
|
|
76
78
|
fetch?: typeof fetch;
|
|
79
|
+
/**
|
|
80
|
+
* Also send the BOT token to `baseUrl`. Off by default: `baseUrl` comes from a
|
|
81
|
+
* repo's config, and a changed config must not be able to redirect the bot
|
|
82
|
+
* token to another host. Tests that stub the API set it.
|
|
83
|
+
*/
|
|
84
|
+
unsafeBotBaseUrl?: boolean;
|
|
77
85
|
}
|
|
78
86
|
interface PluginContext {
|
|
79
87
|
options: DiscordPluginOptions;
|
|
@@ -83,6 +91,8 @@ interface PluginContext {
|
|
|
83
91
|
* resolved on first `send()`, not at plugin load. `describe()` never calls it.
|
|
84
92
|
*/
|
|
85
93
|
defaultWebhookUrl: () => string;
|
|
94
|
+
/** Memoized — resolves the bot token on the first provisioning call; `send()` and `describe()` never touch it. */
|
|
95
|
+
bot: () => DiscordBotClient;
|
|
86
96
|
}
|
|
87
97
|
declare function createContext(options?: DiscordPluginOptions): PluginContext;
|
|
88
98
|
declare function notifications(ctx: PluginContext): Notifications;
|
|
@@ -94,4 +104,4 @@ declare function createPlugin(options?: DiscordPluginOptions): {
|
|
|
94
104
|
};
|
|
95
105
|
declare const AUTH_HINT: string;
|
|
96
106
|
//#endregion
|
|
97
|
-
export { AUTH_HINT, AuthError, type DiscordClient, type DiscordClientOptions, DiscordNotifications, type DiscordNotificationsOptions, DiscordPluginOptions, type DiscordWebhookInfo, PluginContext, type ResolveTokenInput, type VerifyTokenFailure, type VerifyTokenResult, type VerifyTokenSuccess, createContext, createDiscordClient, createPlugin, notifications, parseWebhookUrl, resolveToken, verifyToken };
|
|
107
|
+
export { AUTH_HINT, AuthError, type DiscordClient, type DiscordClientOptions, DiscordNotifications, type DiscordNotificationsOptions, DiscordPluginOptions, type DiscordWebhookInfo, PluginContext, type ResolveTokenInput, type VerifyTokenFailure, type VerifyTokenResult, type VerifyTokenSuccess, createContext, createDiscordClient, createPlugin, notifications, parseWebhookUrl, resolveBotToken, resolveToken, verifyToken };
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { AuthError,
|
|
1
|
+
import { AuthError, createResolveToken } from "@theholocron/cli";
|
|
2
|
+
import { ChannelType, createDiscordBotClient, createDiscordClient, parseWebhookUrl } from "@theholocron/discord-client";
|
|
3
|
+
import { errorMessage } from "@theholocron/misc-utils";
|
|
2
4
|
//#region src/auth.ts
|
|
3
5
|
const resolveToken = createResolveToken({
|
|
4
6
|
envName: "HOLOCRON_DISCORD_WEBHOOK",
|
|
@@ -6,53 +8,126 @@ const resolveToken = createResolveToken({
|
|
|
6
8
|
keyringService: "discord",
|
|
7
9
|
errorMessage: "no Discord webhook URL found. Pass --token <WEBHOOK_URL>, set HOLOCRON_DISCORD_WEBHOOK / DISCORD_WEBHOOK_URL, or run: holocron auth set discord <https://discord.com/api/webhooks/...>"
|
|
8
10
|
});
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
get: async (id, token) => {
|
|
16
|
-
const res = await f(`${base}/webhooks/${id}/${token}`, { method: "GET" });
|
|
17
|
-
if (!res.ok) throw new ProviderApiError(`Discord webhook not found or invalid (${res.status})`, res.status, void 0);
|
|
18
|
-
return res.json();
|
|
19
|
-
},
|
|
20
|
-
execute: async (id, token, content) => {
|
|
21
|
-
const res = await f(`${base}/webhooks/${id}/${token}`, {
|
|
22
|
-
method: "POST",
|
|
23
|
-
headers: { "content-type": "application/json" },
|
|
24
|
-
body: JSON.stringify({ content })
|
|
25
|
-
});
|
|
26
|
-
if (!res.ok) throw new ProviderApiError(`Discord webhook POST failed (${res.status})`, res.status, void 0);
|
|
27
|
-
}
|
|
28
|
-
} };
|
|
29
|
-
}
|
|
30
|
-
/** Parse a Discord webhook URL into its id and token parts. */
|
|
31
|
-
function parseWebhookUrl(url) {
|
|
32
|
-
const match = url.match(/webhooks\/(\d+)\/([^/?#]+)/);
|
|
33
|
-
if (!match) throw new Error(`Invalid Discord webhook URL: ${url}`);
|
|
34
|
-
return {
|
|
35
|
-
id: match[1],
|
|
36
|
-
token: match[2]
|
|
37
|
-
};
|
|
38
|
-
}
|
|
11
|
+
const resolveBotToken = createResolveToken({
|
|
12
|
+
envName: "HOLOCRON_DISCORD_BOT_TOKEN",
|
|
13
|
+
vendorEnvName: "DISCORD_BOT_TOKEN",
|
|
14
|
+
keyringService: "discord-bot",
|
|
15
|
+
errorMessage: "no Discord bot token found. Set HOLOCRON_DISCORD_BOT_TOKEN (or DISCORD_BOT_TOKEN) — it is the bot token from the Discord developer portal (Bot → Reset Token), not a webhook URL."
|
|
16
|
+
});
|
|
39
17
|
//#endregion
|
|
40
18
|
//#region src/capabilities/notifications.ts
|
|
41
|
-
var DiscordNotifications = class {
|
|
19
|
+
var DiscordNotifications = class DiscordNotifications {
|
|
42
20
|
client;
|
|
43
21
|
opts;
|
|
44
22
|
defaultWebhookUrl;
|
|
23
|
+
bot;
|
|
45
24
|
key = "notifications";
|
|
46
25
|
providerName = "discord";
|
|
47
|
-
constructor(client, opts, defaultWebhookUrl) {
|
|
26
|
+
constructor(client, opts, defaultWebhookUrl, bot = () => {
|
|
27
|
+
throw new Error("no Discord bot client configured");
|
|
28
|
+
}) {
|
|
48
29
|
this.client = client;
|
|
49
30
|
this.opts = opts;
|
|
50
31
|
this.defaultWebhookUrl = defaultWebhookUrl;
|
|
32
|
+
this.bot = bot;
|
|
33
|
+
}
|
|
34
|
+
get layout() {
|
|
35
|
+
return this.opts.layout;
|
|
51
36
|
}
|
|
52
37
|
async send(channel, message) {
|
|
53
|
-
const
|
|
38
|
+
const webhookUrl = this.resolve(channel || this.opts.defaultChannel || "");
|
|
39
|
+
const { id, token } = parseWebhookUrl(webhookUrl);
|
|
54
40
|
await this.client.webhooks.execute(id, token, message);
|
|
55
41
|
}
|
|
42
|
+
guild() {
|
|
43
|
+
if (!this.opts.guildId) throw new Error("no Discord server id — set `guildId` in the discord provider options in holocron.config.ts");
|
|
44
|
+
return this.opts.guildId;
|
|
45
|
+
}
|
|
46
|
+
static same(a, b) {
|
|
47
|
+
return a.toLowerCase() === b.toLowerCase();
|
|
48
|
+
}
|
|
49
|
+
async ensureCategory(name) {
|
|
50
|
+
const bot = this.bot();
|
|
51
|
+
const existing = (await bot.guilds.channels(this.guild())).find((c) => c.type === ChannelType.Category && DiscordNotifications.same(c.name, name));
|
|
52
|
+
if (existing) return {
|
|
53
|
+
id: existing.id,
|
|
54
|
+
created: false
|
|
55
|
+
};
|
|
56
|
+
return {
|
|
57
|
+
id: (await bot.guilds.createChannel(this.guild(), {
|
|
58
|
+
name,
|
|
59
|
+
type: ChannelType.Category
|
|
60
|
+
})).id,
|
|
61
|
+
created: true
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Find a channel by name. A name can exist in several categories, so with a
|
|
66
|
+
* parent: prefer the one already there, then an uncategorised one (moved in);
|
|
67
|
+
* never grab a channel that lives under a different category. Announcement
|
|
68
|
+
* channels count: reusing one beats creating a same-named text channel.
|
|
69
|
+
*/
|
|
70
|
+
static findChannel(channels, name, parentId) {
|
|
71
|
+
const named = channels.filter((c) => (c.type === ChannelType.Text || c.type === ChannelType.Announcement) && DiscordNotifications.same(c.name, name));
|
|
72
|
+
if (!parentId) return named[0];
|
|
73
|
+
return named.find((c) => c.parent_id === parentId) ?? named.find((c) => !c.parent_id);
|
|
74
|
+
}
|
|
75
|
+
async ensureChannel(input) {
|
|
76
|
+
const bot = this.bot();
|
|
77
|
+
const parent = input.category ? (await this.ensureCategory(input.category)).id : void 0;
|
|
78
|
+
const existing = DiscordNotifications.findChannel(await bot.guilds.channels(this.guild()), input.name, parent);
|
|
79
|
+
if (existing) {
|
|
80
|
+
const patch = {
|
|
81
|
+
...parent && existing.parent_id !== parent ? { parent_id: parent } : {},
|
|
82
|
+
...input.topic !== void 0 && (existing.topic ?? "") !== input.topic ? { topic: input.topic } : {}
|
|
83
|
+
};
|
|
84
|
+
if (Object.keys(patch).length > 0) await bot.channels.modify(existing.id, patch);
|
|
85
|
+
return {
|
|
86
|
+
id: existing.id,
|
|
87
|
+
created: false
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
id: (await bot.guilds.createChannel(this.guild(), {
|
|
92
|
+
name: input.name,
|
|
93
|
+
type: ChannelType.Text,
|
|
94
|
+
...input.topic ? { topic: input.topic } : {},
|
|
95
|
+
...parent ? { parent_id: parent } : {}
|
|
96
|
+
})).id,
|
|
97
|
+
created: true
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
async ensureWebhook(input) {
|
|
101
|
+
const bot = this.bot();
|
|
102
|
+
const parent = input.category ? (await bot.guilds.channels(this.guild())).find((c) => c.type === ChannelType.Category && DiscordNotifications.same(c.name, input.category))?.id : void 0;
|
|
103
|
+
const channel = DiscordNotifications.findChannel(await bot.guilds.channels(this.guild()), input.channel, parent);
|
|
104
|
+
if (!channel) throw new Error(`no channel named \`${input.channel}\` — create it first`);
|
|
105
|
+
const existing = (await bot.channels.webhooks(channel.id)).find((w) => DiscordNotifications.same(w.name ?? "", input.name));
|
|
106
|
+
if (existing) return {
|
|
107
|
+
created: false,
|
|
108
|
+
...existing.url ? { url: existing.url } : {}
|
|
109
|
+
};
|
|
110
|
+
const made = await bot.channels.createWebhook(channel.id, { name: input.name });
|
|
111
|
+
return {
|
|
112
|
+
created: true,
|
|
113
|
+
...made.url ? { url: made.url } : {}
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
async ensureRole(input) {
|
|
117
|
+
const bot = this.bot();
|
|
118
|
+
const existing = (await bot.guilds.roles(this.guild())).find((r) => DiscordNotifications.same(r.name, input.name));
|
|
119
|
+
if (existing) return {
|
|
120
|
+
id: existing.id,
|
|
121
|
+
created: false
|
|
122
|
+
};
|
|
123
|
+
return {
|
|
124
|
+
id: (await bot.guilds.createRole(this.guild(), {
|
|
125
|
+
name: input.name,
|
|
126
|
+
...input.mentionable !== void 0 ? { mentionable: input.mentionable } : {}
|
|
127
|
+
})).id,
|
|
128
|
+
created: true
|
|
129
|
+
};
|
|
130
|
+
}
|
|
56
131
|
resolve(channel) {
|
|
57
132
|
const alias = this.opts.webhooks?.[channel];
|
|
58
133
|
if (alias) return alias;
|
|
@@ -78,7 +153,7 @@ async function verifyToken(webhookUrl, opts = {}) {
|
|
|
78
153
|
} catch (err) {
|
|
79
154
|
return {
|
|
80
155
|
ok: false,
|
|
81
|
-
message:
|
|
156
|
+
message: errorMessage(err)
|
|
82
157
|
};
|
|
83
158
|
}
|
|
84
159
|
}
|
|
@@ -86,17 +161,27 @@ async function verifyToken(webhookUrl, opts = {}) {
|
|
|
86
161
|
//#region src/index.ts
|
|
87
162
|
function createContext(options = {}) {
|
|
88
163
|
let url;
|
|
164
|
+
let bot;
|
|
89
165
|
return {
|
|
90
166
|
options,
|
|
91
167
|
client: createDiscordClient({
|
|
92
168
|
baseUrl: options.baseUrl,
|
|
93
169
|
fetch: options.fetch
|
|
94
170
|
}),
|
|
95
|
-
defaultWebhookUrl: () => url ??= resolveToken(options)
|
|
171
|
+
defaultWebhookUrl: () => url ??= resolveToken(options),
|
|
172
|
+
bot: () => bot ??= createDiscordBotClient({
|
|
173
|
+
botToken: resolveBotToken({
|
|
174
|
+
env: options.env,
|
|
175
|
+
keyring: options.keyring,
|
|
176
|
+
org: options.org
|
|
177
|
+
}),
|
|
178
|
+
...options.unsafeBotBaseUrl && options.baseUrl ? { baseUrl: options.baseUrl } : {},
|
|
179
|
+
...options.fetch ? { fetch: options.fetch } : {}
|
|
180
|
+
})
|
|
96
181
|
};
|
|
97
182
|
}
|
|
98
183
|
function notifications(ctx) {
|
|
99
|
-
return new DiscordNotifications(ctx.client, ctx.options, ctx.defaultWebhookUrl);
|
|
184
|
+
return new DiscordNotifications(ctx.client, ctx.options, ctx.defaultWebhookUrl, ctx.bot);
|
|
100
185
|
}
|
|
101
186
|
function createPlugin(options = {}) {
|
|
102
187
|
const ctx = createContext(options);
|
|
@@ -107,4 +192,4 @@ function createPlugin(options = {}) {
|
|
|
107
192
|
}
|
|
108
193
|
const AUTH_HINT = "create a webhook in Discord: open your server → channel settings → Integrations → Webhooks → New Webhook, copy the webhook URL, then run: holocron auth set discord <https://discord.com/api/webhooks/...>";
|
|
109
194
|
//#endregion
|
|
110
|
-
export { AUTH_HINT, AuthError, DiscordNotifications, createContext, createDiscordClient, createPlugin, notifications, parseWebhookUrl, resolveToken, verifyToken };
|
|
195
|
+
export { AUTH_HINT, AuthError, DiscordNotifications, createContext, createDiscordClient, createPlugin, notifications, parseWebhookUrl, resolveBotToken, resolveToken, verifyToken };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theholocron/holocron-plugin-discord",
|
|
3
|
-
"version": "5.0.0-alpha.
|
|
3
|
+
"version": "5.0.0-alpha.181",
|
|
4
4
|
"description": "Holocron plugin for Discord. Implements the notifications capability via Discord incoming webhooks.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"discord",
|
|
@@ -8,12 +8,12 @@
|
|
|
8
8
|
"notifications",
|
|
9
9
|
"plugin"
|
|
10
10
|
],
|
|
11
|
-
"homepage": "https://github.com/theholocron/holocron/tree/main/
|
|
11
|
+
"homepage": "https://github.com/theholocron/holocron/tree/main/plugins/holocron-plugin-discord#readme",
|
|
12
12
|
"bugs": "https://github.com/theholocron/holocron/issues",
|
|
13
13
|
"repository": {
|
|
14
14
|
"type": "git",
|
|
15
15
|
"url": "git+https://github.com/theholocron/holocron.git",
|
|
16
|
-
"directory": "
|
|
16
|
+
"directory": "plugins/holocron-plugin-discord"
|
|
17
17
|
},
|
|
18
18
|
"license": "MIT",
|
|
19
19
|
"author": "Newton Koumantzelis",
|
|
@@ -30,11 +30,13 @@
|
|
|
30
30
|
"dist"
|
|
31
31
|
],
|
|
32
32
|
"devDependencies": {
|
|
33
|
-
"@theholocron/
|
|
34
|
-
"@theholocron/
|
|
35
|
-
"@theholocron/
|
|
36
|
-
"@theholocron/
|
|
37
|
-
"@theholocron/
|
|
33
|
+
"@theholocron/discord-client": "^1.43.1",
|
|
34
|
+
"@theholocron/eslint-config": "^8.8.2",
|
|
35
|
+
"@theholocron/http-client": "^1.43.1",
|
|
36
|
+
"@theholocron/misc-utils": "^1.16.0",
|
|
37
|
+
"@theholocron/tsconfig": "^8.8.2",
|
|
38
|
+
"@theholocron/tsdown-config": "^8.8.2",
|
|
39
|
+
"@theholocron/vitest-config": "^8.8.2",
|
|
38
40
|
"@types/node": "^26",
|
|
39
41
|
"@vitest/coverage-v8": "^4.1.11",
|
|
40
42
|
"@vitest/eslint-plugin": "^1.6.27",
|
|
@@ -43,13 +45,15 @@
|
|
|
43
45
|
"eslint-plugin-n": "^18.3.0",
|
|
44
46
|
"globals": "^17.11.0",
|
|
45
47
|
"tsdown": "^0.22.14",
|
|
46
|
-
"tsx": "4.23.
|
|
48
|
+
"tsx": "4.23.15",
|
|
47
49
|
"typescript": "^5.9.3",
|
|
48
50
|
"vitest": "^4.1.11",
|
|
49
|
-
"@theholocron/cli": "5.0.0-alpha.
|
|
51
|
+
"@theholocron/cli": "5.0.0-alpha.181"
|
|
50
52
|
},
|
|
51
53
|
"peerDependencies": {
|
|
52
|
-
"@theholocron/
|
|
54
|
+
"@theholocron/discord-client": "^1.43.1",
|
|
55
|
+
"@theholocron/misc-utils": "^1.16.0",
|
|
56
|
+
"@theholocron/cli": "5.0.0-alpha.181"
|
|
53
57
|
},
|
|
54
58
|
"engines": {
|
|
55
59
|
"node": ">=22"
|