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 still intentionally small, but the core command and agent infrastructure is already in place.
- The bot currently supports
/start,/chat, and/memory. - Plain text messages without a slash command are routed to the
chatcommand automatically. - The chat agent keeps a versioned long-term user memory manifest and can update it through tool calling.
- Successful chat turns are persisted and recalled through PostgreSQL full-text search so the agent can pull relevant older conversation snippets.
- The chat agent can schedule, list, cancel, and reschedule deferred tasks and reminders through Hangfire-backed tools.
- The chat agent can trigger live web search for fresh information.
- Incoming Telegram updates and deferred tasks are processed in the background via Hangfire.
The bot currently supports the following commands:
| Command | Description |
|---|---|
/start |
Registers the Telegram user and sends a welcome message. |
/chat |
General-purpose chat entrypoint. The agent can answer questions, remember useful personal context, manage reminders/tasks, and search the web when needed. |
/memory |
Returns the active long-term UserMemoryManifest currently used to augment chat responses. |
Bot commands are registered with Telegram during application startup.
Examples:
/chat 5 saat sonra Mustafa abiyle toplantımı hatırlat/chat NVIDIA stock price current/memoryyarın sabah 9'da su içmeyi hatırlat
Chat flow:
- The agent combines recent session history, the active memory manifest, pending tasks, and relevant persisted chat turns before answering.
- Memory is stored as versioned
UserMemoryManifestrecords rather than individual memory rows. - Older chat turns are stored in
chat_turnsand searched through PostgreSQL full-text search. - Agent tools currently include web search, memory manifest update, task scheduling, and current time lookup.
- Run
/memoryto inspect the currently active manifest that is being injected into chat context.
Hangfire is currently used for two job types:
| Job | Trigger | Description |
|---|---|---|
CommandUpdateJob |
On each accepted Telegram webhook update | Processes incoming Telegram updates asynchronously. |
DeferredIntentDispatchJob |
Created dynamically for one-time or recurring deferred intents | Wakes the agent up later to execute scheduled reminders/tasks. |
Implementation notes:
- Incoming Telegram updates are enqueued from
BotController. - One-time deferred tasks are scheduled with
IBackgroundJobClient.Schedule. - Recurring deferred tasks are registered dynamically with
IRecurringJobManager.AddOrUpdate. - There is no fixed startup-time recurring reminder job documented as part of the active runtime flow anymore.
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 message text or caption, defaults plain text messages to
chat, resolves the handler from the factory, executes it, and logs errors.
- Parses incoming updates, extracts the command text from message text or caption, defaults plain text messages to
BotController- Receives webhook updates, validates the Telegram secret token, checks allowed chat IDs, and enqueues accepted updates to Hangfire for background processing.
StartCommand- Registers a Telegram user in the database.
ChatCommand- Invokes
AgentService, persists successful chat turns, and sends responses throughTelegramResponseSender.
- Invokes
MemoryCommand- Fetches the active
UserMemoryManifestfor the current chat and sends it back to Telegram.
- Fetches the active
AgentService- Builds the
ChatClientAgent, registers tools, injects personality/memory/task context, and runs chat-history lookup over persisted chat turns before each response.
- Builds the
MemoryContextProvider- Injects the active
UserMemoryManifestinto the chat agent context.
- Injects the active
MemoryToolFunctions- Exposes the tool that updates the user's memory manifest.
TaskToolFunctions- Exposes task scheduling, listing, cancellation, and rescheduling tools backed by
DeferredIntentplus Hangfire.
- Exposes task scheduling, listing, cancellation, and rescheduling tools backed by
ChatTurnService- Persists successful chat turns and searches older turns with PostgreSQL full-text ranking for recall.
WebSearchToolFunctions- Executes Google AI Studio-backed web searches for fresh public information and returns grounded text back to the chat agent.
TelegramResponseSender- Centralizes long Telegram message splitting and Markdown fallback handling for agent-style responses.
- 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 slash command from the incoming text or caption; if there is no slash command, it routes the update tochat.BotCommandFactoryresolves the matching command handler.- For chat requests, the agent session is invoked with personality context, the active memory manifest, pending tasks, and full-text retrieval over persisted prior chat turns.
- The command executes and sends its response either through
TelegramResponseSenderor directly throughITelegramBotClient, depending on the command path.
- .NET 10 SDK
- PostgreSQL
- A Telegram bot token
- An xAI API key
- A Google AI Studio API key
- A webhook URL reachable by Telegram
- A secret token for webhook verification
Optional:
- An OpenRouter API key if you want to keep the optional provider config populated
Set the Bot and AIProviders sections in Assistant.Api/appsettings.Development.json:
{
"Bot": {
"BotToken": "YOUR_BOT_TOKEN",
"WebhookUrl": "YOUR_WEBHOOK_URL",
"SecretToken": "YOUR_SECRET_TOKEN",
"AllowedChatIds": []
},
"AIProviders": {
"OpenRouter": {
"ApiKey": "YOUR_OPENROUTER_API_KEY",
"ApiUrl": "https://openrouter.ai/api/v1",
"Model": "google/gemini-3.1-flash-lite-preview"
},
"GoogleAIStudio": {
"ApiKey": "YOUR_GOOGLE_AI_STUDIO_API_KEY",
"Model": "gemini-3.1-flash-lite-preview"
},
"XAI": {
"ApiKey": "YOUR_XAI_API_KEY",
"ApiUrl": "https://api.x.ai/v1",
"Model": "grok-4-1-fast-reasoning"
},
"DefaultTimeZoneId": "Europe/Istanbul"
}
}Provider notes:
AIProviders:XAIis the main chat/agent provider used byAgentService.AIProviders:GoogleAIStudiois used for live web search.AIProviders:OpenRouterremains configured in the project, but it is not the main active chat path right now.AIProviders:DefaultTimeZoneIdis shared by time-sensitive chat behavior and deferred task scheduling.
Also configure database connection strings in the same file:
{
"ConnectionStrings": {
"PostgreSQL": "Host=...;Port=5432;Database=...;Username=...;Password=...",
"HangfireDb": "Host=...;Port=5432;Database=...;Username=...;Password=..."
}
}dotnet restore
dotnet ef database update --project Assistant.Api
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/
│ ├── Domain/
│ │ └── Configurations/
│ ├── Extensions/
│ ├── Features/
│ │ ├── Chat/
│ │ └── UserManagement/
│ ├── Services/
│ │ ├── Abstracts/
│ │ └── Concretes/
│ └── Screens/
├── Assistant.Api.Tests/
│ ├── Chat/
│ ├── UserManagement/
│ └── Fixtures/
└── Assistant.sln
Near-term focus areas:
- Hardening the memory and deferred-task flows as the agent surface grows