@theholocron/holocron-plugin-discord 5.0.0-alpha.184 → 5.0.0-alpha.185

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/index.d.mts CHANGED
@@ -47,6 +47,25 @@ declare class DiscordNotifications implements Notifications {
47
47
  target: string;
48
48
  } | null>;
49
49
  private guild;
50
+ /**
51
+ * The server's channel list, fetched once per instance. Every ensure* call used
52
+ * to refetch it -- about 40 identical GETs in one setup run -- and Discord
53
+ * rate-limited the route. Our own creates and edits update the copy, so it
54
+ * stays accurate for the run; a new run builds a new instance.
55
+ *
56
+ * It is the in-flight promise that is shared, not the array: callers that start
57
+ * while the first fetch is pending all get the same snapshot, so a later
58
+ * response can never overwrite what an earlier caller added.
59
+ */
60
+ private channelLoad?;
61
+ private channelList;
62
+ /**
63
+ * Create a channel and add it to the shared list. If the create fails, Discord
64
+ * may still have applied it (a timeout looks the same as a refusal), so forget the
65
+ * list: the next ensure refetches the server's real state instead of creating a
66
+ * duplicate.
67
+ */
68
+ private createChannel;
50
69
  private static same;
51
70
  ensureCategory(name: string): Promise<EnsuredResource>;
52
71
  /**
package/dist/index.mjs CHANGED
@@ -58,18 +58,50 @@ var DiscordNotifications = class DiscordNotifications {
58
58
  if (!this.opts.guildId) throw new Error("no Discord server id — set `guildId` in the discord provider options in holocron.config.ts");
59
59
  return this.opts.guildId;
60
60
  }
61
+ /**
62
+ * The server's channel list, fetched once per instance. Every ensure* call used
63
+ * to refetch it -- about 40 identical GETs in one setup run -- and Discord
64
+ * rate-limited the route. Our own creates and edits update the copy, so it
65
+ * stays accurate for the run; a new run builds a new instance.
66
+ *
67
+ * It is the in-flight promise that is shared, not the array: callers that start
68
+ * while the first fetch is pending all get the same snapshot, so a later
69
+ * response can never overwrite what an earlier caller added.
70
+ */
71
+ channelLoad;
72
+ channelList() {
73
+ return this.channelLoad ??= this.bot().guilds.channels(this.guild()).catch((err) => {
74
+ this.channelLoad = void 0;
75
+ throw err;
76
+ });
77
+ }
78
+ /**
79
+ * Create a channel and add it to the shared list. If the create fails, Discord
80
+ * may still have applied it (a timeout looks the same as a refusal), so forget the
81
+ * list: the next ensure refetches the server's real state instead of creating a
82
+ * duplicate.
83
+ */
84
+ async createChannel(body) {
85
+ try {
86
+ const made = await this.bot().guilds.createChannel(this.guild(), body);
87
+ (await this.channelList()).push(made);
88
+ return made;
89
+ } catch (err) {
90
+ this.channelLoad = void 0;
91
+ throw err;
92
+ }
93
+ }
61
94
  static same(a, b) {
62
95
  return a.toLowerCase() === b.toLowerCase();
63
96
  }
64
97
  async ensureCategory(name) {
65
- const bot = this.bot();
66
- const existing = (await bot.guilds.channels(this.guild())).find((c) => c.type === ChannelType.Category && DiscordNotifications.same(c.name, name));
98
+ const existing = (await this.channelList()).find((c) => c.type === ChannelType.Category && DiscordNotifications.same(c.name, name));
67
99
  if (existing) return {
68
100
  id: existing.id,
69
101
  created: false
70
102
  };
71
103
  return {
72
- id: (await bot.guilds.createChannel(this.guild(), {
104
+ id: (await this.createChannel({
73
105
  name,
74
106
  type: ChannelType.Category
75
107
  })).id,
@@ -90,20 +122,28 @@ var DiscordNotifications = class DiscordNotifications {
90
122
  async ensureChannel(input) {
91
123
  const bot = this.bot();
92
124
  const parent = input.category ? (await this.ensureCategory(input.category)).id : void 0;
93
- const existing = DiscordNotifications.findChannel(await bot.guilds.channels(this.guild()), input.name, parent);
125
+ const existing = DiscordNotifications.findChannel(await this.channelList(), input.name, parent);
94
126
  if (existing) {
95
127
  const patch = {
96
128
  ...parent && existing.parent_id !== parent ? { parent_id: parent } : {},
97
129
  ...input.topic !== void 0 && (existing.topic ?? "") !== input.topic ? { topic: input.topic } : {}
98
130
  };
99
- if (Object.keys(patch).length > 0) await bot.channels.modify(existing.id, patch);
131
+ if (Object.keys(patch).length > 0) {
132
+ try {
133
+ await bot.channels.modify(existing.id, patch);
134
+ } catch (err) {
135
+ this.channelLoad = void 0;
136
+ throw err;
137
+ }
138
+ Object.assign(existing, patch);
139
+ }
100
140
  return {
101
141
  id: existing.id,
102
142
  created: false
103
143
  };
104
144
  }
105
145
  return {
106
- id: (await bot.guilds.createChannel(this.guild(), {
146
+ id: (await this.createChannel({
107
147
  name: input.name,
108
148
  type: ChannelType.Text,
109
149
  ...input.topic ? { topic: input.topic } : {},
@@ -114,8 +154,8 @@ var DiscordNotifications = class DiscordNotifications {
114
154
  }
115
155
  async ensureWebhook(input) {
116
156
  const bot = this.bot();
117
- const parent = input.category ? (await bot.guilds.channels(this.guild())).find((c) => c.type === ChannelType.Category && DiscordNotifications.same(c.name, input.category))?.id : void 0;
118
- const channel = DiscordNotifications.findChannel(await bot.guilds.channels(this.guild()), input.channel, parent);
157
+ const parent = input.category ? (await this.channelList()).find((c) => c.type === ChannelType.Category && DiscordNotifications.same(c.name, input.category))?.id : void 0;
158
+ const channel = DiscordNotifications.findChannel(await this.channelList(), input.channel, parent);
119
159
  if (!channel) throw new Error(`no channel named \`${input.channel}\` — create it first`);
120
160
  const existing = (await bot.channels.webhooks(channel.id)).find((w) => DiscordNotifications.same(w.name ?? "", input.name));
121
161
  if (existing) return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theholocron/holocron-plugin-discord",
3
- "version": "5.0.0-alpha.184",
3
+ "version": "5.0.0-alpha.185",
4
4
  "description": "Holocron plugin for Discord. Implements the notifications capability via Discord incoming webhooks.",
5
5
  "keywords": [
6
6
  "discord",
@@ -48,12 +48,12 @@
48
48
  "tsx": "4.23.15",
49
49
  "typescript": "^5.9.3",
50
50
  "vitest": "^4.1.11",
51
- "@theholocron/cli": "5.0.0-alpha.184"
51
+ "@theholocron/cli": "5.0.0-alpha.185"
52
52
  },
53
53
  "peerDependencies": {
54
54
  "@theholocron/discord-client": "^1.43.1",
55
55
  "@theholocron/misc-utils": "^1.16.0",
56
- "@theholocron/cli": "5.0.0-alpha.184"
56
+ "@theholocron/cli": "5.0.0-alpha.185"
57
57
  },
58
58
  "engines": {
59
59
  "node": ">=22"