A public archive of investigations into crypto scams and bad actors.
Nightwatch collects and preserves tweets and Telegram messages from trusted blockchain sleuths, acting as a curated and convenient searchable record of their work.
A ledger of exposure. A watchful memory. A stain that won't fade.
Nightwatch serves as a permanent archive for investigations conducted by trusted blockchain investigators like @zachxbt, sourced from both Twitter and Telegram.
The application indexes tweets and messages from selected accounts/channels, along with media attachments and metadata. It reconstructs Twitter conversation threads and Telegram reply chains to provide better context. This creates a reliable reference point to research potential scams and bad actors, with an easily searchable interface.
- Permanent Archive: Tweets and Telegram messages are stored in a database, ensuring they remain accessible even if deleted from the source platforms.
- Full-Text Search: Search through the entire archive using keywords across both platforms.
- Conversation Context: View entire Twitter threads and Telegram reply chains from relevant accounts/channels.
- Media Preservation: Images and videos attached to tweets are preserved and viewable..
- Regular Updates: Automatic synchronization with Twitter and Telegram to capture new content.
- Deno (v1.37 or higher)
- Node.js (v20 or higher)
- pnpm (v8 or higher)
- A Neon PostgreSQL database
- A TwitterAPI.io API key
- Telegram API Credentials (
TELEGRAM_API_ID
,TELEGRAM_API_HASH
) - A Telegram User Session String (
TELEGRAM_SESSION
) generated withpnpm telegram-login
-
Clone the repository:
git clone https://github.com/polareth/nightwatch.git cd nightwatch
-
Install dependencies:
pnpm install
-
Set up environment variables:
NEON_DATABASE_URL=your_neon_postgres_connection_string TWITTERAPI_API_KEY=your_twitterapi_io_key CRON_SECRET=your_secret_for_cron_jobs TELEGRAM_API_ID=your_telegram_api_id TELEGRAM_API_HASH=your_telegram_api_hash TELEGRAM_SESSION=your_telegram_session_string # See Telegram section below
-
Generate a Telegram session string (if you don't have one):
pnpm telegram-login
Follow the prompts to log in with your Telegram account. The session string will be printed to the console. Add it to your environment variables (
TELEGRAM_SESSION
).
Run the development server:
pnpm dev
The application will be available at http://localhost:5173.
Nightwatch is designed to be deployed on Deno Deploy. The repository includes a GitHub Actions workflow for automatic deployment.
-
Build the application:
pnpm build
-
Deploy manually (if not using GitHub Actions):
pnpm run deploy
- Data Collection: Tweets from specified accounts are fetched from TwitterAPI.io. Messages from specified channels are fetched using the Telegram API.
- Data Processing: Content is parsed and normalized, extracting mentions, URLs, media (Twitter), and reply structures.
- Data Storage: Processed content is stored in a Neon database.
- Data Retrieval: Users query the database through the search interface via the
/api/search
endpoint. - Data Presentation: Results are displayed with highlighting, conversation context, and media previews (Twitter) or indicators (Telegram).
/api/search
: Search for tweets and Telegram messages matching a query./api/periodic-sync
: Trigger a synchronization with Twitter and Telegram (protected by auth). Fetches new content since the last sync./api/initial-sync
: Perform initial backfill for specific Twitter users or Telegram channels (protected by auth)./api/health
: Check the health of the application and its dependencies.
The database uses four main tables:
-
tw_users
: Stores information about Twitter authors.id
: bigint (Twitter user ID)username
: textdisplay_name
: textprofile_picture_url
: textfollowers
: integerfollowing
: integerprofile_bio
: jsonb (bio, mentions, urls)
-
tw_posts
: Stores the tweets.id
: bigint (Tweet ID)url
: texttext
: textuser_id
: bigint (FK totw_users.id
)conversation_id
: bigintcreated_at
: timestamptzuser_mentions
: jsonb (array ofDbMentionType
)urls
: jsonb (array ofDbUrlType
)medias
: jsonb (array ofDbMediaType
)fts_tokens
: tsvector (for full-text search)
-
tg_channels
: Stores information about Telegram channels.id
: bigint (Telegram channel ID)title
: textabout
: textchannel_username
: textadmin_usernames
: text[]
-
tg_messages
: Stores Telegram messages.id
: text (Composite:channel_id-message_id
)message_id
: bigintmessage
: texturl
: textchannel_id
: bigint (FK totg_channels.id
)reply_to_message_id
: bigint (Original message ID it replies to)created_at
: timestamptzurls
: jsonb (array ofDbUrlType
)has_media
: booleanthread_id
: text (ID of the root message in the reply chain)fts_tokens
: tsvector (for full-text search)
You can directly use the reference SQL schema to create the database.
- Twitter: Uses TwitterAPI.io advanced search. Implements batch processing, cursor-based pagination, and differential updates (fetching only new tweets). See
app/lib/sync.server.ts
. - Telegram: Uses
telejs
to connect directly to the Telegram API as a user. Fetches channel info and messages, performing differential updates based on the last stored message ID. Requires API credentials and a user session string. Seeapp/lib/sync.server.ts
.
You can manually trigger syncs via the API endpoints:
# Periodic sync (fetches new content for all configured sources)
curl -X POST "http://localhost:5173/api/periodic-sync" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $CRON_SECRET"
# Initial sync (backfills specific sources)
# Twitter user:
curl -X POST "http://localhost:5173/api/initial-sync?twitter=zachxbt" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $CRON_SECRET"
# Telegram channel:
curl -X POST "http://localhost:5173/api/initial-sync?telegram=some_channel_username" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $CRON_SECRET"
# Multiple sources:
curl -X POST "http://localhost:5173/api/initial-sync?twitter=userA,userB&telegram=channelA,channelB" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $CRON_SECRET"
You can customize the relevant users and channels in app/lib/constants.server.ts
at RELEVANT_SOURCES
. Same for the BATCH_SIZE
.
The application implements caching for search results to improve performance and reduce database load:
- Search Results Caching:
/api/search
results are cached for 1 hour.
You can customize the cache TTL in app/lib/constants.server.ts
at CACHE_TTL
.
Nightwatch uses Deno's built-in cron functionality (Deno.cron
) for regular updates:
- Content Synchronization: Runs
/api/periodic-sync
every 6 hours to fetch new tweets and messages. - Authentication: Jobs are protected by a secret token (
CRON_SECRET
) to prevent unauthorized access.
You can customize the cron schedule directly in server.production.ts
.
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature
) - Commit your changes (
git commit -m 'Add some amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.