@notidotbot/noti-api-client 1.9.63 → 1.9.64
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/classes/adminAnnouncements.d.ts +108 -0
- package/dist/classes/adminAnnouncements.js +52 -0
- package/dist/classes/adminPolls.d.ts +111 -0
- package/dist/classes/adminPolls.js +59 -0
- package/dist/classes/announcements.d.ts +68 -0
- package/dist/classes/announcements.js +45 -0
- package/dist/classes/guildInfluencerPanel.d.ts +266 -0
- package/dist/classes/guildInfluencerPanel.js +98 -0
- package/dist/classes/guildPlatform.d.ts +15 -3
- package/dist/classes/polls.d.ts +117 -0
- package/dist/classes/polls.js +62 -0
- package/dist/core/manager.d.ts +10 -0
- package/dist/core/manager.js +10 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/other/bitfields.d.ts +11 -2
- package/dist/other/bitfields.js +9 -0
- package/dist/other/features.d.ts +1 -1
- package/dist/other/features.js +3 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { WebDataManager } from '../core/manager';
|
|
2
|
+
import { CancelOutWebResponses, WebResponse } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* Influencer Panel - members holding a role the admin chose add THEIR OWN stream from a panel posted in
|
|
5
|
+
* Discord, and Noti posts their lives, videos and posts with the server's settings.
|
|
6
|
+
*
|
|
7
|
+
* The admin sets, per platform, the channel and ping role (and a second channel/role for videos or posts
|
|
8
|
+
* where the platform has them), the live role, a log channel, a "verified" role given while a member has an
|
|
9
|
+
* active stream, and how many accounts one member may add per platform. Influencer streams count toward the
|
|
10
|
+
* server's own per-platform limit; the per-member cap never raises it.
|
|
11
|
+
*
|
|
12
|
+
* A member who loses every required role, or leaves, has their streams PAUSED (the creator is untracked and
|
|
13
|
+
* the slot freed); getting the role back resumes them while there is room. A stream that cannot come back
|
|
14
|
+
* stays paused with reason `limit`.
|
|
15
|
+
*
|
|
16
|
+
* Tiers: free - one required role, cap 1. Premium - several roles, cap up to 3, approvals, member text.
|
|
17
|
+
* Premium Plus - the full panel editor, the Live Now board, webhook identity, cap up to 5. `data.access` from
|
|
18
|
+
* `getGuildInfluencerPanel` says what the server may set; a setting above it is refused with 403.
|
|
19
|
+
*
|
|
20
|
+
* Behind the `influencerPanel` launch flag: every method answers 403 until it is beta/public for the caller,
|
|
21
|
+
* and 403 when the server's plan lacks the Influencer Panel.
|
|
22
|
+
*/
|
|
23
|
+
export declare class APIGuildInfluencerPanel {
|
|
24
|
+
private web;
|
|
25
|
+
constructor(web: WebDataManager);
|
|
26
|
+
/** The setup (`panel` null = never set up), what the plan allows, the platforms on offer and entry counts. */
|
|
27
|
+
getGuildInfluencerPanel({ auth, guildId }: GuildInfluencerPanelFunctionsInput['getGuildInfluencerPanel']): Promise<WebResponse<GuildInfluencerPanelData>>;
|
|
28
|
+
/**
|
|
29
|
+
* Change the setup, field by field - omit one to leave it unchanged. `platforms` upserts the platforms
|
|
30
|
+
* listed and leaves the others alone.
|
|
31
|
+
*
|
|
32
|
+
* 400 names the problem: a bad id, a platform switched on without a channel, a setting the platform does
|
|
33
|
+
* not have, or a channel/role Noti cannot use. **403** for a setting above the plan. Turning a Premium
|
|
34
|
+
* setting OFF is always accepted.
|
|
35
|
+
*
|
|
36
|
+
* After saving, every active influencer stream is updated to the new settings (and paused where its
|
|
37
|
+
* platform went off), a posted panel and board are redrawn, and switching the panel back on resumes what
|
|
38
|
+
* switching it off paused. `applied`, `panelError` and `boardError` report that.
|
|
39
|
+
*/
|
|
40
|
+
updateGuildInfluencerPanel({ auth, guildId, ...body }: GuildInfluencerPanelFunctionsInput['updateGuildInfluencerPanel']): Promise<WebResponse<GuildInfluencerPanelUpdateData>>;
|
|
41
|
+
/** Post the panel to `panelChannelId`, or redraw it where it already is. 400 with the reason when Discord or a permission refuses. */
|
|
42
|
+
createGuildInfluencerPanelMessage({ auth, guildId }: GuildInfluencerPanelFunctionsInput['createGuildInfluencerPanelMessage']): Promise<WebResponse<GuildInfluencerPanelData>>;
|
|
43
|
+
/** Delete the posted panel message. The setup and every stream stay. */
|
|
44
|
+
deleteGuildInfluencerPanelMessage({ auth, guildId }: GuildInfluencerPanelFunctionsInput['deleteGuildInfluencerPanelMessage']): Promise<WebResponse<GuildInfluencerPanelData>>;
|
|
45
|
+
/** Members' streams, newest first (at most 500). `status: 'pending'` is the approval queue. */
|
|
46
|
+
getGuildInfluencerEntries({ auth, guildId, status }: GuildInfluencerPanelFunctionsInput['getGuildInfluencerEntries']): Promise<WebResponse<{
|
|
47
|
+
entries: GuildInfluencerEntry[];
|
|
48
|
+
}>>;
|
|
49
|
+
/**
|
|
50
|
+
* Act on one stream: `pause` (active only), `resume` (paused only - goes through the server's limit
|
|
51
|
+
* again), `approve` or `deny` (pending only; `reason` is sent to the member). 404 when it is not this
|
|
52
|
+
* server's, 409 while the member's streams are being changed.
|
|
53
|
+
*/
|
|
54
|
+
updateGuildInfluencerEntry({ auth, guildId, entryId, ...body }: GuildInfluencerPanelFunctionsInput['updateGuildInfluencerEntry']): Promise<WebResponse<{
|
|
55
|
+
entry: GuildInfluencerEntry;
|
|
56
|
+
}>>;
|
|
57
|
+
/** Remove a stream: the creator is untracked and the member is told in the log channel. */
|
|
58
|
+
deleteGuildInfluencerEntry({ auth, guildId, entryId }: GuildInfluencerPanelFunctionsInput['deleteGuildInfluencerEntry']): Promise<WebResponse<{
|
|
59
|
+
entryId: string;
|
|
60
|
+
}>>;
|
|
61
|
+
}
|
|
62
|
+
/** Platforms the panel offers. */
|
|
63
|
+
export type InfluencerPlatform = 'kick' | 'twitch' | 'youtube' | 'tiktok' | 'rumble' | 'twitter' | 'club';
|
|
64
|
+
/** Every `InfluencerPlatform`, in the order the API lists them. */
|
|
65
|
+
export declare const INFLUENCER_PLATFORMS: readonly InfluencerPlatform[];
|
|
66
|
+
export type InfluencerEntryStatus = 'pending' | 'active' | 'paused' | 'denied';
|
|
67
|
+
/** Why a stream is paused: lost the role, left, panel/platform switched off, paused by an admin, server limit full on resume. */
|
|
68
|
+
export type InfluencerPausedReason = 'role' | 'left' | 'panelOff' | 'manual' | 'limit';
|
|
69
|
+
export type InfluencerEntryAction = 'pause' | 'resume' | 'approve' | 'deny';
|
|
70
|
+
export type GuildInfluencerPlatformSettings = {
|
|
71
|
+
platform: InfluencerPlatform;
|
|
72
|
+
enabled: boolean;
|
|
73
|
+
/** Lives (posts on Club). */
|
|
74
|
+
channelId: string | null;
|
|
75
|
+
pingRoleId: string | null;
|
|
76
|
+
/** Videos (YouTube, TikTok, Rumble) or posts (X). */
|
|
77
|
+
secondaryChannelId: string | null;
|
|
78
|
+
/** YouTube, Rumble and X only - TikTok uses `pingRoleId` for both. */
|
|
79
|
+
secondaryPingRoleId: string | null;
|
|
80
|
+
};
|
|
81
|
+
/** The panel message's look. `title` and `description` on every plan; the rest is Premium Plus. */
|
|
82
|
+
export type GuildInfluencerPanelDesign = {
|
|
83
|
+
title?: string;
|
|
84
|
+
description?: string;
|
|
85
|
+
/** 0x000000-0xFFFFFF. */
|
|
86
|
+
color?: number;
|
|
87
|
+
/** https only. */
|
|
88
|
+
imageUrl?: string;
|
|
89
|
+
footer?: string;
|
|
90
|
+
addLabel?: string;
|
|
91
|
+
mineLabel?: string;
|
|
92
|
+
addEmoji?: string;
|
|
93
|
+
mineEmoji?: string;
|
|
94
|
+
};
|
|
95
|
+
export type GuildInfluencerPanel = {
|
|
96
|
+
dbId: string;
|
|
97
|
+
guildId: string;
|
|
98
|
+
enabled: boolean;
|
|
99
|
+
/** Any one of them lets a member use the panel. More than one is Premium. */
|
|
100
|
+
requiredRoleIds: string[];
|
|
101
|
+
logChannelId: string | null;
|
|
102
|
+
/** Streams one member may add per platform. */
|
|
103
|
+
perMemberLimit: number;
|
|
104
|
+
liveRoleId: string | null;
|
|
105
|
+
verifiedRoleId: string | null;
|
|
106
|
+
/**
|
|
107
|
+
* Creator Managers (every plan): besides Manage Server, members with one of these roles may approve /
|
|
108
|
+
* deny requests from the log channel and use `/influencer add|remove @member` in Discord.
|
|
109
|
+
*/
|
|
110
|
+
managerRoleIds: string[];
|
|
111
|
+
/** Premium. */
|
|
112
|
+
requireApproval: boolean;
|
|
113
|
+
/** Premium. The member's own line replaces the random message. */
|
|
114
|
+
allowMemberText: boolean;
|
|
115
|
+
/** Adds `{member}` (the member's mention) to their notifications. */
|
|
116
|
+
showMemberTag: boolean;
|
|
117
|
+
/**
|
|
118
|
+
* Premium Plus + CustomWebhooks. Influencer notifications (live, offline, video, posts) post through a
|
|
119
|
+
* webhook with `webhookUsername` / `webhookAvatarUrl`. A creator's own custom-message webhook still wins;
|
|
120
|
+
* this wins over the server's default. Applies only while a name or an avatar is set.
|
|
121
|
+
*/
|
|
122
|
+
useWebhook: boolean;
|
|
123
|
+
/** 1-80 characters, no "discord" or "clyde". Takes placeholders, e.g. `{streamerName}`. */
|
|
124
|
+
webhookUsername: string | null;
|
|
125
|
+
/** https only. */
|
|
126
|
+
webhookAvatarUrl: string | null;
|
|
127
|
+
panelChannelId: string | null;
|
|
128
|
+
panelMessageId: string | null;
|
|
129
|
+
panelDesign: GuildInfluencerPanelDesign | null;
|
|
130
|
+
/** Premium Plus. */
|
|
131
|
+
liveBoardChannelId: string | null;
|
|
132
|
+
liveBoardMessageId: string | null;
|
|
133
|
+
createdAt: string;
|
|
134
|
+
updatedAt: string;
|
|
135
|
+
platforms: GuildInfluencerPlatformSettings[];
|
|
136
|
+
};
|
|
137
|
+
/** What the server's plan allows. */
|
|
138
|
+
export type GuildInfluencerPanelAccess = {
|
|
139
|
+
tier: 'free' | 'premium' | 'plus';
|
|
140
|
+
maxRequiredRoles: number;
|
|
141
|
+
maxPerMemberLimit: number;
|
|
142
|
+
approvals: boolean;
|
|
143
|
+
memberText: boolean;
|
|
144
|
+
/** The full panel editor (beyond title and description). */
|
|
145
|
+
panelDesign: boolean;
|
|
146
|
+
liveBoard: boolean;
|
|
147
|
+
/** Webhook identity: Premium Plus AND CustomWebhooks. */
|
|
148
|
+
webhook: boolean;
|
|
149
|
+
};
|
|
150
|
+
/** Which settings a platform has, so the page draws the right fields. */
|
|
151
|
+
export type GuildInfluencerPlatformMeta = {
|
|
152
|
+
platform: InfluencerPlatform;
|
|
153
|
+
secondary: 'video' | 'post' | null;
|
|
154
|
+
secondaryRole: boolean;
|
|
155
|
+
};
|
|
156
|
+
export type GuildInfluencerPanelData = {
|
|
157
|
+
panel: GuildInfluencerPanel | null;
|
|
158
|
+
access: GuildInfluencerPanelAccess;
|
|
159
|
+
platforms: GuildInfluencerPlatformMeta[];
|
|
160
|
+
counts: Record<InfluencerEntryStatus, number>;
|
|
161
|
+
};
|
|
162
|
+
export type GuildInfluencerPanelUpdateData = GuildInfluencerPanelData & {
|
|
163
|
+
/** Active streams updated, paused (platform went off) and resumed (panel switched back on). */
|
|
164
|
+
applied: {
|
|
165
|
+
updated: number;
|
|
166
|
+
paused: number;
|
|
167
|
+
resumed: number;
|
|
168
|
+
};
|
|
169
|
+
/** Why a posted panel could not be redrawn, or null. */
|
|
170
|
+
panelError: string | null;
|
|
171
|
+
/** Why the Live Now board could not be redrawn, or null. */
|
|
172
|
+
boardError: string | null;
|
|
173
|
+
};
|
|
174
|
+
export type GuildInfluencerEntry = {
|
|
175
|
+
dbId: string;
|
|
176
|
+
guildId: string;
|
|
177
|
+
/** The member's Discord id. */
|
|
178
|
+
userId: string;
|
|
179
|
+
platform: InfluencerPlatform;
|
|
180
|
+
streamerUserName: string;
|
|
181
|
+
status: InfluencerEntryStatus;
|
|
182
|
+
pausedReason: InfluencerPausedReason | null;
|
|
183
|
+
deniedReason: string | null;
|
|
184
|
+
/** The member's own line (Premium). */
|
|
185
|
+
note: string | null;
|
|
186
|
+
/** Who approved or denied it. */
|
|
187
|
+
decidedBy: string | null;
|
|
188
|
+
/** Rumble only: 'channel' or 'user'. */
|
|
189
|
+
rumbleType: string | null;
|
|
190
|
+
createdAt: string;
|
|
191
|
+
updatedAt: string;
|
|
192
|
+
};
|
|
193
|
+
/** The setup's editable fields. */
|
|
194
|
+
export type GuildInfluencerPanelUpdate = {
|
|
195
|
+
enabled?: boolean;
|
|
196
|
+
requiredRoleIds?: string[];
|
|
197
|
+
/** Up to 10 roles. Checked against the server: a role that no longer exists is refused. */
|
|
198
|
+
managerRoleIds?: string[];
|
|
199
|
+
logChannelId?: string | null;
|
|
200
|
+
perMemberLimit?: number;
|
|
201
|
+
liveRoleId?: string | null;
|
|
202
|
+
verifiedRoleId?: string | null;
|
|
203
|
+
requireApproval?: boolean;
|
|
204
|
+
allowMemberText?: boolean;
|
|
205
|
+
showMemberTag?: boolean;
|
|
206
|
+
panelChannelId?: string | null;
|
|
207
|
+
panelDesign?: GuildInfluencerPanelDesign | null;
|
|
208
|
+
liveBoardChannelId?: string | null;
|
|
209
|
+
useWebhook?: boolean;
|
|
210
|
+
webhookUsername?: string | null;
|
|
211
|
+
webhookAvatarUrl?: string | null;
|
|
212
|
+
platforms?: Array<{
|
|
213
|
+
platform: InfluencerPlatform;
|
|
214
|
+
} & Partial<Omit<GuildInfluencerPlatformSettings, 'platform'>>>;
|
|
215
|
+
};
|
|
216
|
+
export type GuildInfluencerPanelFunctionsInput = {
|
|
217
|
+
'getGuildInfluencerPanel': {
|
|
218
|
+
auth: string;
|
|
219
|
+
guildId: string;
|
|
220
|
+
};
|
|
221
|
+
'updateGuildInfluencerPanel': {
|
|
222
|
+
auth: string;
|
|
223
|
+
guildId: string;
|
|
224
|
+
} & GuildInfluencerPanelUpdate;
|
|
225
|
+
'createGuildInfluencerPanelMessage': {
|
|
226
|
+
auth: string;
|
|
227
|
+
guildId: string;
|
|
228
|
+
};
|
|
229
|
+
'deleteGuildInfluencerPanelMessage': {
|
|
230
|
+
auth: string;
|
|
231
|
+
guildId: string;
|
|
232
|
+
};
|
|
233
|
+
'getGuildInfluencerEntries': {
|
|
234
|
+
auth: string;
|
|
235
|
+
guildId: string;
|
|
236
|
+
status?: InfluencerEntryStatus;
|
|
237
|
+
};
|
|
238
|
+
'updateGuildInfluencerEntry': {
|
|
239
|
+
auth: string;
|
|
240
|
+
guildId: string;
|
|
241
|
+
entryId: string;
|
|
242
|
+
action: InfluencerEntryAction;
|
|
243
|
+
reason?: string;
|
|
244
|
+
};
|
|
245
|
+
'deleteGuildInfluencerEntry': {
|
|
246
|
+
auth: string;
|
|
247
|
+
guildId: string;
|
|
248
|
+
entryId: string;
|
|
249
|
+
};
|
|
250
|
+
};
|
|
251
|
+
export type GuildInfluencerPanelReturnTypes = {
|
|
252
|
+
'getGuildInfluencerPanelRaw': Awaited<ReturnType<APIGuildInfluencerPanel['getGuildInfluencerPanel']>>;
|
|
253
|
+
'getGuildInfluencerPanelSuccess': CancelOutWebResponses<Awaited<ReturnType<APIGuildInfluencerPanel['getGuildInfluencerPanel']>>>;
|
|
254
|
+
'updateGuildInfluencerPanelRaw': Awaited<ReturnType<APIGuildInfluencerPanel['updateGuildInfluencerPanel']>>;
|
|
255
|
+
'updateGuildInfluencerPanelSuccess': CancelOutWebResponses<Awaited<ReturnType<APIGuildInfluencerPanel['updateGuildInfluencerPanel']>>>;
|
|
256
|
+
'createGuildInfluencerPanelMessageRaw': Awaited<ReturnType<APIGuildInfluencerPanel['createGuildInfluencerPanelMessage']>>;
|
|
257
|
+
'createGuildInfluencerPanelMessageSuccess': CancelOutWebResponses<Awaited<ReturnType<APIGuildInfluencerPanel['createGuildInfluencerPanelMessage']>>>;
|
|
258
|
+
'deleteGuildInfluencerPanelMessageRaw': Awaited<ReturnType<APIGuildInfluencerPanel['deleteGuildInfluencerPanelMessage']>>;
|
|
259
|
+
'deleteGuildInfluencerPanelMessageSuccess': CancelOutWebResponses<Awaited<ReturnType<APIGuildInfluencerPanel['deleteGuildInfluencerPanelMessage']>>>;
|
|
260
|
+
'getGuildInfluencerEntriesRaw': Awaited<ReturnType<APIGuildInfluencerPanel['getGuildInfluencerEntries']>>;
|
|
261
|
+
'getGuildInfluencerEntriesSuccess': CancelOutWebResponses<Awaited<ReturnType<APIGuildInfluencerPanel['getGuildInfluencerEntries']>>>;
|
|
262
|
+
'updateGuildInfluencerEntryRaw': Awaited<ReturnType<APIGuildInfluencerPanel['updateGuildInfluencerEntry']>>;
|
|
263
|
+
'updateGuildInfluencerEntrySuccess': CancelOutWebResponses<Awaited<ReturnType<APIGuildInfluencerPanel['updateGuildInfluencerEntry']>>>;
|
|
264
|
+
'deleteGuildInfluencerEntryRaw': Awaited<ReturnType<APIGuildInfluencerPanel['deleteGuildInfluencerEntry']>>;
|
|
265
|
+
'deleteGuildInfluencerEntrySuccess': CancelOutWebResponses<Awaited<ReturnType<APIGuildInfluencerPanel['deleteGuildInfluencerEntry']>>>;
|
|
266
|
+
};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.INFLUENCER_PLATFORMS = exports.APIGuildInfluencerPanel = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Influencer Panel - members holding a role the admin chose add THEIR OWN stream from a panel posted in
|
|
6
|
+
* Discord, and Noti posts their lives, videos and posts with the server's settings.
|
|
7
|
+
*
|
|
8
|
+
* The admin sets, per platform, the channel and ping role (and a second channel/role for videos or posts
|
|
9
|
+
* where the platform has them), the live role, a log channel, a "verified" role given while a member has an
|
|
10
|
+
* active stream, and how many accounts one member may add per platform. Influencer streams count toward the
|
|
11
|
+
* server's own per-platform limit; the per-member cap never raises it.
|
|
12
|
+
*
|
|
13
|
+
* A member who loses every required role, or leaves, has their streams PAUSED (the creator is untracked and
|
|
14
|
+
* the slot freed); getting the role back resumes them while there is room. A stream that cannot come back
|
|
15
|
+
* stays paused with reason `limit`.
|
|
16
|
+
*
|
|
17
|
+
* Tiers: free - one required role, cap 1. Premium - several roles, cap up to 3, approvals, member text.
|
|
18
|
+
* Premium Plus - the full panel editor, the Live Now board, webhook identity, cap up to 5. `data.access` from
|
|
19
|
+
* `getGuildInfluencerPanel` says what the server may set; a setting above it is refused with 403.
|
|
20
|
+
*
|
|
21
|
+
* Behind the `influencerPanel` launch flag: every method answers 403 until it is beta/public for the caller,
|
|
22
|
+
* and 403 when the server's plan lacks the Influencer Panel.
|
|
23
|
+
*/
|
|
24
|
+
class APIGuildInfluencerPanel {
|
|
25
|
+
web;
|
|
26
|
+
constructor(web) {
|
|
27
|
+
this.web = web;
|
|
28
|
+
}
|
|
29
|
+
/** The setup (`panel` null = never set up), what the plan allows, the platforms on offer and entry counts. */
|
|
30
|
+
async getGuildInfluencerPanel({ auth, guildId }) {
|
|
31
|
+
return await this.web.request({
|
|
32
|
+
method: 'GET', auth,
|
|
33
|
+
endpoint: this.web.qp(`/data/guild/${guildId}/influencer-panel`),
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Change the setup, field by field - omit one to leave it unchanged. `platforms` upserts the platforms
|
|
38
|
+
* listed and leaves the others alone.
|
|
39
|
+
*
|
|
40
|
+
* 400 names the problem: a bad id, a platform switched on without a channel, a setting the platform does
|
|
41
|
+
* not have, or a channel/role Noti cannot use. **403** for a setting above the plan. Turning a Premium
|
|
42
|
+
* setting OFF is always accepted.
|
|
43
|
+
*
|
|
44
|
+
* After saving, every active influencer stream is updated to the new settings (and paused where its
|
|
45
|
+
* platform went off), a posted panel and board are redrawn, and switching the panel back on resumes what
|
|
46
|
+
* switching it off paused. `applied`, `panelError` and `boardError` report that.
|
|
47
|
+
*/
|
|
48
|
+
async updateGuildInfluencerPanel({ auth, guildId, ...body }) {
|
|
49
|
+
return await this.web.request({
|
|
50
|
+
method: 'PATCH', auth,
|
|
51
|
+
endpoint: this.web.qp(`/data/guild/${guildId}/influencer-panel`),
|
|
52
|
+
body,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
/** Post the panel to `panelChannelId`, or redraw it where it already is. 400 with the reason when Discord or a permission refuses. */
|
|
56
|
+
async createGuildInfluencerPanelMessage({ auth, guildId }) {
|
|
57
|
+
return await this.web.request({
|
|
58
|
+
method: 'POST', auth,
|
|
59
|
+
endpoint: this.web.qp(`/data/guild/${guildId}/influencer-panel/message`),
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
/** Delete the posted panel message. The setup and every stream stay. */
|
|
63
|
+
async deleteGuildInfluencerPanelMessage({ auth, guildId }) {
|
|
64
|
+
return await this.web.request({
|
|
65
|
+
method: 'DELETE', auth,
|
|
66
|
+
endpoint: this.web.qp(`/data/guild/${guildId}/influencer-panel/message`),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
/** Members' streams, newest first (at most 500). `status: 'pending'` is the approval queue. */
|
|
70
|
+
async getGuildInfluencerEntries({ auth, guildId, status }) {
|
|
71
|
+
return await this.web.request({
|
|
72
|
+
method: 'GET', auth,
|
|
73
|
+
endpoint: this.web.qp(`/data/guild/${guildId}/influencer-panel/entries`, { status }),
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Act on one stream: `pause` (active only), `resume` (paused only - goes through the server's limit
|
|
78
|
+
* again), `approve` or `deny` (pending only; `reason` is sent to the member). 404 when it is not this
|
|
79
|
+
* server's, 409 while the member's streams are being changed.
|
|
80
|
+
*/
|
|
81
|
+
async updateGuildInfluencerEntry({ auth, guildId, entryId, ...body }) {
|
|
82
|
+
return await this.web.request({
|
|
83
|
+
method: 'PATCH', auth,
|
|
84
|
+
endpoint: this.web.qp(`/data/guild/${guildId}/influencer-panel/entries/${entryId}`),
|
|
85
|
+
body,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
/** Remove a stream: the creator is untracked and the member is told in the log channel. */
|
|
89
|
+
async deleteGuildInfluencerEntry({ auth, guildId, entryId }) {
|
|
90
|
+
return await this.web.request({
|
|
91
|
+
method: 'DELETE', auth,
|
|
92
|
+
endpoint: this.web.qp(`/data/guild/${guildId}/influencer-panel/entries/${entryId}`),
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
exports.APIGuildInfluencerPanel = APIGuildInfluencerPanel;
|
|
97
|
+
/** Every `InfluencerPlatform`, in the order the API lists them. */
|
|
98
|
+
exports.INFLUENCER_PLATFORMS = ['kick', 'twitch', 'youtube', 'tiktok', 'rumble', 'twitter', 'club'];
|
|
@@ -132,9 +132,21 @@ type ResolvesUsersForLiveRole = 'kick' | 'twitch' | 'rumble' | 'tiktok' | 'youtu
|
|
|
132
132
|
* `string[]`, not with `[]`. Discriminate on the element - `typeof first === 'string'` - rather than
|
|
133
133
|
* on length, because an empty array is a legitimate member of both arms.
|
|
134
134
|
*/
|
|
135
|
-
|
|
135
|
+
/**
|
|
136
|
+
* A creator a community member added from the Influencer Panel: the member, and their panel entry (for
|
|
137
|
+
* `guildInfluencerPanel.updateGuildInfluencerEntry` / `deleteGuildInfluencerEntry`). `null` on every
|
|
138
|
+
* creator an admin added. Only on the two GET routes - create/update answer with the row alone.
|
|
139
|
+
*/
|
|
140
|
+
export type GuildStreamerInfluencerLink = {
|
|
141
|
+
userId: string;
|
|
142
|
+
entryId: string;
|
|
143
|
+
};
|
|
144
|
+
type WithInfluencerLink = {
|
|
145
|
+
influencer: GuildStreamerInfluencerLink | null;
|
|
146
|
+
};
|
|
147
|
+
export type GuildSingleStreamerWithFormatedRole<T extends AllPlatforms> = T extends ServedPlatform ? (T extends ResolvesUsersForLiveRole ? Omit<GuildSingleStreamer<T>, 'usersForLiveRole'> & {
|
|
136
148
|
usersForLiveRole: UsersForLiveRole | string[];
|
|
137
|
-
} : GuildSingleStreamer<T> : never;
|
|
149
|
+
} : GuildSingleStreamer<T>) & WithInfluencerLink : never;
|
|
138
150
|
/**
|
|
139
151
|
* The platforms `GET /data/guild/:guildId/:platform` can actually answer.
|
|
140
152
|
*
|
|
@@ -191,7 +203,7 @@ export type Platformstreamers<T extends boolean, P extends AllPlatforms> = {
|
|
|
191
203
|
* The zod type under-claims instead (it also drops the relation FK scalars), and under-claiming
|
|
192
204
|
* cannot be read by accident.
|
|
193
205
|
*/
|
|
194
|
-
streamers: P extends ServedPlatform ? (T extends true ? Pick<GuildSingleStreamer<P>, Extract<keyof GuildSingleStreamer<P>, ShortViewKeySets[P]>> : GuildSingleStreamer<P>)[] : never;
|
|
206
|
+
streamers: P extends ServedPlatform ? ((T extends true ? Pick<GuildSingleStreamer<P>, Extract<keyof GuildSingleStreamer<P>, ShortViewKeySets[P]>> : GuildSingleStreamer<P>) & WithInfluencerLink)[] : never;
|
|
195
207
|
currentTotal: number;
|
|
196
208
|
maxLimit: number;
|
|
197
209
|
premium: {
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { CancelOutWebResponses } from '../types';
|
|
2
|
+
import { WebDataManager } from '../core/manager';
|
|
3
|
+
/**
|
|
4
|
+
* Site polls - developer-run polls on the dashboard ("which feature next?").
|
|
5
|
+
*
|
|
6
|
+
* ── WHO SEES WHAT ──────────────────────────────────────────────────────────
|
|
7
|
+
* `listPolls` needs no session. Everything else needs a logged-in user (any Discord account), so pass `auth`.
|
|
8
|
+
* Behind the `sitePolls` launch flag: while it is hidden for the caller the list is EMPTY (not 403) and a
|
|
9
|
+
* single poll 404s. Draft and scheduled polls are 404 here; developers manage them through `adminPolls`.
|
|
10
|
+
*
|
|
11
|
+
* ── RESULTS ────────────────────────────────────────────────────────────────
|
|
12
|
+
* Each poll sets `resultsVisibility`: `always`, `afterVote` (once this user has voted, or the poll closed) or
|
|
13
|
+
* `afterClose`. The server never sends counts the viewer may not see, so `results` is null until then - do
|
|
14
|
+
* not compute or guess them on the page.
|
|
15
|
+
*
|
|
16
|
+
* Percent is a share of VOTES on a single-choice poll and a share of VOTERS on a multi-choice one (so those
|
|
17
|
+
* can sum past 100). The public list is cached up to 15s, so counts there can trail by that much.
|
|
18
|
+
*/
|
|
19
|
+
export declare class APIPolls {
|
|
20
|
+
private web;
|
|
21
|
+
constructor(web: WebDataManager);
|
|
22
|
+
/** Open polls first, then ones closed in the last 30 days. No session. */
|
|
23
|
+
listPolls(): Promise<import("../types").WebResponse<SitePollList>>;
|
|
24
|
+
/** One poll, plus where the logged-in user stands on it. */
|
|
25
|
+
getPoll({ auth, pollId }: PollsFunctionsInput['getPoll']): Promise<import("../types").WebResponse<SitePollView>>;
|
|
26
|
+
/**
|
|
27
|
+
* Cast or change the logged-in user's vote: `optionIds` is the full pick (1 to `maxChoices`), replacing
|
|
28
|
+
* any earlier one. A refusal is `{ status: 400 | 404 | 409 | 429, error: { code } }`, `code` from
|
|
29
|
+
* `SitePollErrorCode`. Re-sending the same pick is not an error even when changes are locked. 10/minute.
|
|
30
|
+
*/
|
|
31
|
+
setPollVote({ auth, pollId, optionIds }: PollsFunctionsInput['setPollVote']): Promise<import("../types").WebResponse<SitePollView>>;
|
|
32
|
+
/** Withdraw the logged-in user's vote. Only on polls with `allowVoteChange`, while open. */
|
|
33
|
+
deletePollVote({ auth, pollId }: PollsFunctionsInput['deletePollVote']): Promise<import("../types").WebResponse<SitePollView>>;
|
|
34
|
+
}
|
|
35
|
+
export type PollsFunctionsInput = {
|
|
36
|
+
'getPoll': {
|
|
37
|
+
auth: string;
|
|
38
|
+
pollId: string;
|
|
39
|
+
};
|
|
40
|
+
'setPollVote': {
|
|
41
|
+
auth: string;
|
|
42
|
+
pollId: string;
|
|
43
|
+
optionIds: string[];
|
|
44
|
+
};
|
|
45
|
+
'deletePollVote': {
|
|
46
|
+
auth: string;
|
|
47
|
+
pollId: string;
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
export type PollsGetReturnTypes = {
|
|
51
|
+
'listPollsRaw': Awaited<ReturnType<APIPolls['listPolls']>>;
|
|
52
|
+
'listPollsSuccess': CancelOutWebResponses<Awaited<ReturnType<APIPolls['listPolls']>>>;
|
|
53
|
+
'getPollRaw': Awaited<ReturnType<APIPolls['getPoll']>>;
|
|
54
|
+
'getPollSuccess': CancelOutWebResponses<Awaited<ReturnType<APIPolls['getPoll']>>>;
|
|
55
|
+
'setPollVoteRaw': Awaited<ReturnType<APIPolls['setPollVote']>>;
|
|
56
|
+
'setPollVoteSuccess': CancelOutWebResponses<Awaited<ReturnType<APIPolls['setPollVote']>>>;
|
|
57
|
+
'deletePollVoteRaw': Awaited<ReturnType<APIPolls['deletePollVote']>>;
|
|
58
|
+
'deletePollVoteSuccess': CancelOutWebResponses<Awaited<ReturnType<APIPolls['deletePollVote']>>>;
|
|
59
|
+
};
|
|
60
|
+
/** Why a vote or withdrawal was refused. Mirrors `SITE_POLL_REFUSALS` in Noti-v3's `src/modules/sitePolls.ts`. */
|
|
61
|
+
export declare const SITE_POLL_REFUSALS: readonly ["not_open", "closed", "no_choice", "too_many_choices", "invalid_option", "vote_locked"];
|
|
62
|
+
export type SitePollRefusal = typeof SITE_POLL_REFUSALS[number];
|
|
63
|
+
/** Every `error.code` the vote routes return: the refusals, plus `busy` (a vote of theirs is still saving) and `rate_limited`. */
|
|
64
|
+
export type SitePollErrorCode = SitePollRefusal | 'busy' | 'rate_limited';
|
|
65
|
+
export type SitePollState = 'draft' | 'scheduled' | 'open' | 'closed';
|
|
66
|
+
export type SitePollResultsVisibility = 'always' | 'afterVote' | 'afterClose';
|
|
67
|
+
/** One poll as the site shows it. Mirrors `SitePollCard` in Noti-v3. */
|
|
68
|
+
export type SitePoll = {
|
|
69
|
+
pollId: string;
|
|
70
|
+
title: string;
|
|
71
|
+
/** Markdown. */
|
|
72
|
+
description: string | null;
|
|
73
|
+
state: SitePollState;
|
|
74
|
+
/** 1 = single choice. */
|
|
75
|
+
maxChoices: number;
|
|
76
|
+
allowVoteChange: boolean;
|
|
77
|
+
resultsVisibility: SitePollResultsVisibility;
|
|
78
|
+
/** ISO. */
|
|
79
|
+
opensAt: string | null;
|
|
80
|
+
/** ISO. */
|
|
81
|
+
closesAt: string | null;
|
|
82
|
+
/** In display order. */
|
|
83
|
+
options: SitePollOption[];
|
|
84
|
+
};
|
|
85
|
+
export type SitePollOption = {
|
|
86
|
+
optionId: string;
|
|
87
|
+
label: string;
|
|
88
|
+
description: string | null;
|
|
89
|
+
};
|
|
90
|
+
export type SitePollResults = {
|
|
91
|
+
/** In display order. */
|
|
92
|
+
options: Array<{
|
|
93
|
+
optionId: string;
|
|
94
|
+
votes: number;
|
|
95
|
+
percent: number;
|
|
96
|
+
}>;
|
|
97
|
+
totalVotes: number;
|
|
98
|
+
totalVoters: number;
|
|
99
|
+
};
|
|
100
|
+
export type SitePollViewer = {
|
|
101
|
+
votedOptionIds: string[];
|
|
102
|
+
/** Can vote now (open, and not voted yet or changes allowed). */
|
|
103
|
+
canVote: boolean;
|
|
104
|
+
canChangeVote: boolean;
|
|
105
|
+
resultsVisible: boolean;
|
|
106
|
+
};
|
|
107
|
+
export type SitePollList = {
|
|
108
|
+
polls: Array<{
|
|
109
|
+
poll: SitePoll;
|
|
110
|
+
results: SitePollResults | null;
|
|
111
|
+
}>;
|
|
112
|
+
};
|
|
113
|
+
export type SitePollView = {
|
|
114
|
+
poll: SitePoll;
|
|
115
|
+
viewer: SitePollViewer;
|
|
116
|
+
results: SitePollResults | null;
|
|
117
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SITE_POLL_REFUSALS = exports.APIPolls = void 0;
|
|
4
|
+
// Data.
|
|
5
|
+
/**
|
|
6
|
+
* Site polls - developer-run polls on the dashboard ("which feature next?").
|
|
7
|
+
*
|
|
8
|
+
* ── WHO SEES WHAT ──────────────────────────────────────────────────────────
|
|
9
|
+
* `listPolls` needs no session. Everything else needs a logged-in user (any Discord account), so pass `auth`.
|
|
10
|
+
* Behind the `sitePolls` launch flag: while it is hidden for the caller the list is EMPTY (not 403) and a
|
|
11
|
+
* single poll 404s. Draft and scheduled polls are 404 here; developers manage them through `adminPolls`.
|
|
12
|
+
*
|
|
13
|
+
* ── RESULTS ────────────────────────────────────────────────────────────────
|
|
14
|
+
* Each poll sets `resultsVisibility`: `always`, `afterVote` (once this user has voted, or the poll closed) or
|
|
15
|
+
* `afterClose`. The server never sends counts the viewer may not see, so `results` is null until then - do
|
|
16
|
+
* not compute or guess them on the page.
|
|
17
|
+
*
|
|
18
|
+
* Percent is a share of VOTES on a single-choice poll and a share of VOTERS on a multi-choice one (so those
|
|
19
|
+
* can sum past 100). The public list is cached up to 15s, so counts there can trail by that much.
|
|
20
|
+
*/
|
|
21
|
+
class APIPolls {
|
|
22
|
+
web;
|
|
23
|
+
constructor(web) {
|
|
24
|
+
this.web = web;
|
|
25
|
+
}
|
|
26
|
+
/** Open polls first, then ones closed in the last 30 days. No session. */
|
|
27
|
+
async listPolls() {
|
|
28
|
+
return await this.web.request({
|
|
29
|
+
method: 'GET',
|
|
30
|
+
endpoint: this.web.qp('/public/polls'),
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
/** One poll, plus where the logged-in user stands on it. */
|
|
34
|
+
async getPoll({ auth, pollId }) {
|
|
35
|
+
return await this.web.request({
|
|
36
|
+
method: 'GET', auth,
|
|
37
|
+
endpoint: this.web.qp('/public/polls/' + pollId),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Cast or change the logged-in user's vote: `optionIds` is the full pick (1 to `maxChoices`), replacing
|
|
42
|
+
* any earlier one. A refusal is `{ status: 400 | 404 | 409 | 429, error: { code } }`, `code` from
|
|
43
|
+
* `SitePollErrorCode`. Re-sending the same pick is not an error even when changes are locked. 10/minute.
|
|
44
|
+
*/
|
|
45
|
+
async setPollVote({ auth, pollId, optionIds }) {
|
|
46
|
+
return await this.web.request({
|
|
47
|
+
method: 'PUT', auth,
|
|
48
|
+
endpoint: this.web.qp('/public/polls/' + pollId + '/votes/@me'),
|
|
49
|
+
body: { optionIds },
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
/** Withdraw the logged-in user's vote. Only on polls with `allowVoteChange`, while open. */
|
|
53
|
+
async deletePollVote({ auth, pollId }) {
|
|
54
|
+
return await this.web.request({
|
|
55
|
+
method: 'DELETE', auth,
|
|
56
|
+
endpoint: this.web.qp('/public/polls/' + pollId + '/votes/@me'),
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
exports.APIPolls = APIPolls;
|
|
61
|
+
/** Why a vote or withdrawal was refused. Mirrors `SITE_POLL_REFUSALS` in Noti-v3's `src/modules/sitePolls.ts`. */
|
|
62
|
+
exports.SITE_POLL_REFUSALS = ['not_open', 'closed', 'no_choice', 'too_many_choices', 'invalid_option', 'vote_locked'];
|
package/dist/core/manager.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ import { APIGuildDrops } from '../classes/guildDrops';
|
|
|
12
12
|
import { APIGuildBadges } from '../classes/guildBadges';
|
|
13
13
|
import { APIGuildBounties } from '../classes/guildBounties';
|
|
14
14
|
import { APIGuildAutoRoles } from '../classes/guildAutoRoles';
|
|
15
|
+
import { APIGuildInfluencerPanel } from '../classes/guildInfluencerPanel';
|
|
15
16
|
import { APIDropsGames } from '../classes/dropsGames';
|
|
16
17
|
import { APIGames } from '../classes/games';
|
|
17
18
|
import { AxiosResponse } from 'axios';
|
|
@@ -31,6 +32,10 @@ import { APIMinigamesFishing } from '../classes/minigamesFishing';
|
|
|
31
32
|
import { APIPublicFishing } from '../classes/publicFishing';
|
|
32
33
|
import { APIPublicGiveaways } from '../classes/publicGiveaways';
|
|
33
34
|
import { APIPublicDrops } from '../classes/publicDrops';
|
|
35
|
+
import { APIPolls } from '../classes/polls';
|
|
36
|
+
import { APIAnnouncements } from '../classes/announcements';
|
|
37
|
+
import { APIAdminPolls } from '../classes/adminPolls';
|
|
38
|
+
import { APIAdminAnnouncements } from '../classes/adminAnnouncements';
|
|
34
39
|
import { APIGuildFishing } from '../classes/guildFishing';
|
|
35
40
|
import { APIAdminFishing } from '../classes/adminFishing';
|
|
36
41
|
import { APIAdmin } from '../classes/admin';
|
|
@@ -54,6 +59,10 @@ export declare class WebDataManager {
|
|
|
54
59
|
readonly publicFishing: APIPublicFishing;
|
|
55
60
|
readonly publicGiveaways: APIPublicGiveaways;
|
|
56
61
|
readonly publicDrops: APIPublicDrops;
|
|
62
|
+
readonly polls: APIPolls;
|
|
63
|
+
readonly announcements: APIAnnouncements;
|
|
64
|
+
readonly adminPolls: APIAdminPolls;
|
|
65
|
+
readonly adminAnnouncements: APIAdminAnnouncements;
|
|
57
66
|
readonly other: APIOther;
|
|
58
67
|
readonly guild: APIGuild;
|
|
59
68
|
readonly teams: APITeams;
|
|
@@ -67,6 +76,7 @@ export declare class WebDataManager {
|
|
|
67
76
|
readonly guildBadges: APIGuildBadges;
|
|
68
77
|
readonly guildBounties: APIGuildBounties;
|
|
69
78
|
readonly guildAutoRoles: APIGuildAutoRoles;
|
|
79
|
+
readonly guildInfluencerPanel: APIGuildInfluencerPanel;
|
|
70
80
|
readonly dropsGames: APIDropsGames;
|
|
71
81
|
readonly guildPlatform: APIGuildPlatform;
|
|
72
82
|
readonly guildGiveaway: APIGuildGiveaway;
|
package/dist/core/manager.js
CHANGED
|
@@ -18,6 +18,7 @@ const guildDrops_1 = require("../classes/guildDrops");
|
|
|
18
18
|
const guildBadges_1 = require("../classes/guildBadges");
|
|
19
19
|
const guildBounties_1 = require("../classes/guildBounties");
|
|
20
20
|
const guildAutoRoles_1 = require("../classes/guildAutoRoles");
|
|
21
|
+
const guildInfluencerPanel_1 = require("../classes/guildInfluencerPanel");
|
|
21
22
|
const dropsGames_1 = require("../classes/dropsGames");
|
|
22
23
|
const games_1 = require("../classes/games");
|
|
23
24
|
const axios_1 = __importDefault(require("axios"));
|
|
@@ -36,6 +37,10 @@ const minigamesFishing_1 = require("../classes/minigamesFishing");
|
|
|
36
37
|
const publicFishing_1 = require("../classes/publicFishing");
|
|
37
38
|
const publicGiveaways_1 = require("../classes/publicGiveaways");
|
|
38
39
|
const publicDrops_1 = require("../classes/publicDrops");
|
|
40
|
+
const polls_1 = require("../classes/polls");
|
|
41
|
+
const announcements_1 = require("../classes/announcements");
|
|
42
|
+
const adminPolls_1 = require("../classes/adminPolls");
|
|
43
|
+
const adminAnnouncements_1 = require("../classes/adminAnnouncements");
|
|
39
44
|
const guildFishing_1 = require("../classes/guildFishing");
|
|
40
45
|
const adminFishing_1 = require("../classes/adminFishing");
|
|
41
46
|
const admin_1 = require("../classes/admin");
|
|
@@ -58,6 +63,10 @@ class WebDataManager {
|
|
|
58
63
|
publicFishing = new publicFishing_1.APIPublicFishing(this);
|
|
59
64
|
publicGiveaways = new publicGiveaways_1.APIPublicGiveaways(this);
|
|
60
65
|
publicDrops = new publicDrops_1.APIPublicDrops(this);
|
|
66
|
+
polls = new polls_1.APIPolls(this);
|
|
67
|
+
announcements = new announcements_1.APIAnnouncements(this);
|
|
68
|
+
adminPolls = new adminPolls_1.APIAdminPolls(this);
|
|
69
|
+
adminAnnouncements = new adminAnnouncements_1.APIAdminAnnouncements(this);
|
|
61
70
|
other = new other_1.APIOther(this);
|
|
62
71
|
guild = new guild_1.APIGuild(this);
|
|
63
72
|
teams = new teams_1.APITeams(this);
|
|
@@ -71,6 +80,7 @@ class WebDataManager {
|
|
|
71
80
|
guildBadges = new guildBadges_1.APIGuildBadges(this);
|
|
72
81
|
guildBounties = new guildBounties_1.APIGuildBounties(this);
|
|
73
82
|
guildAutoRoles = new guildAutoRoles_1.APIGuildAutoRoles(this);
|
|
83
|
+
guildInfluencerPanel = new guildInfluencerPanel_1.APIGuildInfluencerPanel(this);
|
|
74
84
|
dropsGames = new dropsGames_1.APIDropsGames(this);
|
|
75
85
|
guildPlatform = new guildPlatform_1.APIGuildPlatform(this);
|
|
76
86
|
guildGiveaway = new guildGiveaway_1.APIGuildGiveaway(this);
|