Skip to content

Conversation

alisonhawk
Copy link
Collaborator

@alisonhawk alisonhawk commented Sep 12, 2025

Description:
This pull request implements API usage tracking for both Telegram and Twitter integrations.

  • Tracks total requests and last request timestamp in Redis for each API key.
  • Stores and displays the account handle for each API key:
    • For Twitter: uses the value from the TWITTER_ID environment variable.
    • For Telegram: uses the account username (with @ prefix) if available, otherwise falls back to the Telegram user ID.
  • Ensures usage stats are printed in a human-readable format.
  • Improves reliability and clarity of account identification for usage reporting.

Summary by CodeRabbit

  • New Features

    • Telemetry for Telegram and Twitter usage per account (totals + last-request), conditional on configuration; startup prints API usage once.
  • Bug Fixes

    • Safer Telegram channel resolution and message retrieval with clearer errors; messages normalized and include channel identifiers.
    • Twitter timeline fetch now validates tokens, formats and deduplicates results, and improves error handling.
  • Refactor

    • Centralized, persistent Redis client and streamlined telemetry storage and retrieval.

… track API usage, and refactor message fetching logic
Copy link

coderabbitai bot commented Sep 12, 2025

Walkthrough

Refactors Telegram and Twitter fetch routines for stricter channel/token validation, message normalization, logging, and conditional telemetry; replaces per-call Redis operations with a lazy singleton client and adds trackApiKeyUsage / getApiKeyUsage; index startup logs API usage and removes prior Redis test code.

Changes

Cohort / File(s) Summary
Redis telemetry utilities
src/utils/redisUtils.ts
Replaced per-call Redis lifecycle with a lazy singleton; added trackApiKeyUsage({ accountId, platform }) and getApiKeyUsage({ accountId, platform }); removed runRedisOperation; stores usage under platform-scoped keys (e.g., twitter_accounts:<id>); non-fatal Redis errors are logged.
Telegram fetch + telemetry
src/fetchTelegramMessages.ts
Renamed exported type to TelegramMessages; fetchTelegramMessages(client, channel) resolves client.getEntity(channel), validates peer type/access (throws on forbidden/non-channel), derives channelId from entity.id, fetches history (limit 10), normalizes messages to { id, content, channelId } (skips empties), logs formatted messages, and conditionally invokes trackApiKeyUsage when API_ID is set (resolves account via getMe).
Twitter timeline overhaul + telemetry
src/twitterApi.ts
Added fetchViewerAccount(); fetchHomeTimeline now enforces BEARER/CSRF_TOKEN/AUTH_TOKEN, posts to HomeTimeline GraphQL with headers/cookies, parses/deduplicates timeline into { id, content, authorId }, and calls trackApiKeyUsage({ accountId, platform: 'twitter' }) when available; main/cron now fetch viewer account and logs API usage.
App entrypoint adjustments
src/index.ts
Moved dotenv/config to top; removed earlier Redis test main(); after startup fetch, resolves account via client.getMe() and logs getApiKeyUsage({ accountId, platform: 'telegram' }); preserves cron scheduling and interactive Telegram login flow adjustments.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant App as App / Caller
  participant TG as TelegramClient
  participant Redis as Redis

  Note over App: fetchTelegramMessages(channel)
  App->>TG: getEntity(channel)
  alt resolution error or forbidden
    TG-->>App: throws (error / ChannelForbidden)
    App-->>App: propagate descriptive error
  else resolved channel entity
    App->>TG: GetHistory(peer=entity, limit=10)
    TG-->>App: messages
    App-->>App: normalize → [{id, content, channelId}] (filter empties)
  end
  opt API_ID present
    App->>TG: getMe()
    TG-->>App: account info
    App->>Redis: trackApiKeyUsage({accountId, platform:'telegram'})
    Redis-->>App: OK
  end
  App-->>App: return messages
Loading
sequenceDiagram
  autonumber
  participant App as App / Caller
  participant TW as Twitter GraphQL
  participant Redis as Redis

  Note over App: fetchHomeTimeline()
  App->>App: validate BEARER, CSRF_TOKEN, AUTH_TOKEN
  App->>TW: POST HomeTimeline (headers, cookies, body)
  TW-->>App: response
  alt error
    App-->>App: throw/log error
  else success
    App-->>App: parse & dedupe → [{id, content, authorId}]
    App->>App: fetchViewerAccount() -> accountId
    App->>Redis: trackApiKeyUsage({accountId, platform:'twitter'})
    Redis-->>App: OK
    App-->>App: return timeline
  end
Loading
sequenceDiagram
  autonumber
  participant Caller as Any Caller
  participant Redis as Redis Client

  Note over Caller: getApiKeyUsage({accountId, platform})
  Caller->>Redis: HGETALL <platform_key:accountId>
  Redis-->>Caller: {total_requests, last_request, account_id?}
  Caller-->>Caller: normalize and return
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • tasin2610

Poem

"I nibble logs beneath the moonlight,
Counting hops of API delight.
Channels found and tweets in rows,
Redis stores each tiny nose.
A rabbit hops — metrics tucked tight! 🐰"

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% 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 accurately and concisely summarizes the primary change: adding API usage tracking for Telegram and Twitter accounts; this matches the changes in redisUtils (track/get functions) and the telemetry integrations in fetchTelegramMessages, twitterApi, and index without extraneous detail.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch add/api-usage

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


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: 2

Caution

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

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

63-85: Make Redis endpoint configurable and add basic socket timeouts

