Skip to content

Conversation

jackcooper20
Copy link
Collaborator

@jackcooper20 jackcooper20 commented Sep 21, 2025

Summary by CodeRabbit

  • New Features
    • Added endpoints to list Twitter/Telegram account usage and retrieve full Telegram account metadata, with centralized loading/decryption for improved reliability.
  • Refactor
    • Consolidated account retrieval and usage tracking flows; broad string/style normalization without behavior changes.
  • Tests
    • Reworked rotation demos/tests with clearer Redis preflight checks and streamlined execution.
  • Documentation
    • Added a descriptive subtitle to the README.
  • Style
    • Standardized code formatting across the project.
  • Chores
    • Introduced Prettier configuration, a format script, and a dev dependency for automated formatting.

Copy link

coderabbitai bot commented Sep 21, 2025

Walkthrough

Introduces Prettier configuration and formatting updates across the codebase. Adds new account retrieval and usage-reporting methods to Telegram and Twitter account managers. Refactors tests for modular redis checks and rotation flows. Updates package.json with a formatting script and Prettier devDependency. Minor README subtitle and type declaration quoting change.

Changes

Cohort / File(s) Change summary
Prettier config & scripts
\.prettierrc, package.json
Add Prettier config; add format script and devDependency (prettier@^3.6.2).
Readme & types
README.md, src/types/input.d.ts
Add README subtitle; switch module declaration quotes to single quotes.
Global style normalization
src/fetchTelegramMessages.ts, src/index.ts, src/lib/encryption.ts, src/lib/utils/string.ts, src/telegram.ts, src/twitterApi.ts, src/utils/moveEnvToRedis.ts, src/utils/showEnvVariables.ts
String/spacing/indentation normalization; no functional changes.
Base manager formatting
src/services/BaseAccountManager.ts
Whitespace/formatting only; behavior unchanged.
Telegram account manager enhancements
src/services/telegramAccountManager.ts
Add protected fetchAllAccounts(); add public getAllAccountsUsage() and getAllAccountsWithCredentials(); consolidate Redis/decryption loading flow.
Twitter account manager enhancements
src/services/twitterAccountManager.ts
Add protected fetchAllAccounts(); add private getApiKeyUsageLocal(); add protected trackApiKeyUsageLocal(); add public getAllAccountsUsage(); refactor to use new helpers.
Redis utils signature formatting
src/utils/redisUtils.ts
Reformat public function/type signatures (trackApiKeyUsage, getApiKeyUsage, getBatchApiKeyUsage, interface dataType); no logic changes.
Tests refactor
src/tests/rotationDemo.ts, src/tests/telegramRotationDemo.ts, src/tests/testRotation.ts, src/tests/testTelegramRotation.ts
Restructure test orchestration, add dedicated Redis checks, modularize rotation flows; no exported signature changes.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Dev as Developer/Tooling
  participant App as App
  participant TGM as TelegramAccountManager
  participant TwM as TwitterAccountManager
  participant Redis as Redis
  note over App: Account usage listing

  App->>TGM: getAllAccountsUsage()
  TGM->>TGM: fetchAllAccounts()
  TGM->>Redis: GET telegram-accounts
  Redis-->>TGM: Encrypted accounts JSON
  TGM->>TGM: Decrypt & parse
  TGM-->>App: [{accountId, lastUsed?, totalRequests?}, ...]

  App->>TwM: getAllAccountsUsage()
  TwM->>TwM: fetchAllAccounts()
  TwM->>Redis: GET twitter-accounts
  Redis-->>TwM: Encrypted accounts JSON
  TwM->>TwM: Decrypt, build accountId
  TwM->>Redis: HGET usage:{accountId}
  Redis-->>TwM: {total_requests, last_request}
  TwM-->>App: Accounts with usage
  note over TwM,Redis: trackApiKeyUsageLocal() updates usage counters on demand
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • tasin2610

Poem

A hop, a bop, I trim the code just right,
With Prettier whiskers brushing quotes to light.
I fetch the keys, I tally what you use—
Redis burrows whisper all the news.
New trails mapped, rotations neat—
Thump-thump! This bunny’s refactor is sweet. 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title is concise and accurately describes the primary intent of the changeset—adding Prettier configuration and a "format" script to package.json—which matches the widespread formatting edits in the diff. However, the PR also introduces non-formatting code changes (notably new methods in src/services/twitterAccountManager.ts and src/services/telegramAccountManager.ts) that the title does not mention. Because the title correctly captures the main objective but omits these notable functional additions, it is acceptable but could be clearer for reviewers.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/integrate-prettier

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/index.ts (1)

