Assistant is a personal Telegram bot project built mainly for entertainment, experimentation, and learning.
At this stage, it is not intended to be a production-grade or public SaaS product.
- The implementation is intentionally minimal.
- A clean and extensible command infrastructure is already in place.
- Current command support includes startup, natural-language reminder creation, and expense statement analysis.
- Credit card statement PDFs can now be analyzed and persisted as billing-period expense summaries.
- Incoming Telegram updates are queued and processed in the background via Hangfire.
- A Hangfire recurring job is configured to send end-of-workday reminders to registered users.
- Long-term user memories now store Gemini embeddings in PostgreSQL via
pgvector, enabling semantic recall instead of only recency-based retrieval. - AI-backed turns now combine personality context, semantically matched memories, and a rolling chat summary to keep longer conversations coherent.
- A memory-maintenance pipeline has been added to archive stale memories and consolidate highly similar durable memories.
The bot currently supports the following commands:
| Command | Description |
|---|---|
/start |
Starts the assistant and sends a welcome message. |
/remind |
Creates one-time or recurring reminders from natural language input (private chat only). |
/expense |
Shows the user's current expense summary and can analyze uploaded credit card statement PDFs. |
Bot commands are registered with Telegram during application startup via SetMyCommands.
Example:
/remind 5 saat sonra Mustafa abiyle toplantımı hatırlat/expense
Expense flow:
- Run
/expensein a private chat to see the currently saved total expense amount. - Upload a credit card statement PDF with the
/expensecommand caption to trigger analysis. - The bot extracts statement text through Markitdown, asks the AI agent for a single billing-period summary, and saves that result to the
Expensestable.
The project includes a recurring Hangfire job:
| Job ID | Schedule (Cron) | Time Zone | Description |
|---|---|---|---|
workday-end-reminder |
0 14 * * 1-5 |
UTC | Runs every weekday at 14:00 UTC and sends a workday-end reminder message to all users in TelegramUsers. |
Implementation notes:
WorkdayEndReminderJobfetches chat IDs from the database and sends reminders one-by-one viaITelegramBotClient.- Delivery failures are logged per chat; successful and failed counts are summarized at the end of each run.
- The recurring schedule is registered at application startup in
UseHangfireRecurringJobs. CommandUpdateJobis also registered as a Hangfire background job and is used to process incoming Telegram updates asynchronously.MemoryMaintenanceJobandMemoryMaintenanceServiceare now implemented for nightly semantic-memory cleanup, but the recurringAddOrUpdate(...)registration is currently commented out.
IBotCommand- Defines the command contract:
Command,Description, andExecuteAsync(...).
- Defines the command contract:
BotCommandFactory- Resolves command handlers by command name.
CommandUpdateHandler- Parses incoming updates, extracts the command text from either message text or caption, resolves the handler from the factory, executes it, and logs errors.
BotController- Receives webhook updates, validates the Telegram secret token, checks allowed chat IDs, and enqueues accepted updates to Hangfire for background processing.
AgentService- Builds AI-backed conversations with personality context, semantically relevant memories, and reduced chat history before calling the model.
ExpenseCommand- Returns the user's current expense total or handles statement PDF uploads for automated expense registration.
ExpenseAnalysisService- Sends uploaded PDFs to Markitdown for text extraction, invokes the AI agent with tool-calling, and persists a single billing-period expense summary.
EmbeddingService- Generates Gemini document/query embeddings and normalizes vectors before persistence and search.
MemoryService- Persists memory embeddings and status metadata, backfills missing embeddings, and performs cosine-distance semantic search for the current user input.
AssistantChatReducer- Replaces older chat turns with a rolling assistant summary while preserving recent turns verbatim.
MemoryMaintenanceService- Archives stale memories, skips time-bound or contradictory clusters, and asks Gemini to merge durable near-duplicate memories.
- Telegram sends an update to
POST /bot/update. - The request secret token is validated in
BotController. - If configured,
BotControllerchecksBot:AllowedChatIdsand rejects unauthorized chats. BotControllerenqueues the accepted update as a Hangfire background job.CommandUpdateJobinvokesCommandUpdateHandler.CommandUpdateHandlerextracts the/commandfrom the incoming text or caption.BotCommandFactoryresolves the matching command handler.- The command executes and sends responses through
ITelegramBotClient.
For AI-backed flows, AgentService injects personality context, semantically relevant long-term memories, and reduced chat history before invoking the model.
- .NET 8 SDK
- A Telegram bot token
- A Google Gemini API key
- A PostgreSQL database with
pgvectorsupport available - A Markitdown service endpoint for PDF-to-markdown conversion
- A webhook URL reachable by Telegram
- A secret token for webhook verification
Set the Bot and AI sections in Assistant.Api/appsettings.Development.json:
{
"Bot": {
"BotToken": "YOUR_BOT_TOKEN",
"WebhookUrl": "YOUR_WEBHOOK_URL",
"SecretToken": "YOUR_SECRET_TOKEN",
"AllowedChatIds": []
},
"AI": {
"GoogleApiKey": "YOUR_GOOGLE_GEMINI_API_KEY",
"Model": "gemini-3.1-flash-lite-preview",
"EmbeddingModel": "gemini-embedding-2-preview",
"EmbeddingDimensions": 768,
"MemoryMaintenanceModel": "gemini-3.1-flash-lite-preview",
"MemoryArchiveAfterDaysLow": 30,
"MemoryArchiveAfterDaysMedium": 90,
"MemoryConsolidationCron": "0 3 * * *",
"DefaultTimeZoneId": "Europe/Istanbul"
},
"Markitdown": {
"Endpoint": "YOUR_MARKITDOWN_ENDPOINT"
}
}Also configure database connection strings in the same file:
{
"ConnectionStrings": {
"PostgreSQL": "Host=...;Port=5432;Database=...;Username=...;Password=...",
"HangfireDb": "Host=...;Port=5432;Database=...;Username=...;Password=..."
}
}The semantic-memory migration enables the PostgreSQL vector extension and adds a vector(768) embedding column to user_memories, so the target PostgreSQL instance must have pgvector installed.
dotnet restore
dotnet run --project Assistant.ApiWebhook endpoint used by this API:
POST /bot/update
In development, Hangfire Dashboard is available at:
GET /hangfire
Assistant/
├── Assistant.Api/
│ ├── Controllers/
│ │ └── BotController.cs
│ ├── Data/
│ │ ├── Configurations/
│ │ └── Migrations/
│ ├── Services/
│ │ ├── Abstracts/
│ │ └── Concretes/
│ ├── Extensions/
│ └── Domain/
│ ├── Configurations/
│ └── Entities/
└── Assistant.sln
Near-term focus areas:
- Richer expense reporting and breakdowns on top of persisted billing-period summaries
- Additional document-driven workflows using the existing background processing pipeline