Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ POSTGRES_DB=myapp_dev
POSTGRES_HOST=db
POSTGRES_PORT=5432
DEBUG=false
LOG_LEVEL=INFO
LOG_LEVEL=INFO
TELEGRAM_BOT_TOKEN=your_bot_token_here
TELEGRAM_WEBHOOK_SECRET=some-long-random-secret
7 changes: 6 additions & 1 deletion app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
import logging
from contextlib import asynccontextmanager
from app.config import settings
from app.database import engine
from app.database import engine, Base
from app.models import TaskInput

logger = logging.getLogger(__name__)

Expand All @@ -26,6 +27,10 @@ async def lifespan(app: FastAPI):

app = FastAPI(lifespan=lifespan)

@app.on_event("startup")
async def on_startup():
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)

# CORS if needed during development
app.add_middleware(
Expand Down
3 changes: 3 additions & 0 deletions app/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from app.models.task_input import TaskInput

__all__ = ["TaskInput"]
25 changes: 25 additions & 0 deletions app/models/task_input.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from datetime import datetime
from sqlalchemy import DateTime, Integer, String, Text, JSON
from sqlalchemy.orm import Mapped, mapped_column

from app.database import Base


class TaskInput(Base):
__tablename__ = "task_inputs"

id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)

source: Mapped[str] = mapped_column(String(50), nullable=False)
original_text: Mapped[str] = mapped_column(Text, nullable=False)

extracted_event: Mapped[dict] = mapped_column(JSON, nullable=False)

telegram_chat_id: Mapped[str | None] = mapped_column(String(100), nullable=True)
telegram_message_id: Mapped[str | None] = mapped_column(String(100), nullable=True)

created_at: Mapped[datetime] = mapped_column(
DateTime,
default=datetime.utcnow,
nullable=False,
)
28 changes: 28 additions & 0 deletions app/services/task_input_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from sqlalchemy.ext.asyncio import AsyncSession

from app.models.task_input import TaskInput


async def save_task_input(
db: AsyncSession,
source: str,
original_text: str,
extracted_event: dict,
telegram_chat_id: str | None = None,
telegram_message_id: str | None = None,
) -> TaskInput:

task_input = TaskInput(
source=source,
original_text=original_text,
extracted_event=extracted_event,
telegram_chat_id=telegram_chat_id,
telegram_message_id=telegram_message_id,
)

db.add(task_input)

await db.commit()
await db.refresh(task_input)

return task_input
86 changes: 86 additions & 0 deletions app/services/telegram_bot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import os
import logging

from dotenv import load_dotenv
from telegram import Update
from telegram.ext import ApplicationBuilder, CommandHandler, MessageHandler, ContextTypes, filters
Comment on lines +4 to +6

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Missing telegram dependency 🐞 Bug ≡ Correctness

app/services/telegram_bot.py imports the telegram package, but the project dependency list does
not include python-telegram-bot, so importing/running the bot will fail with
ModuleNotFoundError. This blocks the Telegram integration from working at all.
Agent Prompt
## Issue description
`app/services/telegram_bot.py` imports `telegram` / `telegram.ext`, but the repository does not declare a dependency that provides those modules. This will crash at import-time with `ModuleNotFoundError: No module named 'telegram'`.

## Issue Context
- The Telegram bot implementation lives in `app/services/telegram_bot.py`.
- Dependencies are declared in `pyproject.toml`; lockfile should be updated accordingly.

## Fix
1. Add `python-telegram-bot` to `[project].dependencies` in `pyproject.toml`.
2. Re-lock dependencies (e.g., `uv add python-telegram-bot` or equivalent) so `uv.lock` is updated.

## Fix Focus Areas
- pyproject.toml[1-13]
- app/services/telegram_bot.py[1-13]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

from app.services.event_extractor import extract_event_from_text
from app.services.openrouter_client import OpenRouterError
from sqlalchemy.ext.asyncio import AsyncSession

from app.database import AsyncSessionLocal
from app.services.task_input_service import save_task_input

load_dotenv()

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
await update.message.reply_text(
"Hi! Send me a task and I’ll turn it into a TaskCraft event draft."
)


async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
message_text = update.message.text
timezone = "Asia/Jerusalem"

await update.message.reply_text("Processing task... ⏳")

try:
event = await extract_event_from_text(message_text, timezone)

async with AsyncSessionLocal() as db:
await save_task_input(
db=db,
source="telegram",
original_text=message_text,
extracted_event=event,
telegram_chat_id=str(update.effective_chat.id),
telegram_message_id=str(update.message.message_id),
)
response = f"""
✅ Event Draft Created

📌 Title: {event["title"]}
🕒 Start: {event["start_at"]}
🕔 End: {event["end_at"]}
📅 All day: {event["all_day"]}
🎯 Confidence: {event["confidence"]}

📝 Notes:
{event["notes"]}
""".strip()

if event["missing_info"]:
response += "\n\n❓ Missing info:\n" + "\n".join(
f"- {item}" for item in event["missing_info"]
)

await update.message.reply_text(response)

except OpenRouterError as e:
await update.message.reply_text(f"❌ OpenRouter failed:\n{str(e)}")

except Exception as e:
await update.message.reply_text(f"❌ Something went wrong:\n{str(e)}")

def run_bot() -> None:
token = os.getenv("TELEGRAM_BOT_TOKEN")

if not token:
raise RuntimeError("Missing TELEGRAM_BOT_TOKEN in .env")

app = ApplicationBuilder().token(token).build()

app.add_handler(CommandHandler("start", start))
app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message))

logger.info("Starting Telegram bot with polling...")
app.run_polling()


if __name__ == "__main__":
run_bot()
Loading