31-35: Don’t reach into a manager’s protected Redis client.

Accessing (telegramAccountManager as any).redisClient breaks encapsulation and risks connection state conflicts. Use a short‑lived client here or expose a proper helper on the manager.

-  const redisClient = (telegramAccountManager as any).redisClient;
-  await redisClient.connect?.();
-  const sessionKey = `telegram_session:${account.accountId}`;
-  let sessionStr = await redisClient.get(sessionKey);
-  const isInteractive = Boolean(process.stdin.isTTY);
+  const sessionKey = `telegram_session:${account.accountId}`;
+  const tmpRedis = createClient({ url: process.env.REDIS_URL });
+  await tmpRedis.connect();
+  let sessionStr = await tmpRedis.get(sessionKey);
+  await tmpRedis.quit();
+  const isInteractive = Boolean(process.stdin.isTTY);

Add import:

 import { StringSession } from 'telegram/sessions';
+import { createClient } from 'redis';
src/utils/redisUtils.ts (1)

1-6: Use env Redis URL; drop unused import.

Hardcoding redis://localhost:6379 breaks non-local deployments. Also crypto isn’t used.

-import crypto from 'crypto';
-import { createClient } from 'redis';
+import 'dotenv/config';
+import { createClient } from 'redis';
@@
-const redisClient = createClient({ url: 'redis://localhost:6379' });
+const redisClient = createClient({ url: process.env.REDIS_URL || 'redis://localhost:6379' });
🧹 Nitpick comments (15)
src/utils/showEnvVariables.ts (1)

41-44: Harden per-field decryption; avoid crashing on non-cipher fields.

When accounts = [{ error: 'Failed to parse' }] and --decrypt is used, decrypt() will throw. Guard or fallback to masked value.

-        Object.entries(acc).forEach(([k, v]) => {
-          const shown = decryptFlag && decryptFn ? decryptFn(v as string) : mask(v as string);
-          console.log(`  ${k}: ${shown}`);
-        });
+        Object.entries(acc).forEach(([k, v]) => {
+          let shown = mask(v as string);
+          if (decryptFlag && decryptFn && k !== 'error') {
+            try {
+              shown = decryptFn(v as string);
+            } catch {
+              shown = '(decrypt failed)';
+            }
+          }
+          console.log(`  ${k}: ${shown}`);
+        });
src/utils/moveEnvToRedis.ts (2)

32-40: Whitelist “other” vars via explicit opt‑in.

Store non‑Twitter/Telegram env only if MOVE_ALL_ENV=1.

