@satorijs/adapter-discord 3.5.7 → 3.6.1

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/lib/bot.d.ts CHANGED
@@ -1,11 +1,15 @@
1
1
  import { Bot, Context, Fragment, Quester, Schema, SendOptions } from '@satorijs/satori';
2
2
  import { DiscordMessenger } from './message';
3
- import { Internal } from './types';
3
+ import { Internal, Webhook } from './types';
4
4
  import { WsClient } from './ws';
5
5
  export declare class DiscordBot extends Bot<DiscordBot.Config> {
6
6
  http: Quester;
7
7
  internal: Internal;
8
+ webhooks: Record<string, Webhook>;
9
+ webhookLock: Record<string, Promise<Webhook>>;
8
10
  constructor(ctx: Context, config: DiscordBot.Config);
11
+ private _ensureWebhook;
12
+ ensureWebhook(channelId: string): Promise<Webhook>;
9
13
  getSelf(): Promise<import("@satorijs/core").Universal.User>;
10
14
  sendMessage(channelId: string, content: Fragment, guildId?: string, options?: SendOptions): Promise<string[]>;
11
15
  sendPrivateMessage(channelId: string, content: Fragment, options?: SendOptions): Promise<string[]>;
package/lib/index.js CHANGED
@@ -41,7 +41,8 @@ __export(src_exports, {
41
41
  adaptSession: () => adaptSession,
42
42
  adaptUser: () => adaptUser,
43
43
  default: () => src_default,
44
- prepareMessage: () => prepareMessage
44
+ prepareMessage: () => prepareMessage,
45
+ sanitize: () => sanitize
45
46
  });
46
47
  module.exports = __toCommonJS(src_exports);
47
48
 
@@ -50,6 +51,7 @@ var import_satori5 = require("@satorijs/satori");
50
51
 
51
52
  // satori/adapters/discord/src/utils.ts
52
53
  var import_satori = require("@satorijs/satori");
54
+ var sanitize = /* @__PURE__ */ __name((val) => val.replace(/[\\*_`~|()\[\]]/g, "\\$&").replace(/@everyone/g, () => "\\@everyone").replace(/@here/g, () => "\\@here"), "sanitize");
53
55
  var adaptUser = /* @__PURE__ */ __name((user) => ({
54
56
  userId: user.id,
55
57
  avatar: `https://cdn.discordapp.com/avatars/${user.id}/${user.avatar}.png`,
@@ -87,20 +89,20 @@ async function adaptMessage(bot, meta, session = {}) {
87
89
  session.content = meta.content.replace(/<@[!&]?(.+?)>/g, (_, id) => {
88
90
  var _a2;
89
91
  if (meta.mention_roles.includes(id)) {
90
- return (0, import_satori.segment)("at", { role: id }).toString();
92
+ return (0, import_satori.h)("at", { role: id }).toString();
91
93
  } else {
92
94
  const user = (_a2 = meta.mentions) == null ? void 0 : _a2.find((u) => u.id === id || `${u.username}#${u.discriminator}` === id);
93
- return import_satori.segment.at(id, { name: user == null ? void 0 : user.username }).toString();
95
+ return import_satori.h.at(id, { name: user == null ? void 0 : user.username }).toString();
94
96
  }
95
97
  }).replace(/<a?:(.*):(.+?)>/g, (_, name, id) => {
96
98
  const animated = _[1] === "a";
97
- return (0, import_satori.segment)("face", { id, name, animated, platform }, [
98
- import_satori.segment.image(`https://cdn.discordapp.com/emojis/${id}.gif?quality=lossless`)
99
+ return (0, import_satori.h)("face", { id, name, animated, platform }, [
100
+ import_satori.h.image(`https://cdn.discordapp.com/emojis/${id}.gif?quality=lossless`)
99
101
  ]).toString();
100
- }).replace(/@everyone/g, () => (0, import_satori.segment)("at", { type: "all" }).toString()).replace(/@here/g, () => (0, import_satori.segment)("at", { type: "here" }).toString()).replace(/<#(.+?)>/g, (_, id) => {
102
+ }).replace(/@everyone/g, () => (0, import_satori.h)("at", { type: "all" }).toString()).replace(/@here/g, () => (0, import_satori.h)("at", { type: "here" }).toString()).replace(/<#(.+?)>/g, (_, id) => {
101
103
  var _a2;
102
104
  const channel = (_a2 = meta.mention_channels) == null ? void 0 : _a2.find((c) => c.id === id);
103
- return import_satori.segment.sharp(id, { name: channel == null ? void 0 : channel.name }).toString();
105
+ return import_satori.h.sharp(id, { name: channel == null ? void 0 : channel.name }).toString();
104
106
  });
105
107
  }
106
108
  if ((_c = meta.attachments) == null ? void 0 : _c.length) {
@@ -109,25 +111,25 @@ async function adaptMessage(bot, meta, session = {}) {
109
111
  session.content += meta.attachments.map((v) => {
110
112
  var _a2, _b2, _c2;
111
113
  if (v.height && v.width && ((_a2 = v.content_type) == null ? void 0 : _a2.startsWith("image/"))) {
112
- return (0, import_satori.segment)("image", {
114
+ return (0, import_satori.h)("image", {
113
115
  url: v.url,
114
116
  proxy_url: v.proxy_url,
115
117
  file: v.filename
116
118
  });
117
119
  } else if (v.height && v.width && ((_b2 = v.content_type) == null ? void 0 : _b2.startsWith("video/"))) {
118
- return (0, import_satori.segment)("video", {
120
+ return (0, import_satori.h)("video", {
119
121
  url: v.url,
120
122
  proxy_url: v.proxy_url,
121
123
  file: v.filename
122
124
  });
123
125
  } else if ((_c2 = v.content_type) == null ? void 0 : _c2.startsWith("audio/")) {
124
- return (0, import_satori.segment)("record", {
126
+ return (0, import_satori.h)("record", {
125
127
  url: v.url,
126
128
  proxy_url: v.proxy_url,
127
129
  file: v.filename
128
130
  });
129
131
  } else {
130
- return (0, import_satori.segment)("file", {
132
+ return (0, import_satori.h)("file", {
131
133
  url: v.url,
132
134
  proxy_url: v.proxy_url,
133
135
  file: v.filename
@@ -137,16 +139,16 @@ async function adaptMessage(bot, meta, session = {}) {
137
139
  }
138
140
  for (const embed of meta.embeds) {
139
141
  if (embed.image) {
140
- session.content += (0, import_satori.segment)("image", { url: embed.image.url, proxy_url: embed.image.proxy_url });
142
+ session.content += (0, import_satori.h)("image", { url: embed.image.url, proxy_url: embed.image.proxy_url });
141
143
  }
142
144
  if (embed.thumbnail) {
143
- session.content += (0, import_satori.segment)("image", { url: embed.thumbnail.url, proxy_url: embed.thumbnail.proxy_url });
145
+ session.content += (0, import_satori.h)("image", { url: embed.thumbnail.url, proxy_url: embed.thumbnail.proxy_url });
144
146
  }
145
147
  if (embed.video) {
146
- session.content += (0, import_satori.segment)("video", { url: embed.video.url, proxy_url: embed.video.proxy_url });
148
+ session.content += (0, import_satori.h)("video", { url: embed.video.url, proxy_url: embed.video.proxy_url });
147
149
  }
148
150
  }
149
- session.elements = import_satori.segment.parse(session.content);
151
+ session.elements = import_satori.h.parse(session.content);
150
152
  if (meta.message_reference) {
151
153
  const { message_id, channel_id } = meta.message_reference;
152
154
  session.quote = await bot.getMessage(channel_id, message_id);
@@ -175,6 +177,12 @@ __name(prepareReactionSession, "prepareReactionSession");
175
177
  async function adaptSession(bot, input) {
176
178
  const session = bot.session();
177
179
  if (input.t === "MESSAGE_CREATE") {
180
+ if (input.d.webhook_id) {
181
+ const webhook = await bot.ensureWebhook(input.d.channel_id);
182
+ if (webhook.id === input.d.webhook_id) {
183
+ return;
184
+ }
185
+ }
178
186
  session.type = "message";
179
187
  await adaptMessage(bot, input.d, session);
180
188
  } else if (input.t === "MESSAGE_UPDATE") {
@@ -215,36 +223,78 @@ __name(adaptSession, "adaptSession");
215
223
  // satori/adapters/discord/src/message.ts
216
224
  var import_satori2 = require("@satorijs/satori");
217
225
  var import_form_data = __toESM(require("form-data"));
226
+ var logger = new import_satori2.Logger("discord");
227
+ var State = class {
228
+ // forward: send the first message and create a thread
229
+ constructor(type) {
230
+ this.type = type;
231
+ this.author = {};
232
+ this.quote = {};
233
+ this.channel = {};
234
+ this.fakeMessageMap = {};
235
+ // [userInput] = discord messages
236
+ this.threadCreated = false;
237
+ }
238
+ };
239
+ __name(State, "State");
218
240
  var DiscordMessenger = class extends import_satori2.Messenger {
219
241
  constructor() {
220
242
  super(...arguments);
243
+ this.stack = [new State("message")];
221
244
  this.buffer = "";
222
245
  this.addition = {};
223
246
  this.figure = null;
224
247
  this.mode = "default";
225
248
  }
226
249
  async post(data, headers) {
250
+ var _a, _b, _c;
227
251
  try {
228
- const result = await this.bot.http.post(`/channels/${this.channelId}/messages`, data, { headers });
252
+ let url = `/channels/${this.channelId}/messages`;
253
+ if (this.stack[0].author.nickname || this.stack[0].author.avatar || this.stack[0].type === "forward" && !this.stack[0].threadCreated) {
254
+ const webhook = await this.ensureWebhook();
255
+ url = `/webhooks/${webhook.id}/${webhook.token}?wait=true`;
256
+ }
257
+ if (this.stack[0].type === "forward" && ((_a = this.stack[0].channel) == null ? void 0 : _a.id)) {
258
+ if (this.stack[1].author.nickname || this.stack[1].author.avatar) {
259
+ const webhook = await this.ensureWebhook();
260
+ url = `/webhooks/${webhook.id}/${webhook.token}?wait=true&thread_id=${(_b = this.stack[0].channel) == null ? void 0 : _b.id}`;
261
+ } else {
262
+ url = `/channels/${this.stack[0].channel.id}/messages`;
263
+ }
264
+ }
265
+ const result = await this.bot.http.post(url, data, { headers });
229
266
  const session = this.bot.session();
230
- await adaptMessage(this.bot, result, session);
267
+ const message = await adaptMessage(this.bot, result, session);
231
268
  session.app.emit(session, "send", session);
232
269
  this.results.push(session);
270
+ if (this.stack[0].type === "forward" && !this.stack[0].threadCreated) {
271
+ this.stack[0].threadCreated = true;
272
+ const thread = await this.bot.internal.startThreadFromMessage(this.channelId, result.id, {
273
+ name: "Forward",
274
+ auto_archive_duration: 60
275
+ });
276
+ this.stack[0].channel = thread;
277
+ }
278
+ return message;
233
279
  } catch (e) {
280
+ if (import_satori2.Quester.isAxiosError(e) && ((_c = e.response) == null ? void 0 : _c.data.code) === 10015) {
281
+ logger.debug("webhook has been deleted, recreating..., %o", e.response.data);
282
+ if (!this.bot.webhookLock[this.channelId])
283
+ this.bot.webhooks[this.channelId] = null;
284
+ await this.ensureWebhook();
285
+ return this.post(data, headers);
286
+ }
234
287
  this.errors.push(e);
235
288
  }
236
289
  }
237
290
  async sendEmbed(attrs, payload) {
238
- const { filename, data, mime } = await this.bot.ctx.http.file(attrs.url);
291
+ const { filename, data, mime } = await this.bot.ctx.http.file(attrs.url, attrs);
239
292
  const form = new import_form_data.default();
240
293
  const value = process.env.KOISHI_ENV === "browser" ? new Blob([data], { type: mime }) : Buffer.from(data);
241
294
  form.append("file", value, attrs.file || filename);
242
295
  form.append("payload_json", JSON.stringify(payload));
243
296
  return this.post(form, form.getHeaders());
244
297
  }
245
- async sendContent(content, addition) {
246
- return this.post({ ...addition, content });
247
- }
248
298
  async sendAsset(type, attrs, addition) {
249
299
  const { handleMixedContent, handleExternalAsset } = this.bot.config;
250
300
  if (handleMixedContent === "separate" && addition.content) {
@@ -268,7 +318,8 @@ var DiscordMessenger = class extends import_satori2.Messenger {
268
318
  return sendDirect();
269
319
  }
270
320
  return await this.bot.ctx.http.head(attrs.url, {
271
- headers: { accept: type + "/*" }
321
+ headers: { accept: type + "/*" },
322
+ timeout: +attrs.timeout || void 0
272
323
  }).then((headers) => {
273
324
  if (headers["content-type"].startsWith(type)) {
274
325
  return sendDirect();
@@ -277,6 +328,9 @@ var DiscordMessenger = class extends import_satori2.Messenger {
277
328
  }
278
329
  }, sendDownload);
279
330
  }
331
+ async ensureWebhook() {
332
+ return this.bot.ensureWebhook(this.channelId);
333
+ }
280
334
  async flush() {
281
335
  const content = this.buffer.trim();
282
336
  if (!content)
@@ -286,9 +340,10 @@ var DiscordMessenger = class extends import_satori2.Messenger {
286
340
  this.addition = {};
287
341
  }
288
342
  async visit(element) {
343
+ var _a;
289
344
  const { type, attrs, children } = element;
290
345
  if (type === "text") {
291
- this.buffer += attrs.content.replace(/[\\*_`~|()]/g, "\\$&");
346
+ this.buffer += sanitize(attrs.content);
292
347
  } else if (type === "b" || type === "strong") {
293
348
  this.buffer += "**";
294
349
  await this.render(children);
@@ -355,7 +410,7 @@ var DiscordMessenger = class extends import_satori2.Messenger {
355
410
  ...this.addition,
356
411
  embeds: [{ ...attrs }]
357
412
  });
358
- } else if (type === "record") {
413
+ } else if (type === "audio") {
359
414
  await this.sendAsset("file", attrs, {
360
415
  ...this.addition,
361
416
  content: this.buffer.trim()
@@ -371,20 +426,78 @@ var DiscordMessenger = class extends import_satori2.Messenger {
371
426
  });
372
427
  this.buffer = "";
373
428
  this.mode = "default";
374
- } else if (type === "quote") {
375
- await this.flush();
376
- this.addition.message_reference = {
377
- message_id: attrs.id
378
- };
379
- } else if (type === "message") {
429
+ } else if (type === "message" && !attrs.forward) {
380
430
  if (this.mode === "figure") {
381
431
  await this.render(children);
382
432
  this.buffer += "\n";
383
433
  } else {
434
+ const resultLength = +this.results.length;
384
435
  await this.flush();
436
+ const [author] = import_satori2.segment.select(children, "author");
437
+ if (author) {
438
+ const { avatar, nickname } = author.attrs;
439
+ if (avatar)
440
+ this.addition.avatar_url = avatar;
441
+ if (nickname)
442
+ this.addition.username = nickname;
443
+ if (this.stack[0].type === "message") {
444
+ this.stack[0].author = author.attrs;
445
+ }
446
+ if (this.stack[0].type === "forward") {
447
+ this.stack[1].author = author.attrs;
448
+ }
449
+ }
450
+ const [quote] = import_satori2.segment.select(children, "quote");
451
+ if (quote) {
452
+ const parse = /* @__PURE__ */ __name((val) => val.replace(/\\([\\*_`~|()\[\]])/g, "$1"), "parse");
453
+ const message = this.stack[this.stack[0].type === "forward" ? 1 : 0];
454
+ if (!message.author.avatar && !message.author.nickname && this.stack[0].type !== "forward") {
455
+ await this.flush();
456
+ this.addition.message_reference = {
457
+ message_id: quote.attrs.id
458
+ };
459
+ } else {
460
+ let replyId = quote.attrs.id, channelId = this.channelId;
461
+ if (this.stack[0].type === "forward" && ((_a = this.stack[0].fakeMessageMap[quote.attrs.id]) == null ? void 0 : _a.length) >= 1) {
462
+ replyId = this.stack[0].fakeMessageMap[quote.attrs.id][0].messageId;
463
+ channelId = this.stack[0].fakeMessageMap[quote.attrs.id][0].channelId;
464
+ }
465
+ const quoted = await this.bot.getMessage(channelId, replyId);
466
+ this.addition.embeds = [{
467
+ description: [
468
+ sanitize(parse(quoted.elements.filter((v) => v.type === "text").join("")).slice(0, 30)),
469
+ `<t:${Math.ceil(quoted.timestamp / 1e3)}:R> [[ ↑ ]](https://discord.com/channels/${this.guildId}/${channelId}/${replyId})`
470
+ ].join("\n\n"),
471
+ author: {
472
+ name: quoted.author.nickname || quoted.author.username,
473
+ icon_url: quoted.author.avatar
474
+ }
475
+ }];
476
+ }
477
+ }
385
478
  await this.render(children);
386
479
  await this.flush();
480
+ const newLength = +this.results.length;
481
+ const sentMessages = this.results.slice(resultLength, newLength);
482
+ if (this.stack[0].type === "forward" && attrs.id) {
483
+ this.stack[0].fakeMessageMap[attrs.id] = sentMessages;
484
+ }
485
+ if (this.stack[0].type === "message") {
486
+ this.stack[0].author = {};
487
+ }
488
+ if (this.stack[0].type === "forward") {
489
+ this.stack[1].author = {};
490
+ }
387
491
  }
492
+ } else if (type === "message" && attrs.forward) {
493
+ this.stack.unshift(new State("forward"));
494
+ await this.render(children);
495
+ await this.flush();
496
+ await this.bot.internal.modifyChannel(this.stack[0].channel.id, {
497
+ archived: true,
498
+ locked: true
499
+ });
500
+ this.stack.shift();
388
501
  } else {
389
502
  await this.render(children);
390
503
  }
@@ -1445,13 +1558,13 @@ Internal.define({
1445
1558
 
1446
1559
  // satori/adapters/discord/src/types/webhook.ts
1447
1560
  var Webhook2;
1448
- ((Webhook3) => {
1561
+ ((Webhook4) => {
1449
1562
  let Type;
1450
1563
  ((Type2) => {
1451
1564
  Type2[Type2["INCOMING"] = 1] = "INCOMING";
1452
1565
  Type2[Type2["CHANNEL_FOLLOWER"] = 2] = "CHANNEL_FOLLOWER";
1453
1566
  Type2[Type2["APPLICATION"] = 3] = "APPLICATION";
1454
- })(Type = Webhook3.Type || (Webhook3.Type = {}));
1567
+ })(Type = Webhook4.Type || (Webhook4.Type = {}));
1455
1568
  })(Webhook2 || (Webhook2 = {}));
1456
1569
  Internal.define({
1457
1570
  "/channels/{channel.id}/webhooks": {
@@ -1487,7 +1600,7 @@ Internal.define({
1487
1600
 
1488
1601
  // satori/adapters/discord/src/ws.ts
1489
1602
  var import_satori4 = require("@satorijs/satori");
1490
- var logger = new import_satori4.Logger("discord");
1603
+ var logger2 = new import_satori4.Logger("discord");
1491
1604
  var WsClient = class extends import_satori4.Adapter.WsClient {
1492
1605
  constructor() {
1493
1606
  super(...arguments);
@@ -1502,7 +1615,7 @@ var WsClient = class extends import_satori4.Adapter.WsClient {
1502
1615
  return this.bot.http.ws(url);
1503
1616
  }
1504
1617
  heartbeat() {
1505
- logger.debug(`heartbeat d ${this._d}`);
1618
+ logger2.debug(`heartbeat d ${this._d}`);
1506
1619
  this.bot.socket.send(JSON.stringify({
1507
1620
  op: 1 /* HEARTBEAT */,
1508
1621
  d: this._d
@@ -1515,16 +1628,16 @@ var WsClient = class extends import_satori4.Adapter.WsClient {
1515
1628
  try {
1516
1629
  parsed = JSON.parse(data.toString());
1517
1630
  } catch (error) {
1518
- return logger.warn("cannot parse message", data);
1631
+ return logger2.warn("cannot parse message", data);
1519
1632
  }
1520
- logger.debug(require("util").inspect(parsed, false, null, true));
1633
+ logger2.debug(require("util").inspect(parsed, false, null, true));
1521
1634
  if (parsed.s) {
1522
1635
  this._d = parsed.s;
1523
1636
  }
1524
1637
  if (parsed.op === 10 /* HELLO */) {
1525
1638
  this._ping = setInterval(() => this.heartbeat(), parsed.d.heartbeat_interval);
1526
1639
  if (this._sessionId) {
1527
- logger.debug("resuming");
1640
+ logger2.debug("resuming");
1528
1641
  this.bot.socket.send(JSON.stringify({
1529
1642
  op: 6 /* RESUME */,
1530
1643
  d: {
@@ -1549,7 +1662,7 @@ var WsClient = class extends import_satori4.Adapter.WsClient {
1549
1662
  if (parsed.d)
1550
1663
  return;
1551
1664
  this._sessionId = "";
1552
- logger.warn("offline: invalid session");
1665
+ logger2.warn("offline: invalid session");
1553
1666
  this.bot.offline();
1554
1667
  (_a = this.bot.socket) == null ? void 0 : _a.close();
1555
1668
  }
@@ -1561,7 +1674,7 @@ var WsClient = class extends import_satori4.Adapter.WsClient {
1561
1674
  self.selfId = self.userId;
1562
1675
  delete self.userId;
1563
1676
  Object.assign(this.bot, self);
1564
- logger.debug("session_id " + this._sessionId);
1677
+ logger2.debug("session_id " + this._sessionId);
1565
1678
  return this.bot.online();
1566
1679
  }
1567
1680
  if (parsed.t === "RESUMED") {
@@ -1573,7 +1686,7 @@ var WsClient = class extends import_satori4.Adapter.WsClient {
1573
1686
  }
1574
1687
  if (parsed.op === 7 /* RECONNECT */) {
1575
1688
  this.bot.offline();
1576
- logger.warn("offline: discord request reconnect");
1689
+ logger2.warn("offline: discord request reconnect");
1577
1690
  (_b = this.bot.socket) == null ? void 0 : _b.close();
1578
1691
  }
1579
1692
  });
@@ -1597,6 +1710,8 @@ var import_package = require("../package.json");
1597
1710
  var DiscordBot = class extends import_satori5.Bot {
1598
1711
  constructor(ctx, config) {
1599
1712
  super(ctx, config);
1713
+ this.webhooks = {};
1714
+ this.webhookLock = {};
1600
1715
  this.http = ctx.http.extend({
1601
1716
  ...config,
1602
1717
  headers: {
@@ -1608,6 +1723,31 @@ var DiscordBot = class extends import_satori5.Bot {
1608
1723
  this.internal = new Internal(this.http);
1609
1724
  ctx.plugin(WsClient, this);
1610
1725
  }
1726
+ async _ensureWebhook(channelId) {
1727
+ let webhook;
1728
+ const webhooks = await this.internal.getChannelWebhooks(channelId);
1729
+ const selfId = this.selfId;
1730
+ if (!webhooks.find((v) => v.name === "Koishi" && v.user.id === selfId)) {
1731
+ webhook = await this.internal.createWebhook(channelId, {
1732
+ name: "Koishi"
1733
+ });
1734
+ } else {
1735
+ webhook = webhooks.find((v) => v.name === "Koishi" && v.user.id === this.selfId);
1736
+ }
1737
+ return this.webhooks[channelId] = webhook;
1738
+ }
1739
+ async ensureWebhook(channelId) {
1740
+ var _a;
1741
+ if (this.webhooks[channelId] === null) {
1742
+ delete this.webhooks[channelId];
1743
+ delete this.webhookLock[channelId];
1744
+ }
1745
+ if (this.webhooks[channelId]) {
1746
+ delete this.webhookLock[channelId];
1747
+ return this.webhooks[channelId];
1748
+ }
1749
+ return (_a = this.webhookLock)[channelId] || (_a[channelId] = this._ensureWebhook(channelId));
1750
+ }
1611
1751
  async getSelf() {
1612
1752
  const data = await this.internal.getCurrentUser();
1613
1753
  return adaptUser(data);
@@ -1622,7 +1762,7 @@ var DiscordBot = class extends import_satori5.Bot {
1622
1762
  await this.internal.deleteMessage(channelId, messageId);
1623
1763
  }
1624
1764
  async editMessage(channelId, messageId, content) {
1625
- const elements = import_satori5.segment.normalize(content);
1765
+ const elements = import_satori5.h.normalize(content);
1626
1766
  content = elements.toString();
1627
1767
  const image = elements.find((v) => v.type === "image");
1628
1768
  if (image) {
@@ -1702,6 +1842,7 @@ var src_default = DiscordBot;
1702
1842
  adaptMessage,
1703
1843
  adaptSession,
1704
1844
  adaptUser,
1705
- prepareMessage
1845
+ prepareMessage,
1846
+ sanitize
1706
1847
  });
1707
1848
  //# sourceMappingURL=index.js.map