diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..7a4def3 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,32 @@ +const tsPlugin = require('@typescript-eslint/eslint-plugin'); +const tsParser = require('@typescript-eslint/parser'); + +module.exports = [ + { + ignores: ['build/', 'dist/', 'node_modules/', 'logs/', 'coverage/'], + }, + { + files: ['src/**/*.ts'], + languageOptions: { + parser: tsParser, + parserOptions: { + ecmaVersion: 2022, + sourceType: 'module', + }, + globals: { + console: 'readonly', + process: 'readonly', + Buffer: 'readonly', + __dirname: 'readonly', + __filename: 'readonly', + }, + }, + plugins: { + '@typescript-eslint': tsPlugin, + }, + rules: { + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }], + }, + }, +]; diff --git a/package.json b/package.json index 5a0e6bd..5c8ca7a 100644 --- a/package.json +++ b/package.json @@ -9,8 +9,8 @@ "train:audio": "tsc && node build/Agent/training/TrainWithAudio.js", "train:youtube": "tsc && node build/Agent/training/youtubeURL.js", "test": "jest", - "lint": "eslint src --ext .ts", - "lint:fix": "eslint src --ext .ts --fix", + "lint": "eslint src", + "lint:fix": "eslint src --fix", "format": "prettier --write .", "format:check": "prettier --check .", "typecheck": "tsc -p tsconfig.json --noEmit", diff --git a/src/utils/index.test.ts b/src/utils/index.test.ts index cbb88db..4110a21 100644 --- a/src/utils/index.test.ts +++ b/src/utils/index.test.ts @@ -4,6 +4,7 @@ import { Instagram_cookiesExist, getIgDailyState, incrementIgDailyCount, + isCookieValid, loadCookies, } from './index'; @@ -68,6 +69,46 @@ describe('utils', () => { expect(backup).toBeTruthy(); }); + test('Instagram_cookiesExist accepts session cookies with expires -1', async () => { + await fs.mkdir(cookiesDir, { recursive: true }); + await fs.writeFile( + cookiesPath, + JSON.stringify([{ name: 'sessionid', value: 'abc', expires: -1 }]), + 'utf-8', + ); + + expect(await Instagram_cookiesExist()).toBe(true); + }); + + test('Instagram_cookiesExist rejects expired timed cookies', async () => { + await fs.mkdir(cookiesDir, { recursive: true }); + const past = Math.floor(Date.now() / 1000) - 3600; + await fs.writeFile( + cookiesPath, + JSON.stringify([{ name: 'sessionid', value: 'abc', expires: past }]), + 'utf-8', + ); + + expect(await Instagram_cookiesExist()).toBe(false); + }); + + test('Instagram_cookiesExist accepts future timed cookies', async () => { + await fs.mkdir(cookiesDir, { recursive: true }); + const future = Math.floor(Date.now() / 1000) + 3600; + await fs.writeFile( + cookiesPath, + JSON.stringify([{ name: 'sessionid', value: 'abc', expires: future }]), + 'utf-8', + ); + + expect(await Instagram_cookiesExist()).toBe(true); + }); + + test('isCookieValid treats Puppeteer session cookies as valid', () => { + const now = Math.floor(Date.now() / 1000); + expect(isCookieValid({ name: 'sessionid', expires: -1 }, now)).toBe(true); + }); + test('loadCookies returns [] for invalid JSON and backs up file', async () => { await fs.mkdir(cookiesDir, { recursive: true }); await fs.writeFile(cookiesPath, '{"bad_json":', 'utf-8'); diff --git a/src/utils/index.ts b/src/utils/index.ts index f032a0e..94eed65 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -27,6 +27,17 @@ async function withFileLock(filePath: string, fn: () => Promise): Promise< } } +type CookieLike = { name: string; expires?: number }; + +/** Puppeteer session cookies use expires -1; timed cookies use a Unix timestamp. */ +export const isCookieValid = (cookie: CookieLike | undefined, nowSec: number): boolean => { + if (!cookie) return false; + const { expires } = cookie; + if (expires === -1 || expires === undefined) return true; + if (typeof expires !== 'number') return false; + return expires > nowSec; +}; + /** Cookie file path for a given account key (legacy default path kept for "default"). */ export function getInstagramCookiesPath(accountKey: string = 'default'): string { const key = accountKey || 'default'; @@ -36,12 +47,6 @@ export function getInstagramCookiesPath(accountKey: string = 'default'): string return `./cookies/Instagramcookies-${key}.json`; } -const isCookieValid = (cookie: { expires?: number } | undefined, now: number): boolean => { - if (!cookie) return false; - if (cookie.expires === undefined || cookie.expires === -1) return true; - return cookie.expires > now; -}; - /** * Checks if valid Instagram cookies exist and are not expired * @returns True if valid cookies exist, false otherwise