-  for (const [key, value] of Object.entries(envVars)) {
+  const allowOther = process.env.MOVE_ALL_ENV === '1';
+  for (const [key, value] of Object.entries(envVars)) {
     if (twitterKeys.includes(key)) {
       twitterAccount[key] = encrypt(value);
     } else if (telegramKeys.includes(key)) {
       telegramAccount[key] = encrypt(value);
-    } else {
-      otherVars[key] = encrypt(value);
+    } else if (allowOther) {
+      otherVars[key] = encrypt(value);
     }
   }

72-72: Close Redis gracefully; avoid force process.exit(0).

Use finally/quit; let the process exit naturally.

-moveEnvToRedis().then(() => process.exit(0));
+moveEnvToRedis()
+  .finally(async () => {
+    if (redisClient.isOpen) await redisClient.quit();
+  });
src/services/BaseAccountManager.ts (2)

21-23: Constructor log may print undefined platform.

this.platform might not be initialized during base constructor. Guard the label.

-    this.redisClient.on('error', (err) => {
-      console.error(`Redis Client Error in ${this.platform}AccountManager:`, err);
-    });
+    this.redisClient.on('error', (err) => {
+      const p = (this as any).platform ?? 'Base';
+      console.error(`Redis Client Error in ${p}AccountManager:`, err);
+    });

46-47: Lock TTL likely too short for API calls.

15s may expire mid‑request, allowing concurrent claim of the same account. Bump TTL and/or make it configurable.

-      const ok = await this.redisClient.set(lockKey, '1', { NX: true, PX: 15000 });
+      const ttlMs = Number(process.env.ACCOUNT_LOCK_TTL_MS ?? '300000'); // default 5m
+      const ok = await this.redisClient.set(lockKey, '1', { NX: true, PX: ttlMs });

Optional: emit heartbeats or extend the lock while in-flight.

src/utils/redisUtils.ts (1)

12-17: Prefer client’s isOpen over separate flag.

Avoid desync between redisConnected and actual client state.

-async function ensureRedisConnected() {
-  if (!redisConnected) {
-    await redisClient.connect();
-    redisConnected = true;
-  }
-}
+async function ensureRedisConnected() {
+  if (!redisClient.isOpen) {
+    await redisClient.connect();
+  }
+}
src/tests/testRotation.ts (2)

12-12: Use a type‑only import or drop the unused interface.

TwitterAccount isn’t referenced as a type anywhere in this file. Remove it or import it as a type‑only to avoid an unnecessary runtime import in certain bundlers.

-import { twitterAccountManager, TwitterAccount } from '../services/twitterAccountManager';
+import { twitterAccountManager } from '../services/twitterAccountManager';
+// or, if you plan to annotate helpers later:
+// import type { TwitterAccount } from '../services/twitterAccountManager';

24-28: This empty-result branch is effectively unreachable.

twitterAccountManager.getAllAccountsUsage() currently throws when no accounts exist (upstream fetchAllAccounts()), and main() already preflights Redis via testRedisConnection(). Consider removing this branch to reduce confusion.

If you prefer returning an empty array instead of throwing, align that in TwitterAccountManager and keep this branch; otherwise remove it.

src/services/telegramAccountManager.ts (2)

60-61: Unify Redis usage reads with the manager’s own client (consistency with Twitter).

This calls a global getApiKeyUsage(...) that manages its own connection, while the rest of this class uses this.redisClient. Prefer a local helper like Twitter’s getApiKeyUsageLocal for connection reuse and fewer sockets.

-        const usage = await getApiKeyUsage({ accountId, platform: 'telegram' });
+        const usage = await this.getApiKeyUsageLocal(accountId);

Add a local helper (outside this range):

private async getApiKeyUsageLocal(
  accountId: string
): Promise<{ total_requests: number; last_request: string | null }> {
  await this.ensureConnected();
  const key = `telegram_accounts:${accountId}`;
  const data = await this.redisClient.hGetAll(key);
  return {
    total_requests: data?.total_requests ? parseInt(data.total_requests, 10) : 0,
    last_request: data?.last_request ?? null
  };
}

You can then drop the getApiKeyUsage import.


19-20: Remove unused usageKeyPrefix or use it when composing keys.

Search shows only the abstract declaration and per-platform assignments (src/services/BaseAccountManager.ts:15, src/services/twitterAccountManager.ts:19, src/services/telegramAccountManager.ts:19) with no other references. Either remove the abstract property and the concrete assignments, or reference this.usageKeyPrefix in BaseAccountManager when building usage/storage keys.

src/tests/testTelegramRotation.ts (3)

13-13: Drop the unused interface or import it as type‑only.

TelegramAccount isn’t referenced as a type; avoid unnecessary runtime import.

-import { telegramAccountManager, TelegramAccount } from '../services/telegramAccountManager';
+import { telegramAccountManager } from '../services/telegramAccountManager';
+// or:
+// import type { TelegramAccount } from '../services/telegramAccountManager';

31-36: Be careful printing identifiers; prefer masked IDs in logs.

Until TelegramAccountManager switches to hashed accountId, logs expose decrypted API IDs. Either mask the value here, or adopt the hashed id change in the manager (recommended).

Example for the first occurrence:

-      console.log(`   Account ${index + 1}: ${account.accountId}`);
+      const id = String(account.accountId);
+      console.log(`   Account ${index + 1}: ${id.slice(0, 8)}…`);

Replicate for other prints in this file.

Also applies to: 64-68, 75-76


145-146: Add an ESM‑safe main guard.

Same note as the Twitter test: extend the guard to work under "type": "module".

Reuse the verification script from the Twitter test comment to decide whether to add an ESM path.

src/services/twitterAccountManager.ts (2)

45-63: Bound or batch the per‑account Redis reads to improve scalability.

The loop awaits each hGetAll sequentially. For N accounts this adds latency. Batch with Promise.all (or a small concurrency limit) after synchronous decrypts.

Example sketch:

const accounts = encryptedAccounts.map((ea, i) => {
  const credentials = { /* decrypt */ };
  const token = credentials.TWITTER_AUTH_TOKEN;
  const accountId = `twitter_${createHash('sha256').update(token).digest('hex').slice(0, 12)}`;
  return { accountId, credentials };
});

const usages = await Promise.all(
  accounts.map(a => this.getApiKeyUsageLocal(a.accountId))
);

return accounts.map((a, i) => ({
  ...a,
  lastUsed: usages[i].last_request || undefined,
  totalRequests: usages[i].total_requests
}));

If Redis load is a concern, use a limiter (e.g., p‑limit) to cap concurrency.


19-20: usageKeyPrefix appears unused in this class.

Either use it to compose keys (${usageKeyPrefix}:${accountId}) or remove to avoid drift with Telegram.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4817c91 and a055f43.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (20)
  • .prettierrc (1 hunks)
  • README.md (1 hunks)
  • package.json (1 hunks)
  • src/fetchTelegramMessages.ts (4 hunks)
  • src/index.ts (5 hunks)
  • src/lib/encryption.ts (1 hunks)
  • src/lib/utils/string.ts (1 hunks)
  • src/services/BaseAccountManager.ts (1 hunks)
  • src/services/telegramAccountManager.ts (1 hunks)
  • src/services/twitterAccountManager.ts (1 hunks)
  • src/telegram.ts (1 hunks)
  • src/tests/rotationDemo.ts (2 hunks)
  • src/tests/telegramRotationDemo.ts (2 hunks)
  • src/tests/testRotation.ts (2 hunks)
  • src/tests/testTelegramRotation.ts (2 hunks)
  • src/twitterApi.ts (6 hunks)
  • src/types/input.d.ts (1 hunks)
  • src/utils/moveEnvToRedis.ts (1 hunks)
  • src/utils/redisUtils.ts (4 hunks)
  • src/utils/showEnvVariables.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (9)
src/utils/showEnvVariables.ts (2)
src/lib/encryption.ts (1)
  • decrypt (27-39)
src/lib/utils/string.ts (1)
  • mask (7-10)
src/tests/telegramRotationDemo.ts (1)
src/services/telegramAccountManager.ts (1)
  • telegramAccountManager (105-105)
src/tests/rotationDemo.ts (1)
src/services/twitterAccountManager.ts (1)
  • twitterAccountManager (117-117)
src/services/twitterAccountManager.ts (1)
src/lib/encryption.ts (1)
  • decrypt (27-39)
src/tests/testTelegramRotation.ts (1)
src/services/telegramAccountManager.ts (1)
  • telegramAccountManager (105-105)
src/services/telegramAccountManager.ts (2)
src/lib/encryption.ts (1)
  • decrypt (27-39)
src/utils/redisUtils.ts (2)
  • getApiKeyUsage (66-99)
  • trackApiKeyUsage (18-52)
src/tests/testRotation.ts (1)
src/services/twitterAccountManager.ts (1)
  • twitterAccountManager (117-117)
src/utils/moveEnvToRedis.ts (1)
src/lib/encryption.ts (1)
  • encrypt (19-25)
src/twitterApi.ts (1)
src/services/twitterAccountManager.ts (2)
  • TwitterAccount (5-14)
  • twitterAccountManager (117-117)
🔇 Additional comments (26)
src/lib/utils/string.ts (1)

8-9: LGTM! Formatting consistency maintained.

The indentation adjustments align with the Prettier configuration while preserving the function's logic and behavior.

README.md (1)

3-3: LGTM! Helpful descriptive subtitle added.

The new subtitle clearly describes the purpose of the ingestion engine, improving documentation clarity for new users and contributors.

src/lib/encryption.ts (3)

10-16: LGTM! Formatting improvements while preserving security.

The formatting changes improve readability while maintaining all security validations and key management logic.


20-25: LGTM! Consistent formatting applied to encryption function.

The formatting updates maintain the secure encryption implementation with proper IV generation and auth tag handling.


28-39: LGTM! Decryption function formatting maintained securely.

All security-critical operations (format validation, scheme checking, auth tag verification) are preserved with improved formatting.

src/tests/telegramRotationDemo.ts (2)

4-4: LGTM! Minor formatting improvement.

The addition of a blank line after the header comment improves readability without affecting functionality.


16-98: LGTM! Consistent formatting applied throughout.

The formatting changes improve code consistency and readability while preserving all demo functionality, error handling, and account rotation simulation logic.

package.json (2)

9-11: LGTM! Essential formatting script added.

The new format script enables consistent code formatting across the project using Prettier.


14-14: Verify Prettier version for security and compatibility.

Ensure the Prettier version is secure and compatible with the project's Node.js version.
Based on the search results, there are security concerns with Prettier:

  1. CVE-2025-8263: A vulnerability affecting Prettier up to version 3.6.2, with a recommendation to upgrade to the latest version beyond 3.6.2
  2. No direct vulnerabilities for 3.6.2: Snyk shows no direct vulnerabilities for this specific version

However, the CVE suggests upgrading beyond 3.6.2 for security. Let me check the latest version:Based on the search results, the latest version of Prettier is 3.6.2, which is the version used in this PR. However, CVE-2025-8263 affects Prettier up to version 3.6.2 and recommends upgrading to the latest version beyond 3.6.2.

The contradiction here is that 3.6.2 is currently the latest version, but the CVE suggests upgrading beyond 3.6.2. This indicates that the CVE has a CVSS base score of 4.3 (Medium severity) and relates to inefficient regular expression complexity in the parseNestedCSS function.

Given that:

  1. No direct vulnerabilities have been found for Prettier in Snyk's vulnerability database
  2. The CVE affects CSS parsing specifically and has medium severity
  3. 3.6.2 is currently the latest available version
  4. This is a development dependency used only for formatting

The risk is acceptable for now, but monitoring for updates is recommended.

.prettierrc (1)

1-10: LGTM! Well-configured Prettier settings.

The configuration provides sensible defaults with appropriate line width (120), consistent quote style (single quotes), and proper formatting rules that align with modern JavaScript/TypeScript best practices.

src/types/input.d.ts (1)

1-1: LGTM! Quote style consistency applied.

The change from double quotes to single quotes aligns with the new Prettier configuration while maintaining the module declaration functionality.

src/fetchTelegramMessages.ts (4)

1-1: LGTM! Import formatting consistency maintained.

The formatting adjustments align with the Prettier configuration while preserving all functionality.


13-13: LGTM! Error message formatting improved.

The quote style change maintains error message clarity and consistency.


41-41: LGTM! Parameter formatting consistency.

The formatting change improves readability without affecting the API call functionality.


47-47: LGTM! String literal formatting consistency.

Both property access checks and console messages now use consistent single-quote formatting as per the Prettier configuration.

Also applies to: 57-57

src/telegram.ts (1)

26-29: Double‑check printing of session secrets.

Even behind PRINT_TG_SESSION === '1', this prints a highly sensitive session string. Confirm this never runs in shared logs/CI; consider writing to a file path explicitly passed via CLI instead.

src/index.ts (1)

37-39: Good guard for non‑interactive runs.

Clear message and early fail if no session in Redis and no TTY.

src/twitterApi.ts (1)

154-156: Good: usage tracking after successful fetch.

This aligns with the locking flow in the account manager.

src/tests/rotationDemo.ts (1)

90-91: Nice: deterministic cleanup.

Disconnecting the manager in finally avoids Redis leaks during demos.

src/services/BaseAccountManager.ts (1)

68-72: Graceful disconnect logic looks good.

Straightforward and idempotent.

src/utils/redisUtils.ts (1)

134-135: LGTM on batch usage mapping.

Null handling and defaulting are correct.

src/tests/testRotation.ts (2)

39-55: Rotation flow looks solid.

Good, straightforward exercise of “select earliest → mark used → wait → repeat,” with a final consistency probe. No issues spotted here.

If you flip TwitterAccountManager.getAllAccountsUsage() to usage‑only (see manager comment), this test remains valid since it doesn’t rely on credentials.

Also applies to: 59-76


142-144: Keep CJS guard — no ESM guard needed

package.json has no "type" field and tsconfig.json sets compilerOptions.module = "commonjs", so require.main === module is sufficient.
Location: src/tests/testRotation.ts (lines 142–144)

src/services/telegramAccountManager.ts (1)

31-41: Parsing and “no accounts” error handling look good.

Clear failures for missing keys and malformed JSON, and a guard to ensure at least one decrypted account.

Also applies to: 74-79

src/tests/testTelegramRotation.ts (1)

43-57: Rotation flow is clean and mirrors the Twitter test.

Straightforward iterations with a timestamp guard and final verification. Looks good.

Also applies to: 72-79

src/services/twitterAccountManager.ts (1)

56-59: Good call on hashing the token for accountId.

Stable, non‑reversible ID that’s safe to log and use as a Redis key component.

Do your Twitter auth tokens rotate in practice? If so, usage history will fragment across IDs. If there’s a stable user identifier available (e.g., account numeric ID), consider hashing that instead.

Comment on lines +56 to +58
// Generate account ID from API ID (for uniqueness)
const accountId = `telegram_${credentials.TELEGRAM_API_ID}`;

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid leaking raw TELEGRAM_API_ID in accountId; hash it like Twitter.

accountId = telegram_${TELEGRAM_API_ID} exposes the decrypted API ID in logs and Redis keys. Use a stable, non‑reversible hash (mirrors Twitter).

-        // Generate account ID from API ID (for uniqueness)
-        const accountId = `telegram_${credentials.TELEGRAM_API_ID}`;
+        // Generate stable, non-reversible account ID (SHA-256, 12 hex chars)
+        const idSource = credentials.TELEGRAM_API_ID;
+        const accountId = `telegram_${createHash('sha256').update(idSource).digest('hex').slice(0, 12)}`;

Also add the import:

-import { BaseAccountManager, BaseAccount } from './BaseAccountManager';
+import { BaseAccountManager, BaseAccount } from './BaseAccountManager';
+import { createHash } from 'crypto';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Generate account ID from API ID (for uniqueness)
const accountId = `telegram_${credentials.TELEGRAM_API_ID}`;
import { BaseAccountManager, BaseAccount } from './BaseAccountManager';
import { createHash } from 'crypto';
// Generate stable, non-reversible account ID (SHA-256, 12 hex chars)
const idSource = credentials.TELEGRAM_API_ID;
const accountId = `telegram_${createHash('sha256').update(idSource).digest('hex').slice(0, 12)}`;
🤖 Prompt for AI Agents
In src/services/telegramAccountManager.ts around lines 56 to 58, the code builds
accountId by concatenating the raw TELEGRAM_API_ID which may leak sensitive
data; instead compute a stable non‑reversible hash (e.g., SHA‑256 hex) of
credentials.TELEGRAM_API_ID and use `telegram_<hash>` as the accountId, and add
the required import from Node's crypto module (e.g., import { createHash } from
'crypto') so the hash computation is deterministic and irreversible.