Hardcoded URL impedes deployments; add REDIS_URL and minimal reconnect/timeout settings.

 export async function runRedisOperation(operation: (client: RedisClientType) => Promise<void>): Promise<void> {
-  const client: RedisClientType = createClient({
-    url: 'redis://localhost:6379'
-  });
+  const client: RedisClientType = createClient({
+    url: process.env.REDIS_URL ?? 'redis://localhost:6379',
+    socket: {
+      connectTimeout: 5000,
+      reconnectStrategy: (retries) => Math.min(retries * 50, 2000),
+    },
+  });

Optional next step: memoize a singleton Redis client to avoid per-call connect/quit overhead when telemetry is frequent.

src/twitterApi.ts (1)

104-114: Add timeout and network error handling to external POST

fetch can hang; add an AbortController and clearer errors.

-  const response = await fetch(url, {
-    method: "POST",
-    headers,
-    body: JSON.stringify(body),
-  });
+  const controller = new AbortController();
+  const timeoutMs = Number(process.env.TW_TIMEOUT_MS ?? 15000);
+  const tm = setTimeout(() => controller.abort(), timeoutMs);
+  let response: Response;
+  try {
+    response = await fetch(url, {
+      method: "POST",
+      headers,
+      body: JSON.stringify(body),
+      signal: controller.signal,
+    });
+  } catch (e: any) {
+    if (e?.name === 'AbortError') {
+      throw new Error(`Twitter API request timed out after ${timeoutMs}ms`);
+    }
+    throw e;
+  } finally {
+    clearTimeout(tm);
+  }
🧹 Nitpick comments (9)
src/utils/redisUtils.ts (3)

24-36: Harden parsing and return typing

Avoid implicit radix and NaN leakage when parsing totals.

-  await runRedisOperation(async (client) => {
+  await runRedisOperation(async (client) => {
     const key = `api_usage:${apiKey}`;
     const data = await client.hGetAll(key);
-    result.total_requests = data.total_requests ? parseInt(data.total_requests) : 0;
+    const total = Number(data.total_requests);
+    result.total_requests = Number.isFinite(total) ? total : 0;
     result.last_request = data.last_request ? data.last_request : null;
     if (data.account_handle) {
       result.account_handle = data.account_handle;
     }
   });

41-55: Align printed field name with stored field (account_handle)

Log label says account_id but Redis field is account_handle.

   console.log('Telegram API usage:', {
     total_requests: telegramUsage.total_requests,
     last_request: telegramUsage.last_request || 'No last Telegram request recorded.',
-    account_id: telegramUsage.account_handle || 'No account handle recorded.'
+    account_handle: telegramUsage.account_handle || 'No account handle recorded.'
   });
 ...
   console.log('Twitter API usage:', {
     total_requests: twitterUsage.total_requests,
     last_request: twitterUsage.last_request || 'No last Twitter request recorded.',
-    account_id: twitterUsage.account_handle || 'No account id recorded.'
+    account_handle: twitterUsage.account_handle || 'No account handle recorded.'
   });

58-60: ESM/CJS entrypoint check

require.main fails under ESM. Make the check conditional.

-if (require.main === module) {
-  main();
-}
+// Works in CJS; no-op in ESM where `require` is undefined.
+if (typeof require !== 'undefined' && require.main === module) {
+  main();
+}

If the repo is ESM-only, consider removing this example block entirely.

src/twitterApi.ts (2)

5-28: Unused helper fetchViewerAccount

It’s not referenced. Either wire it to derive TWITTER_ID when absent or remove to reduce surface area.


172-180: Align output label with Redis field

Use account_handle to match stored field name.

   console.log('Twitter API usage:', {
     total_requests: usage.total_requests,
     last_request: usage.last_request,
-    account_id: usage.account_handle
+    account_handle: usage.account_handle
   });
src/index.ts (1)

41-52: Align output label with Redis field

Consistent naming helps when grepping logs.

     const usage = await getApiKeyUsage(process.env.API_ID as string);
     console.log('Telegram API usage:', {
       total_requests: usage.total_requests,
       last_request: usage.last_request,
-      account_id: usage.account_handle
+      account_handle: usage.account_handle
     });
src/fetchTelegramMessages.ts (3)

13-26: Prefer getInputEntity to ensure the correct peer type

messages.GetHistory expects an input peer; resolving to Api.Channel then passing it can be brittle. Convert to an input peer explicitly.


31-36: Use an input peer for GetHistory

Minor adjustment to avoid peer-type mismatches.

-  const messages = await client.invoke(
-    new Api.messages.GetHistory({
-      peer: entity,
-      limit: 10,
-    })
-  );
+  const inputPeer = await client.getInputEntity(entity);
+  const messages = await client.invoke(
+    new Api.messages.GetHistory({
+      peer: inputPeer,
+      limit: 10,
+    })
+  );

53-75: Telemetry key may conflate multiple Telegram accounts

Using only API_ID aggregates usage across any accounts sharing the app id; account_handle will flip-flop. Consider composing the usage key with the account id (e.g., ${API_ID}:${me.id}) so metrics are per-account.

I can provide a small helper to compute a stable key for Telegram and update index.ts to query using the same composite key. Want that patch?

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cecc9f2 and d82070a.

📒 Files selected for processing (4)
  • src/fetchTelegramMessages.ts (1 hunks)
  • src/index.ts (3 hunks)
  • src/twitterApi.ts (4 hunks)
  • src/utils/redisUtils.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
src/fetchTelegramMessages.ts (2)
src/utils/redisUtils.ts (1)
  • trackApiKeyUsage (7-17)
src/telegram.ts (2)
  • console (11-52)
  • msg (44-46)
src/utils/redisUtils.ts (1)
src/testRedis.ts (1)
  • main (11-17)
src/twitterApi.ts (1)
src/utils/redisUtils.ts (2)
  • trackApiKeyUsage (7-17)
  • getApiKeyUsage (24-36)
src/index.ts (1)
src/utils/redisUtils.ts (1)
  • getApiKeyUsage (24-36)

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: 3

🧹 Nitpick comments (4)
src/utils/redisUtils.ts (4)

1-1: Fix crypto import for cross-env compatibility (ESM/CJS-safe).

Prefer named import from node:crypto to avoid default-import pitfalls with Node core modules and bundlers.

-import crypto from 'crypto';
+import { createHash } from 'node:crypto';
@@
-    const hash = crypto.createHash('sha256').update(apiKey).digest('hex').slice(0, 32);
+    const hash = createHash('sha256').update(apiKey).digest('hex').slice(0, 32);
@@
-    const hash = crypto.createHash('sha256').update(apiKey).digest('hex').slice(0, 32);
+    const hash = createHash('sha256').update(apiKey).digest('hex').slice(0, 32);

Also applies to: 15-15, 38-38


8-13: Short-circuit before opening a Redis connection.

Move the empty-key guard outside runRedisOperation to avoid needless connects when apiKey is blank/whitespace.

 export async function trackApiKeyUsage(apiKey: string, accountHandle?: string): Promise<void> {
-  await runRedisOperation(async (client) => {
-    if (!apiKey?.trim()) {
-      console.warn('trackApiKeyUsage: empty apiKey; skipping');
-      return;
-    }
+  if (!apiKey?.trim()) {
+    console.warn('trackApiKeyUsage: empty apiKey; skipping');
+    return;
+  }
+  await runRedisOperation(async (client) => {

76-78: Parameterize Redis URL via env for deploys.

Hardcoding localhost limits flexibility in non-dev environments.

-  const client: RedisClientType = createClient({
-    url: 'redis://localhost:6379'
-  });
+  const client: RedisClientType = createClient({
+    url: process.env.REDIS_URL ?? 'redis://localhost:6379'
+  });

75-97: Consider reusing a singleton Redis client.

Connecting/tearing down per operation is costly under load (usage tracking runs on every API call). A lightweight singleton with health checks will reduce latency and connection churn.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d82070a and 686d5bf.

📒 Files selected for processing (1)
  • src/utils/redisUtils.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/utils/redisUtils.ts (2)
src/index.ts (1)
  • main (57-63)
src/testRedis.ts (1)
  • main (11-17)
🔇 Additional comments (2)
src/utils/redisUtils.ts (2)

18-25: Atomic HINCRBY + HSET via MULTI looks good.

Good job ensuring the increment and metadata update are atomic.


70-72: No change needed — keep require.main guard (CommonJS).
tsconfig.json sets "module": "commonjs", so require.main is defined; leave the guard in src/utils/redisUtils.ts (lines 70–72).

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: 4

♻️ Duplicate comments (4)
src/utils/redisUtils.ts (2)

18-46: Do not key Redis by apiKey/token; key by accountId with a stable prefix (security + correctness).

Current logic compares apiKey against account-id envs and builds keys like api_usage:${apiKey}. This leaks secrets in key names and doesn’t satisfy the required twitter_accounts:{accountId} / telegram_accounts:{accountId} scheme. Make accountId required and derive the key from platform + accountId.

-export async function trackApiKeyUsage(apiKey: string, accountId?: string): Promise<void> {
-  if (!apiKey?.trim()) {
+type Platform = 'twitter' | 'telegram';
+
+export async function trackApiKeyUsage(
+  apiKey: string,
+  accountId: string,
+  platform: Platform
+): Promise<void> {
+  if (!apiKey?.trim()) {
     console.warn('trackApiKeyUsage: empty apiKey; skipping');
     return;
   }
 
   try {
     await ensureRedisConnected();
-    let key: string;
-    if (process.env.TWITTER_ACCOUNT_ID && apiKey === process.env.TWITTER_ACCOUNT_ID) {
-      key = `twitter_accounts:${apiKey}`;
-    } else if (process.env.TELEGRAM_ACCOUNT_ID && apiKey === process.env.TELEGRAM_ACCOUNT_ID) {
-      key = `telegram_accounts:${apiKey}`;
-    } else {
-      key = `api_usage:${apiKey}`;
-    }
+    if (!accountId?.trim()) {
+      console.warn('trackApiKeyUsage: empty accountId; skipping');
+      return;
+    }
+    const key = platform === 'twitter'
+      ? `twitter_accounts:${accountId}`
+      : `telegram_accounts:${accountId}`;
     const now = new Date().toISOString();
     await redisClient
       .multi()
       .hIncrBy(key, 'total_requests', 1)
       .hSet(key, {
         last_request: now,
-        ...(accountId ? { account_id: accountId } : {}),
+        account_id: accountId,
       })
       .exec();

53-78: Usage reads must mirror the same accountId-based keying.

Reading by api_usage:${apiKey} won’t find data after the write fix. Accept platform and accountId, and build keys identically to writes.

-export async function getApiKeyUsage(apiKey: string): Promise<{ total_requests: number; last_request: string | null; account_id?: string }> {
-  let result: { total_requests: number; last_request: string | null; account_id?: string } = { total_requests: 0, last_request: null };
-  if (!apiKey?.trim()) {
+export async function getApiKeyUsage(
+  platform: 'twitter' | 'telegram',
+  accountId: string
+): Promise<{ total_requests: number; last_request: string | null; account_id?: string }> {
+  const result: { total_requests: number; last_request: string | null; account_id?: string } = { total_requests: 0, last_request: null };
+  if (!accountId?.trim()) {
     return result;
   }
 
   try {
     await ensureRedisConnected();
-    let key: string;
-    if (process.env.TWITTER_ACCOUNT_ID && apiKey === process.env.TWITTER_ACCOUNT_ID) {
-      key = `twitter_accounts:${apiKey}`;
-    } else if (process.env.TELEGRAM_ACCOUNT_ID && apiKey === process.env.TELEGRAM_ACCOUNT_ID) {
-      key = `telegram_accounts:${apiKey}`;
-    } else {
-      key = `api_usage:${apiKey}`;
-    }
+    const key = platform === 'twitter'
+      ? `twitter_accounts:${accountId}`
+      : `telegram_accounts:${accountId}`;
     const data = await redisClient.hGetAll(key);
     result.total_requests = data.total_requests ? parseInt(data.total_requests) : 0;
     result.last_request = data.last_request ? data.last_request : null;
     if (data.account_id) {
       result.account_id = data.account_id;
     }
   } catch (err) {
     console.error('Redis operation failed:', err);
   }
   return result;
 }
src/twitterApi.ts (2)

156-163: Track usage with numeric accountId and platform, not handle.

Use Viewer API to resolve userId (or TWITTER_ACCOUNT_ID fallback) and pass platform.

-  if (AUTH_TOKEN) {
-    // Use TWITTER_ID from environment variable for account handle (no '@' prefix)
-    const accountHandle = process.env.TWITTER_ID ? process.env.TWITTER_ID : "unknown";
-    console.log("Authenticated account:", accountHandle);
-
-    await trackApiKeyUsage(AUTH_TOKEN, accountHandle);
-  }
+  if (AUTH_TOKEN) {
+    const viewer = await fetchViewerAccount();
+    const accountId = viewer?.userId ?? process.env.TWITTER_ACCOUNT_ID;
+    if (accountId) {
+      console.log("Authenticated account id:", accountId);
+      await trackApiKeyUsage(AUTH_TOKEN, accountId, 'twitter');
+    } else {
+      console.warn("Cannot determine Twitter account id; skipping usage tracking.");
+    }
+  }

1-6: Load env before reading AUTH_TOKEN.

AUTH_TOKEN is read before dotenv.config(), so it may be undefined.

-import dotenv from 'dotenv';
+import dotenv from 'dotenv';
+dotenv.config();
 import cron from 'node-cron';
 import { getApiKeyUsage } from './utils/redisUtils';
 import { trackApiKeyUsage } from './utils/redisUtils';
-const AUTH_TOKEN = process.env.AUTH_TOKEN;
-dotenv.config();
+const AUTH_TOKEN = process.env.AUTH_TOKEN;
🧹 Nitpick comments (4)
src/utils/redisUtils.ts (2)

1-1: Remove unused import.

crypto isn’t used.

-import crypto from 'crypto';
+// import crypto from 'crypto'; // unused

8-16: Reset connection flag on disconnect.

If Redis disconnects, redisConnected remains true; subsequent calls won’t reconnect.

 redisClient.on('error', (err) => {
   console.error('Redis Client Error', err);
 });
+redisClient.on('end', () => {
+  redisConnected = false;
+});
src/fetchTelegramMessages.ts (2)

47-50: Avoid noisy logs in hot path.

Guard per-message logs behind an env flag.

-      console.log(formatted);
+      if (process.env.DEBUG_TG === '1') console.log(formatted);

10-12: Clarify error message.

This function accepts channel; refer to the parameter, not the env.

-    throw new Error("TG_CHANNEL environment variable is not set.");
+    throw new Error("Channel is required.");
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 897070f and ea34ff9.

📒 Files selected for processing (4)
  • src/fetchTelegramMessages.ts (1 hunks)
  • src/index.ts (3 hunks)
  • src/twitterApi.ts (4 hunks)
  • src/utils/redisUtils.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
src/index.ts (1)
src/utils/redisUtils.ts (1)
  • getApiKeyUsage (53-79)
src/fetchTelegramMessages.ts (2)
src/utils/redisUtils.ts (1)
  • trackApiKeyUsage (18-46)
src/telegram.ts (2)
  • console (11-52)
  • msg (44-46)
src/twitterApi.ts (1)
src/utils/redisUtils.ts (2)
  • trackApiKeyUsage (18-46)
  • getApiKeyUsage (53-79)
src/utils/redisUtils.ts (1)
src/testRedis.ts (2)
  • main (11-17)
  • err (7-9)

Comment on lines 83 to 107
// Example: Use environment variables for API keys
async function main() {
const telegramAccountId = process.env.TELEGRAM_ACCOUNT_ID;
if (telegramAccountId?.trim()) {
const telegramUsage = await getApiKeyUsage(telegramAccountId);
console.log('Telegram API usage:', {
total_requests: telegramUsage.total_requests,
last_request: telegramUsage.last_request || 'No last Telegram request recorded.',
account_id: telegramUsage.account_id || 'No account id recorded.'
});
} else {
console.log('Telegram API usage: TELEGRAM_ACCOUNT_ID not set.');
}

const twitterAccountId = process.env.TWITTER_ACCOUNT_ID;
if (twitterAccountId?.trim()) {
const twitterUsage = await getApiKeyUsage(twitterAccountId);
console.log('Twitter API usage:', {
total_requests: twitterUsage.total_requests,
last_request: twitterUsage.last_request || 'No last Twitter request recorded.',
account_id: twitterUsage.account_id || 'No account id recorded.'
});
} else {
console.log('Twitter API usage: TWITTER_ACCOUNT_ID not set.');
}
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Update the example to the new API (platform + accountId).

Ensure the demo reads usage by account id, not token/api id.

-  if (telegramAccountId?.trim()) {
-    const telegramUsage = await getApiKeyUsage(telegramAccountId);
+  if (telegramAccountId?.trim()) {
+    const telegramUsage = await getApiKeyUsage('telegram', telegramAccountId);
     console.log('Telegram API usage:', {
       total_requests: telegramUsage.total_requests,
       last_request: telegramUsage.last_request || 'No last Telegram request recorded.',
       account_id: telegramUsage.account_id || 'No account id recorded.'
     });
   } else {
     console.log('Telegram API usage: TELEGRAM_ACCOUNT_ID not set.');
   }
 
   const twitterAccountId = process.env.TWITTER_ACCOUNT_ID;
   if (twitterAccountId?.trim()) {
-    const twitterUsage = await getApiKeyUsage(twitterAccountId);
+    const twitterUsage = await getApiKeyUsage('twitter', twitterAccountId);
     console.log('Twitter API usage:', {
       total_requests: twitterUsage.total_requests,
       last_request: twitterUsage.last_request || 'No last Twitter request recorded.',
       account_id: twitterUsage.account_id || 'No account id recorded.'
     });

To catch any remaining old call sites, run:


🏁 Script executed:

#!/bin/bash
# Find and review all usages to update signatures.
rg -nP '\b(trackApiKeyUsage|getApiKeyUsage)\s*\(' -C2

Length of output: 2785


Change getApiKeyUsage/trackApiKeyUsage to (platform, accountId) and update all callers

Multiple call sites still pass tokens/API IDs. Update the functions and callers to accept a platform string plus the accountId; then re-run a search to confirm no remaining old calls.

  • Change signatures in src/utils/redisUtils.ts:

    • trackApiKeyUsage(apiKey: string, accountId?: string) -> trackApiKeyUsage(platform: string, accountId?: string)
    • getApiKeyUsage(apiKey: string) -> getApiKeyUsage(platform: string, accountId: string)
  • Update the example in src/utils/redisUtils.ts (main):

-  if (telegramAccountId?.trim()) {
-    const telegramUsage = await getApiKeyUsage(telegramAccountId);
+  if (telegramAccountId?.trim()) {
+    const telegramUsage = await getApiKeyUsage('telegram', telegramAccountId);
     console.log('Telegram API usage:', {
       total_requests: telegramUsage.total_requests,
       last_request: telegramUsage.last_request || 'No last Telegram request recorded.',
       account_id: telegramUsage.account_id || 'No account id recorded.'
     });
   } else {
     console.log('Telegram API usage: TELEGRAM_ACCOUNT_ID not set.');
   }
 
   const twitterAccountId = process.env.TWITTER_ACCOUNT_ID;
   if (twitterAccountId?.trim()) {
-    const twitterUsage = await getApiKeyUsage(twitterAccountId);
+    const twitterUsage = await getApiKeyUsage('twitter', twitterAccountId);
     console.log('Twitter API usage:', {
       total_requests: twitterUsage.total_requests,
       last_request: twitterUsage.last_request || 'No last Twitter request recorded.',
       account_id: twitterUsage.account_id || 'No account id recorded.'
     });
  • Update callers found by your search:

    • src/fetchTelegramMessages.ts:68 — await trackApiKeyUsage(apiId, accountId) -> await trackApiKeyUsage('telegram', accountId)
    • src/twitterApi.ts:162 — await trackApiKeyUsage(AUTH_TOKEN, accountHandle) -> await trackApiKeyUsage('twitter', accountHandle)
    • src/twitterApi.ts:174 — const usage = await getApiKeyUsage(process.env.AUTH_TOKEN as string) -> const usage = await getApiKeyUsage('twitter', accountHandle)
    • src/index.ts:46 — const usage = await getApiKeyUsage(process.env.API_ID as string) -> const usage = await getApiKeyUsage('telegram', process.env.TELEGRAM_ACCOUNT_ID as string)
  • After edits run: rg -nP '\b(trackApiKeyUsage|getApiKeyUsage)\s*(' -C2 to verify no remaining old-signature calls.

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

🤖 Prompt for AI Agents
In src/utils/redisUtils.ts around lines 83 to 107, update the function
signatures and all callers to use (platform, accountId) instead of the old
apiKey-only parameters: change trackApiKeyUsage(apiKey: string, accountId?:
string) to trackApiKeyUsage(platform: string, accountId?: string) and change
getApiKeyUsage(apiKey: string) to getApiKeyUsage(platform: string, accountId:
string); update the sample main() in this file to pass platform strings
('telegram'/'twitter') and accountId accordingly; update caller sites as listed
in the review (src/fetchTelegramMessages.ts:68 → await
trackApiKeyUsage('telegram', accountId), src/twitterApi.ts:162 → await
trackApiKeyUsage('twitter', accountHandle), src/twitterApi.ts:174 → const usage
= await getApiKeyUsage('twitter', accountHandle), src/index.ts:46 → const usage
= await getApiKeyUsage('telegram', process.env.TELEGRAM_ACCOUNT_ID as string));
then run rg -nP '\b(trackApiKeyUsage|getApiKeyUsage)\s*\(' -C2 to confirm there
are no remaining calls using the old signatures.

…yUsage functions to accept accountId and platform parameters for improved clarity and consistency
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: 4

♻️ Duplicate comments (1)
src/twitterApi.ts (1)

156-161: Track usage with the same accountId you later read with (viewer.userId → fallback TWITTER_ACCOUNT_ID).

Current code tracks using TWITTER_ID but reads using viewer/TWITTER_ACCOUNT_ID, producing mismatched keys and empty stats.

-// Track API usage after successful fetch
-if (process.env.TWITTER_ID) {
-  const accountId = process.env.TWITTER_ID;
-  console.log("Authenticated account:", accountId);
-  await trackApiKeyUsage({ accountId, platform: 'twitter' });
-}
+// Track API usage after successful fetch
+const viewer = await fetchViewerAccount();
+const accountId = viewer?.userId ?? process.env.TWITTER_ACCOUNT_ID;
+if (!accountId) {
+  console.warn('trackApiKeyUsage skipped: missing TWITTER_ACCOUNT_ID and Viewer lookup failed.');
+} else {
+  await trackApiKeyUsage({ accountId, platform: 'twitter' });
+}
🧹 Nitpick comments (8)
src/utils/redisUtils.ts (3)

54-61: Shared key builder to avoid drift between read/write paths.

Both functions duplicate platform→key mapping. Extract a helper to keep them in lockstep.

Add above the exports (outside selected lines):

function usageKey(platform: 'telegram'|'twitter', accountId: string): string {
  return `${platform === 'twitter' ? 'twitter_accounts' : 'telegram_accounts'}:${accountId}`;
}

Then replace the if/else blocks in both functions with:

const key = usageKey(platform, accountId);

73-75: Parse numbers with explicit radix.

Be explicit to avoid edge cases.

-result.total_requests = data.total_requests ? parseInt(data.total_requests) : 0;
+result.total_requests = data.total_requests ? parseInt(data.total_requests, 10) : 0;

1-1: Remove unused import.

crypto isn’t used.

-import crypto from 'crypto';
src/twitterApi.ts (3)

5-5: Remove unused AUTH_TOKEN constant (and avoid reading env before dotenv.config).

This variable is never used and is read before dotenv.config().

-const AUTH_TOKEN = process.env.AUTH_TOKEN;

191-196: Reduce noisy logs in scheduled job (print counts, not full payload).

Large payloads in logs are hard to scan and can leak data.

-  const timeline = await fetchHomeTimeline();
-  console.log('Fetched timeline:', timeline);
+  const timeline = await fetchHomeTimeline();
+  console.log('Fetched timeline:', { count: timeline.length, firstId: timeline[0]?.id });

106-116: Add request timeouts to external calls.

Prevent hangs on slow endpoints with AbortController.

Example for both fetches:

+  const ac = new AbortController();
+  const t = setTimeout(() => ac.abort(), 20000);
   const response = await fetch(url, {
     method: "POST",
     headers,
     body: JSON.stringify(body),
+    signal: ac.signal,
   });
+  clearTimeout(t);

Repeat similarly in fetchViewerAccount().

Also applies to: 17-21

src/fetchTelegramMessages.ts (2)

10-12: Fix error message to reflect function input, not env.

This function takes a channel param; mentioning TG_CHANNEL is misleading.

-  if (!channel) {
-    throw new Error("TG_CHANNEL environment variable is not set.");
-  }
+  if (!channel) {
+    throw new Error("channel is required.");
+  }

49-50: Reduce per-message console logs or gate behind DEBUG.

Printing every message is noisy and may leak content.

-      console.log(formatted);
+      if (process.env.DEBUG === '1') console.log(formatted);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ea34ff9 and 364040e.

📒 Files selected for processing (4)
  • src/fetchTelegramMessages.ts (1 hunks)
  • src/index.ts (3 hunks)
  • src/twitterApi.ts (4 hunks)
  • src/utils/redisUtils.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/index.ts
🧰 Additional context used
🧬 Code graph analysis (3)
src/twitterApi.ts (1)
src/utils/redisUtils.ts (2)
  • trackApiKeyUsage (18-46)
  • getApiKeyUsage (54-82)
src/utils/redisUtils.ts (1)
src/testRedis.ts (1)
  • main (11-17)
src/fetchTelegramMessages.ts (2)
src/utils/redisUtils.ts (1)
  • trackApiKeyUsage (18-46)
src/telegram.ts (2)
  • console (11-52)
  • msg (44-46)

Comment on lines +13 to +14
const apiId = process.env.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

Telemetry must be best-effort: don’t gate on API_ID and don’t throw if getMe() fails.

Tracking should never break message fetching. Also, gating on API_ID is unrelated.

-  const apiId = process.env.API_ID;
@@
-  // Track API usage after successful fetch
-  if (apiId) {
-    let accountId: string;
-    try {
-      const me = await client.getMe();
-      if (me && me.id) {
-        accountId = String(me.id);
-      } else {
-        throw new Error('Unable to determine Telegram account ID');
-      }
-    } catch (e) {
-      throw new Error('Unable to determine Telegram account ID');
-    }
-    await trackApiKeyUsage({ accountId, platform: 'telegram' });
-  }
+  // Track API usage after successful fetch (best-effort)
+  try {
+    const me = await client.getMe();
+    const accountId = me?.id ? String(me.id) : '';
+    if (accountId) {
+      await trackApiKeyUsage({ accountId, platform: 'telegram' });
+    } else {
+      console.warn('trackApiKeyUsage skipped: unable to determine Telegram account ID');
+    }
+  } catch (e) {
+    console.warn('trackApiKeyUsage skipped: unable to determine Telegram account ID', e);
+  }

Also applies to: 55-69

🤖 Prompt for AI Agents
In src/fetchTelegramMessages.ts around lines 13-14 (and similarly 55-69), the
code currently gates behavior on process.env.API_ID and allows getMe() failures
to throw, which can break message fetching; change it so telemetry is
best-effort: do not early-return or throw if API_ID is missing, and do not let
getMe() errors propagate. Specifically, stop gating core message fetching on
API_ID (treat API_ID as optional), wrap any telemetry/getMe() calls in a
try/catch that logs the error (or swallows it) without throwing, and proceed
with fetching messages regardless of telemetry success; ensure any telemetry
initialization is conditional and non-blocking so missing API_ID or getMe()
failures do not affect the main flow.

Comment on lines 172 to 180
const viewer = await fetchViewerAccount();
const accountId = viewer?.userId ?? process.env.TWITTER_ACCOUNT_ID;
if (!accountId) throw new Error('Missing TWITTER_ACCOUNT_ID and Viewer lookup failed.');
const usage = await getApiKeyUsage(accountId, 'twitter');
console.log('Twitter API usage:', {
total_requests: usage.total_requests,
last_request: usage.last_request,
account_id: usage.account_id
});
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

LGTM on usage read flow; ensure env var naming is consistent repo-wide.

This aligns with the tracking change above. Make sure TWITTER_ID is not used elsewhere.

Run:


🏁 Script executed:

#!/bin/bash
# Find lingering TWITTER_ID usages and old telemetry calls.
rg -nP '\bTWITTER_ID\b|trackApiKeyUsage\s*\(|getApiKeyUsage\s*\(' -C2

Length of output: 2371


Unify Twitter env var: replace TWITTER_ID with TWITTER_ACCOUNT_ID

LGTM on the usage-read flow; src/twitterApi.ts still uses process.env.TWITTER_ID (lines 157–161) while later code reads process.env.TWITTER_ACCOUNT_ID (lines 173–176). Replace the TWITTER_ID branch and ensure trackApiKeyUsage/getApiKeyUsage use the same accountId source/format.

🤖 Prompt for AI Agents
In src/twitterApi.ts around lines 157–176, the code inconsistently reads
process.env.TWITTER_ID in one place and process.env.TWITTER_ACCOUNT_ID later;
replace the TWITTER_ID branch with TWITTER_ACCOUNT_ID, derive accountId once
(e.g., const accountId = viewer?.userId ?? process.env.TWITTER_ACCOUNT_ID),
ensure accountId is a string/normalized format, and update calls to
trackApiKeyUsage/getApiKeyUsage to accept and use that same accountId
variable/format so both tracking and usage-read use the identical source.

Comment on lines +5 to +17
const redisClient = createClient({ url: 'redis://localhost:6379' });
let redisConnected = false;

client.on('error', (err) => {
console.error('Redis Client Error', err);
});
redisClient.on('error', (err) => {
console.error('Redis Client Error', err);
});

async function ensureRedisConnected() {
if (!redisConnected) {
await redisClient.connect();
redisConnected = true;
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Make Redis URL configurable and fix connection check (use client.isOpen instead of a manual flag).

Hardcoding localhost breaks deployments and the boolean flag won’t recover after disconnects. Prefer REDIS_URL and client.isOpen.

Apply:

-import { createClient } from 'redis';
+import { createClient } from 'redis';

-// Singleton Redis client
-const redisClient = createClient({ url: 'redis://localhost:6379' });
-let redisConnected = false;
+// Singleton Redis client
+const redisClient = createClient({ url: process.env.REDIS_URL ?? 'redis://localhost:6379' });

 async function ensureRedisConnected() {
-  if (!redisConnected) {
-    await redisClient.connect();
-    redisConnected = true;
-  }
+  if (!redisClient.isOpen) {
+    await redisClient.connect();
+  }
 }
📝 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 redisClient = createClient({ url: 'redis://localhost:6379' });
let redisConnected = false;
client.on('error', (err) => {
console.error('Redis Client Error', err);
});
redisClient.on('error', (err) => {
console.error('Redis Client Error', err);
});
async function ensureRedisConnected() {
if (!redisConnected) {
await redisClient.connect();
redisConnected = true;
}
}
const redisClient = createClient({ url: process.env.REDIS_URL ?? 'redis://localhost:6379' });
redisClient.on('error', (err) => {
console.error('Redis Client Error', err);
});
async function ensureRedisConnected() {
if (!redisClient.isOpen) {
await redisClient.connect();
}
}
🤖 Prompt for AI Agents
In src/utils/redisUtils.ts around lines 5-17, the Redis URL is hardcoded and the
connection state uses a manual boolean that won't reflect real disconnects;
change createClient to read the URL from process.env.REDIS_URL (with an optional
fallback if you want), remove the redisConnected flag, and replace checks with
redisClient.isOpen (i.e., only call await redisClient.connect() when
!redisClient.isOpen). Keep the existing error handler and optionally add
listeners for 'end'/'reconnecting' for visibility, but do not rely on a manual
boolean to determine connection state.

…eyUsage functions to accept an object with accountId and platform parameters for improved clarity and error handling
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: 1

Caution

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

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

1-200: Unify Twitter env var usage — replace TWITTER_ID with TWITTER_ACCOUNT_ID (or add a backward-compatible fallback)
Found legacy TWITTER_ID in src/twitterApi.ts (lines 157–160) while the main flow uses TWITTER_ACCOUNT_ID (lines 173–174). Replace the legacy usage or use: const accountId = process.env.TWITTER_ACCOUNT_ID ?? process.env.TWITTER_ID. No TELEGRAM_ID occurrences found. Telemetry helpers are defined in src/utils/redisUtils.ts and are used in src/twitterApi.ts, src/fetchTelegramMessages.ts, and src/index.ts.

♻️ Duplicate comments (3)
src/twitterApi.ts (2)

156-160: Remove stray debug log.

Avoid logging user/account IDs in production.

-    console.log("Authenticated account:", accountId);

156-161: Unify on TWITTER_ACCOUNT_ID (avoid TWITTER_ID drift).

Tracking uses TWITTER_ID while reading usage later uses TWITTER_ACCOUNT_ID, creating split keys.

-  if (process.env.TWITTER_ID) {
-    const accountId = process.env.TWITTER_ID;
-    console.log("Authenticated account:", accountId);
-    await trackApiKeyUsage({ accountId, platform: 'twitter' });
-  }
+  if (process.env.TWITTER_ACCOUNT_ID) {
+    const accountId = process.env.TWITTER_ACCOUNT_ID;
+    await trackApiKeyUsage({ accountId, platform: 'twitter' });
+  }

If you want to support legacy TWITTER_ID, read it only as a fallback and emit a deprecation warning.

src/utils/redisUtils.ts (1)

5-16: Make Redis connection robust: REDIS_URL + isOpen (drop manual flag).

Hardcoded URL and a boolean flag are brittle across reconnects and deployments.

-// Singleton Redis client
-const redisClient = createClient({ url: 'redis://localhost:6379' });
-let redisConnected = false;
+// Singleton Redis client
+const redisClient = createClient({ url: process.env.REDIS_URL ?? 'redis://localhost:6379' });
 
 redisClient.on('error', (err) => {
   console.error('Redis Client Error', err);
 });
 
 async function ensureRedisConnected() {
-  if (!redisConnected) {
-    await redisClient.connect();
-    redisConnected = true;
-  }
+  if (!redisClient.isOpen) {
+    await redisClient.connect();
+  }
 }
🧹 Nitpick comments (7)
src/utils/redisUtils.ts (5)

1-1: Remove unused import.

crypto isn’t used anymore.

-import crypto from 'crypto';

18-22: Input contract: decide between “fail-fast” vs “best-effort” for empty accountId.

Maintainer comments suggest throwing when inputs are missing; current code logs and returns.

-  if (!accountId?.trim()) {
-    console.warn('trackApiKeyUsage: empty accountId; skipping');
-    return;
-  }
+  if (!accountId?.trim()) {
+    throw new Error('trackApiKeyUsage: accountId is required');
+  }

If you want to keep ingestion non-blocking, throw at call sites during config/boot and keep this function best-effort.


35-42: Optional: consider key TTL to bound storage.

If usage isn’t meant to live forever, set an expiry (e.g., 90d) in the same MULTI.

     await redisClient
       .multi()
       .hIncrBy(key, 'total_requests', 1)
       .hSet(key, {
         last_request: now,
         account_id: accountId,
       })
+      .expire(key, 60 * 60 * 24 * 90) // 90 days
       .exec();

55-58: Name the interface clearly.

dataType is vague; prefer AccountUsageQuery.

-interface dataType {
+interface AccountUsageQuery {
   accountId: string,
   platform: 'telegram' | 'twitter'
 }

And update the function signature accordingly.


60-88: Parsing nit: specify radix for parseInt.

Avoids accidental octal parsing.

-    result.total_requests = data.total_requests ? parseInt(data.total_requests) : 0;
+    result.total_requests = data.total_requests ? parseInt(data.total_requests, 10) : 0;
src/twitterApi.ts (2)

191-199: Tone down scheduled logging.

Dumping full timelines every 5 minutes will spam logs and may leak content.

-    const timeline = await fetchHomeTimeline();
-    console.log('Fetched timeline:', timeline);
+    const timeline = await fetchHomeTimeline();
+    console.log('Fetched timeline batch size:', timeline.length);
+    // Optionally log first N ids for tracing:
+    // console.debug('Tweet IDs:', timeline.slice(0, 5).map(t => t.id));

39-55: Minor: cookie formatting.

Consider adding a space after ; for consistency (auth_token=...; ct0=...). No functional change.

-  const cookie = `auth_token=${process.env.AUTH_TOKEN};ct0=${process.env.CSRF_TOKEN}`;
+  const cookie = `auth_token=${process.env.AUTH_TOKEN}; ct0=${process.env.CSRF_TOKEN}`;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 364040e and 9e3cd4e.

📒 Files selected for processing (3)
  • src/index.ts (3 hunks)
  • src/twitterApi.ts (4 hunks)
  • src/utils/redisUtils.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/index.ts
🧰 Additional context used
🧬 Code graph analysis (2)
src/utils/redisUtils.ts (1)
src/testRedis.ts (2)
  • main (11-17)
  • err (7-9)
src/twitterApi.ts (1)
src/utils/redisUtils.ts (2)
  • trackApiKeyUsage (18-46)
  • getApiKeyUsage (60-88)
🔇 Additional comments (2)
src/utils/redisUtils.ts (1)

24-33: Good: strict platform validation.

Failing on unknown platforms prevents silent writes to wrong keys.

src/twitterApi.ts (1)

172-180: LGTM on reading usage by accountId + platform.

Matches the Redis keying scheme and avoids tokens.

Comment on lines 1 to 6
import dotenv from 'dotenv';
import cron from 'node-cron';
import { getApiKeyUsage } from './utils/redisUtils';
import { trackApiKeyUsage } from './utils/redisUtils';
const AUTH_TOKEN = process.env.AUTH_TOKEN;
dotenv.config();
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Load .env before reading env vars; remove unused constant.

AUTH_TOKEN is read before dotenv.config() and never used.

-import dotenv from 'dotenv';
+import dotenv from 'dotenv';
+dotenv.config();
 import cron from 'node-cron';
 import { getApiKeyUsage } from './utils/redisUtils';
 import { trackApiKeyUsage } from './utils/redisUtils';
-const AUTH_TOKEN = process.env.AUTH_TOKEN;
-dotenv.config();
📝 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
import dotenv from 'dotenv';
import cron from 'node-cron';
import { getApiKeyUsage } from './utils/redisUtils';
import { trackApiKeyUsage } from './utils/redisUtils';
const AUTH_TOKEN = process.env.AUTH_TOKEN;
dotenv.config();
import dotenv from 'dotenv';
dotenv.config();
import cron from 'node-cron';
import { getApiKeyUsage } from './utils/redisUtils';
import { trackApiKeyUsage } from './utils/redisUtils';
🤖 Prompt for AI Agents
In src/twitterApi.ts around lines 1 to 6, dotenv.config() is called after
reading AUTH_TOKEN and AUTH_TOKEN is never used; move dotenv.config() to the top
of the file before any process.env access, remove the unused AUTH_TOKEN
constant, and (optionally) consolidate the two redisUtils imports into a single
import statement to keep imports tidy.

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