Source code for src.commands.user_prompt_command

# src/commands/user_prompt_command.py
import logging
import time
import os

from bot.state import StateManager, get_team_id
from services.prompt_service import get_available_topics

_TIME_FORMAT = "%I:%M:%S %p"

_PRESET_TIME_OPTIONS = [
    {"text": {"type": "plain_text", "text": "09:30:00 AM"}, "value": "09:30:00 AM"},
    {"text": {"type": "plain_text", "text": "10:00:00 AM"}, "value": "10:00:00 AM"},
    {"text": {"type": "plain_text", "text": "10:30:00 AM"}, "value": "10:30:00 AM"},
    {"text": {"type": "plain_text", "text": "11:00:00 AM"}, "value": "11:00:00 AM"},
    {"text": {"type": "plain_text", "text": "11:30:00 AM"}, "value": "11:30:00 AM"},
    {"text": {"type": "plain_text", "text": "12:00:00 PM"}, "value": "12:00:00 PM"},
    {"text": {"type": "plain_text", "text": "12:30:00 PM"}, "value": "12:30:00 PM"},
    {"text": {"type": "plain_text", "text": "01:00:00 PM"}, "value": "01:00:00 PM"},
    {"text": {"type": "plain_text", "text": "02:00:00 PM"}, "value": "02:00:00 PM"},
    {"text": {"type": "plain_text", "text": "03:00:00 PM"}, "value": "03:00:00 PM"},
]


