discord-message-transcript 1.2.0-dev-next.0.16 → 1.2.0-dev-next.0.19

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.
@@ -2,48 +2,55 @@ import https from 'https';
2
2
  import http from 'http';
3
3
  import { CustomWarn } from "discord-message-transcript-base";
4
4
  import crypto from 'crypto';
5
+ import { getCDNLimiter } from "./limiter.js";
5
6
  export async function cdnResolver(url, cdnOptions) {
6
- return new Promise((resolve, reject) => {
7
- const client = url.startsWith('https') ? https : http;
8
- const request = client.get(url, { headers: { "User-Agent": "discord-message-transcript" } }, async (response) => {
9
- if (response.statusCode !== 200) {
10
- response.destroy();
11
- CustomWarn(`This is not an issue with the package. Using the original URL as fallback instead of uploading to CDN.
7
+ const limit = getCDNLimiter();
8
+ return limit(async () => {
9
+ return new Promise((resolve, reject) => {
10
+ const client = url.startsWith('https') ? https : http;
11
+ const request = client.request(url, {
12
+ method: 'HEAD',
13
+ headers: { "User-Agent": "discord-message-transcript" }
14
+ }, async (response) => {
15
+ if (response.statusCode !== 200) {
16
+ response.destroy();
17
+ CustomWarn(`This is not an issue with the package. Using the original URL as fallback instead of uploading to CDN.
12
18
  Failed to fetch attachment with status code: ${response.statusCode} from ${url}.`);
13
- return resolve(url);
14
- }
15
- const contentType = response.headers["content-type"];
16
- const splitContentType = contentType ? contentType?.split('/') : [];
17
- if (!contentType || splitContentType.length != 2 || splitContentType[0].length == 0 || splitContentType[1].length == 0) {
18
- response.destroy();
19
- CustomWarn(`This is not an issue with the package. Using the original URL as fallback instead of uploading to CDN.
19
+ return resolve(url);
20
+ }
21
+ const contentType = response.headers["content-type"];
22
+ const splitContentType = contentType ? contentType?.split('/') : [];
23
+ if (!contentType || splitContentType.length != 2 || splitContentType[0].length == 0 || splitContentType[1].length == 0) {
24
+ response.destroy();
25
+ CustomWarn(`This is not an issue with the package. Using the original URL as fallback instead of uploading to CDN.
20
26
  Failed to receive a valid content-type from ${url}.`);
27
+ return resolve(url);
28
+ }
29
+ response.destroy();
30
+ const isImage = contentType.startsWith('image/') && contentType !== 'image/gif';
31
+ const isAudio = contentType.startsWith('audio/');
32
+ const isVideo = contentType.startsWith('video/') || contentType === 'image/gif';
33
+ if ((cdnOptions.includeImage && isImage) ||
34
+ (cdnOptions.includeAudio && isAudio) ||
35
+ (cdnOptions.includeVideo && isVideo) ||
36
+ (cdnOptions.includeOthers && !isAudio && !isImage && !isVideo)) {
37
+ return resolve(await cdnRedirectType(url, contentType, cdnOptions));
38
+ }
21
39
  return resolve(url);
22
- }
23
- response.destroy();
24
- const isImage = contentType.startsWith('image/') && contentType !== 'image/gif';
25
- const isAudio = contentType.startsWith('audio/');
26
- const isVideo = contentType.startsWith('video/') || contentType === 'image/gif';
27
- if ((cdnOptions.includeImage && isImage) ||
28
- (cdnOptions.includeAudio && isAudio) ||
29
- (cdnOptions.includeVideo && isVideo) ||
30
- (cdnOptions.includeOthers && !isAudio && !isImage && !isVideo)) {
31
- return resolve(await cdnRedirectType(url, contentType, cdnOptions));
32
- }
33
- return resolve(url);
34
- });
35
- request.on('error', (err) => {
36
- CustomWarn(`This is not an issue with the package. Using the original URL as fallback instead of uploading to CDN.
40
+ });
41
+ request.on('error', (err) => {
42
+ CustomWarn(`This is not an issue with the package. Using the original URL as fallback instead of uploading to CDN.
37
43
  Error: ${err.message}`);
38
- return resolve(url);
39
- });
40
- request.setTimeout(15000, () => {
41
- request.destroy();
42
- CustomWarn(`This is not an issue with the package. Using the original URL as fallback instead of uploading to CDN.
44
+ return resolve(url);
45
+ });
46
+ request.setTimeout(15000, () => {
47
+ request.destroy();
48
+ CustomWarn(`This is not an issue with the package. Using the original URL as fallback instead of uploading to CDN.
43
49
  Request timeout for ${url}.`);
44
- resolve(url);
50
+ resolve(url);
51
+ });
52
+ request.end();
45
53
  });
46
- request.end();
47
54
  });
