This file contains guidelines and commands for agentic coding agents working in this TypeScript news aggregation and Mastodon bot repository.
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 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
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.
# Deploy to AWS Lambda
serverless deploy
# Deploy specific function
serverless deploy function -f emol
strictNullChecks: true)dist/src/// 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";
Portal, RedisClient)I prefix (IArticle, IScraperOptions)LogLevels, Emojis)_name, _mastodonClient)LOG_LEVEL, REDIS_CONN)scraper-articles.ts, log-levels.ts)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);
}
}
}
export interface IArticle {
title: string
content: string
link: string
image: File | null
author: string
date: string
}
enum LogLevels {
DEBUG = "debug",
INFO = "info"
};
export default LogLevels;
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);
}
}
console.log() for general loggingconsole.error() for errorsconsole.debug() for debug information (only when LOG_LEVEL === "debug")${this._name} | Messagesrc/config.tsprocess.env.VAR ?? "default"import config from "../config"standard-with-typescriptsrc/
├── 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
Each news portal follows this structure:
handler.ts - Lambda handler functiondefinition.yml - Serverless function definitionscraper.ts - Portal-specific scraping logicPortal class from src/portales/portal.tsmasto library for API callscreateRestAPIClient({ url: config.MASTODON_URL, accessToken })client.v1.statuses.create({ status: message, mediaIds })client.v2.media.create({ file: image, description })60 * 60 * 24)Emojis enum${EMOJI} Title\n\nContent\n\n${HASHTAGS}\n\n${LINK}bun.lockb)package-lock.json)axios, cheerio, chrono-nodemasto (Mastodon API)postgres, redisdate-fns-tz, dotenv, cronsrc/test/ directory