Created
February 12, 2026 05:34
-
-
Save lynnaloo/5ffb9e7620d6db95dab4d3787a5703fd to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * ChatterActionItemParser | |
| * | |
| * Invocable Apex class designed to be called from a Salesforce Flow. | |
| * Accepts a chatter summary (e.g., from an AI prompt summarization) and | |
| * parses out numbered action items, returning them as a List<String> | |
| * that can be iterated in a Flow Loop to create Tasks. | |
| * | |
| * Supported formats: | |
| * 1. Action item text | |
| * 2. Action item text | |
| * - Action item text | |
| * * Action item text | |
| * | |
| * Multi-line action items are concatenated until the next item or end of text. | |
| */ | |
| public with sharing class ChatterActionItemParser { | |
| // ── Flow Input ────────────────────────────────────────────────────── | |
| public class ActionItemRequest { | |
| @InvocableVariable(label='Chatter Summary Text' description='The full AI-generated chatter summary containing action items' required=true) | |
| public String summaryText; | |
| } | |
| // ── Flow Output ───────────────────────────────────────────────────── | |
| public class ActionItemResult { | |
| @InvocableVariable(label='Action Items' description='Ordered list of parsed action items') | |
| public List<String> actionItems; | |
| @InvocableVariable(label='Item Count' description='Number of action items found') | |
| public Integer itemCount; | |
| @InvocableVariable(label='Success' description='Whether parsing completed without error') | |
| public Boolean success; | |
| @InvocableVariable(label='Error Message' description='Error details if parsing failed') | |
| public String errorMessage; | |
| } | |
| // ── Invocable Method ──────────────────────────────────────────────── | |
| @InvocableMethod( | |
| label='Parse Chatter Action Items' | |
| description='Extracts numbered/bulleted action items from an AI chatter summary and returns them as a string collection for Flow iteration.' | |
| category='Chatter Utilities' | |
| ) | |
| public static List<ActionItemResult> parseActionItems(List<ActionItemRequest> requests) { | |
| List<ActionItemResult> results = new List<ActionItemResult>(); | |
| for (ActionItemRequest req : requests) { | |
| ActionItemResult result = new ActionItemResult(); | |
| result.actionItems = new List<String>(); | |
| result.success = true; | |
| try { | |
| if (String.isBlank(req.summaryText)) { | |
| result.success = false; | |
| result.errorMessage = 'Summary text is blank or null.'; | |
| result.itemCount = 0; | |
| results.add(result); | |
| continue; | |
| } | |
| result.actionItems = extractActionItems(req.summaryText); | |
| result.itemCount = result.actionItems.size(); | |
| } catch (Exception ex) { | |
| result.success = false; | |
| result.errorMessage = ex.getMessage(); | |
| result.itemCount = 0; | |
| } | |
| results.add(result); | |
| } | |
| return results; | |
| } | |
| // ── Core Parsing Logic ────────────────────────────────────────────── | |
| /** | |
| * Extracts action items from the summary text. | |
| * Looks for a header line (e.g., "Action items" / "action items include:") | |
| * then parses numbered (1. 2. 3.) or bulleted (- / *) list entries. | |
| * Multi-line items are joined with a space. | |
| */ | |
| @TestVisible | |
| private static List<String> extractActionItems(String summaryText) { | |
| List<String> items = new List<String>(); | |
| // Normalize line breaks | |
| String normalized = summaryText.replace('\r\n', '\n').replace('\r', '\n'); | |
| List<String> lines = normalized.split('\n'); | |
| // Regex patterns | |
| // Numbered item: "1. Some text" or "1) Some text" | |
| // Bulleted item: "- Some text" or "* Some text" | |
| String numberedPattern = '^\\s*\\d+[.\\)]\\s+(.+)'; | |
| String bulletPattern = '^\\s*[-*]\\s+(.+)'; | |
| // Determine where the action items section begins. | |
| // We look for a line containing "action item" (case-insensitive). | |
| Integer startIndex = 0; | |
| for (Integer i = 0; i < lines.size(); i++) { | |
| if (lines[i].toLowerCase().contains('action item')) { | |
| startIndex = i + 1; // start parsing from the line after the header | |
| break; | |
| } | |
| } | |
| // If no header found, scan the entire text for list items | |
| if (startIndex == 0) { | |
| startIndex = 0; | |
| } | |
| String currentItem = null; | |
| for (Integer i = startIndex; i < lines.size(); i++) { | |
| String line = lines[i]; | |
| String trimmed = line.trim(); | |
| // Skip empty lines between items | |
| if (String.isBlank(trimmed)) { | |
| // If we have a pending item, finalize it | |
| if (currentItem != null) { | |
| items.add(currentItem.trim()); | |
| currentItem = null; | |
| } | |
| continue; | |
| } | |
| // Check for numbered item | |
| Boolean isNumbered = Pattern.matches(numberedPattern, trimmed); | |
| // Check for bulleted item | |
| Boolean isBulleted = Pattern.matches(bulletPattern, trimmed); | |
| if (isNumbered || isBulleted) { | |
| // Save any previous item | |
| if (currentItem != null) { | |
| items.add(currentItem.trim()); | |
| } | |
| // Extract the text after the marker | |
| if (isNumbered) { | |
| currentItem = trimmed.replaceFirst('^\\s*\\d+[.\\)]\\s+', ''); | |
| } else { | |
| currentItem = trimmed.replaceFirst('^\\s*[-*]\\s+', ''); | |
| } | |
| } else if (currentItem != null) { | |
| // Continuation line for a multi-line action item | |
| currentItem += ' ' + trimmed; | |
| } | |
| // Lines before any list item starts (and after the header) are ignored | |
| } | |
| // Don't forget the last item | |
| if (currentItem != null) { | |
| items.add(currentItem.trim()); | |
| } | |
| return items; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment