@@ -406,6 +406,79 @@ async def _execute_medhack(
406406
407407 return f"Patient #{ new_case ['id' ]} ({ title } ) is now live!"
408408
409+ # --- Announcement creation ---
410+ MEDHACK_ANNOUNCE_ADMIN_IDS = ["U08DD0DCL4D" , "U05QPB483K9" , "U08CWAPMQH0" , "U07QJ5L0EHY" ]
411+ announce_keywords = ["announce" , "announcement" , "post announcement" , "create announcement" ]
412+ is_announcement = any (k in text_lower for k in announce_keywords )
413+
414+ if is_announcement :
415+ if user_id not in MEDHACK_ANNOUNCE_ADMIN_IDS :
416+ return f"<@{ user_id } > Sorry, only authorized MedHack admins can create announcements."
417+
418+ # Use LLM to extract title and body
419+ extract_prompt = f"""Extract the announcement title and body from this message.
420+ The user wants to create an announcement for the MedHack: Frontiers website.
421+
422+ User message: "{ text } "
423+
424+ Return ONLY valid JSON with two keys: "title" and "body".
425+ If you cannot determine a clear title or body from the message, set the missing field to null.
426+
427+ Example: {{"title": "Workshop Schedule Update", "body": "The AI workshop has been moved to Room 3B at 2pm."}}
428+
429+ JSON:"""
430+ openai_client = get_llm_client ("openai" )
431+ extract_response = await openai_client .chat ([
432+ {"role" : "system" , "content" : "You extract structured data from text. Return valid JSON only." },
433+ {"role" : "user" , "content" : extract_prompt }
434+ ], model = "gpt-4o-mini" , max_tokens = 1024 )
435+
436+ try :
437+ content = extract_response .content .strip ()
438+ if content .startswith ("```" ):
439+ content = re .sub (r'^```\w*\n?' , '' , content )
440+ content = re .sub (r'\n?```$' , '' , content )
441+ extracted = json .loads (content )
442+ except json .JSONDecodeError :
443+ extracted = {}
444+
445+ ann_title = extracted .get ("title" )
446+ ann_body = extracted .get ("body" )
447+
448+ if not ann_title or not ann_body :
449+ return (
450+ f"<@{ user_id } > I need both a *title* and *body* for the announcement. "
451+ f"Please try again with something like:\n "
452+ f"_\" Create an announcement titled 'Workshop Update' with body 'The AI workshop is moved to 2pm.'\" _"
453+ )
454+
455+ # Call the backend — use Roo's bot user ID so the announcement
456+ # author avatar is always Roo's, not the requesting human's.
457+ from ..clients .mlai_backend import MLAIBackendClient
458+ from ..slack_client import get_bot_user_id
459+ backend = MLAIBackendClient ()
460+ bot_id = get_bot_user_id ()
461+ result = await backend .medhack_create_announcement (ann_title , ann_body , bot_id or user_id )
462+
463+ if result is None :
464+ return f"<@{ user_id } > Something went wrong creating the announcement. Please try again later."
465+
466+ status_code = result .get ("status_code" )
467+ if status_code == 400 :
468+ return f"<@{ user_id } > The announcement couldn't be created — the server said something is missing. Details: { result .get ('detail' , 'unknown' )} "
469+ if status_code in (401 , 403 ):
470+ return f"<@{ user_id } > Authorization error creating the announcement. Please contact an admin."
471+ if status_code is not None :
472+ return f"<@{ user_id } > Unexpected error (HTTP { status_code } ): { result .get ('detail' , 'unknown' )} "
473+
474+ # Success — confirm and post to channel
475+ confirm_msg = f"Announcement *\" { ann_title } \" * has been posted to the MedHack: Frontiers website."
476+ if channel_id :
477+ from ..slack_client import post_message
478+ post_message (channel = channel_id , text = confirm_msg , thread_ts = thread_ts )
479+
480+ return confirm_msg
481+
409482 # --- Determine mode: event info vs diagnosis game ---
410483 event_keywords = ["when" , "where" , "ticket" , "register" , "schedule" , "speaker" ,
411484 "venue" , "price" , "event" , "medhack" , "frontiers" , "sign up" ]
0 commit comments