telegram-reader
v1.4.0
Published
Read Telegram channel posts via MTProto as flat rows with text and lightweight photo previews — built for LLM agents, analytics, and trading-signal backtests with backtest-kit
Maintainers
Readme
telegram-reader
A library for reading posts from Telegram channels via MTProto (a userbot built on gramjs). It returns channel messages as flat rows with text and a lightweight photo preview — a convenient format for feeding an LLM agent, analytics, or a trading-signal backtest.
Features
- QR-code authorization — one interactive session, after which the client reuses the saved
session.txt. - Three fetch modes:
scrapeDay— all posts of a calendar day (UTC);scrapeLookback— a sliding "last N minutes/hours/days" window free of look-ahead bias, suitable for both backtesting and live mode;scrapePage— classiclimit/offsetpagination deep into the channel history.
- Photo previews: the image is downloaded in full size, rotated according to EXIF, and compressed to a JPEG 800px wide (quality 80) via
sharp; it lands in the row as base64. Downloads go through a pool (maxExec: 5) to avoid OOM on weak hardware. - Posts with neither text nor photo are skipped; the result is always ordered newest-first.
Installation and build
npm install
npm run build # rollup → build/index.cjs, build/index.mjs + types.d.tsRequires Node.js (sharp and fs/promises are used).
Setup
Get an
api_idandapi_hashat my.telegram.org and pass them viasetConfigbefore the first call to any other function (recommended way):import { setConfig } from "telegram-reader"; setConfig({ CC_TELEGRAM_API_ID: 1234567, CC_TELEGRAM_API_HASH: "0123456789abcdef0123456789abcdef", });Values set through
setConfigtake priority over everything else. Alternatively, use environment variables (a.envfile works too):CC_TELEGRAM_API_ID=1234567 CC_TELEGRAM_API_HASH=0123456789abcdef0123456789abcdefResolution order:
setConfig→ environment variables → built-in defaults. The current effective values can be inspected withgetConfig().Create
session.txtby signing in once. The recommended way is a one-liner:node -e 'require("telegram-reader").signIn()'Or call it from your own code:
import { signIn } from "telegram-reader"; await signIn(); // Session saved to ./session.txtA QR code appears in the console — scan it in Telegram (Settings → Devices → Link Desktop Device); if 2FA is enabled, you will be prompted for the password. The session is saved to
./session.txtand reused by all subsequent calls.
Usage
Each result row has the following shape:
interface ScraperMessage {
id: number; // message id within the channel
channel: string; // channel exactly as passed in the request
content: string; // post text ("" if photo-only)
date: Date; // publication time
photo: string | null; // base64 JPEG preview or null
}All posts of a day
import { scrapeDay } from "telegram-reader";
const rows = await scrapeDay({
channel: "some_channel",
when: new Date("2026-09-19"), // only the UTC date part matters
});scrapeDay covers the whole day, including posts published after when — in a backtest that is look-ahead bias. Use scrapeLookback for backtesting.
Sliding window (no look-ahead bias)
import { scrapeLookback } from "telegram-reader";
// the last 6 hours relative to the "current moment"
const rows = await scrapeLookback({
channel: "some_channel",
when: new Date(), // in a backtest — the simulated "now"
limit: 6,
dimension: "hour", // "minute" | "hour" | "day", defaults to "minute"
});The window is [when - limit * dimension, when): a post dated exactly when or later never makes it into the result — at that moment it is not yet "visible".
Paging through history
import { scrapePage } from "telegram-reader";
const when = new Date(); // pin the boundary so pages don't drift
const page1 = await scrapePage({ channel: "some_channel", when, limit: 20, offset: 0 });
const page2 = await scrapePage({ channel: "some_channel", when, limit: 20, offset: 20 });Direct client access
import { getTelegram } from "telegram-reader";
const client = await getTelegram(); // authorized TelegramClient (singleton)REPL
npm run replBuilds the project and starts Node with .env loaded — handy for testing the functions by hand.
