PulseTask is a serverless personal operating system for time, tasks, energy, and spending. It connects Telegram, Cloudflare Workers, Google Apps Script, and Google Sheets so a weekly schedule becomes an interactive assistant: reminders, task actions, queue-based work sessions, smart rescheduling, finance logging, and weekly reports.
It is designed as a single-user personal edition: one Telegram chat, one Google Sheet, no VPS, no database server, and no always-on local machine.
- Sends Telegram reminders about one hour before scheduled tasks.
- Tracks
Done,Skip,Start,Pause,Later, energy, and smart reschedule actions. - Adds unplanned Telegram tasks with preview and confirmation.
- Keeps unscheduled Telegram work in a visible Google Sheets Queue.
- Starts Queue tasks into one-hour Active Sessions with
Done/Continue 1hcontrols. - Records actual time, session time, planned time, and unplanned work.
- Builds daily and weekly productivity summaries.
- Records 1-5 energy levels and generates a seven-day hourly heatmap.
- Logs expenses and income from Telegram into a dedicated
Finance_Logsheet. - Tracks current finance balance and weekly spending by category.
- Keeps credentials out of source code through Apps Script Properties and Cloudflare Secrets.
flowchart TB
%% PulseTask high-level system map
U["Telegram User<br/>Tasks β’ Energy β’ Finance"]:::user
TG["Telegram Bot API<br/>Webhook + Bot Messages"]:::telegram
subgraph CF["Cloudflare Edge"]
W["Worker<br/>src/index.js"]:::worker
AUTH["Chat guard<br/>TELEGRAM_CHAT_ID"]:::worker
ROUTER["Callback + command router<br/>Queue β’ Active β’ Finance β’ Reports"]:::worker
SESSION["Short-lived finance session<br/>Persistent keyboard flow"]:::worker
end
subgraph GAS["Google Apps Script Web App"]
API["doPost API<br/>WORKER_API_SECRET"]:::apps
TASKS["Task engine<br/>Done β’ Skip β’ Start β’ Pause β’ Later"]:::apps
QUEUE["Queue + Active Session engine<br/>Start now β’ Done early β’ Continue 1h"]:::apps
FIN["Finance engine<br/>Income β’ Expense β’ Balance β’ Weekly report"]:::apps
REPORTS["Reports + heatmap<br/>Daily β’ Weekly β’ Energy"]:::apps
TRIGGERS["Time triggers<br/>5m reminders β’ weekly reports β’ hourly check-ins"]:::apps
end
subgraph GS["Google Sheets"]
MAIN["Main schedule<br/>Start β’ Finish β’ State β’ Weekdays"]:::sheet
Q["Queue<br/>L1:Q"]:::sheet
ACTIVE["Active Sessions<br/>R1:X"]:::sheet
DYN["Dynamic_Schedule"]:::sheet
ACTION["Action_Log"]:::sheet
MOOD["Mood_Log"]:::sheet
FINLOG["Finance_Log"]:::sheet
WEEKLY["Weekly_Report"]:::sheet
HEAT["Energy_Heatmap"]:::sheet
REM["Reminder_Log"]:::sheet
end
U -->|"tap button / send text"| TG
TG -->|"webhook update"| W
W --> AUTH --> ROUTER
ROUTER --> SESSION
ROUTER -->|"authenticated JSON"| API
API --> TASKS
API --> QUEUE
API --> FIN
API --> REPORTS
TRIGGERS --> TASKS
TRIGGERS --> REPORTS
TRIGGERS --> FIN
TASKS <--> MAIN
TASKS <--> DYN
TASKS --> ACTION
TASKS --> MOOD
TASKS --> REM
QUEUE <--> Q
QUEUE <--> ACTIVE
QUEUE <--> DYN
FIN <--> FINLOG
REPORTS --> WEEKLY
REPORTS --> HEAT
REPORTS --> ACTION
REPORTS --> MOOD
API -->|"Telegram messages<br/>reminders + reports"| TG
TG -->|"inline keyboards<br/>persistent bottom buttons"| U
classDef user fill:#111827,stroke:#374151,color:#ffffff
classDef telegram fill:#e0f2fe,stroke:#0284c7,color:#0c4a6e
classDef worker fill:#fff7ed,stroke:#f97316,color:#7c2d12
classDef apps fill:#eef2ff,stroke:#6366f1,color:#312e81
classDef sheet fill:#ecfdf5,stroke:#10b981,color:#064e3b
| Layer | Role |
|---|---|
| Telegram | Chat UI, persistent buttons, inline actions, finance/task flows |
| Cloudflare Worker | Webhook endpoint, chat authorization, callback parsing, fast Telegram responses |
| Google Apps Script | Schedule logic, task state, finance state, reports, triggers, Google Sheets writes |
| Google Sheets | Main plan, queue, active sessions, dynamic schedule, logs, reports |
| Technology | Where it lives | Why it is used |
|---|---|---|
| Telegram Bot API | External chat interface | Low-friction personal UI with persistent buttons and inline actions |
| Cloudflare Worker | cloudflare-worker/src/index.js |
Public webhook endpoint, fast callback acknowledgement, chat authorization, and lightweight session routing |
| Google Apps Script Web App | apps-script/Code.gs |
Trusted automation layer with direct access to Google Sheets, triggers, and UrlFetchApp |
| Google Sheets | User-owned spreadsheet | Human-readable storage for schedule, Queue, Active Sessions, logs, reports, and finance records |
| Apps Script Triggers | Installed by initializePulseTask() |
Reminder polling, weekly reports, and one-hour Queue follow-ups |
| Cloudflare Secrets | Worker runtime | Stores Telegram token, chat ID, Apps Script URL, and shared API secret |
| Apps Script Properties | Google runtime | Stores the same bot credentials plus schedule and finance configuration |
The architecture intentionally keeps persistence inside the user-owned Google Sheet. The Worker stays stateless except for short-lived finance wizard state, so it can be redeployed without migrating data.
| Flow | Transport | Authentication | Result |
|---|---|---|---|
| Telegram -> Worker | Telegram webhook POST | Telegram bot token at webhook registration time | Worker receives messages, commands, and callback queries |
| Worker -> Telegram | Telegram Bot API HTTPS calls | TELEGRAM_BOT_TOKEN in Cloudflare Secrets |
Bot sends messages, inline keyboards, reports, and persistent keyboard updates |
| Worker -> Apps Script | HTTPS POST to APPS_SCRIPT_URL |
WORKER_API_SECRET in every JSON payload |
Apps Script mutates Sheets or returns data for Queue, Finance, and reports |
| Apps Script -> Telegram | Telegram Bot API HTTPS calls | TELEGRAM_BOT_TOKEN in Apps Script Properties |
Time-based reminders and scheduled reports are sent without a user webhook event |
| Apps Script -> Google Sheets | SpreadsheetApp APIs | Script executes as the sheet owner | Logs, dynamic tasks, Queue rows, Active Sessions, reports, and finance rows are written |
Important communication rules:
- Telegram webhook updates should go to Cloudflare, not Apps Script.
- Apps Script
/execis public by deployment design, but every POST is rejected unless the shared secret matches. - The Worker ignores messages from any chat except
TELEGRAM_CHAT_ID. - Inline callback queries are acknowledged quickly by the Worker, then heavier work is delegated to Apps Script.
- Time-based jobs are initiated inside Apps Script triggers, so reminders and weekly reports do not depend on the Worker receiving a chat message.
The Telegram webhook points to Cloudflare, not directly to Apps Script. Cloudflare signs every Apps Script request with WORKER_API_SECRET.
Send /start once after deploying the Worker. Telegram installs a persistent bottom keyboard:
β Add Task π₯ Queue
βΆοΈ Active π° Finance
You can add work from Telegram without editing the sheet manually:
Review PulseTask release
/add Review PulseTask release
/add Development | Improve Telegram finance flow
/add 18:30-20:00 | Research | Read robotics paper
/add 90m | Deep Work | Weekly review
/add now-20:00 | Personal | Clean inbox
If no time is provided, PulseTask suggests the nearest available 60-minute slot.
Confirmed tasks can be:
- added to Queue for later;
- added and started immediately;
- cancelled before saving.
Tasks saved to Queue stay visible in the main sheet and do not reserve time until you start them.
Use π₯ Queue or /queue to pick pending work. Starting a queued task:
- creates a one-hour Active Session;
- starts the timer;
- removes the item from Queue;
- sends immediate
DoneandContinue 1hcontrols.
If you finish early, press Done. If you need more time, press Continue 1h; PulseTask extends the planned finish time and schedules another check-in.
Use βΆοΈ Active or /active to reopen controls for running Queue tasks.
Use π° Finance or /finance.
The current finance flow is step-by-step:
- choose
πΈ Add Expenseorπ΅ Add Income; - type only the amount;
- choose a category button;
- optionally add a note, or send
-; - confirm with
Save.
Expense categories include Food, Transport, Bills, Loan, Rent, Health, Education, Shopping, and Other. Income categories include Salary, Freelance, Gift, Investment, Refund, and Other.
Legacy one-line commands still work:
/expense 250000 | Food | groceries
/expense 1200000 | Loan | car payment
/income 5000000 | Salary | July payment
Finance records are written to Finance_Log. Set the optional Apps Script property FINANCE_STARTING_BALANCE if the balance should start from an existing amount instead of zero.
| Command | Result |
|---|---|
/start |
Installs the persistent Telegram keyboard |
/help |
Shows task examples |
/add ... |
Creates a task draft |
/queue |
Lists queued tasks |
/active |
Lists running Queue tasks |
/finance |
Opens finance tools |
/expense ... |
Starts or submits an expense |
/income ... |
Starts or submits income |
/today |
Sends todayβs report |
/week |
Sends the last seven days report |
/heatmap |
Rebuilds the energy heatmap |
/test |
Sends a test action card |
The main schedule tab needs these headers in row 1:
Start | Finish | Time Duration | State | Saturday | Sunday | Monday | Tuesday | Wednesday | Thursday | Friday
PulseTask creates or maintains these generated sheets:
| Sheet | Purpose |
|---|---|
Action_Log |
Task actions, timing, source, status, and completion records |
Mood_Log |
Energy ratings and mood labels |
Reminder_Log |
Deduplication for sent reminders |
Dynamic_Schedule |
Rescheduled, queued, and Telegram-created tasks |
Weekly_Report |
Aggregated wellbeing and productivity metrics |
Energy_Heatmap |
Seven-day hourly energy grid |
Finance_Log |
Income, expenses, categories, notes, and balance after each transaction |
On the main schedule tab, PulseTask also creates:
- Queue section at
L1:Q - Active Sessions section at
R1:X
See docs/GOOGLE_SHEETS_SCHEMA.md for the exact schema.
- Telegram account and bot token from @BotFather
- Google account and Google Sheet
- Cloudflare account
- Node.js 20+
- Git and npm
Create a bot with BotFather, then send /start to the bot once.
Get your chat ID:
$BOT_TOKEN = "YOUR_TELEGRAM_BOT_TOKEN"
Invoke-RestMethod -Uri "https://api.telegram.org/bot$BOT_TOKEN/getUpdates"Use message.chat.id as TELEGRAM_CHAT_ID.
Open your Google Sheet, then:
- go to Extensions β Apps Script;
- replace the editor contents with apps-script/Code.gs;
- add Script Properties:
| Property | Value |
|---|---|
TELEGRAM_BOT_TOKEN |
Telegram bot token |
TELEGRAM_CHAT_ID |
Your private chat ID |
WORKER_API_SECRET |
Long shared secret, 32+ characters |
MAIN_SHEET_NAME |
Main schedule sheet name, for example Sheet1 |
TIMEZONE |
IANA timezone, for example Asia/Tehran |
FINANCE_STARTING_BALANCE |
Optional starting balance |
Run:
initializePulseTask()This validates configuration, creates missing generated sheets, installs project triggers, and builds the heatmap.
Deploy as a Web App:
Execute as: Me
Who has access: Anyone
Copy the /exec URL. This is the Worker secret APPS_SCRIPT_URL.
After every Code.gs change, publish a new Web App version from:
Deploy β Manage deployments β Edit β New version β Deploy
cd cloudflare-worker
npm install
npx wrangler loginSet production secrets:
npx wrangler secret put TELEGRAM_BOT_TOKEN
npx wrangler secret put TELEGRAM_CHAT_ID
npx wrangler secret put APPS_SCRIPT_URL
npx wrangler secret put WORKER_API_SECRETWORKER_API_SECRET must match the Apps Script property exactly.
Build and deploy:
npm run build
npm run deployThe Worker health endpoint should return:
{
"ok": true,
"service": "PulseTask Telegram Worker",
"version": "2.7-persistent-finance-keyboard"
}$BOT_TOKEN = "YOUR_TELEGRAM_BOT_TOKEN"
$WORKER_URL = "https://YOUR-WORKER.YOUR-SUBDOMAIN.workers.dev"
Invoke-RestMethod `
-Uri "https://api.telegram.org/bot$BOT_TOKEN/setWebhook" `
-Method Post `
-ContentType "application/json" `
-Body (@{
url = $WORKER_URL
drop_pending_updates = $true
} | ConvertTo-Json)Verify:
Invoke-RestMethod -Uri "https://api.telegram.org/bot$BOT_TOKEN/getWebhookInfo"Then send /start in Telegram.
Worker commands:
| Command | Purpose |
|---|---|
npm run dev |
Run Wrangler locally |
npm run build |
Dry-run Worker deployment |
npm run deploy |
Deploy Worker |
npm run tail |
Stream Worker logs |
Apps Script test helpers:
runPulseTaskTests()
testTelegram()
testNextUpcomingReminder()
testTodayReport()
testWeeklyReport()
testHeatmap()Recommended release checks:
node --check cloudflare-worker/src/index.js
cd cloudflare-worker
npm run buildFor Apps Script, run runPulseTaskTests() in the Apps Script editor.
initializePulseTask() installs:
checkUpcomingTaskRemindersevery five minutes;sendWeeklyFinanceReportevery Friday around 23:30;sendWeeklyWellbeingReportevery Friday around 23:45.
Queue tasks create one-time follow-up triggers. Continue 1h replaces the follow-up trigger; Done clears it.
- Never commit real bot tokens, chat IDs, Apps Script URLs, or shared secrets.
- Use Apps Script Properties for Google-side credentials.
- Use Wrangler Secrets for Cloudflare production credentials.
- Keep
.dev.varslocal and ignored. - Rotate leaked Telegram tokens immediately through BotFather.
- Treat the Apps Script
/execURL as public; authorization depends onWORKER_API_SECRET.
See SECURITY.md and docs/SECURITY_GUIDE.md.
| Symptom | Check |
|---|---|
| Telegram buttons are missing | Send /start once after Worker deployment |
| Buttons show broken emoji text | Redeploy the latest Worker and send /start |
| Apps Script returns HTML | Use the deployed /exec URL, not /dev |
| Apps Script changes do not apply | Create a new Web App version |
| Reminders do not arrive | Check triggers, timezone, weekday headers, task time, and Reminder_Log |
| Queue task does not appear busy | That is expected until it is started |
| Finance balance starts at zero | Set FINANCE_STARTING_BALANCE |
| Worker errors are unclear | Run npm run tail in cloudflare-worker |
PulseTask/
βββ apps-script/
β βββ Code.gs
β βββ appsscript.json
βββ cloudflare-worker/
β βββ src/index.js
β βββ package.json
β βββ wrangler.jsonc
β βββ .dev.vars.example
βββ docs/
βββ examples/
βββ CONTRIBUTING.md
βββ SECURITY.md
βββ README.md
Useful docs:
- docs/ARCHITECTURE.md
- docs/SETUP.md
- docs/GOOGLE_SHEETS_SCHEMA.md
- docs/OPERATIONS.md
- docs/TROUBLESHOOTING.md
- docs/ROADMAP.md
PulseTask is currently a personal single-user system. It does not include multi-user onboarding, OAuth account linking, hosted billing, a public dashboard, or a production database. Those are future product directions, not required for the personal edition.
PulseTask is released under the MIT License.