agent-twitter-summary
Version:
A twitter client for agents
143 lines (125 loc) • 3.96 kB
text/typescript
import { ProxyAgent } from 'undici';
import { Scraper } from './scraper';
import fs from 'fs';
export interface ScraperTestOptions {
/**
* Authentication method preference for the scraper.
* - 'api': Use Twitter API keys and tokens.
* - 'cookies': Resume session using cookies.
* - 'password': Use username/password for login.
* - 'anonymous': No authentication.
*/
authMethod: 'api' | 'cookies' | 'password' | 'anonymous';
}
export async function getScraper(
options: Partial<ScraperTestOptions> = { authMethod: 'cookies' },
) {
const username = process.env['TWITTER_USERNAME'];
const password = process.env['TWITTER_PASSWORD'];
const email = process.env['TWITTER_EMAIL'];
const twoFactorSecret = process.env['TWITTER_2FA_SECRET'];
const apiKey = process.env['TWITTER_API_KEY'];
const apiSecretKey = process.env['TWITTER_API_SECRET_KEY'];
const accessToken = process.env['TWITTER_ACCESS_TOKEN'];
const accessTokenSecret = process.env['TWITTER_ACCESS_TOKEN_SECRET'];
let cookiesArray: any = null;
// try to read cookies by reading cookies.json with fs and parsing
// check if cookies.json exists
if (!fs.existsSync('./cookies.json')) {
console.error(
'cookies.json not found, using password auth - this is NOT recommended!',
);
} else {
try {
const cookiesText = fs.readFileSync('./cookies.json', 'utf8');
cookiesArray = JSON.parse(cookiesText);
} catch (e) {
console.error('Error parsing cookies.json', e);
}
}
const cookieStrings = cookiesArray?.map(
(cookie: any) =>
`${cookie.key}=${cookie.value}; Domain=${cookie.domain}; Path=${
cookie.path
}; ${cookie.secure ? 'Secure' : ''}; ${
cookie.httpOnly ? 'HttpOnly' : ''
}; SameSite=${cookie.sameSite || 'Lax'}`,
);
const proxyUrl = process.env['PROXY_URL'];
let agent: any;
if (
options.authMethod === 'cookies' &&
(!cookieStrings || cookieStrings.length === 0)
) {
console.warn(
'TWITTER_COOKIES variable is not defined, reverting to password auth (not recommended)',
);
options.authMethod = 'password';
}
if (options.authMethod === 'password' && !(username && password)) {
throw new Error(
'TWITTER_USERNAME and TWITTER_PASSWORD variables must be defined.',
);
}
if (proxyUrl) {
// Parse the proxy URL
const url = new URL(proxyUrl);
const username = url.username;
const password = url.password;
// Strip auth from URL if present
url.username = '';
url.password = '';
const agentOptions: any = {
uri: url.toString(),
requestTls: {
rejectUnauthorized: false,
},
};
// Add Basic auth if credentials exist
if (username && password) {
agentOptions.token = `Basic ${Buffer.from(
`${username}:${password}`,
).toString('base64')}`;
}
agent = new ProxyAgent(agentOptions);
}
const scraper = new Scraper({
transform: {
request: (input, init) => {
if (agent) {
return [input, { ...init, dispatcher: agent }];
}
return [input, init];
},
},
});
if (
options.authMethod === 'api' &&
username &&
password &&
apiKey &&
apiSecretKey &&
accessToken &&
accessTokenSecret
) {
await scraper.login(
username,
password,
email,
twoFactorSecret,
apiKey,
apiSecretKey,
accessToken,
accessTokenSecret,
);
} else if (options.authMethod === 'cookies' && cookieStrings?.length) {
await scraper.setCookies(cookieStrings);
} else if (options.authMethod === 'password' && username && password) {
await scraper.login(username, password, email, twoFactorSecret);
} else {
console.warn(
'No valid authentication method available. Ensure at least one of the following is configured: API credentials, cookies, or username/password.',
);
}
return scraper;
}