[docs] def send_user_prompt_invitation(client, user_id: str, team_id: str) -> None: """ DM a user to invite them to create today's prompt. Called by the scheduler when it randomly selects a user. """ try: client.chat_postMessage( channel=user_id, text="You've been selected to create today's vibe check prompt! :writing_hand:", blocks=[ { "type": "section", "text": { "type": "mrkdwn", "text": ( ":writing_hand: *You've been selected to create today's vibe check prompt!*\n\n" "You can write your own prompt, choose a topic, and pick when it goes out. " "Hit the button below to get started." ), }, }, { "type": "actions", "block_id": f"user_prompt_invite|{team_id}", "elements": [ { "type": "button", "text": {"type": "plain_text", "text": ":pencil: Create Today's Prompt"}, "action_id": "open_user_prompt_modal", "style": "primary", } ], }, ], ) logging.info(f"[USER PROMPT] Sent prompt invitation to user {user_id} (team {team_id})") except Exception as e: logging.error(f"[USER PROMPT] Failed to DM user {user_id}: {e}")
[docs] def register_user_prompt_handlers(bolt_app, state_manager: StateManager): """ Registers the action and modal handlers for the user-created daily prompt feature. """ @bolt_app.action("open_user_prompt_modal") def handle_open_modal(ack, body, client): ack() team_id = get_team_id(body) expiry = int(time.time()) + 300 # 5-minute window from button click topics = get_available_topics() topic_options = [{"text": {"type": "plain_text", "text": t}, "value": t} for t in topics] client.views_open( trigger_id=body["trigger_id"], view={ "type": "modal", "callback_id": "user_prompt_modal", "private_metadata": f"{team_id}|{expiry}", "title": {"type": "plain_text", "text": "Create Today's Prompt"}, "submit": {"type": "plain_text", "text": "Submit"}, "close": {"type": "plain_text", "text": "Cancel"}, "blocks": [ { "type": "section", "text": { "type": "mrkdwn", "text": "Pick a topic for today's prompt — required. Then optionally write your own prompt below. If you leave it blank, a random prompt from that topic will be used.", }, }, { "type": "input", "block_id": "topic_block", "label": {"type": "plain_text", "text": "Topic"}, "element": { "type": "static_select", "action_id": "topic_select", "placeholder": {"type": "plain_text", "text": "Pick a topic..."}, "options": topic_options, }, }, { "type": "input", "block_id": "custom_prompt_block", "optional": True, "label": {"type": "plain_text", "text": "Write your own prompt (optional)"}, "hint": {"type": "plain_text", "text": "Leave blank to use a random prompt from the topic above."}, "element": { "type": "plain_text_input", "action_id": "custom_prompt_input", "multiline": True, "placeholder": {"type": "plain_text", "text": "e.g. If you could live anywhere in the world, where would it be?"}, }, }, { "type": "input", "block_id": "time_block", "optional": True, "label": {"type": "plain_text", "text": "Send time"}, "hint": {"type": "plain_text", "text": "Leave blank to keep today's scheduled time."}, "element": { "type": "static_select", "action_id": "time_select", "placeholder": {"type": "plain_text", "text": "Pick a time..."}, "options": _PRESET_TIME_OPTIONS, }, }, { "type": "input", "block_id": "send_now_block", "optional": True, "label": {"type": "plain_text", "text": "Send immediately"}, "element": { "type": "checkboxes", "action_id": "send_now_check", "options": [ { "text": {"type": "plain_text", "text": "Post the prompt to the channel right now"}, "value": "send_now", } ], }, }, ], }, ) @bolt_app.view("user_prompt_modal") def handle_user_prompt_modal(ack, body, client): metadata = body["view"].get("private_metadata", "") parts = metadata.split("|", 1) team_id = parts[0] or get_team_id(body) expiry = int(parts[1]) if len(parts) > 1 and parts[1].isdigit() else None if expiry and time.time() > expiry: ack( response_action="errors", errors={"topic_block": "Sorry, your 5-minute window to create a prompt has expired. The bot will use a regular prompt today."}, ) logging.info(f"[USER PROMPT] Submission rejected — window expired for team {team_id}") return ack() state = state_manager.get_state(team_id) values = body["view"]["state"]["values"] # Topic (required) topic_opt = ( values.get("topic_block", {}) .get("topic_select", {}) .get("selected_option") ) topic = topic_opt["value"] if topic_opt else None # Custom prompt text (optional) custom_text = ( values.get("custom_prompt_block", {}) .get("custom_prompt_input", {}) .get("value") or "" ).strip() # Time (optional) time_opt = ( values.get("time_block", {}) .get("time_select", {}) .get("selected_option") ) selected_time = time_opt["value"] if time_opt else None # Send now checkbox (optional) send_now = bool( values.get("send_now_block", {}) .get("send_now_check", {}) .get("selected_options") ) if send_now: from bot.posting import post_custom_prompt from services.prompt_service import get_random_prompt_by_topic channel = state.get_active_channel() or os.getenv("DEFAULT_CHANNEL") prompt_text = custom_text or (get_random_prompt_by_topic(topic)[1] if topic else None) logging.info(f"[USER PROMPT] send_now=True channel={channel!r} prompt_text={prompt_text!r}") if prompt_text and channel: ts = post_custom_prompt( client, prompt_text, channel=channel, team_id=team_id, prefix_text="Prompt of the day:", footnote_text="user-created vibe check", ) if ts: state.set_last_prompt_ts(ts) logging.info(f"[USER PROMPT] Sent immediately to {channel} for team {team_id}") else: if custom_text: state.set_pending_custom_prompt(custom_text) logging.info(f"[USER PROMPT] Custom prompt set for team {team_id}: {custom_text!r}") elif topic: state.set_pending_topic(topic) logging.info(f"[USER PROMPT] Topic set for team {team_id}: {topic}") if selected_time: state.set_daily_target_time(selected_time) logging.info(f"[USER PROMPT] Time overridden for team {team_id}: {selected_time}") user_id = body["user"]["id"] summary_parts = [] if send_now: summary_parts.append("sent to the channel now") else: if custom_text: summary_parts.append(f"prompt: _{custom_text}_") elif topic: summary_parts.append(f"topic: `{topic}`") else: summary_parts.append("a random prompt") if selected_time: summary_parts.append(f"at `{selected_time}`") client.chat_postMessage( channel=user_id, text=f":tada: Got it! Today's vibe check will use {' '.join(summary_parts)}. Thanks for contributing! :tada:", )