plain url input accepted

This commit is contained in:
2026-05-10 18:46:51 +01:00
parent 821302a9be
commit 5b434abd06
3 changed files with 36 additions and 1 deletions

View File

@@ -101,6 +101,7 @@ python main.py
The bot listens for commands: The bot listens for commands:
- `/op <url or paste>` — parse an opportunity - `/op <url or paste>` — parse an opportunity
- `/ev <url or paste>` — parse an event - `/ev <url or paste>` — parse an event
- If you send a URL directly in chat, the bot will ask whether to process it as an event or an opportunity using buttons.
If you paste text (instead of sending a URL), the bot will parse it and when you click Save it will prompt you for a source URL (or you can `/skip`). If you paste text (instead of sending a URL), the bot will parse it and when you click Save it will prompt you for a source URL (or you can `/skip`).

Binary file not shown.

36
bot.py
View File

@@ -3,6 +3,7 @@ import asyncio
import sys import sys
import concurrent.futures import concurrent.futures
import logging import logging
import re
from contextlib import suppress from contextlib import suppress
from dotenv import load_dotenv from dotenv import load_dotenv
@@ -94,6 +95,17 @@ def retry(max_attempts=3, backoff_factor=2, initial_delay=1):
task_queue = asyncio.Queue() task_queue = asyncio.Queue()
def is_http_url(text: str) -> bool:
return bool(re.match(r'^https?://\S+$', text.strip()))
def build_url_choice_keyboard(url: str):
return InlineKeyboardMarkup([
[InlineKeyboardButton("📅 Process as Event", callback_data='choose_type:event')],
[InlineKeyboardButton("📋 Process as Opportunity", callback_data='choose_type:opportunity')],
])
def build_entry_summary(data, entry_type, saved=False): def build_entry_summary(data, entry_type, saved=False):
if entry_type == "event": if entry_type == "event":
event_datetime = data.get('date_time') or data.get('datetime') event_datetime = data.get('date_time') or data.get('datetime')
@@ -182,7 +194,8 @@ async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
"Welcome! I can extract arts opportunities and events.\n\n" "Welcome! I can extract arts opportunities and events.\n\n"
"📋 **Commands:**\n" "📋 **Commands:**\n"
"/op <url> - Extract an opportunity\n" "/op <url> - Extract an opportunity\n"
"/ev <url> - Extract an event" "/ev <url> - Extract an event\n\n"
"You can also send a URL directly and I will ask whether to process it as an event or opportunity."
) )
async def handle_opportunity(update: Update, context: ContextTypes.DEFAULT_TYPE): async def handle_opportunity(update: Update, context: ContextTypes.DEFAULT_TYPE):
@@ -230,6 +243,15 @@ async def handle_followup_text(update: Update, context: ContextTypes.DEFAULT_TYP
return return
if not context.user_data.get('awaiting_save_url'): if not context.user_data.get('awaiting_save_url'):
text = (update.message.text or '').strip()
if not text or not is_http_url(text):
return
context.user_data['pending_url_to_process'] = text
await update.message.reply_text(
"What should I do with this URL?",
reply_markup=build_url_choice_keyboard(text)
)
return return
text = update.message.text.strip() text = update.message.text.strip()
@@ -257,6 +279,18 @@ async def button_handler(update: Update, context: ContextTypes.DEFAULT_TYPE):
query = update.callback_query query = update.callback_query
await query.answer() await query.answer()
if query.data.startswith('choose_type:'):
pending_url = context.user_data.get('pending_url_to_process')
if not pending_url:
await query.edit_message_text("❌ I couldn't find a pending URL to process.")
return
entry_type = query.data.split(':', 1)[1]
context.user_data['pending_url_to_process'] = None
await query.edit_message_text(f"📥 Queued URL for {entry_type} processing...")
await task_queue.put((update, context, pending_url, entry_type, 'url'))
return
if query.data == 'save_db': if query.data == 'save_db':
data = context.user_data.get('last_extracted') data = context.user_data.get('last_extracted')
entry_type = context.user_data.get('last_entry_type', 'opportunity') entry_type = context.user_data.get('last_entry_type', 'opportunity')