AGENTS.md 6.1 KB

AGENTS.md

This file contains guidelines and commands for agentic coding agents working in this TypeScript news aggregation and Mastodon bot repository.

Project Overview

This is a TypeScript-based news aggregation system that scrapes 20+ Chilean news portals and posts articles to Mastodon (mastodon.cl). The system runs on AWS Lambda using the Serverless Framework and includes interactive bots for fortune telling, reminders, and anniversaries.

Build and Development Commands

Core Commands

# Build TypeScript to JavaScript
bun run build
# or
npx tsc

# Run tests (currently placeholder)
bun run test

# Lint code
bun run lint
# or
npx eslint src/

# Start local development server
bun run offline
# or
serverless offline

Running Individual Tests

Currently no testing framework is implemented. The test script is a placeholder ("test": "exit 0"). When adding tests, use Jest or Vitest and update this section.

Deployment

# Deploy to AWS Lambda
serverless deploy

# Deploy specific function
serverless deploy function -f emol

Code Style Guidelines

TypeScript Configuration

  • Target: ES6
  • Module System: CommonJS
  • Strict Mode: Enabled (strictNullChecks: true)
  • Output Directory: dist/
  • Source Directory: src/

Import Style

// External libraries first
import "dotenv/config";
import { type Handler } from "aws-lambda";
import { createRestAPIClient, mastodon } from "masto";

// Internal modules with relative paths
import ScraperArticles from "../utils/scraper-articles";
import RedisClient from "../libs/redis-client";
import LogLevels from "../enums/log-levels";
import config from "../config";

// Type imports
import { type IScraperArticlesOptions } from "../interfaces/scaper-articles-options";

Naming Conventions

  • Classes: PascalCase (Portal, RedisClient)
  • Interfaces: PascalCase with I prefix (IArticle, IScraperOptions)
  • Enums: PascalCase (LogLevels, Emojis)
  • Variables: camelCase (_name, _mastodonClient)
  • Constants: UPPER_SNAKE_CASE (LOG_LEVEL, REDIS_CONN)
  • Files: kebab-case (scraper-articles.ts, log-levels.ts)

Class Structure

export default class Portal {
  private readonly _name: string;
  private readonly _mastodonClient: mastodon.rest.Client;

  constructor(name: string, accessToken: string, options: IOptions) {
    this._name = name;
    // Initialize properties
  }

  public async run(): Promise<void> {
    try {
      // Main logic
    } catch (err: any) {
      // Error handling
    }
  }

  public getHandler(): Handler {
    return async (event, context) => {
      await this.run(event, context);
    }
  }
}

Interface Definitions

export interface IArticle {
  title: string
  content: string
  link: string
  image: File | null
  author: string
  date: string
}

Enum Definitions

enum LogLevels {
  DEBUG = "debug",
  INFO = "info"
};

export default LogLevels;

Error Handling

try {
  // Code that might throw
} catch (err: any) {
  console.log(`${this._name} | An error has occurred\n`);
  console.error(err.message);
  if (config.LOG_LEVEL === LogLevels.DEBUG) {
    console.debug("Additional context:", event, context);
  }
}

Logging

  • Use console.log() for general logging
  • Use console.error() for errors
  • Use console.debug() for debug information (only when LOG_LEVEL === "debug")
  • Include class name in logs: ${this._name} | Message

Environment Configuration

  • All configuration in src/config.ts
  • Use environment variables with fallbacks: process.env.VAR ?? "default"
  • Import config: import config from "../config"

ESLint Rules

  • Quotes: Disabled (can use single or double)
  • Semicolons: Disabled (optional)
  • Base: standard-with-typescript

File Organization

src/
├── agents/          # Interactive Mastodon bots
├── enums/           # TypeScript enums
├── interfaces/      # TypeScript interfaces
├── libs/            # Client libraries (redis, postgres)
├── portales/        # News portal scrapers
│   └── [portal]/
│       ├── handler.ts
│       ├── definition.yml
│       └── scraper.ts
├── utils/           # Utility functions
├── test/            # Test files
└── config.ts        # Central configuration

Portal Implementation Pattern

Each news portal follows this structure:

  1. handler.ts - Lambda handler function
  2. definition.yml - Serverless function definition
  3. scraper.ts - Portal-specific scraping logic
  4. Extends base Portal class from src/portales/portal.ts

Mastodon Integration

  • Use masto library for API calls
  • Create client: createRestAPIClient({ url: config.MASTODON_URL, accessToken })
  • Post statuses: client.v1.statuses.create({ status: message, mediaIds })
  • Upload media: client.v2.media.create({ file: image, description })

Redis Caching

  • Use Redis client for duplicate prevention
  • Store article links with expiration
  • Key pattern: article link, Value: date string
  • Default expiration: 24 hours (60 * 60 * 24)

Message Formatting

  • Include emojis from Emojis enum
  • Limit message length to 400 characters
  • Format: ${EMOJI} Title\n\nContent\n\n${HASHTAGS}\n\n${LINK}
  • Handle special cases (subscriber-only content, publirreportajes)

Development Notes

Package Manager

  • Primary: Bun (uses bun.lockb)
  • Fallback: npm (has package-lock.json)

Dependencies

  • Scraping: axios, cheerio, chrono-node
  • Social: masto (Mastodon API)
  • Database: postgres, redis
  • Utilities: date-fns-tz, dotenv, cron

Testing

  • No testing framework currently implemented
  • When adding tests, consider Jest or Vitest
  • Test files should be excluded from TypeScript compilation
  • Place tests in src/test/ directory

Deployment

  • Uses Serverless Framework
  • AWS Lambda with Node.js 20.x runtime
  • Functions triggered hourly via CloudWatch Events
  • Timeout: 60 seconds

Security

  • Never commit secrets or API keys
  • Use environment variables for sensitive data
  • Access tokens stored in environment variables
  • Redis connection string configurable