48
55
  }
49
56
  async function cdnRedirectType(url, contentType, cdnOptions) {
@@ -61,7 +68,8 @@ Error: ${error?.message ?? error}`);
61
68
  }
62
69
  }
63
70
  case "CLOUDINARY": {
64
- return '';
71
+ return await cloudinaryResolver(url, cdnOptions.cloudName, cdnOptions.apiKey, cdnOptions.apiSecret);
72
+ ;
65
73
  }
66
74
  case "UPLOADCARE": {
67
75
  return await uploadCareResolver(url, cdnOptions.publicKey);
@@ -1,7 +1,8 @@
1
1
  import { TextBasedChannel } from "discord.js";
2
2
  import { JsonAuthor, JsonMessage, TranscriptOptionsBase } from "discord-message-transcript-base";
3
3
  import { CDNOptions, MapMentions } from "../types/types.js";
4
- export declare function fetchMessages(channel: TextBasedChannel, options: TranscriptOptionsBase, cdnOptions: CDNOptions | null, authors: Map<string, JsonAuthor>, mentions: MapMentions, after?: string): Promise<{
4
+ export declare function fetchMessages(channel: TextBasedChannel, options: TranscriptOptionsBase, cdnOptions: CDNOptions | null, authors: Map<string, JsonAuthor>, mentions: MapMentions, before?: string): Promise<{
5
5
  messages: JsonMessage[];
6
6
  end: boolean;
7
+ lastMessageId?: string;
7
8
  }>;
@@ -2,8 +2,8 @@ import { EmbedType } from "discord.js";
2
2
  import { componentsToJson } from "./componentToJson.js";
3
3
  import { urlResolver } from "./urlResolver.js";
4
4
  import { getMentions } from "./getMentions.js";
5
- export async function fetchMessages(channel, options, cdnOptions, authors, mentions, after) {
6
- const originalMessages = await channel.messages.fetch({ limit: 100, cache: false, after: after });
5
+ export async function fetchMessages(channel, options, cdnOptions, authors, mentions, before) {
6
+ const originalMessages = await channel.messages.fetch({ limit: 100, cache: false, before: before });
7
7
  const rawMessages = await Promise.all(originalMessages.map(async (message) => {
8
8
  const attachments = await Promise.all(message.attachments.map(async (attachment) => {
9
9
  return {
@@ -89,9 +89,10 @@ export async function fetchMessages(channel, options, cdnOptions, authors, menti
89
89
  system: message.system,
90
90
  };
91
91
  }));
92
+ const lastMessageId = originalMessages.last()?.id;
92
93
  const messages = rawMessages.filter(m => !(!options.includeEmpty && m.attachments.length == 0 && m.components.length == 0 && m.content == "" && m.embeds.length == 0 && !m.poll));
93
94
  const end = originalMessages.size !== 100;
94
- return { messages, end };
95
+ return { messages, end, lastMessageId };
95
96
  }
96
97
  function formatTimeLeftPoll(timestamp) {
97
98
  const now = new Date();
@@ -1,49 +1,53 @@
1
1
  import https from 'https';
2
2
  import http from 'http';
3
3
  import { CustomWarn } from 'discord-message-transcript-base';
4
+ import { getBase64Limiter } from './limiter.js';
4
5
  export async function imageToBase64(url) {
5
- return new Promise((resolve, reject) => {
6
- const client = url.startsWith('https') ? https : http;
7
- const request = client.get(url, { headers: { "User-Agent": "discord-message-transcript" } }, (response) => {
8
- if (response.statusCode !== 200) {
9
- response.destroy();
10
- CustomWarn(`This is not an issue with the package. Using the original URL as fallback instead of converting to base64.
6
+ const limit = getBase64Limiter();
7
+ return limit(async () => {
8
+ return new Promise((resolve, reject) => {
9
+ const client = url.startsWith('https') ? https : http;
10
+ const request = client.get(url, { headers: { "User-Agent": "discord-message-transcript" } }, (response) => {
11
+ if (response.statusCode !== 200) {
12
+ response.destroy();
13
+ CustomWarn(`This is not an issue with the package. Using the original URL as fallback instead of converting to base64.
11
14
  Failed to fetch image with status code: ${response.statusCode} from ${url}.`);
12
- return resolve(url);
13
- }
14
- const contentType = response.headers['content-type'];
15
- if (!contentType || !contentType.startsWith('image/') || contentType === 'image/gif') {
16
- response.destroy();
17
- return resolve(url);
18
- }
19
- const chunks = [];
20
- response.on('data', (chunk) => {
21
- chunks.push(chunk);
22
- });
23
- response.on('end', () => {
24
- const buffer = Buffer.concat(chunks);
25
- const base64 = buffer.toString('base64');
26
- resolve(`data:${contentType};base64,${base64}`);
27
- });
28
- response.on('error', (err) => {
29
- CustomWarn(`This is not an issue with the package. Using the original URL as fallback instead of converting to base64.
15
+ return resolve(url);
16
+ }
17
+ const contentType = response.headers['content-type'];
18
+ if (!contentType || !contentType.startsWith('image/') || contentType === 'image/gif') {
19
+ response.destroy();
20
+ return resolve(url);
21
+ }
22
+ const chunks = [];
23
+ response.on('data', (chunk) => {
24
+ chunks.push(chunk);
25
+ });
26
+ response.on('end', () => {
27
+ const buffer = Buffer.concat(chunks);
28
+ const base64 = buffer.toString('base64');
29
+ resolve(`data:${contentType};base64,${base64}`);
30
+ });
31
+ response.on('error', (err) => {
32
+ CustomWarn(`This is not an issue with the package. Using the original URL as fallback instead of converting to base64.
30
33
  Stream error while fetching from ${url}.
31
34
  Error: ${err.message}`);
32
- resolve(url);
35
+ resolve(url);
36
+ });
33
37
  });
34
- });
35
- request.on('error', (err) => {
36
- CustomWarn(`This is not an issue with the package. Using the original URL as fallback instead of converting to base64.
38
+ request.on('error', (err) => {
39
+ CustomWarn(`This is not an issue with the package. Using the original URL as fallback instead of converting to base64.
37
40
  Error fetching image from ${url}
38
41
  Error: ${err.message}`);
39
- return resolve(url);
40
- });
41
- request.setTimeout(15000, () => {
42
- request.destroy();
43
- CustomWarn(`This is not an issue with the package. Using the original URL as fallback instead of converting to base64.
42
+ return resolve(url);
43
+ });
44
+ request.setTimeout(15000, () => {
45
+ request.destroy();
46
+ CustomWarn(`This is not an issue with the package. Using the original URL as fallback instead of converting to base64.
44
47
  Request timeout for ${url}.`);
45
- resolve(url);
48
+ resolve(url);
49
+ });
50
+ request.end();
46
51
  });
47
- request.end();
48
52
  });
49
53
  }
@@ -0,0 +1,5 @@
1
+ export declare function getCDNLimiter(): <T>(fn: () => Promise<T>) => Promise<T>;
2
+ export declare function getBase64Limiter(): <T>(fn: () => Promise<T>) => Promise<T>;
3
+ export declare function setCDNConcurrency(n: number): void;
4
+ export declare function setBase64Concurrency(n: number): void;
5
+ export declare function createLimiter(concurrency: number): <T>(fn: () => Promise<T>) => Promise<T>;
@@ -0,0 +1,47 @@
1
+ const globalLimiters = {
2
+ cdn: createLimiter(12),
3
+ base64: createLimiter(6)
4
+ };
5
+ export function getCDNLimiter() {
6
+ return globalLimiters.cdn;
7
+ }
8
+ export function getBase64Limiter() {
9
+ return globalLimiters.base64;
10
+ }
11
+ export function setCDNConcurrency(n) {
12
+ globalLimiters.cdn = createLimiter(n);
13
+ }
14
+ export function setBase64Concurrency(n) {
15
+ globalLimiters.base64 = createLimiter(n);
16
+ }
17
+ export function createLimiter(concurrency) {
18
+ if (concurrency <= 0) {
19
+ throw new Error("Limiter must be greater than 0");
20
+ }
21
+ let active = 0;
22
+ const queue = [];
23
+ const next = () => {
24
+ active = Math.max(0, active - 1);
25
+ if (queue.length > 0 && active < concurrency) {
26
+ const run = queue.shift();
27
+ run?.();
28
+ }
29
+ };
30
+ return function limit(fn) {
31
+ return new Promise((resolve, reject) => {
32
+ const run = () => {
33
+ active++;
34
+ Promise.resolve()
35
+ .then(fn)
36
+ .then(resolve, reject)
37
+ .finally(next);
38
+ };
39
+ if (active < concurrency) {
40
+ run();
41
+ }
42
+ else {
43
+ queue.push(run);
44
+ }
45
+ });
46
+ };
47
+ }
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export { CreateTranscriptOptions, ConvertTranscriptOptions, TranscriptOptions, ReturnType, CDNOptions, MimeType } from "./types/types.js";
2
2
  export { ReturnFormat, LocalDate, TimeZone } from "discord-message-transcript-base";
3
+ export { setBase64Concurrency, setCDNConcurrency } from './core/limiter.js';
3
4
  import { TextBasedChannel } from "discord.js";
4
5
  import { ConvertTranscriptOptions, CreateTranscriptOptions, OutputType, ReturnType } from "./types/types.js";
5
6
  /**
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  export { ReturnType } from "./types/types.js";
2
2
  export { ReturnFormat } from "discord-message-transcript-base";
3
+ export { setBase64Concurrency, setCDNConcurrency } from './core/limiter.js';
3
4
  import { AttachmentBuilder } from "discord.js";
4
5
  import { Json } from "./renderers/json/json.js";
5
6
  import { fetchMessages } from "./core/fetchMessages.js";
@@ -55,9 +56,9 @@ export async function createTranscript(channel, options = {}) {
55
56
  users: new Map(),
56
57
  };
57
58
  while (true) {
58
- const { messages, end } = await fetchMessages(channel, internalOptions, options.cdnOptions ?? null, authors, mentions, lastMessageID);
59
+ const { messages, end, lastMessageId } = await fetchMessages(channel, internalOptions, options.cdnOptions ?? null, authors, mentions, lastMessageID);
59
60
  jsonTranscript.addMessages(messages);
60
- lastMessageID = messages[messages.length - 1]?.id;
61
+ lastMessageID = lastMessageId;
61
62
  if (end || (jsonTranscript.messages.length >= quantity && quantity != 0)) {
62
63
  break;
63
64
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "discord-message-transcript",
3
- "version": "1.2.0-dev-next.0.16",
3
+ "version": "1.2.0-dev-next.0.19",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -48,7 +48,7 @@
48
48
  "typescript": "^5.9.3"
49
49
  },
50
50
  "dependencies": {
51
- "discord-message-transcript-base": "1.2.0-dev-next.0.16"
51
+ "discord-message-transcript-base": "1.2.0-dev-next.0.19"
52
52
  },
53
53
  "peerDependencies": {
54
54
  "discord.js": ">=14.19.0 <15"