discord-html-transcripts-fix 1.1.0 → 1.3.0

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.js CHANGED
@@ -1,136 +1,133 @@
1
1
  "use strict";
2
2
  /// <reference path="./discord-components.d.ts" />
3
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
- if (k2 === undefined) k2 = k;
5
- var desc = Object.getOwnPropertyDescriptor(m, k);
6
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
- desc = { enumerable: true, get: function() { return m[k]; } };
8
- }
9
- Object.defineProperty(o, k2, desc);
10
- }) : (function(o, m, k, k2) {
11
- if (k2 === undefined) k2 = k;
12
- o[k2] = m[k];
13
- }));
14
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
16
- };
17
3
  var __importDefault = (this && this.__importDefault) || function (mod) {
18
4
  return (mod && mod.__esModule) ? mod : { "default": mod };
19
5
  };
6
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
7
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p))
8
+ Object.defineProperty(exports, p, { enumerable: true, get: function() { return m[p]; } });
9
+ };
20
10
  Object.defineProperty(exports, "__esModule", { value: true });
21
11
  exports.TranscriptImageDownloader = exports.DiscordMessages = void 0;
22
12
  exports.generateFromMessages = generateFromMessages;
23
13
  exports.createTranscript = createTranscript;
14
+ const debug_1 = __importDefault(require("debug"));
24
15
  const discord_js_1 = require("discord.js");
25
16
  const generator_1 = __importDefault(require("./generator"));
26
17
  const types_1 = require("./types");
27
18
  const images_1 = require("./downloader/images");
28
- // re-exports
19
+
20
+ const log = (0, debug_1.default)('discord-html-transcripts-fix');
21
+
29
22
  var transcript_1 = require("./generator/transcript");
30
23
  Object.defineProperty(exports, "DiscordMessages", { enumerable: true, get: function () { return __importDefault(transcript_1).default; } });
31
24
  var images_2 = require("./downloader/images");
32
25
  Object.defineProperty(exports, "TranscriptImageDownloader", { enumerable: true, get: function () { return images_2.TranscriptImageDownloader; } });
33
- // version check (warn only — never kill the host process from a library)
26
+
27
+ // Version check — warn only. NEVER kill host process.
34
28
  const versionPrefix = discord_js_1.version.split('.')[0];
35
29
  if (versionPrefix !== '14' && versionPrefix !== '15') {
36
- console.warn(`[discord-html-transcripts-fix] Compatible with discord.js v14.x and v15.x, you are using v${discord_js_1.version}. Continuing — transcripts may render incorrectly.`);
30
+ console.warn(`[discord-html-transcripts-fix] Compatible with discord.js v14/v15, you are using v${discord_js_1.version}. Continuing anyway.`);
37
31
  }
