@minesa-org/mini-interaction 0.4.16 → 0.4.17
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 +47 -0
- package/dist/clients/MiniInteraction.d.ts +14 -0
- package/dist/clients/MiniInteraction.js +41 -0
- package/dist/clients/__tests__/MiniInteraction.test.d.ts +1 -0
- package/dist/clients/__tests__/MiniInteraction.test.js +110 -0
- package/dist/core/http/DiscordRestClient.d.ts +2 -1
- package/dist/core/http/DiscordRestClient.js +12 -2
- package/dist/index.d.ts +1 -0
- package/dist/types/RoleConnectionMetadata.d.ts +14 -0
- package/dist/types/RoleConnectionMetadata.js +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -139,6 +139,53 @@ try {
|
|
|
139
139
|
|
|
140
140
|
---
|
|
141
141
|
|
|
142
|
+
## 🔗 Linked Role Metadata
|
|
143
|
+
|
|
144
|
+
Register application role connection metadata with `mini.registerMetadata(...)`.
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
import {
|
|
148
|
+
MiniInteraction,
|
|
149
|
+
RoleConnectionMetadataTypes,
|
|
150
|
+
} from '@minesa-org/mini-interaction';
|
|
151
|
+
|
|
152
|
+
const mini = new MiniInteraction({
|
|
153
|
+
applicationId: process.env.DISCORD_APPLICATION_ID,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
await mini.registerMetadata(process.env.DISCORD_BOT_TOKEN!, [
|
|
157
|
+
{
|
|
158
|
+
key: 'is_miniapp',
|
|
159
|
+
name: 'Is Mini App?',
|
|
160
|
+
description: 'Is the user an assistant?',
|
|
161
|
+
type: RoleConnectionMetadataTypes.BooleanEqual,
|
|
162
|
+
},
|
|
163
|
+
]);
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Localization maps use `locale -> string` objects for `name_localizations` and `description_localizations`.
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
await mini.registerMetadata(process.env.DISCORD_BOT_TOKEN!, [
|
|
170
|
+
{
|
|
171
|
+
key: 'is_miniapp',
|
|
172
|
+
name: 'Is Mini App?',
|
|
173
|
+
description: 'Is the user an assistant?',
|
|
174
|
+
type: RoleConnectionMetadataTypes.BooleanEqual,
|
|
175
|
+
name_localizations: {
|
|
176
|
+
tr: 'Mini Uygulama mi?',
|
|
177
|
+
de: 'Ist Mini-App?',
|
|
178
|
+
},
|
|
179
|
+
description_localizations: {
|
|
180
|
+
tr: 'Kullanici bir assistant mi?',
|
|
181
|
+
de: 'Benutzer ist ein Assistent?',
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
]);
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
142
189
|
## 📜 License
|
|
143
190
|
|
|
144
191
|
MIT © [Minesa](https://github.com/minesa-org)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type DiscordRestClientOptions } from "../core/http/DiscordRestClient.js";
|
|
2
|
+
import type { RegisterMetadataResult, RoleConnectionMetadataInput } from "../types/RoleConnectionMetadata.js";
|
|
3
|
+
export type MiniInteractionOptions = {
|
|
4
|
+
applicationId?: string;
|
|
5
|
+
apiBaseUrl?: DiscordRestClientOptions["apiBaseUrl"];
|
|
6
|
+
maxRetries?: DiscordRestClientOptions["maxRetries"];
|
|
7
|
+
fetchImplementation?: DiscordRestClientOptions["fetchImplementation"];
|
|
8
|
+
};
|
|
9
|
+
export declare class MiniInteraction {
|
|
10
|
+
private readonly options;
|
|
11
|
+
constructor(options?: MiniInteractionOptions);
|
|
12
|
+
get applicationId(): string;
|
|
13
|
+
registerMetadata(botToken: string, metadata: RoleConnectionMetadataInput[]): Promise<RegisterMetadataResult>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { DiscordRestClient } from "../core/http/DiscordRestClient.js";
|
|
2
|
+
export class MiniInteraction {
|
|
3
|
+
options;
|
|
4
|
+
constructor(options = {}) {
|
|
5
|
+
this.options = options;
|
|
6
|
+
const fetchImpl = options.fetchImplementation ?? globalThis.fetch;
|
|
7
|
+
if (typeof fetchImpl !== "function") {
|
|
8
|
+
throw new Error("[MiniInteraction] fetch is not available. Provide a global fetch implementation.");
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
get applicationId() {
|
|
12
|
+
const resolvedApplicationId = this.options.applicationId ??
|
|
13
|
+
(typeof process !== "undefined"
|
|
14
|
+
? process.env.DISCORD_APPLICATION_ID
|
|
15
|
+
: undefined);
|
|
16
|
+
if (!resolvedApplicationId) {
|
|
17
|
+
throw new Error("[MiniInteraction] Missing Discord application ID. Set options.applicationId or DISCORD_APPLICATION_ID.");
|
|
18
|
+
}
|
|
19
|
+
return resolvedApplicationId;
|
|
20
|
+
}
|
|
21
|
+
async registerMetadata(botToken, metadata) {
|
|
22
|
+
if (!botToken) {
|
|
23
|
+
throw new Error("[MiniInteraction] botToken is required");
|
|
24
|
+
}
|
|
25
|
+
if (!Array.isArray(metadata) || metadata.length === 0) {
|
|
26
|
+
throw new Error("[MiniInteraction] metadata must be a non-empty array payload");
|
|
27
|
+
}
|
|
28
|
+
const rest = new DiscordRestClient({
|
|
29
|
+
token: botToken,
|
|
30
|
+
applicationId: this.applicationId,
|
|
31
|
+
apiBaseUrl: this.options.apiBaseUrl,
|
|
32
|
+
maxRetries: this.options.maxRetries,
|
|
33
|
+
fetchImplementation: this.options.fetchImplementation,
|
|
34
|
+
});
|
|
35
|
+
const payload = metadata.map((field) => ({
|
|
36
|
+
...field,
|
|
37
|
+
type: field.type,
|
|
38
|
+
}));
|
|
39
|
+
return rest.putApplicationRoleConnectionMetadata(payload);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import { MiniInteraction } from "../MiniInteraction.js";
|
|
4
|
+
import { RoleConnectionMetadataTypes } from "../../types/RoleConnectionMetadataTypes.js";
|
|
5
|
+
test("registerMetadata sends the Discord payload and returns the response", async () => {
|
|
6
|
+
const originalAppId = process.env.DISCORD_APPLICATION_ID;
|
|
7
|
+
process.env.DISCORD_APPLICATION_ID = "app_123";
|
|
8
|
+
const calls = [];
|
|
9
|
+
const fetchImpl = (async (input, init) => {
|
|
10
|
+
calls.push({ input: String(input), init });
|
|
11
|
+
return new Response(JSON.stringify([
|
|
12
|
+
{
|
|
13
|
+
key: "is_miniapp",
|
|
14
|
+
name: "Is Mini App?",
|
|
15
|
+
description: "Is the user an assistant?",
|
|
16
|
+
type: RoleConnectionMetadataTypes.BooleanEqual,
|
|
17
|
+
name_localizations: { tr: "Mini Uygulama mi?" },
|
|
18
|
+
description_localizations: { tr: "Kullanici bir assistant mi?" },
|
|
19
|
+
},
|
|
20
|
+
]), { status: 200 });
|
|
21
|
+
});
|
|
22
|
+
try {
|
|
23
|
+
const mini = new MiniInteraction({ fetchImplementation: fetchImpl });
|
|
24
|
+
const metadata = await mini.registerMetadata("bot-token", [
|
|
25
|
+
{
|
|
26
|
+
key: "is_miniapp",
|
|
27
|
+
name: "Is Mini App?",
|
|
28
|
+
description: "Is the user an assistant?",
|
|
29
|
+
type: RoleConnectionMetadataTypes.BooleanEqual,
|
|
30
|
+
name_localizations: { tr: "Mini Uygulama mi?" },
|
|
31
|
+
description_localizations: { tr: "Kullanici bir assistant mi?" },
|
|
32
|
+
},
|
|
33
|
+
]);
|
|
34
|
+
assert.equal(calls.length, 1);
|
|
35
|
+
assert.equal(calls[0]?.input, "https://discord.com/api/v10/applications/app_123/role-connections/metadata");
|
|
36
|
+
assert.equal(calls[0]?.init?.method, "PUT");
|
|
37
|
+
assert.deepEqual(JSON.parse(String(calls[0]?.init?.body)), [
|
|
38
|
+
{
|
|
39
|
+
key: "is_miniapp",
|
|
40
|
+
name: "Is Mini App?",
|
|
41
|
+
description: "Is the user an assistant?",
|
|
42
|
+
type: RoleConnectionMetadataTypes.BooleanEqual,
|
|
43
|
+
name_localizations: { tr: "Mini Uygulama mi?" },
|
|
44
|
+
description_localizations: { tr: "Kullanici bir assistant mi?" },
|
|
45
|
+
},
|
|
46
|
+
]);
|
|
47
|
+
assert.deepEqual(metadata, [
|
|
48
|
+
{
|
|
49
|
+
key: "is_miniapp",
|
|
50
|
+
name: "Is Mini App?",
|
|
51
|
+
description: "Is the user an assistant?",
|
|
52
|
+
type: RoleConnectionMetadataTypes.BooleanEqual,
|
|
53
|
+
name_localizations: { tr: "Mini Uygulama mi?" },
|
|
54
|
+
description_localizations: { tr: "Kullanici bir assistant mi?" },
|
|
55
|
+
},
|
|
56
|
+
]);
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
if (originalAppId === undefined) {
|
|
60
|
+
delete process.env.DISCORD_APPLICATION_ID;
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
process.env.DISCORD_APPLICATION_ID = originalAppId;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
test("registerMetadata throws when metadata is empty", async () => {
|
|
68
|
+
const mini = new MiniInteraction({ applicationId: "app_123" });
|
|
69
|
+
await assert.rejects(() => mini.registerMetadata("bot-token", []), /\[MiniInteraction\] metadata must be a non-empty array payload/);
|
|
70
|
+
});
|
|
71
|
+
test("registerMetadata throws when application id cannot be resolved", async () => {
|
|
72
|
+
const originalAppId = process.env.DISCORD_APPLICATION_ID;
|
|
73
|
+
delete process.env.DISCORD_APPLICATION_ID;
|
|
74
|
+
try {
|
|
75
|
+
const mini = new MiniInteraction();
|
|
76
|
+
await assert.rejects(() => mini.registerMetadata("bot-token", [
|
|
77
|
+
{
|
|
78
|
+
key: "is_miniapp",
|
|
79
|
+
name: "Is Mini App?",
|
|
80
|
+
description: "Is the user an assistant?",
|
|
81
|
+
type: RoleConnectionMetadataTypes.BooleanEqual,
|
|
82
|
+
},
|
|
83
|
+
]), /\[MiniInteraction\] Missing Discord application ID/);
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
if (originalAppId === undefined) {
|
|
87
|
+
delete process.env.DISCORD_APPLICATION_ID;
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
process.env.DISCORD_APPLICATION_ID = originalAppId;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
test("registerMetadata includes the response body in thrown errors", async () => {
|
|
95
|
+
const fetchImpl = (async () => new Response(JSON.stringify({ message: "Bad metadata" }), {
|
|
96
|
+
status: 400,
|
|
97
|
+
}));
|
|
98
|
+
const mini = new MiniInteraction({
|
|
99
|
+
applicationId: "app_123",
|
|
100
|
+
fetchImplementation: fetchImpl,
|
|
101
|
+
});
|
|
102
|
+
await assert.rejects(() => mini.registerMetadata("bot-token", [
|
|
103
|
+
{
|
|
104
|
+
key: "is_miniapp",
|
|
105
|
+
name: "Is Mini App?",
|
|
106
|
+
description: "Is the user an assistant?",
|
|
107
|
+
type: RoleConnectionMetadataTypes.BooleanEqual,
|
|
108
|
+
},
|
|
109
|
+
]), /\[DiscordRestClient\] PUT \/applications\/app_123\/role-connections\/metadata failed: 400 {"message":"Bad metadata"}/);
|
|
110
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { APIChannel } from 'discord-api-types/v10';
|
|
1
|
+
import type { APIChannel, RESTPutAPIApplicationRoleConnectionMetadataJSONBody, RESTPutAPIApplicationRoleConnectionMetadataResult } from 'discord-api-types/v10';
|
|
2
2
|
import { DiscordSentMessage } from '../messages/DiscordSentMessage.js';
|
|
3
3
|
import { type BaseDiscordMessageOptions, type DiscordReaction, type DiscordSendMessageOptions, type DiscordStartThreadOptions } from '../messages/message-payloads.js';
|
|
4
4
|
import { DiscordWebhook } from '../webhooks/DiscordWebhook.js';
|
|
@@ -29,5 +29,6 @@ export declare class DiscordRestClient {
|
|
|
29
29
|
startThread(options: DiscordStartThreadOptions): Promise<APIChannel>;
|
|
30
30
|
addReaction(channelId: string, messageId: string, reaction: DiscordReaction): Promise<void>;
|
|
31
31
|
webhook(id: string, token: string): DiscordWebhook;
|
|
32
|
+
putApplicationRoleConnectionMetadata(body: RESTPutAPIApplicationRoleConnectionMetadataJSONBody): Promise<RESTPutAPIApplicationRoleConnectionMetadataResult>;
|
|
32
33
|
}
|
|
33
34
|
export {};
|
|
@@ -48,13 +48,17 @@ export class DiscordRestClient {
|
|
|
48
48
|
if (response.ok) {
|
|
49
49
|
if (response.status === 204)
|
|
50
50
|
return undefined;
|
|
51
|
-
|
|
51
|
+
const responseText = await response.text();
|
|
52
|
+
if (!responseText)
|
|
53
|
+
return undefined;
|
|
54
|
+
return JSON.parse(responseText);
|
|
52
55
|
}
|
|
53
56
|
if (response.status >= 500 && attempt < this.maxRetries) {
|
|
54
57
|
await sleep(150 * (attempt + 1));
|
|
55
58
|
continue;
|
|
56
59
|
}
|
|
57
|
-
|
|
60
|
+
const errorBody = await response.text();
|
|
61
|
+
lastError = new Error(`[DiscordRestClient] ${requestInit.method ?? 'GET'} ${path} failed: ${response.status}${errorBody ? ` ${errorBody}` : ''}`);
|
|
58
62
|
break;
|
|
59
63
|
}
|
|
60
64
|
throw lastError instanceof Error ? lastError : new Error('[DiscordRestClient] unknown request failure');
|
|
@@ -127,6 +131,12 @@ export class DiscordRestClient {
|
|
|
127
131
|
webhook(id, token) {
|
|
128
132
|
return new DiscordWebhook(this, id, token);
|
|
129
133
|
}
|
|
134
|
+
putApplicationRoleConnectionMetadata(body) {
|
|
135
|
+
return this.request(`/applications/${this.options.applicationId}/role-connections/metadata`, {
|
|
136
|
+
method: 'PUT',
|
|
137
|
+
body: JSON.stringify(body),
|
|
138
|
+
});
|
|
139
|
+
}
|
|
130
140
|
}
|
|
131
141
|
function getDefaultContentTypeHeader(body) {
|
|
132
142
|
return body instanceof FormData ? {} : { 'Content-Type': 'application/json' };
|
package/dist/index.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ export { MiniDatabase } from "./database/MiniDatabase.js";
|
|
|
25
25
|
export { generateOAuthUrl, getOAuthTokens, refreshAccessToken, getDiscordUser, ensureValidToken, } from "./oauth/DiscordOAuth.js";
|
|
26
26
|
export type { OAuthConfig, OAuthTokens, DiscordUser, } from "./oauth/DiscordOAuth.js";
|
|
27
27
|
export { OAuthTokenStorage } from "./oauth/OAuthTokenStorage.js";
|
|
28
|
+
export type { DiscordLocale, LocalizationMap, RegisterMetadataResult, RoleConnectionMetadata, RoleConnectionMetadataInput, } from "./types/RoleConnectionMetadata.js";
|
|
28
29
|
export { DiscordRestClient } from "./core/http/DiscordRestClient.js";
|
|
29
30
|
export type { DiscordRestClientOptions } from "./core/http/DiscordRestClient.js";
|
|
30
31
|
export { DiscordSentMessage } from "./core/messages/DiscordSentMessage.js";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { APIApplicationRoleConnectionMetadata, Locale, RESTPutAPIApplicationRoleConnectionMetadataResult } from "discord-api-types/v10";
|
|
2
|
+
import type { RoleConnectionMetadataTypes } from "./RoleConnectionMetadataTypes.js";
|
|
3
|
+
export type DiscordLocale = `${Locale}`;
|
|
4
|
+
export type LocalizationMap = Partial<Record<DiscordLocale, string>>;
|
|
5
|
+
export type RoleConnectionMetadataInput = {
|
|
6
|
+
key: string;
|
|
7
|
+
name: string;
|
|
8
|
+
description: string;
|
|
9
|
+
type: RoleConnectionMetadataTypes;
|
|
10
|
+
name_localizations?: LocalizationMap;
|
|
11
|
+
description_localizations?: LocalizationMap;
|
|
12
|
+
};
|
|
13
|
+
export type RoleConnectionMetadata = APIApplicationRoleConnectionMetadata;
|
|
14
|
+
export type RegisterMetadataResult = RESTPutAPIApplicationRoleConnectionMetadataResult;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED