@notidotbot/noti-api-client 1.9.73 → 1.9.75

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.
Files changed (59) hide show
  1. package/dist/classes/@me.d.ts +45 -0
  2. package/dist/classes/@me.js +25 -0
  3. package/dist/classes/guildInGame.d.ts +259 -0
  4. package/dist/classes/guildInGame.js +114 -0
  5. package/dist/classes/guildInfluencerPanel.d.ts +13 -0
  6. package/dist/classes/guildMessageScheduler.d.ts +15 -1
  7. package/dist/classes/publicGiveaways.d.ts +1 -1
  8. package/dist/classes/publicGiveaways.js +1 -1
  9. package/dist/core/manager.d.ts +2 -0
  10. package/dist/core/manager.js +2 -0
  11. package/dist/index.d.ts +6 -0
  12. package/dist/index.js +19 -1
  13. package/dist/other/bitfields.d.ts +8 -2
  14. package/dist/other/bitfields.js +6 -0
  15. package/dist/other/features.d.ts +1 -1
  16. package/dist/other/features.js +2 -0
  17. package/dist/other/inGameAlerts.d.ts +83 -0
  18. package/dist/other/inGameAlerts.js +65 -0
  19. package/dist/other/inGameMiniMessage.d.ts +14 -0
  20. package/dist/other/inGameMiniMessage.js +135 -0
  21. package/dist/other/multiDestination.d.ts +89 -0
  22. package/dist/other/multiDestination.js +109 -0
  23. package/dist/other/types.d.ts +2 -0
  24. package/dist/other/zod/clubStreamer.zod.d.ts +12 -0
  25. package/dist/other/zod/clubStreamer.zod.js +10 -0
  26. package/dist/other/zod/giveaways.zod.d.ts +17 -0
  27. package/dist/other/zod/giveaways.zod.js +8 -0
  28. package/dist/other/zod/kickStreamer.zod.d.ts +23 -0
  29. package/dist/other/zod/kickStreamer.zod.js +11 -0
  30. package/dist/other/zod/rumbleStreamer.zod.d.ts +25 -0
  31. package/dist/other/zod/rumbleStreamer.zod.js +11 -0
  32. package/dist/other/zod/scheduledMessage.zod.d.ts +10 -2
  33. package/dist/other/zod/scheduledMessage.zod.js +29 -5
  34. package/dist/other/zod/schema.zod.d.ts +6 -0
  35. package/dist/other/zod/schema.zod.js +7 -1
  36. package/dist/other/zod/tiktokStreamer.zod.d.ts +25 -0
  37. package/dist/other/zod/tiktokStreamer.zod.js +11 -0
  38. package/dist/other/zod/twitchStreamer.zod.d.ts +23 -0
  39. package/dist/other/zod/twitchStreamer.zod.js +11 -0
  40. package/dist/other/zod/twitterStreamer.zod.d.ts +25 -0
  41. package/dist/other/zod/twitterStreamer.zod.js +11 -0
  42. package/dist/other/zod/youtubeStreamer.zod.d.ts +26 -0
  43. package/dist/other/zod/youtubeStreamer.zod.js +11 -0
  44. package/dist/other/zod/zod.d.ts +15 -0
  45. package/dist/other/zod/zod.js +12 -0
  46. package/dist/tsconfig.tsbuildinfo +1 -1
  47. package/package.json +1 -1
  48. package/prisma/generated/ts-prisma.d.ts +129 -0
  49. package/prisma/schema/models/clubStreamer.prisma +9 -0
  50. package/prisma/schema/models/giveaways.prisma +9 -0
  51. package/prisma/schema/models/inGame.prisma +77 -0
  52. package/prisma/schema/models/kickStreamer.prisma +11 -0
  53. package/prisma/schema/models/rumbleStreamer.prisma +12 -0
  54. package/prisma/schema/models/scheduledMessage.prisma +10 -1
  55. package/prisma/schema/models/tiktokStreamer.prisma +11 -0
  56. package/prisma/schema/models/twitchStreamer.prisma +12 -0
  57. package/prisma/schema/models/twitterStreamer.prisma +12 -0
  58. package/prisma/schema/models/youtubeStreamer.prisma +11 -0
  59. package/prisma/schema/schema.prisma +16 -0
@@ -129,6 +129,8 @@ export declare const GuildFeatures: {
129
129
  StatusRoleKeepOnOffline: bigint;
130
130
  YouTubeReplayChannel: bigint;
131
131
  LivePostUpdates: bigint;
132
+ MultiDestination: bigint;
133
+ InGameRewards: bigint;
132
134
  };
133
135
  export declare const GuildFeaturesText: Record<keyof typeof GuildFeatures, string>;
134
136
  export declare const UserFeatures: {
@@ -245,8 +247,10 @@ export declare class GuildFeaturesManager extends BitFieldManager<typeof GuildFe
245
247
  StatusRoleKeepOnOffline: bigint;
246
248
  YouTubeReplayChannel: bigint;
247
249
  LivePostUpdates: bigint;
250
+ MultiDestination: bigint;
251
+ InGameRewards: bigint;
248
252
  };
249
- t: Record<"StickyMessages" | "LinkedRoles" | "StatusRoles" | "Starboards" | "KickClips" | "StatCounters" | "BaseSyncableRoles" | "FullSyncableRoles" | "LiveRole" | "VoteSkipping" | "KickAuditLogs" | "LeaderboardCommand" | "RefreshingLeaderboard" | "LeaderBoardSyncRoles" | "BetaAccess" | "PriorityNotifications" | "KickNotifications" | "TwitchNotifications" | "ImportEmojis" | "ImportEmojisWithSub" | "CustomWebhooks" | "AnyMessageEditor" | "SyncSocialUsernames" | "AutoDeleteNotifications" | "TestNotificationCommand" | "CustomCooldowns" | "WelcomeAndLeaveNotifications" | "NotificationPingRoleId" | "SwitchStreamerAndGuildEmbeds" | "BirthdayNotifications" | "AllowNotSendingOfflineNotifications" | "AutoStatusLiveRoles" | "RumbleNotifications" | "AllowAutoPublishNotifications" | "NotificationOverrides" | "UseCustomButtons" | "AllowVodFeatures" | "TikTokNotifications" | "ManageBranding" | "CustomStarboards" | "AutoThreadOnStarboard" | "StarboardRewardRole" | "Giveaways" | "GiveawayLogs" | "ModifyGiveawayWhileRunning" | "ScheduleGiveaways" | "GiveawayRequirements" | "GiveawayBonusEntries" | "GiveawayEntryLimit" | "DisableDoubleVoteEntry" | "YouTubeNotifications" | "RandomMessages" | "WeeklyBirthdayPanel" | "GameNotifications" | "GameCategoryRouting" | "GameAllTimeLowAlerts" | "GameChangelogs" | "GameRobloxTracking" | "FixUrlWebhook" | "FixUrlCustomRules" | "KickClipperSubscribe" | "WhatnotNotifications" | "ScheduledEventMessages" | "ScheduledMessageWebhooks" | "ReactionRoles" | "AdvancedRolePanels" | "KickClipForwarding" | "TwitchClipForwarding" | "ClubNotifications" | "RolePanelWebhooks" | "Fishing" | "FishingCustomization" | "FishingWebhooks" | "StreamRoles" | "RoleFilters" | "TwitterNotifications" | "TwitterLiveNotifications" | "BadgeAllGames" | "AutoRoles" | "AutoRoleTimers" | "AutoRoleInvites" | "StickyMessageRoleFilters" | "GiveawayClaims" | "GiveawayWinnerRole" | "InfluencerPanel" | "InfluencerPanelPremium" | "InfluencerPanelPlus" | "StatusRoleKeepOnOffline" | "YouTubeReplayChannel" | "LivePostUpdates", string>;
253
+ t: Record<"StickyMessages" | "LinkedRoles" | "StatusRoles" | "Starboards" | "KickClips" | "StatCounters" | "BaseSyncableRoles" | "FullSyncableRoles" | "LiveRole" | "VoteSkipping" | "KickAuditLogs" | "LeaderboardCommand" | "RefreshingLeaderboard" | "LeaderBoardSyncRoles" | "BetaAccess" | "PriorityNotifications" | "KickNotifications" | "TwitchNotifications" | "ImportEmojis" | "ImportEmojisWithSub" | "CustomWebhooks" | "AnyMessageEditor" | "SyncSocialUsernames" | "AutoDeleteNotifications" | "TestNotificationCommand" | "CustomCooldowns" | "WelcomeAndLeaveNotifications" | "NotificationPingRoleId" | "SwitchStreamerAndGuildEmbeds" | "BirthdayNotifications" | "AllowNotSendingOfflineNotifications" | "AutoStatusLiveRoles" | "RumbleNotifications" | "AllowAutoPublishNotifications" | "NotificationOverrides" | "UseCustomButtons" | "AllowVodFeatures" | "TikTokNotifications" | "ManageBranding" | "CustomStarboards" | "AutoThreadOnStarboard" | "StarboardRewardRole" | "Giveaways" | "GiveawayLogs" | "ModifyGiveawayWhileRunning" | "ScheduleGiveaways" | "GiveawayRequirements" | "GiveawayBonusEntries" | "GiveawayEntryLimit" | "DisableDoubleVoteEntry" | "YouTubeNotifications" | "RandomMessages" | "WeeklyBirthdayPanel" | "GameNotifications" | "GameCategoryRouting" | "GameAllTimeLowAlerts" | "GameChangelogs" | "GameRobloxTracking" | "FixUrlWebhook" | "FixUrlCustomRules" | "KickClipperSubscribe" | "WhatnotNotifications" | "ScheduledEventMessages" | "ScheduledMessageWebhooks" | "ReactionRoles" | "AdvancedRolePanels" | "KickClipForwarding" | "TwitchClipForwarding" | "ClubNotifications" | "RolePanelWebhooks" | "Fishing" | "FishingCustomization" | "FishingWebhooks" | "StreamRoles" | "RoleFilters" | "TwitterNotifications" | "TwitterLiveNotifications" | "BadgeAllGames" | "AutoRoles" | "AutoRoleTimers" | "AutoRoleInvites" | "StickyMessageRoleFilters" | "GiveawayClaims" | "GiveawayWinnerRole" | "InfluencerPanel" | "InfluencerPanelPremium" | "InfluencerPanelPlus" | "StatusRoleKeepOnOffline" | "YouTubeReplayChannel" | "LivePostUpdates" | "MultiDestination" | "InGameRewards", string>;
250
254
  all: bigint;
251
255
  static b: {
252
256
  BaseSyncableRoles: bigint;
@@ -339,8 +343,10 @@ export declare class GuildFeaturesManager extends BitFieldManager<typeof GuildFe
339
343
  StatusRoleKeepOnOffline: bigint;
340
344
  YouTubeReplayChannel: bigint;
341
345
  LivePostUpdates: bigint;
346
+ MultiDestination: bigint;
347
+ InGameRewards: bigint;
342
348
  };
343
- static t: Record<"StickyMessages" | "LinkedRoles" | "StatusRoles" | "Starboards" | "KickClips" | "StatCounters" | "BaseSyncableRoles" | "FullSyncableRoles" | "LiveRole" | "VoteSkipping" | "KickAuditLogs" | "LeaderboardCommand" | "RefreshingLeaderboard" | "LeaderBoardSyncRoles" | "BetaAccess" | "PriorityNotifications" | "KickNotifications" | "TwitchNotifications" | "ImportEmojis" | "ImportEmojisWithSub" | "CustomWebhooks" | "AnyMessageEditor" | "SyncSocialUsernames" | "AutoDeleteNotifications" | "TestNotificationCommand" | "CustomCooldowns" | "WelcomeAndLeaveNotifications" | "NotificationPingRoleId" | "SwitchStreamerAndGuildEmbeds" | "BirthdayNotifications" | "AllowNotSendingOfflineNotifications" | "AutoStatusLiveRoles" | "RumbleNotifications" | "AllowAutoPublishNotifications" | "NotificationOverrides" | "UseCustomButtons" | "AllowVodFeatures" | "TikTokNotifications" | "ManageBranding" | "CustomStarboards" | "AutoThreadOnStarboard" | "StarboardRewardRole" | "Giveaways" | "GiveawayLogs" | "ModifyGiveawayWhileRunning" | "ScheduleGiveaways" | "GiveawayRequirements" | "GiveawayBonusEntries" | "GiveawayEntryLimit" | "DisableDoubleVoteEntry" | "YouTubeNotifications" | "RandomMessages" | "WeeklyBirthdayPanel" | "GameNotifications" | "GameCategoryRouting" | "GameAllTimeLowAlerts" | "GameChangelogs" | "GameRobloxTracking" | "FixUrlWebhook" | "FixUrlCustomRules" | "KickClipperSubscribe" | "WhatnotNotifications" | "ScheduledEventMessages" | "ScheduledMessageWebhooks" | "ReactionRoles" | "AdvancedRolePanels" | "KickClipForwarding" | "TwitchClipForwarding" | "ClubNotifications" | "RolePanelWebhooks" | "Fishing" | "FishingCustomization" | "FishingWebhooks" | "StreamRoles" | "RoleFilters" | "TwitterNotifications" | "TwitterLiveNotifications" | "BadgeAllGames" | "AutoRoles" | "AutoRoleTimers" | "AutoRoleInvites" | "StickyMessageRoleFilters" | "GiveawayClaims" | "GiveawayWinnerRole" | "InfluencerPanel" | "InfluencerPanelPremium" | "InfluencerPanelPlus" | "StatusRoleKeepOnOffline" | "YouTubeReplayChannel" | "LivePostUpdates", string>;
349
+ static t: Record<"StickyMessages" | "LinkedRoles" | "StatusRoles" | "Starboards" | "KickClips" | "StatCounters" | "BaseSyncableRoles" | "FullSyncableRoles" | "LiveRole" | "VoteSkipping" | "KickAuditLogs" | "LeaderboardCommand" | "RefreshingLeaderboard" | "LeaderBoardSyncRoles" | "BetaAccess" | "PriorityNotifications" | "KickNotifications" | "TwitchNotifications" | "ImportEmojis" | "ImportEmojisWithSub" | "CustomWebhooks" | "AnyMessageEditor" | "SyncSocialUsernames" | "AutoDeleteNotifications" | "TestNotificationCommand" | "CustomCooldowns" | "WelcomeAndLeaveNotifications" | "NotificationPingRoleId" | "SwitchStreamerAndGuildEmbeds" | "BirthdayNotifications" | "AllowNotSendingOfflineNotifications" | "AutoStatusLiveRoles" | "RumbleNotifications" | "AllowAutoPublishNotifications" | "NotificationOverrides" | "UseCustomButtons" | "AllowVodFeatures" | "TikTokNotifications" | "ManageBranding" | "CustomStarboards" | "AutoThreadOnStarboard" | "StarboardRewardRole" | "Giveaways" | "GiveawayLogs" | "ModifyGiveawayWhileRunning" | "ScheduleGiveaways" | "GiveawayRequirements" | "GiveawayBonusEntries" | "GiveawayEntryLimit" | "DisableDoubleVoteEntry" | "YouTubeNotifications" | "RandomMessages" | "WeeklyBirthdayPanel" | "GameNotifications" | "GameCategoryRouting" | "GameAllTimeLowAlerts" | "GameChangelogs" | "GameRobloxTracking" | "FixUrlWebhook" | "FixUrlCustomRules" | "KickClipperSubscribe" | "WhatnotNotifications" | "ScheduledEventMessages" | "ScheduledMessageWebhooks" | "ReactionRoles" | "AdvancedRolePanels" | "KickClipForwarding" | "TwitchClipForwarding" | "ClubNotifications" | "RolePanelWebhooks" | "Fishing" | "FishingCustomization" | "FishingWebhooks" | "StreamRoles" | "RoleFilters" | "TwitterNotifications" | "TwitterLiveNotifications" | "BadgeAllGames" | "AutoRoles" | "AutoRoleTimers" | "AutoRoleInvites" | "StickyMessageRoleFilters" | "GiveawayClaims" | "GiveawayWinnerRole" | "InfluencerPanel" | "InfluencerPanelPremium" | "InfluencerPanelPlus" | "StatusRoleKeepOnOffline" | "YouTubeReplayChannel" | "LivePostUpdates" | "MultiDestination" | "InGameRewards", string>;
344
350
  static all: bigint;
345
351
  convertBitToField<T extends EnumValues<typeof GuildFeatures>>(bit: T): string | null;
346
352
  }
@@ -235,6 +235,10 @@ exports.GuildFeatures = {
235
235
  YouTubeReplayChannel: 1n << 89n,
236
236
  // Live post updates: edit the live post when the streamer changes their title or category. Premium Plus.
237
237
  LivePostUpdates: 1n << 90n,
238
+ // Multiple notification channels: up to 2 extra channels per creator, each with its own ping role. Premium Plus.
239
+ MultiDestination: 1n << 91n,
240
+ // Minecraft reward commands: a giveaway runs a preset command on the winner's game server. Premium.
241
+ InGameRewards: 1n << 92n,
238
242
  };
239
243
  exports.GuildFeaturesText = {
240
244
  BaseSyncableRoles: 'Basic Syncable Roles',
@@ -329,6 +333,8 @@ exports.GuildFeaturesText = {
329
333
  StatusRoleKeepOnOffline: 'Status Roles: Remove When Offline',
330
334
  YouTubeReplayChannel: 'YouTube: Replay Channel',
331
335
  LivePostUpdates: 'Live Post Updates',
336
+ MultiDestination: 'Multiple Notification Channels',
337
+ InGameRewards: 'Minecraft: Reward Commands',
332
338
  };
333
339
  exports.UserFeatures = {
334
340
  HasOneServerPremium: 1n << 0n,
@@ -1,4 +1,4 @@
1
- export declare const FEATURE_NAMES: readonly ["freeGames", "gameChangelogs", "robloxNotifications", "fixLinks", "clipperNotifications", "scheduledMessages", "linkedRoles", "reactionRoles", "clipForwarding", "club", "whatnot", "fishing", "giveaways", "starboards", "syncedRoles", "birthdays", "statusRoles", "autoStatusLiveRoles", "streamRoles", "badgeNotifications", "kickBounties", "autoRoles", "giveawayClaims", "influencerPanel", "publicGiveaways", "publicDrops", "sitePolls", "siteAnnouncements", "offlineGracePeriod", "youtubeReplays", "livePostUpdates", "twitter", "voteToTest", "voteToAddSocial", "aiAssistant", "voteToUseAssistant", "assistantShareChat", "emotes", "liveRoles", "stickyMessages", "welcomeLeave", "statCounters", "voteSkipping"];
1
+ export declare const FEATURE_NAMES: readonly ["freeGames", "gameChangelogs", "robloxNotifications", "fixLinks", "clipperNotifications", "scheduledMessages", "linkedRoles", "reactionRoles", "clipForwarding", "club", "whatnot", "fishing", "giveaways", "starboards", "syncedRoles", "birthdays", "statusRoles", "autoStatusLiveRoles", "streamRoles", "badgeNotifications", "kickBounties", "autoRoles", "giveawayClaims", "influencerPanel", "publicGiveaways", "publicDrops", "sitePolls", "siteAnnouncements", "offlineGracePeriod", "youtubeReplays", "livePostUpdates", "multiDestination", "inGame", "twitter", "voteToTest", "voteToAddSocial", "aiAssistant", "voteToUseAssistant", "assistantShareChat", "emotes", "liveRoles", "stickyMessages", "welcomeLeave", "statCounters", "voteSkipping"];
2
2
  export type FeatureName = (typeof FEATURE_NAMES)[number];
3
3
  export type FeatureStatus = 'off' | 'beta' | 'public';
4
4
  export type ResolvedFeature = 'public' | 'beta' | 'hidden';
@@ -36,6 +36,8 @@ exports.FEATURE_NAMES = [
36
36
  'offlineGracePeriod',
37
37
  'youtubeReplays',
38
38
  'livePostUpdates',
39
+ 'multiDestination',
40
+ 'inGame',
39
41
  'twitter',
40
42
  'voteToTest',
41
43
  'voteToAddSocial',
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Which notifications can show in Minecraft, per platform (phases/in-game/ P3, TRACKER G3/G3c/G3d).
3
+ *
4
+ * Mirrors `src/modules/inGame/alertTypes.ts` in Noti-v3; the server's `sdkDrift` test diffs everything below this
5
+ * header against it.
6
+ *
7
+ * Deliberately has NO imports, like `multiDestination.ts`: the zod schemas and the SDK mirror pull from here. The
8
+ * client's copy (`src/other/inGameAlerts.ts`) is diffed against this file by `sdkDrift`.
9
+ *
10
+ * What each platform's send paths really emit. YouTube has no offline, ever (`src/modules/manager/CLAUDE.md`); its
11
+ * `replay` is the "stream replay is up" post. Whatnot is not here: its creators cannot be saved (G3c). Clips,
12
+ * clip forwarding, drops and VODs are not in v1 - no creator-row producer.
13
+ */
14
+ export declare const IN_GAME_TYPES: {
15
+ readonly kick: readonly ["live", "offline"];
16
+ readonly twitch: readonly ["live", "offline"];
17
+ readonly rumble: readonly ["live", "offline", "video"];
18
+ readonly tiktok: readonly ["live", "offline", "video"];
19
+ readonly youtube: readonly ["live", "video", "replay"];
20
+ readonly twitter: readonly ["live", "offline", "post"];
21
+ readonly club: readonly ["post"];
22
+ };
23
+ export type InGamePlatform = keyof typeof IN_GAME_TYPES;
24
+ export type InGameAlertType = (typeof IN_GAME_TYPES)[InGamePlatform][number];
25
+ /** A creator may pick at most this many connected servers to show on (plan max is 10). */
26
+ export declare const MAX_IN_GAME_SERVER_PICKS = 10;
27
+ /** What the cluster needs to fan one notification out: which connected servers. Empty = every active one. */
28
+ export type InGameTarget = {
29
+ serverIds: string[];
30
+ };
31
+ /**
32
+ * The creator columns, as the manager reads them. REQUIRED, not optional: a manager query that forgot to select them
33
+ * must be a type error, not a creator who silently never shows in Minecraft.
34
+ */
35
+ export type InGameColumns = {
36
+ inGame: boolean;
37
+ inGameTypes: string[];
38
+ inGameServerIds: string[];
39
+ };
40
+ /** The stored type list for a platform: only types it has, no repeats. Empty means every type it has. */
41
+ export declare function normalizeInGameTypes(platform: string, input: unknown): InGameAlertType[];
42
+ /** The stored server picks: ids only, no repeats, capped. */
43
+ export declare function normalizeInGameServerIds(input: unknown): string[];
44
+ /**
45
+ * The ONE decision for whether a notification may go in-game (P3 `shouldFanOut`), made on the manager when the
46
+ * payload is built. Null means "not in-game": a setup test, the switch off, or a type this creator did not pick.
47
+ *
48
+ * `isTest`: a live, upload or post dispatched to a single guild is always a setup test (`functions.ts`, the /test
49
+ * dispatch). A single-guild OFFLINE is a held offline being delivered - real - so offline callers pass false.
50
+ */
51
+ export declare function inGameTarget(platform: string, type: InGameAlertType, row: InGameColumns, isTest: boolean): InGameTarget | null;
52
+ /** The creator fields only an `inGame`-enabled user may send. The creator routes 403 a body that names any of them. */
53
+ export declare const IN_GAME_CREATOR_FIELDS: readonly ["inGame", "inGameTypes", "inGameServerIds"];
54
+ export declare const bodyTouchesInGame: (body: unknown) => boolean;
55
+ /**
56
+ * One alert as it is sent to a game server (`event.payload`). Codes and raw values, never prose: the plugin words
57
+ * and formats everything (TRACKER G5). Untrusted text (titles, names) is inserted by the plugin as UNPARSED text, so a
58
+ * title cannot inject markup or commands (R8).
59
+ *
60
+ * Never carries a ping role, the random message or a Discord member - those are Discord's.
61
+ */
62
+ export type InGameAlert = {
63
+ platform: InGamePlatform;
64
+ type: InGameAlertType;
65
+ /** The creator's name as the platform shows it (not Discord-escaped). */
66
+ streamerName: string;
67
+ /** Stream title, video title, or the post's text. */
68
+ title: string | null;
69
+ /** Where a click should go: the stream, the video, the post. */
70
+ url: string | null;
71
+ /** May be empty: Kick often has no thumbnail at go-live. */
72
+ thumbnail: string | null;
73
+ category?: string | null;
74
+ viewers?: number | null;
75
+ followers?: number | null;
76
+ /** Offline: how long the stream ran, in ms. */
77
+ durationMs?: number | null;
78
+ totalViews?: number | null;
79
+ /** Offline: the stream's VOD when the platform gives one. */
80
+ vodUrl?: string | null;
81
+ /** Live/offline pairing for replay: the platform's id for this stream when there is one. */
82
+ streamId?: string | null;
83
+ };
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.bodyTouchesInGame = exports.IN_GAME_CREATOR_FIELDS = exports.MAX_IN_GAME_SERVER_PICKS = exports.IN_GAME_TYPES = void 0;
4
+ exports.normalizeInGameTypes = normalizeInGameTypes;
5
+ exports.normalizeInGameServerIds = normalizeInGameServerIds;
6
+ exports.inGameTarget = inGameTarget;
7
+ /**
8
+ * Which notifications can show in Minecraft, per platform (phases/in-game/ P3, TRACKER G3/G3c/G3d).
9
+ *
10
+ * Mirrors `src/modules/inGame/alertTypes.ts` in Noti-v3; the server's `sdkDrift` test diffs everything below this
11
+ * header against it.
12
+ *
13
+ * Deliberately has NO imports, like `multiDestination.ts`: the zod schemas and the SDK mirror pull from here. The
14
+ * client's copy (`src/other/inGameAlerts.ts`) is diffed against this file by `sdkDrift`.
15
+ *
16
+ * What each platform's send paths really emit. YouTube has no offline, ever (`src/modules/manager/CLAUDE.md`); its
17
+ * `replay` is the "stream replay is up" post. Whatnot is not here: its creators cannot be saved (G3c). Clips,
18
+ * clip forwarding, drops and VODs are not in v1 - no creator-row producer.
19
+ */
20
+ exports.IN_GAME_TYPES = {
21
+ kick: ['live', 'offline'],
22
+ twitch: ['live', 'offline'],
23
+ rumble: ['live', 'offline', 'video'],
24
+ tiktok: ['live', 'offline', 'video'],
25
+ youtube: ['live', 'video', 'replay'],
26
+ twitter: ['live', 'offline', 'post'],
27
+ club: ['post'],
28
+ };
29
+ /** A creator may pick at most this many connected servers to show on (plan max is 10). */
30
+ exports.MAX_IN_GAME_SERVER_PICKS = 10;
31
+ const isPlatform = (p) => Object.prototype.hasOwnProperty.call(exports.IN_GAME_TYPES, p);
32
+ /** The stored type list for a platform: only types it has, no repeats. Empty means every type it has. */
33
+ function normalizeInGameTypes(platform, input) {
34
+ if (!isPlatform(platform) || !Array.isArray(input))
35
+ return [];
36
+ const allowed = exports.IN_GAME_TYPES[platform];
37
+ return [...new Set(input.filter((t) => typeof t === 'string' && allowed.includes(t)))];
38
+ }
39
+ /** The stored server picks: ids only, no repeats, capped. */
40
+ function normalizeInGameServerIds(input) {
41
+ if (!Array.isArray(input))
42
+ return [];
43
+ return [...new Set(input.filter((id) => typeof id === 'string' && /^[0-9a-f-]{36}$/i.test(id)))].slice(0, exports.MAX_IN_GAME_SERVER_PICKS);
44
+ }
45
+ /**
46
+ * The ONE decision for whether a notification may go in-game (P3 `shouldFanOut`), made on the manager when the
47
+ * payload is built. Null means "not in-game": a setup test, the switch off, or a type this creator did not pick.
48
+ *
49
+ * `isTest`: a live, upload or post dispatched to a single guild is always a setup test (`functions.ts`, the /test
50
+ * dispatch). A single-guild OFFLINE is a held offline being delivered - real - so offline callers pass false.
51
+ */
52
+ function inGameTarget(platform, type, row, isTest) {
53
+ if (isTest || !row.inGame || !isPlatform(platform))
54
+ return null;
55
+ if (!exports.IN_GAME_TYPES[platform].includes(type))
56
+ return null;
57
+ const types = normalizeInGameTypes(platform, row.inGameTypes);
58
+ if (types.length && !types.includes(type))
59
+ return null;
60
+ return { serverIds: normalizeInGameServerIds(row.inGameServerIds) };
61
+ }
62
+ /** The creator fields only an `inGame`-enabled user may send. The creator routes 403 a body that names any of them. */
63
+ exports.IN_GAME_CREATOR_FIELDS = ['inGame', 'inGameTypes', 'inGameServerIds'];
64
+ const bodyTouchesInGame = (body) => !!body && typeof body === 'object' && exports.IN_GAME_CREATOR_FIELDS.some((key) => key in body);
65
+ exports.bodyTouchesInGame = bodyTouchesInGame;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * The MiniMessage a dashboard user may write for Minecraft (Noti in-game P5): colours, formatting, hover text and
3
+ * http(s) links only. `forbiddenMiniMessageTag(text)` names the first tag the API would refuse, or returns null.
4
+ *
5
+ * Mirrors `src/modules/inGame/miniMessage.ts` in Noti-v3; the server's `sdkDrift` test diffs everything below this
6
+ * header against it.
7
+ */
8
+ export declare const NAMED_COLOURS: readonly ["black", "dark_blue", "dark_green", "dark_aqua", "dark_red", "dark_purple", "gold", "gray", "grey", "dark_gray", "dark_grey", "blue", "green", "aqua", "red", "light_purple", "yellow", "white"];
9
+ /** Escape text so MiniMessage shows it as written: a backslash before every tag opener (and before backslashes). */
10
+ export declare const escapeMiniMessage: (text: string) => string;
11
+ /** An http(s) URL safe inside `<click:open_url:'...'>`, or null. Same rule as the plugin's `safeUrl`. */
12
+ export declare function safeMiniMessageUrl(url: string | null | undefined): string | null;
13
+ /** The first tag a dashboard user may not use, as they wrote it, or null when the text is fine. */
14
+ export declare const forbiddenMiniMessageTag: (text: string) => string | null;
@@ -0,0 +1,135 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.forbiddenMiniMessageTag = exports.escapeMiniMessage = exports.NAMED_COLOURS = void 0;
4
+ exports.safeMiniMessageUrl = safeMiniMessageUrl;
5
+ /**
6
+ * The MiniMessage a dashboard user may write for Minecraft (Noti in-game P5): colours, formatting, hover text and
7
+ * http(s) links only. `forbiddenMiniMessageTag(text)` names the first tag the API would refuse, or returns null.
8
+ *
9
+ * Mirrors `src/modules/inGame/miniMessage.ts` in Noti-v3; the server's `sdkDrift` test diffs everything below this
10
+ * header against it.
11
+ */
12
+ exports.NAMED_COLOURS = [
13
+ 'black', 'dark_blue', 'dark_green', 'dark_aqua', 'dark_red', 'dark_purple', 'gold', 'gray', 'grey', 'dark_gray', 'dark_grey',
14
+ 'blue', 'green', 'aqua', 'red', 'light_purple', 'yellow', 'white',
15
+ ];
16
+ const DECORATIONS = ['bold', 'b', 'italic', 'i', 'em', 'underlined', 'u', 'strikethrough', 'st', 'obfuscated', 'obf'];
17
+ const SIMPLE = new Set([...exports.NAMED_COLOURS, ...DECORATIONS, 'reset', 'newline', 'br', 'rainbow', 'gradient', 'color', 'colour', 'c']);
18
+ const HEX = /^#[0-9a-f]{6}$/i;
19
+ /** Escape text so MiniMessage shows it as written: a backslash before every tag opener (and before backslashes). */
20
+ const escapeMiniMessage = (text) => text.replace(/§./g, '').replace(/\\/g, '\\\\').replace(/</g, '\\<');
21
+ exports.escapeMiniMessage = escapeMiniMessage;
22
+ /** An http(s) URL safe inside `<click:open_url:'...'>`, or null. Same rule as the plugin's `safeUrl`. */
23
+ function safeMiniMessageUrl(url) {
24
+ const u = (url ?? '').trim();
25
+ if (!/^https?:\/\/\S+$/i.test(u))
26
+ return null;
27
+ return u.replace(/\\/g, '%5C').replace(/'/g, '%27').replace(/"/g, '%22').replace(/</g, '%3C').replace(/>/g, '%3E');
28
+ }
29
+ /** Split a tag body on `:`, keeping quoted arguments ('...' or "...") whole and unquoted. */
30
+ function splitArgs(body) {
31
+ const out = [];
32
+ let cur = '';
33
+ let quote = null;
34
+ for (let i = 0; i < body.length; i++) {
35
+ const ch = body[i];
36
+ if (quote) {
37
+ if (ch === '\\' && i + 1 < body.length) {
38
+ cur += body[++i];
39
+ continue;
40
+ }
41
+ if (ch === quote) {
42
+ quote = null;
43
+ continue;
44
+ }
45
+ cur += ch;
46
+ }
47
+ else if (ch === '\'' || ch === '"') {
48
+ quote = ch;
49
+ }
50
+ else if (ch === ':') {
51
+ out.push(cur);
52
+ cur = '';
53
+ }
54
+ else {
55
+ cur += ch;
56
+ }
57
+ }
58
+ out.push(cur);
59
+ return out;
60
+ }
61
+ /** The index of the `>` closing the tag that opens at `start`, honouring quotes; -1 when it never closes. */
62
+ function tagEnd(text, start) {
63
+ let quote = null;
64
+ for (let i = start + 1; i < text.length; i++) {
65
+ const ch = text[i];
66
+ if (quote) {
67
+ if (ch === '\\') {
68
+ i++;
69
+ continue;
70
+ }
71
+ if (ch === quote)
72
+ quote = null;
73
+ }
74
+ else if (ch === '\'' || ch === '"') {
75
+ quote = ch;
76
+ }
77
+ else if (ch === '<') {
78
+ return -1; // a second opener first: the first `<` is plain text to MiniMessage
79
+ }
80
+ else if (ch === '>') {
81
+ return i;
82
+ }
83
+ }
84
+ return -1;
85
+ }
86
+ /** Why one tag is not allowed, or null when it is. */
87
+ function checkTag(body, depth) {
88
+ const closing = body.startsWith('/');
89
+ const args = splitArgs(closing ? body.slice(1) : body);
90
+ const rawName = (args[0] ?? '').trim().toLowerCase();
91
+ const name = rawName.startsWith('!') ? rawName.slice(1) : rawName;
92
+ if (closing)
93
+ return name === '' || SIMPLE.has(name) || HEX.test(name) || name === 'hover' || name === 'click' ? null : `<${body}>`;
94
+ if (HEX.test(name))
95
+ return null;
96
+ if (rawName.startsWith('!'))
97
+ return DECORATIONS.includes(name) ? null : `<${body}>`;
98
+ if (SIMPLE.has(name))
99
+ return null;
100
+ if (name === 'click') {
101
+ if ((args[1] ?? '').toLowerCase() !== 'open_url')
102
+ return `<${body}> (only open_url links)`;
103
+ return safeMiniMessageUrl(args.slice(2).join(':')) ? null : `<${body}> (links must start with http:// or https://)`;
104
+ }
105
+ if (name === 'hover') {
106
+ if ((args[1] ?? '').toLowerCase() !== 'show_text')
107
+ return `<${body}> (only show_text)`;
108
+ if (depth > 3)
109
+ return `<${body}> (nested too deep)`;
110
+ return firstForbiddenTag(args.slice(2).join(':'), depth + 1);
111
+ }
112
+ return `<${body}>`;
113
+ }
114
+ function firstForbiddenTag(text, depth) {
115
+ for (let i = 0; i < text.length; i++) {
116
+ const ch = text[i];
117
+ if (ch === '\\') {
118
+ i++;
119
+ continue;
120
+ }
121
+ if (ch !== '<')
122
+ continue;
123
+ const end = tagEnd(text, i);
124
+ if (end < 0)
125
+ continue;
126
+ const problem = checkTag(text.slice(i + 1, end), depth);
127
+ if (problem)
128
+ return problem;
129
+ i = end;
130
+ }
131
+ return null;
132
+ }
133
+ /** The first tag a dashboard user may not use, as they wrote it, or null when the text is fine. */
134
+ const forbiddenMiniMessageTag = (text) => firstForbiddenTag(text, 0);
135
+ exports.forbiddenMiniMessageTag = forbiddenMiniMessageTag;
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Multi-destination notifications - up to two extra channels per creator, each with its own ping role.
3
+ * Premium Plus (`MultiDestination`), launch flag `multiDestination`. Plan: phases/multi-destination/.
4
+ *
5
+ * Mirrors `src/modules/multiDestination.ts` in Noti-v3. Hand-copied; the server's `sdkDrift` test diffs everything
6
+ * below this header against it.
7
+ *
8
+ * One source of truth for the rules, because they have to agree across the zod schemas, the save
9
+ * transforms, the send path and the SDK. Deliberately has NO imports, for the same reason as
10
+ * `offlineGrace.ts`: the zod schemas pull from here, and a cycle through them is someone's afternoon.
11
+ *
12
+ * The creator's own channel + `pingRoleId` is destination 1 and is not stored here. `extraDestinations`
13
+ * holds only the extras, so a server that never opts in has nothing new anywhere.
14
+ */
15
+ /** Extra destinations a creator may have, on top of their own channel. 3 channels total. */
16
+ export declare const MAX_EXTRA_DESTINATIONS = 2;
17
+ /**
18
+ * Which post types an extra destination can be limited to, per platform. Empty `types` = everything.
19
+ *
20
+ * Offline is NOT a type: the offline path only ever edits or deletes the live post it announced, so a
21
+ * copy's offline follows its own live copy. Kick clips/VODs and the X post cooldown are separate
22
+ * features with their own channels, not notification types, so they are not here either.
23
+ */
24
+ export declare const DESTINATION_TYPES: {
25
+ readonly kick: readonly ["live"];
26
+ readonly twitch: readonly ["live"];
27
+ readonly rumble: readonly ["live", "video"];
28
+ readonly tiktok: readonly ["live", "video"];
29
+ readonly youtube: readonly ["live", "video", "replay"];
30
+ readonly twitter: readonly ["live", "post"];
31
+ readonly club: readonly ["post"];
32
+ };
33
+ export type DestinationPlatform = keyof typeof DESTINATION_TYPES;
34
+ export type DestinationType = (typeof DESTINATION_TYPES)[DestinationPlatform][number];
35
+ export type ExtraDestination = {
36
+ channelId: string;
37
+ /** A role id, `everyone`, `here`, or null for no ping - the same values the creator's own `pingRoleId` takes. */
38
+ pingRoleId: string | null;
39
+ types: DestinationType[];
40
+ };
41
+ /**
42
+ * A copy of the live post sent to an extra destination, so the offline can edit or delete it. `pingRoleId` is
43
+ * the role the copy was posted with: the offline edit rewrites the content, and it must show the copy's own
44
+ * role, not destination 1's - and not whatever the settings say by the time the stream ends.
45
+ */
46
+ export type ExtraPost = {
47
+ channelId: string;
48
+ messageId: string;
49
+ pingRoleId: string | null;
50
+ };
51
+ /**
52
+ * The stored form of whatever the dashboard sent. The save transforms call this, not only zod.
53
+ *
54
+ * Zod alone is not enough here: the creator routes validate with the PARTIAL schema, and `deepPartialify`
55
+ * keeps an array's `max` but drops custom refinements (the no-duplicates check) and makes every field
56
+ * of an item optional. So a row with no channel, a channel listed twice, or a type the platform does not
57
+ * have could all reach the database. This drops them instead of failing the whole save:
58
+ *
59
+ * - no valid `channelId` -> dropped
60
+ * - a channel already listed -> the later row dropped
61
+ * - a `pingRoleId` that is not a role / everyone / here -> no ping (null)
62
+ * - a type this platform does not have, or a repeat -> dropped from `types`
63
+ * - beyond `MAX_EXTRA_DESTINATIONS` -> cut
64
+ *
65
+ * `null` and anything that is not a list mean "no extras" and store `[]`: a Json column cannot take a
66
+ * plain null through Prisma, same as `Guild.statChannelFormats` storing `{}`.
67
+ */
68
+ export declare function normalizeExtraDestinations(platform: DestinationPlatform, input: unknown): ExtraDestination[];
69
+ /**
70
+ * The extras one notification goes to: the stored list, re-normalized (a row written before a rule changed
71
+ * still obeys today's rule), keeping only destinations that want this post type and whose channel is not
72
+ * already receiving it.
73
+ *
74
+ * `taken` is destination 1's channel AFTER any override redirect. An extra pointing at the same channel would
75
+ * post the notification there twice, so it is dropped here rather than rejected on save - a partial save body
76
+ * usually does not carry the creator's own channel, so the save could not check it.
77
+ */
78
+ export declare function extrasFor(platform: DestinationPlatform, stored: unknown, type: DestinationType, taken: (string | null | undefined)[]): ExtraDestination[];
79
+ /**
80
+ * The stored copies of a live post (`*CurrentlyLive.extraPosts`), read safely. Written only by the bot, but it is
81
+ * a Json column: anything malformed is dropped rather than trusted, and a pre-`pingRoleId` row reads as no role.
82
+ */
83
+ export declare function readExtraPosts(stored: unknown): ExtraPost[];
84
+ /** "Is there a live post to finish?" - destination 1's message, or any copy. Offline and sweep filters use this. */
85
+ export declare const hasLivePost: (live: {
86
+ channelId?: string | null;
87
+ messageId?: string | null;
88
+ extraPosts?: unknown;
89
+ } | null | undefined) => boolean;
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ /**
3
+ * Multi-destination notifications - up to two extra channels per creator, each with its own ping role.
4
+ * Premium Plus (`MultiDestination`), launch flag `multiDestination`. Plan: phases/multi-destination/.
5
+ *
6
+ * Mirrors `src/modules/multiDestination.ts` in Noti-v3. Hand-copied; the server's `sdkDrift` test diffs everything
7
+ * below this header against it.
8
+ *
9
+ * One source of truth for the rules, because they have to agree across the zod schemas, the save
10
+ * transforms, the send path and the SDK. Deliberately has NO imports, for the same reason as
11
+ * `offlineGrace.ts`: the zod schemas pull from here, and a cycle through them is someone's afternoon.
12
+ *
13
+ * The creator's own channel + `pingRoleId` is destination 1 and is not stored here. `extraDestinations`
14
+ * holds only the extras, so a server that never opts in has nothing new anywhere.
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.hasLivePost = exports.DESTINATION_TYPES = exports.MAX_EXTRA_DESTINATIONS = void 0;
18
+ exports.normalizeExtraDestinations = normalizeExtraDestinations;
19
+ exports.extrasFor = extrasFor;
20
+ exports.readExtraPosts = readExtraPosts;
21
+ /** Extra destinations a creator may have, on top of their own channel. 3 channels total. */
22
+ exports.MAX_EXTRA_DESTINATIONS = 2;
23
+ /**
24
+ * Which post types an extra destination can be limited to, per platform. Empty `types` = everything.
25
+ *
26
+ * Offline is NOT a type: the offline path only ever edits or deletes the live post it announced, so a
27
+ * copy's offline follows its own live copy. Kick clips/VODs and the X post cooldown are separate
28
+ * features with their own channels, not notification types, so they are not here either.
29
+ */
30
+ exports.DESTINATION_TYPES = {
31
+ kick: ['live'],
32
+ twitch: ['live'],
33
+ rumble: ['live', 'video'],
34
+ tiktok: ['live', 'video'],
35
+ youtube: ['live', 'video', 'replay'],
36
+ twitter: ['live', 'post'],
37
+ club: ['post'],
38
+ };
39
+ const SNOWFLAKE = /^\d{17,21}$/;
40
+ const isPingTarget = (v) => typeof v === 'string' && (SNOWFLAKE.test(v) || v === 'everyone' || v === 'here');
41
+ /**
42
+ * The stored form of whatever the dashboard sent. The save transforms call this, not only zod.
43
+ *
44
+ * Zod alone is not enough here: the creator routes validate with the PARTIAL schema, and `deepPartialify`
45
+ * keeps an array's `max` but drops custom refinements (the no-duplicates check) and makes every field
46
+ * of an item optional. So a row with no channel, a channel listed twice, or a type the platform does not
47
+ * have could all reach the database. This drops them instead of failing the whole save:
48
+ *
49
+ * - no valid `channelId` -> dropped
50
+ * - a channel already listed -> the later row dropped
51
+ * - a `pingRoleId` that is not a role / everyone / here -> no ping (null)
52
+ * - a type this platform does not have, or a repeat -> dropped from `types`
53
+ * - beyond `MAX_EXTRA_DESTINATIONS` -> cut
54
+ *
55
+ * `null` and anything that is not a list mean "no extras" and store `[]`: a Json column cannot take a
56
+ * plain null through Prisma, same as `Guild.statChannelFormats` storing `{}`.
57
+ */
58
+ function normalizeExtraDestinations(platform, input) {
59
+ if (!Array.isArray(input))
60
+ return [];
61
+ const allowed = exports.DESTINATION_TYPES[platform];
62
+ const seen = new Set();
63
+ const out = [];
64
+ for (const raw of input) {
65
+ if (out.length >= exports.MAX_EXTRA_DESTINATIONS)
66
+ break;
67
+ if (!raw || typeof raw !== 'object')
68
+ continue;
69
+ const row = raw;
70
+ if (typeof row.channelId !== 'string' || !SNOWFLAKE.test(row.channelId) || seen.has(row.channelId))
71
+ continue;
72
+ seen.add(row.channelId);
73
+ const types = Array.isArray(row.types)
74
+ ? [...new Set(row.types.filter((t) => typeof t === 'string' && allowed.includes(t)))]
75
+ : [];
76
+ out.push({ channelId: row.channelId, pingRoleId: isPingTarget(row.pingRoleId) ? row.pingRoleId : null, types });
77
+ }
78
+ return out;
79
+ }
80
+ /**
81
+ * The extras one notification goes to: the stored list, re-normalized (a row written before a rule changed
82
+ * still obeys today's rule), keeping only destinations that want this post type and whose channel is not
83
+ * already receiving it.
84
+ *
85
+ * `taken` is destination 1's channel AFTER any override redirect. An extra pointing at the same channel would
86
+ * post the notification there twice, so it is dropped here rather than rejected on save - a partial save body
87
+ * usually does not carry the creator's own channel, so the save could not check it.
88
+ */
89
+ function extrasFor(platform, stored, type, taken) {
90
+ const used = new Set(taken.filter((id) => !!id));
91
+ return normalizeExtraDestinations(platform, stored).filter((d) => (!d.types.length || d.types.includes(type)) && !used.has(d.channelId));
92
+ }
93
+ /**
94
+ * The stored copies of a live post (`*CurrentlyLive.extraPosts`), read safely. Written only by the bot, but it is
95
+ * a Json column: anything malformed is dropped rather than trusted, and a pre-`pingRoleId` row reads as no role.
96
+ */
97
+ function readExtraPosts(stored) {
98
+ if (!Array.isArray(stored))
99
+ return [];
100
+ return stored.flatMap((raw) => {
101
+ const row = raw;
102
+ if (!row || typeof row.channelId !== 'string' || !SNOWFLAKE.test(row.channelId) || typeof row.messageId !== 'string' || !SNOWFLAKE.test(row.messageId))
103
+ return [];
104
+ return [{ channelId: row.channelId, messageId: row.messageId, pingRoleId: isPingTarget(row.pingRoleId) ? row.pingRoleId : null }];
105
+ });
106
+ }
107
+ /** "Is there a live post to finish?" - destination 1's message, or any copy. Offline and sweep filters use this. */
108
+ const hasLivePost = (live) => !!(live?.channelId && live?.messageId) || readExtraPosts(live?.extraPosts).length > 0;
109
+ exports.hasLivePost = hasLivePost;
@@ -23,6 +23,8 @@ export type GuildPlanType = {
23
23
  defaultAutoRoles: number | null;
24
24
  /** Scheduled messages a server may keep (switched-off one-time messages count). Checked on create. */
25
25
  defaultScheduledMessages: number | null;
26
+ /** Minecraft: connected game servers kept active; the rest are paused (pinned + oldest stay). */
27
+ defaultInGameServers: number | null;
26
28
  defaultStatusRoles: number | null;
27
29
  defaultStreamRoles: number | null;
28
30
  maxOfflineGraceMinutes: number | null;
@@ -10,6 +10,18 @@ export declare const ClubStreamerZod: {
10
10
  guildId: z.ZodString;
11
11
  usePerStreamerEmbeds: z.ZodNullable<z.ZodBoolean>;
12
12
  autoPublishIfAnnouncmentChannel: z.ZodNullable<z.ZodBoolean>;
13
+ extraDestinations: z.ZodNullable<z.ZodArray<z.ZodObject<{
14
+ channelId: z.ZodString;
15
+ pingRoleId: z.ZodNullable<z.ZodUnion<readonly [z.ZodString, z.ZodLiteral<"everyone">, z.ZodLiteral<"here">]>>;
16
+ types: z.ZodArray<z.ZodEnum<{
17
+ post: "post";
18
+ }>>;
19
+ }, z.core.$strip>>>;
20
+ inGame: z.ZodBoolean;
21
+ inGameTypes: z.ZodArray<z.ZodEnum<{
22
+ post: "post";
23
+ }>>;
24
+ inGameServerIds: z.ZodArray<z.ZodString>;
13
25
  customMessages: z.ZodArray<z.ZodLazy<z.ZodObject<{
14
26
  content: z.ZodNullable<z.ZodString>;
15
27
  showInviteButton: z.ZodNullable<z.ZodBoolean>;
@@ -2,6 +2,8 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ClubStreamerZodPartial = exports.ClubStreamerZod = void 0;
4
4
  const schema_zod_1 = require("./schema.zod");
5
+ const multiDestination_1 = require("../multiDestination");
6
+ const inGameAlerts_1 = require("../inGameAlerts");
5
7
  const zod_1 = require("./zod");
6
8
  const zod_2 = require("zod");
7
9
  const ClubStreamerSchema = zod_2.z.object({
@@ -9,6 +11,14 @@ const ClubStreamerSchema = zod_2.z.object({
9
11
  guildId: zod_1.SnowFlake,
10
12
  usePerStreamerEmbeds: zod_2.z.boolean().nullable(),
11
13
  autoPublishIfAnnouncmentChannel: zod_2.z.boolean().nullable(),
14
+ /** Premium Plus (`MultiDestination`): up to 2 extra channels, each with its own ping role and post types. */
15
+ extraDestinations: zod_1.CentralZod.ExtraDestinations(multiDestination_1.DESTINATION_TYPES.club).nullable(),
16
+ /** Minecraft (`inGame` launch flag): show this creator's notifications on connected game servers. Off by default. */
17
+ inGame: zod_2.z.boolean(),
18
+ /** Which notifications show in Minecraft; empty = every type in `IN_GAME_TYPES.club`. */
19
+ inGameTypes: (0, zod_1.NoDuplicate)(zod_2.z.array(zod_2.z.enum(inGameAlerts_1.IN_GAME_TYPES.club))),
20
+ /** Which connected servers (InGameServer.id); empty = every active one. Max 10. */
21
+ inGameServerIds: (0, zod_1.NoDuplicate)(zod_2.z.array(zod_2.z.string().uuid())).max(inGameAlerts_1.MAX_IN_GAME_SERVER_PICKS),
12
22
  customMessages: (0, zod_1.NoDuplicate)(zod_2.z.array(zod_2.z.lazy(() => CSCustomMessageSchema)), ['type']),
13
23
  notificationChannelId: zod_1.SnowFlake.nullable(),
14
24
  showNotifyButton: zod_2.z.boolean().nullable(),