Comment on lines +111 to +113
async getAllAccountsUsage(): Promise<TwitterAccount[]> {
return await this.fetchAllAccounts();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

getAllAccountsUsage should not return credentials; split the API.

The method name implies usage‑only, but it returns full accounts incl. decrypted credentials. Align with the Telegram manager and reduce accidental exposure.

-  async getAllAccountsUsage(): Promise<TwitterAccount[]> {
-    return await this.fetchAllAccounts();
-  }
+  async getAllAccountsUsage(): Promise<
+    Array<{ accountId: string; lastUsed?: string; totalRequests?: number }>
+  > {
+    const accounts = await this.fetchAllAccounts();
+    return accounts.map(({ accountId, lastUsed, totalRequests }) => ({
+      accountId,
+      lastUsed,
+      totalRequests
+    }));
+  }
+
+  /**
+   * Get all accounts with credentials (full info)
+   */
+  async getAllAccountsWithCredentials(): Promise<TwitterAccount[]> {
+    return await this.fetchAllAccounts();
+  }

The current tests use only usage fields, so this change should be non‑breaking.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async getAllAccountsUsage(): Promise<TwitterAccount[]> {
return await this.fetchAllAccounts();
}
async getAllAccountsUsage(): Promise<
Array<{ accountId: string; lastUsed?: string; totalRequests?: number }>
> {
const accounts = await this.fetchAllAccounts();
return accounts.map(({ accountId, lastUsed, totalRequests }) => ({
accountId,
lastUsed,
totalRequests
}));
}
/**
* Get all accounts with credentials (full info)
*/
async getAllAccountsWithCredentials(): Promise<TwitterAccount[]> {
return await this.fetchAllAccounts();
}
🤖 Prompt for AI Agents
In src/services/twitterAccountManager.ts around lines 111-113,
getAllAccountsUsage currently returns full accounts including credentials by
calling fetchAllAccounts; change it to return usage-only data (no decrypted
credentials) to match the Telegram manager: either add a new
fetchAllAccountsUsage() that returns sanitized TwitterAccountUsage objects or
map the result of fetchAllAccounts() to strip credential fields before
returning, and keep a separate fetchAllAccounts()/getAllAccounts() API that
continues to return full accounts when truly needed; update return types
accordingly and ensure existing tests still pass since they only rely on usage
fields.

Comment on lines 7 to 10
const apiId = 26767039;
const apiHash = "5c9c82971de30b5e71030c27878b8115";
const stringSession = new StringSession(""); // empty = new login
const apiHash = '5c9c82971de30b5e71030c27878b8115';
const stringSession = new StringSession(''); // empty = new login

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove hardcoded Telegram credentials; load from env.

Leaking apiId/apiHash in source is a critical secret exposure risk. Read from environment and validate.

+import 'dotenv/config';
 // Replace these with your values
-const apiId = 26767039;
-const apiHash = '5c9c82971de30b5e71030c27878b8115';
-const stringSession = new StringSession(''); // empty = new login
+const apiId = Number(process.env.TELEGRAM_API_ID);
+const apiHash = process.env.TELEGRAM_API_HASH || '';
+const stringSession = new StringSession(process.env.TELEGRAM_SESSION || ''); // empty = new login
+if (!Number.isFinite(apiId) || !apiHash) {
+  throw new Error('Missing TELEGRAM_API_ID/TELEGRAM_API_HASH in environment');
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const apiId = 26767039;
const apiHash = "5c9c82971de30b5e71030c27878b8115";
const stringSession = new StringSession(""); // empty = new login
const apiHash = '5c9c82971de30b5e71030c27878b8115';
const stringSession = new StringSession(''); // empty = new login
import 'dotenv/config';
const apiId = Number(process.env.TELEGRAM_API_ID);
const apiHash = process.env.TELEGRAM_API_HASH || '';
const stringSession = new StringSession(process.env.TELEGRAM_SESSION || ''); // empty = new login
if (!Number.isFinite(apiId) || !apiHash) {
throw new Error('Missing TELEGRAM_API_ID/TELEGRAM_API_HASH in environment');
}
🤖 Prompt for AI Agents
In src/telegram.ts around lines 7 to 10 the Telegram credentials are hardcoded
(apiId/apiHash/stringSession); replace them with environment-driven values: read
API_ID (parseInt) and API_HASH (string) from process.env (optionally load dotenv
earlier), read STRING_SESSION from env or keep empty fallback, validate that
API_ID and API_HASH are present and API_ID is a valid number, and if not throw
or exit with a clear error so the app never runs with embedded secrets.

Comment on lines +16 to 19
const res = await fetch(url, { method: 'GET', headers });
if (!res.ok) {
console.error("Viewer API request failed:", res.status, res.statusText);
console.error('Viewer API request failed:', res.status, res.statusText);
return null;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add a timeout to external requests (Viewer).

Network calls can hang indefinitely; use AbortController.

-  const res = await fetch(url, { method: 'GET', headers });
+  const controller = new AbortController();
+  const t = setTimeout(() => controller.abort(), 10000);
+  let res: Response;
+  try {
+    res = await fetch(url, { method: 'GET', headers, signal: controller.signal });
+  } finally {
+    clearTimeout(t);
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const res = await fetch(url, { method: 'GET', headers });
if (!res.ok) {
console.error("Viewer API request failed:", res.status, res.statusText);
console.error('Viewer API request failed:', res.status, res.statusText);
return null;
const controller = new AbortController();
const t = setTimeout(() => controller.abort(), 10000);
let res: Response;
try {
res = await fetch(url, { method: 'GET', headers, signal: controller.signal });
} finally {
clearTimeout(t);
}
if (!res.ok) {
console.error('Viewer API request failed:', res.status, res.statusText);
return null;

Comment on lines 105 to 109
const response = await fetch(url, {
method: "POST",
method: 'POST',
headers,
body: JSON.stringify(body),
body: JSON.stringify(body)
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add a timeout to HomeTimeline request.

Same reasoning; prevent hung cron workers.

-  const response = await fetch(url, {
-    method: 'POST',
-    headers,
-    body: JSON.stringify(body)
-  });
+  const controller = new AbortController();
+  const t = setTimeout(() => controller.abort(), 15000);
+  let response: Response;
+  try {
+    response = await fetch(url, {
+      method: 'POST',
+      headers,
+      body: JSON.stringify(body),
+      signal: controller.signal
+    });
+  } finally {
+    clearTimeout(t);
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const response = await fetch(url, {
method: "POST",
method: 'POST',
headers,
body: JSON.stringify(body),
body: JSON.stringify(body)
});
const controller = new AbortController();
const t = setTimeout(() => controller.abort(), 15000);
let response: Response;
try {
response = await fetch(url, {
method: 'POST',
headers,
body: JSON.stringify(body),
signal: controller.signal
});
} finally {
clearTimeout(t);
}
🤖 Prompt for AI Agents
In src/twitterApi.ts around lines 105-109, the fetch POST for the HomeTimeline
request has no timeout and can hang; add an AbortController-based timeout:
create an AbortController, pass controller.signal into fetch options, start a
timer (use a configurable constant like HOME_TIMELINE_TIMEOUT_MS) that calls
controller.abort() after the timeout, and clear the timer once fetch completes;
also handle the abort error path (treat as a timeout error) in the surrounding
try/catch so cron workers fail fast and the request is retried/handled
appropriately.

Comment on lines +16 to +21
const envVars: Record<string, string> = {};
const EXCLUDE = new Set(['ENCRYPTION_KEY']);
for (const [key, value] of Object.entries(process.env)) {
if (!value || EXCLUDE.has(key)) continue;
envVars[key] = value;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid dumping all env vars into Redis by default.

Current logic encrypts and stores every env var except ENCRYPTION_KEY, which can capture unrelated secrets (CI tokens, app creds). Gate “other” vars behind an explicit flag or a whitelist.

-  const envVars: Record<string, string> = {};
+  const envVars: Record<string, string> = {};
   const EXCLUDE = new Set(['ENCRYPTION_KEY']);

(see loop change below)

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/utils/moveEnvToRedis.ts around lines 16 to 21, the current loop copies
every env var except ENCRYPTION_KEY into Redis which risks leaking unrelated
secrets; change the logic to only move a safe whitelist of env keys by default
(e.g., APP_NAME, NODE_ENV, PORT, etc.), and require an explicit opt-in flag
(ENV_REDIS_MOVE_ALLOW_OTHERS or similar) to include all other vars; retain
ENCRYPTION_KEY in the exclusion set, log when non-whitelisted vars are skipped,
and if the opt-in flag is set, then allow copying the remaining vars while still
excluding ENCRYPTION_KEY.

Comment on lines +12 to +14
await redisClient.connect();
await showAccounts(redisClient, decryptFlag, decryptFn);
await redisClient.quit();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Ensure Redis is closed on error (use try/finally).

If showAccounts throws (e.g., bad JSON), quit() is never called. Wrap connect/quit in try/finally.

-  await redisClient.connect();
-  await showAccounts(redisClient, decryptFlag, decryptFn);
-  await redisClient.quit();
+  await redisClient.connect();
+  try {
+    await showAccounts(redisClient, decryptFlag, decryptFn);
+  } finally {
+    await redisClient.quit();
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
await redisClient.connect();
await showAccounts(redisClient, decryptFlag, decryptFn);
await redisClient.quit();
await redisClient.connect();
try {
await showAccounts(redisClient, decryptFlag, decryptFn);
} finally {
await redisClient.quit();
}
🤖 Prompt for AI Agents
In src/utils/showEnvVariables.ts around lines 12 to 14, the code calls await
redisClient.connect(); then await showAccounts(...); then await
redisClient.quit(); but if showAccounts throws, quit() never runs; wrap the
showAccounts call (and any subsequent work) in a try/finally so that
redisClient.quit() is always awaited in the finally block (optionally checking a
connected flag or catching errors from quit), i.e., connect first, then try {
await showAccounts(...) } finally { await redisClient.quit(); } to ensure Redis
is closed on error.

Copy link
Contributor

@tasin2610 tasin2610 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks jack for the pr. ignore code rabbits review. formatting files causes this

@tasin2610 tasin2610 merged commit c456771 into main Sep 21, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants