agent-twitter-summary
Version:
A twitter client for agents
200 lines (171 loc) • 7.05 kB
text/typescript
// src/test.ts
import 'dotenv/config';
import { Space, SpaceConfig } from './core/Space';
import { Scraper } from '../scraper';
import { RecordToDiskPlugin } from './plugins/RecordToDiskPlugin';
import { SttTtsPlugin } from './plugins/SttTtsPlugin';
import { IdleMonitorPlugin } from './plugins/IdleMonitorPlugin';
import { HlsRecordPlugin } from './plugins/HlsRecordPlugin';
/**
* Main test entry point
*/
async function main() {
console.log('[Test] Starting...');
// 1) Twitter login with your scraper
const scraper = new Scraper();
await scraper.login(
process.env.TWITTER_USERNAME!,
process.env.TWITTER_PASSWORD!,
);
// 2) Create the Space instance
// Set debug=true if you want more logs
const space = new Space(scraper, { debug: false });
// --------------------------------------------------------------------------------
// EXAMPLE 1: Record raw speaker audio via RecordToDiskPlugin (local plugin approach)
// --------------------------------------------------------------------------------
const recordPlugin = new RecordToDiskPlugin();
space.use(recordPlugin);
// --------------------------------------------------------------------------------
// EXAMPLE 2: HLSRecordPlugin => record final Space mix as .ts file via HLS
// (Requires the "scraper" to fetch the HLS URL, and ffmpeg installed.)
// --------------------------------------------------------------------------------
const hlsPlugin = new HlsRecordPlugin();
// If you want, you can override the default output path in pluginConfig, for example:
// space.use(hlsPlugin, { outputPath: '/tmp/my_custom_space.ts' });
space.use(hlsPlugin);
// Create our TTS/STT plugin instance
const sttTtsPlugin = new SttTtsPlugin();
space.use(sttTtsPlugin, {
openAiApiKey: process.env.OPENAI_API_KEY,
elevenLabsApiKey: process.env.ELEVENLABS_API_KEY,
voiceId: 'D38z5RcWu1voky8WS1ja', // example
// You can also initialize systemPrompt, chatContext, etc. here if you wish
// systemPrompt: "You are a calm and friendly AI assistant."
});
// Create an IdleMonitorPlugin to stop after 60s of silence
const idlePlugin = new IdleMonitorPlugin(60_000, 10_000);
space.use(idlePlugin);
// If idle occurs, say goodbye and end the Space
space.on('idleTimeout', async (info) => {
console.log(`[Test] idleTimeout => no audio for ${info.idleMs}ms.`);
await sttTtsPlugin.speakText('Ending Space due to inactivity. Goodbye!');
await new Promise((r) => setTimeout(r, 10_000));
await space.stop();
console.log('[Test] Space stopped due to silence.');
process.exit(0);
});
// 3) Initialize the Space
const config: SpaceConfig = {
mode: 'INTERACTIVE',
title: 'AI Chat - Dynamic GPT Config',
description: 'Space that demonstrates dynamic GPT personalities.',
languages: ['en'],
};
const broadcastInfo = await space.initialize(config);
const spaceUrl = broadcastInfo.share_url.replace('broadcasts', 'spaces');
console.log('[Test] Space created =>', spaceUrl);
// (Optional) Tweet out the Space link
await scraper.sendTweet(`${config.title} ${spaceUrl}`);
console.log('[Test] Tweet sent');
// ---------------------------------------
// Example of dynamic GPT usage:
// You can change the system prompt at runtime
setTimeout(() => {
console.log('[Test] Changing system prompt to a new persona...');
sttTtsPlugin.setSystemPrompt(
'You are a very sarcastic AI who uses short answers.',
);
}, 45_000);
// Another example: after some time, switch to GPT-4
setTimeout(() => {
console.log('[Test] Switching GPT model to "gpt-4" (if available)...');
sttTtsPlugin.setGptModel('gpt-4');
}, 60_000);
// Also, demonstrate how to manually call askChatGPT and speak the result
setTimeout(async () => {
console.log('[Test] Asking GPT for an introduction...');
try {
const response = await sttTtsPlugin['askChatGPT']('Introduce yourself');
console.log('[Test] ChatGPT introduction =>', response);
// Then speak it
await sttTtsPlugin.speakText(response);
} catch (err) {
console.error('[Test] askChatGPT error =>', err);
}
}, 75_000);
// Example: periodically speak a greeting every 60s
setInterval(() => {
sttTtsPlugin
.speakText('Hello everyone, this is an automated greeting.')
.catch((err) => console.error('[Test] speakText() =>', err));
}, 20_000);
// 4) Some event listeners
space.on('speakerRequest', async (req) => {
console.log('[Test] Speaker request =>', req);
await space.approveSpeaker(req.userId, req.sessionUUID);
// Remove the speaker after 10 seconds (testing only)
setTimeout(() => {
console.log(
`[Test] Removing speaker => userId=${req.userId} (after 60s)`,
);
space.removeSpeaker(req.userId).catch((err) => {
console.error('[Test] removeSpeaker error =>', err);
});
}, 60_000);
});
// When a user reacts, send back an emoji to test the flow
space.on('guestReaction', (evt) => {
// Pick a random emoji from the list
const emojis = ['💯', '✨', '🙏', '🎮'];
const emoji = emojis[Math.floor(Math.random() * emojis.length)];
space.reactWithEmoji(emoji);
});
space.on('error', (err) => {
console.error('[Test] Space Error =>', err);
});
// ==================================================
// BEEP GENERATION (500 ms) @16kHz => 8000 samples
// ==================================================
const beepDurationMs = 500;
const sampleRate = 16000;
const totalSamples = (sampleRate * beepDurationMs) / 1000; // 8000
const beepFull = new Int16Array(totalSamples);
// Sine wave: 440Hz, amplitude ~12000
const freq = 440;
const amplitude = 12000;
for (let i = 0; i < beepFull.length; i++) {
const t = i / sampleRate;
beepFull[i] = amplitude * Math.sin(2 * Math.PI * freq * t);
}
const FRAME_SIZE = 160;
/**
* Send a beep by slicing beepFull into frames of 160 samples
*/
async function sendBeep() {
console.log('[Test] Starting beep...');
for (let offset = 0; offset < beepFull.length; offset += FRAME_SIZE) {
const portion = beepFull.subarray(offset, offset + FRAME_SIZE);
const frame = new Int16Array(FRAME_SIZE);
frame.set(portion);
space.pushAudio(frame, sampleRate);
await new Promise((r) => setTimeout(r, 10));
}
console.log('[Test] Finished beep');
}
// Example: Send beep every 5s (currently commented out)
// setInterval(() => {
// sendBeep().catch((err) => console.error('[Test] beep error =>', err));
// }, 5000);
console.log('[Test] Space is running... press Ctrl+C to exit.');
// Graceful shutdown
process.on('SIGINT', async () => {
console.log('\n[Test] Caught interrupt signal, stopping...');
await space.stop();
console.log('[Test] Space stopped. Bye!');
process.exit(0);
});
}
main().catch((err) => {
console.error('[Test] Unhandled main error =>', err);
process.exit(1);
});