38
- /**
39
- *
40
- * @param messages The messages to generate a transcript from
41
- * @param channel The channel the messages are from (used for header and guild name)
42
- * @param options The options to use when generating the transcript
43
- * @returns The generated transcript
44
- */
32
+
45
33
  async function generateFromMessages(messages, channel, options = {}) {
46
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
47
- // turn messages into an array
48
34
  const transformedMessages = messages instanceof discord_js_1.Collection ? Array.from(messages.values()) : messages;
49
- // figure out how the user wants images saved
50
- let resolveImageSrc = (_b = (_a = options.callbacks) === null || _a === void 0 ? void 0 : _a.resolveImageSrc) !== null && _b !== void 0 ? _b : ((attachment) => attachment.url);
35
+
36
+ // Image-src resolution
37
+ let resolveImageSrc = options.callbacks?.resolveImageSrc ?? ((attachment) => attachment.url);
51
38
  if (options.saveImages) {
52
- if ((_c = options.callbacks) === null || _c === void 0 ? void 0 : _c.resolveImageSrc) {
53
- console.warn(`[discord-html-transcripts] You have specified both saveImages and resolveImageSrc, please only specify one. resolveImageSrc will be used.`);
54
- }
55
- else {
39
+ if (options.callbacks?.resolveImageSrc) {
40
+ log('saveImages + resolveImageSrc both set — resolveImageSrc wins');
41
+ } else {
56
42
  resolveImageSrc = new images_1.TranscriptImageDownloader().build();
57
- console.log('Using default downloader');
43
+ log('using default image downloader');
58
44
  }
59
45
  }
60
- // render the messages
61
- const html = await (0, generator_1.default)({
46
+
47
+ // Pre-build resolution maps from messages so mentions resolve to a name even
48
+ // when the user/role/channel isn't in client cache and can't be fetched.
49
+ const userIndex = new Map();
50
+ const roleIndex = new Map();
51
+ const channelIndex = new Map();
52
+ for (const m of transformedMessages) {
53
+ if (m.author) userIndex.set(m.author.id, m.author);
54
+ if (m.member?.user) userIndex.set(m.member.user.id, m.member.user);
55
+ if (m.interaction?.user) userIndex.set(m.interaction.user.id, m.interaction.user);
56
+ if (m.mentions) {
57
+ if (m.mentions.users) for (const [id, u] of m.mentions.users) userIndex.set(id, u);
58
+ if (m.mentions.roles) for (const [id, r] of m.mentions.roles) roleIndex.set(id, r);
59
+ if (m.mentions.channels) for (const [id, c] of m.mentions.channels) channelIndex.set(id, c);
60
+ }
61
+ }
62
+ if (!channel.isDMBased() && channel.guild) {
63
+ // pre-warm every role we can see (cheap — cache only)
64
+ for (const [id, r] of channel.guild.roles.cache) roleIndex.set(id, r);
65
+ }
66
+
67
+ const callbacks = Object.assign({
68
+ resolveImageSrc,
69
+ resolveChannel: async (id) => channelIndex.get(id) || channel.client.channels.fetch(id).catch(() => null),
70
+ resolveUser: async (id) => userIndex.get(id) || channel.client.users.fetch(id).catch(() => null),
71
+ resolveRole: channel.isDMBased()
72
+ ? () => null
73
+ : async (id) => roleIndex.get(id) || channel.guild?.roles.fetch(id).catch(() => null),
74
+ }, options.callbacks ?? {});
75
+
76
+ const rendered = await (0, generator_1.default)({
62
77
  messages: transformedMessages,
63
78
  channel,
64
- saveImages: (_d = options.saveImages) !== null && _d !== void 0 ? _d : false,
65
- callbacks: Object.assign({ resolveImageSrc, resolveChannel: async (id) => channel.client.channels.fetch(id).catch(() => null), resolveUser: async (id) => channel.client.users.fetch(id).catch(() => null), resolveRole: channel.isDMBased() ? () => null : async (id) => { var _a; return (_a = channel.guild) === null || _a === void 0 ? void 0 : _a.roles.fetch(id).catch(() => null); } }, ((_e = options.callbacks) !== null && _e !== void 0 ? _e : {})),
66
- poweredBy: (_f = options.poweredBy) !== null && _f !== void 0 ? _f : true,
67
- footerText: (_g = options.footerText) !== null && _g !== void 0 ? _g : 'Exported {number} message{s}.',
68
- favicon: (_h = options.favicon) !== null && _h !== void 0 ? _h : 'guild',
69
- hydrate: (_j = options.hydrate) !== null && _j !== void 0 ? _j : false,
79
+ saveImages: options.saveImages ?? false,
80
+ callbacks,
81
+ poweredBy: options.poweredBy ?? true,
82
+ footerText: options.footerText ?? 'Exported {number} message{s}.',
83
+ favicon: options.favicon ?? 'guild',
84
+ hydrate: options.hydrate ?? false,
85
+ language: options.language ?? 'en',
86
+ i18n: options.i18n ?? undefined,
87
+ stream: options.returnType === 'stream' || options.stream === true,
88
+ returnType: options.returnType,
70
89
  });
71
- // get the time it took to render the messages
72
- // const renderTime = process.hrtime(startTime);
73
- // console.log(
74
- // `[discord-html-transcripts] Rendered ${transformedMessages.length} messages in ${renderTime[0]}s ${
75
- // renderTime[1] / 1000000
76
- // }ms`
77
- // );
78
- // return the html in the specified format
90
+
91
+ // Stream pass-through
92
+ if (options.returnType === 'stream' || options.stream === true) {
93
+ return rendered; // Node readable stream
94
+ }
95
+
79
96
  if (options.returnType === types_1.ExportReturnType.Buffer) {
80
- return Buffer.from(html);
97
+ return Buffer.from(rendered);
81
98
  }
82
99
  if (options.returnType === types_1.ExportReturnType.String) {
83
- return html;
100
+ return rendered;
84
101
  }
85
- return new discord_js_1.AttachmentBuilder(Buffer.from(html), {
86
- name: (_k = options.filename) !== null && _k !== void 0 ? _k : `transcript-${channel.id}.html`,
102
+ return new discord_js_1.AttachmentBuilder(Buffer.from(rendered), {
103
+ name: options.filename ?? `transcript-${channel.id}.html`,
87
104
  });
88
105
  }
89
- /**
90
- *
91
- * @param channel The channel to create a transcript from
92
- * @param options The options to use when creating the transcript
93
- * @returns The generated transcript
94
- */
106
+
95
107
  async function createTranscript(channel, options = {}) {
96
- // validate type
97
108
  if (!channel.isTextBased()) {
98
- // @ts-expect-error(2339): run-time check
99
109
  throw new TypeError(`Provided channel must be text-based, received ${channel.type}`);
100
110
  }
101
- // fetch messages
102
111
  let allMessages = [];
103
112
  let lastMessageId;
104
113
  const { limit, filter } = options;
105
114
  const resolvedLimit = typeof limit === 'undefined' || limit === -1 ? Infinity : limit;
106
- // until there are no more messages, keep fetching
107
115
  while (true) {
108
- // create fetch options
109
116
  const fetchLimitOptions = { limit: 100, before: lastMessageId };
110
- if (!lastMessageId)
111
- delete fetchLimitOptions.before;
112
- // fetch messages
117
+ if (!lastMessageId) delete fetchLimitOptions.before;
113
118
  const messages = await channel.messages.fetch(fetchLimitOptions);
114
119
  const filteredMessages = typeof filter === 'function' ? messages.filter(filter) : messages;
115
- // add the messages to the array
116
- allMessages.push(...filteredMessages.values());
117
- // Get the last key of 'messages', not 'filteredMessages' because you will be refetching the same messages
120
+ for (const m of filteredMessages.values()) allMessages.push(m);
118
121
  lastMessageId = messages.lastKey();
119
- // if there are no more messages, break
120
- if (messages.size < 100)
121
- break;
122
- // if the limit has been reached, break
123
- if (allMessages.length >= resolvedLimit)
124
- break;
122
+ if (messages.size < 100) break;
123
+ if (allMessages.length >= resolvedLimit) break;
124
+ }
125
+ if (resolvedLimit < allMessages.length) {
126
+ allMessages = allMessages.slice(0, resolvedLimit);
125
127
  }
126
- if (resolvedLimit < allMessages.length)
127
- allMessages = allMessages.slice(0, limit);
128
- // generate the transcript
129
128
  return generateFromMessages(allMessages.reverse(), channel, options);
130
129
  }
131
- exports.default = {
132
- createTranscript,
133
- generateFromMessages,
134
- };
130
+
131
+ exports.default = { createTranscript, generateFromMessages };
135
132
  __exportStar(require("./types"), exports);
136
- //# sourceMappingURL=index.js.map
133
+ //# sourceMappingURL=index.js.map