feat: Add Official Microsoft & Gemini Skills (845+ Total)
🚀 Impact Significantly expands the capabilities of **Antigravity Awesome Skills** by integrating official skill collections from **Microsoft** and **Google Gemini**. This update increases the total skill count to **845+**, making the library even more comprehensive for AI coding assistants. ✨ Key Changes 1. New Official Skills - **Microsoft Skills**: Added a massive collection of official skills from [microsoft/skills](https://github.com/microsoft/skills). - Includes Azure, .NET, Python, TypeScript, and Semantic Kernel skills. - Preserves the original directory structure under `skills/official/microsoft/`. - Includes plugin skills from the `.github/plugins` directory. - **Gemini Skills**: Added official Gemini API development skills under `skills/gemini-api-dev/`. 2. New Scripts & Tooling - **`scripts/sync_microsoft_skills.py`**: A robust synchronization script that: - Clones the official Microsoft repository. - Preserves the original directory heirarchy. - Handles symlinks and plugin locations. - Generates attribution metadata. - **`scripts/tests/inspect_microsoft_repo.py`**: Debug tool to inspect the remote repository structure. - **`scripts/tests/test_comprehensive_coverage.py`**: Verification script to ensure 100% of skills are captured during sync. 3. Core Improvements - **`scripts/generate_index.py`**: Enhanced frontmatter parsing to safely handle unquoted values containing `@` symbols and commas (fixing issues with some Microsoft skill descriptions). - **`package.json`**: Added `sync:microsoft` and `sync:all-official` scripts for easy maintenance. 4. Documentation - Updated `README.md` to reflect the new skill counts (845+) and added Microsoft/Gemini to the provider list. - Updated `CATALOG.md` and `skills_index.json` with the new skills. 🧪 Verification - Ran `scripts/tests/test_comprehensive_coverage.py` to verify all Microsoft skills are detected. - Validated `generate_index.py` fixes by successfully indexing the new skills.
This commit is contained in:
@@ -0,0 +1,254 @@
|
||||
---
|
||||
name: azure-communication-callautomation-java
|
||||
description: Build call automation workflows with Azure Communication Services Call Automation Java SDK. Use when implementing IVR systems, call routing, call recording, DTMF recognition, text-to-speech, or AI-powered call flows.
|
||||
package: com.azure:azure-communication-callautomation
|
||||
---
|
||||
|
||||
# Azure Communication Call Automation (Java)
|
||||
|
||||
Build server-side call automation workflows including IVR systems, call routing, recording, and AI-powered interactions.
|
||||
|
||||
## Installation
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.azure</groupId>
|
||||
<artifactId>azure-communication-callautomation</artifactId>
|
||||
<version>1.6.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
## Client Creation
|
||||
|
||||
```java
|
||||
import com.azure.communication.callautomation.CallAutomationClient;
|
||||
import com.azure.communication.callautomation.CallAutomationClientBuilder;
|
||||
import com.azure.identity.DefaultAzureCredentialBuilder;
|
||||
|
||||
// With DefaultAzureCredential
|
||||
CallAutomationClient client = new CallAutomationClientBuilder()
|
||||
.endpoint("https://<resource>.communication.azure.com")
|
||||
.credential(new DefaultAzureCredentialBuilder().build())
|
||||
.buildClient();
|
||||
|
||||
// With connection string
|
||||
CallAutomationClient client = new CallAutomationClientBuilder()
|
||||
.connectionString("<connection-string>")
|
||||
.buildClient();
|
||||
```
|
||||
|
||||
## Key Concepts
|
||||
|
||||
| Class | Purpose |
|
||||
|-------|---------|
|
||||
| `CallAutomationClient` | Make calls, answer/reject incoming calls, redirect calls |
|
||||
| `CallConnection` | Actions in established calls (add participants, terminate) |
|
||||
| `CallMedia` | Media operations (play audio, recognize DTMF/speech) |
|
||||
| `CallRecording` | Start/stop/pause recording |
|
||||
| `CallAutomationEventParser` | Parse webhook events from ACS |
|
||||
|
||||
## Create Outbound Call
|
||||
|
||||
```java
|
||||
import com.azure.communication.callautomation.models.*;
|
||||
import com.azure.communication.common.CommunicationUserIdentifier;
|
||||
import com.azure.communication.common.PhoneNumberIdentifier;
|
||||
|
||||
// Call to PSTN number
|
||||
PhoneNumberIdentifier target = new PhoneNumberIdentifier("+14255551234");
|
||||
PhoneNumberIdentifier caller = new PhoneNumberIdentifier("+14255550100");
|
||||
|
||||
CreateCallOptions options = new CreateCallOptions(
|
||||
new CommunicationUserIdentifier("<user-id>"), // Source
|
||||
List.of(target)) // Targets
|
||||
.setSourceCallerId(caller)
|
||||
.setCallbackUrl("https://your-app.com/api/callbacks");
|
||||
|
||||
CreateCallResult result = client.createCall(options);
|
||||
String callConnectionId = result.getCallConnectionProperties().getCallConnectionId();
|
||||
```
|
||||
|
||||
## Answer Incoming Call
|
||||
|
||||
```java
|
||||
// From Event Grid webhook - IncomingCall event
|
||||
String incomingCallContext = "<incoming-call-context-from-event>";
|
||||
|
||||
AnswerCallOptions options = new AnswerCallOptions(
|
||||
incomingCallContext,
|
||||
"https://your-app.com/api/callbacks");
|
||||
|
||||
AnswerCallResult result = client.answerCall(options);
|
||||
CallConnection callConnection = result.getCallConnection();
|
||||
```
|
||||
|
||||
## Play Audio (Text-to-Speech)
|
||||
|
||||
```java
|
||||
CallConnection callConnection = client.getCallConnection(callConnectionId);
|
||||
CallMedia callMedia = callConnection.getCallMedia();
|
||||
|
||||
// Play text-to-speech
|
||||
TextSource textSource = new TextSource()
|
||||
.setText("Welcome to Contoso. Press 1 for sales, 2 for support.")
|
||||
.setVoiceName("en-US-JennyNeural");
|
||||
|
||||
PlayOptions playOptions = new PlayOptions(
|
||||
List.of(textSource),
|
||||
List.of(new CommunicationUserIdentifier("<target-user>")));
|
||||
|
||||
callMedia.play(playOptions);
|
||||
|
||||
// Play audio file
|
||||
FileSource fileSource = new FileSource()
|
||||
.setUrl("https://storage.blob.core.windows.net/audio/greeting.wav");
|
||||
|
||||
callMedia.play(new PlayOptions(List.of(fileSource), List.of(target)));
|
||||
```
|
||||
|
||||
## Recognize DTMF Input
|
||||
|
||||
```java
|
||||
// Recognize DTMF tones
|
||||
DtmfTone stopTones = DtmfTone.POUND;
|
||||
|
||||
CallMediaRecognizeDtmfOptions recognizeOptions = new CallMediaRecognizeDtmfOptions(
|
||||
new CommunicationUserIdentifier("<target-user>"),
|
||||
5) // Max tones to collect
|
||||
.setInterToneTimeout(Duration.ofSeconds(5))
|
||||
.setStopTones(List.of(stopTones))
|
||||
.setInitialSilenceTimeout(Duration.ofSeconds(15))
|
||||
.setPlayPrompt(new TextSource().setText("Enter your account number followed by pound."));
|
||||
|
||||
callMedia.startRecognizing(recognizeOptions);
|
||||
```
|
||||
|
||||
## Recognize Speech
|
||||
|
||||
```java
|
||||
// Speech recognition with AI
|
||||
CallMediaRecognizeSpeechOptions speechOptions = new CallMediaRecognizeSpeechOptions(
|
||||
new CommunicationUserIdentifier("<target-user>"))
|
||||
.setEndSilenceTimeout(Duration.ofSeconds(2))
|
||||
.setSpeechLanguage("en-US")
|
||||
.setPlayPrompt(new TextSource().setText("How can I help you today?"));
|
||||
|
||||
callMedia.startRecognizing(speechOptions);
|
||||
```
|
||||
|
||||
## Call Recording
|
||||
|
||||
```java
|
||||
CallRecording callRecording = client.getCallRecording();
|
||||
|
||||
// Start recording
|
||||
StartRecordingOptions recordingOptions = new StartRecordingOptions(
|
||||
new ServerCallLocator("<server-call-id>"))
|
||||
.setRecordingChannel(RecordingChannel.MIXED)
|
||||
.setRecordingContent(RecordingContent.AUDIO_VIDEO)
|
||||
.setRecordingFormat(RecordingFormat.MP4);
|
||||
|
||||
RecordingStateResult recordingResult = callRecording.start(recordingOptions);
|
||||
String recordingId = recordingResult.getRecordingId();
|
||||
|
||||
// Pause/resume/stop
|
||||
callRecording.pause(recordingId);
|
||||
callRecording.resume(recordingId);
|
||||
callRecording.stop(recordingId);
|
||||
|
||||
// Download recording (after RecordingFileStatusUpdated event)
|
||||
callRecording.downloadTo(recordingUrl, Paths.get("recording.mp4"));
|
||||
```
|
||||
|
||||
## Add Participant to Call
|
||||
|
||||
```java
|
||||
CallConnection callConnection = client.getCallConnection(callConnectionId);
|
||||
|
||||
CommunicationUserIdentifier participant = new CommunicationUserIdentifier("<user-id>");
|
||||
AddParticipantOptions addOptions = new AddParticipantOptions(participant)
|
||||
.setInvitationTimeout(Duration.ofSeconds(30));
|
||||
|
||||
AddParticipantResult result = callConnection.addParticipant(addOptions);
|
||||
```
|
||||
|
||||
## Transfer Call
|
||||
|
||||
```java
|
||||
// Blind transfer
|
||||
PhoneNumberIdentifier transferTarget = new PhoneNumberIdentifier("+14255559999");
|
||||
TransferCallToParticipantResult result = callConnection.transferCallToParticipant(transferTarget);
|
||||
```
|
||||
|
||||
## Handle Events (Webhook)
|
||||
|
||||
```java
|
||||
import com.azure.communication.callautomation.CallAutomationEventParser;
|
||||
import com.azure.communication.callautomation.models.events.*;
|
||||
|
||||
// In your webhook endpoint
|
||||
public void handleCallback(String requestBody) {
|
||||
List<CallAutomationEventBase> events = CallAutomationEventParser.parseEvents(requestBody);
|
||||
|
||||
for (CallAutomationEventBase event : events) {
|
||||
if (event instanceof CallConnected) {
|
||||
CallConnected connected = (CallConnected) event;
|
||||
System.out.println("Call connected: " + connected.getCallConnectionId());
|
||||
} else if (event instanceof RecognizeCompleted) {
|
||||
RecognizeCompleted recognized = (RecognizeCompleted) event;
|
||||
// Handle DTMF or speech recognition result
|
||||
DtmfResult dtmfResult = (DtmfResult) recognized.getRecognizeResult();
|
||||
String tones = dtmfResult.getTones().stream()
|
||||
.map(DtmfTone::toString)
|
||||
.collect(Collectors.joining());
|
||||
System.out.println("DTMF received: " + tones);
|
||||
} else if (event instanceof PlayCompleted) {
|
||||
System.out.println("Audio playback completed");
|
||||
} else if (event instanceof CallDisconnected) {
|
||||
System.out.println("Call ended");
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Hang Up Call
|
||||
|
||||
```java
|
||||
// Hang up for all participants
|
||||
callConnection.hangUp(true);
|
||||
|
||||
// Hang up only this leg
|
||||
callConnection.hangUp(false);
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
```java
|
||||
import com.azure.core.exception.HttpResponseException;
|
||||
|
||||
try {
|
||||
client.answerCall(options);
|
||||
} catch (HttpResponseException e) {
|
||||
if (e.getResponse().getStatusCode() == 404) {
|
||||
System.out.println("Call not found or already ended");
|
||||
} else if (e.getResponse().getStatusCode() == 400) {
|
||||
System.out.println("Invalid request: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
AZURE_COMMUNICATION_ENDPOINT=https://<resource>.communication.azure.com
|
||||
AZURE_COMMUNICATION_CONNECTION_STRING=endpoint=https://...;accesskey=...
|
||||
CALLBACK_BASE_URL=https://your-app.com/api/callbacks
|
||||
```
|
||||
|
||||
## Trigger Phrases
|
||||
|
||||
- "call automation Java", "IVR Java", "interactive voice response"
|
||||
- "call recording Java", "DTMF recognition Java"
|
||||
- "text to speech call", "speech recognition call"
|
||||
- "answer incoming call", "transfer call Java"
|
||||
- "Azure Communication Services call automation"
|
||||
@@ -0,0 +1,91 @@
|
||||
---
|
||||
name: azure-communication-callingserver-java
|
||||
description: Azure Communication Services CallingServer (legacy) Java SDK. Note - This SDK is deprecated. Use azure-communication-callautomation instead for new projects. Only use this skill when maintaining legacy code.
|
||||
package: com.azure:azure-communication-callingserver
|
||||
---
|
||||
|
||||
# Azure Communication CallingServer (Java) - DEPRECATED
|
||||
|
||||
> **⚠️ DEPRECATED**: This SDK has been renamed to **Call Automation**. For new projects, use `azure-communication-callautomation` instead. This skill is for maintaining legacy code only.
|
||||
|
||||
## Migration to Call Automation
|
||||
|
||||
```xml
|
||||
<!-- OLD (deprecated) -->
|
||||
<dependency>
|
||||
<groupId>com.azure</groupId>
|
||||
<artifactId>azure-communication-callingserver</artifactId>
|
||||
<version>1.0.0-beta.5</version>
|
||||
</dependency>
|
||||
|
||||
<!-- NEW (use this instead) -->
|
||||
<dependency>
|
||||
<groupId>com.azure</groupId>
|
||||
<artifactId>azure-communication-callautomation</artifactId>
|
||||
<version>1.6.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
## Class Name Changes
|
||||
|
||||
| CallingServer (Old) | Call Automation (New) |
|
||||
|---------------------|----------------------|
|
||||
| `CallingServerClient` | `CallAutomationClient` |
|
||||
| `CallingServerClientBuilder` | `CallAutomationClientBuilder` |
|
||||
| `CallConnection` | `CallConnection` (same) |
|
||||
| `ServerCall` | Removed - use `CallConnection` |
|
||||
|
||||
## Legacy Client Creation
|
||||
|
||||
```java
|
||||
// OLD WAY (deprecated)
|
||||
import com.azure.communication.callingserver.CallingServerClient;
|
||||
import com.azure.communication.callingserver.CallingServerClientBuilder;
|
||||
|
||||
CallingServerClient client = new CallingServerClientBuilder()
|
||||
.connectionString("<connection-string>")
|
||||
.buildClient();
|
||||
|
||||
// NEW WAY
|
||||
import com.azure.communication.callautomation.CallAutomationClient;
|
||||
import com.azure.communication.callautomation.CallAutomationClientBuilder;
|
||||
|
||||
CallAutomationClient client = new CallAutomationClientBuilder()
|
||||
.connectionString("<connection-string>")
|
||||
.buildClient();
|
||||
```
|
||||
|
||||
## Legacy Recording
|
||||
|
||||
```java
|
||||
// OLD WAY
|
||||
StartRecordingOptions options = new StartRecordingOptions(serverCallId)
|
||||
.setRecordingStateCallbackUri(callbackUri);
|
||||
|
||||
StartCallRecordingResult result = client.startRecording(options);
|
||||
String recordingId = result.getRecordingId();
|
||||
|
||||
client.pauseRecording(recordingId);
|
||||
client.resumeRecording(recordingId);
|
||||
client.stopRecording(recordingId);
|
||||
|
||||
// NEW WAY - see azure-communication-callautomation skill
|
||||
```
|
||||
|
||||
## For New Development
|
||||
|
||||
**Do not use this SDK for new projects.**
|
||||
|
||||
See the `azure-communication-callautomation-java` skill for:
|
||||
- Making outbound calls
|
||||
- Answering incoming calls
|
||||
- Call recording
|
||||
- DTMF recognition
|
||||
- Text-to-speech / speech-to-text
|
||||
- Adding/removing participants
|
||||
- Call transfer
|
||||
|
||||
## Trigger Phrases
|
||||
|
||||
- "callingserver legacy", "deprecated calling SDK"
|
||||
- "migrate callingserver to callautomation"
|
||||
310
skills/official/microsoft/java/communication/chat/SKILL.md
Normal file
310
skills/official/microsoft/java/communication/chat/SKILL.md
Normal file
@@ -0,0 +1,310 @@
|
||||
---
|
||||
name: azure-communication-chat-java
|
||||
description: Build real-time chat applications with Azure Communication Services Chat Java SDK. Use when implementing chat threads, messaging, participants, read receipts, typing notifications, or real-time chat features.
|
||||
package: com.azure:azure-communication-chat
|
||||
---
|
||||
|
||||
# Azure Communication Chat (Java)
|
||||
|
||||
Build real-time chat applications with thread management, messaging, participants, and read receipts.
|
||||
|
||||
## Installation
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.azure</groupId>
|
||||
<artifactId>azure-communication-chat</artifactId>
|
||||
<version>1.6.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
## Client Creation
|
||||
|
||||
```java
|
||||
import com.azure.communication.chat.ChatClient;
|
||||
import com.azure.communication.chat.ChatClientBuilder;
|
||||
import com.azure.communication.chat.ChatThreadClient;
|
||||
import com.azure.communication.common.CommunicationTokenCredential;
|
||||
|
||||
// ChatClient requires a CommunicationTokenCredential (user access token)
|
||||
String endpoint = "https://<resource>.communication.azure.com";
|
||||
String userAccessToken = "<user-access-token>";
|
||||
|
||||
CommunicationTokenCredential credential = new CommunicationTokenCredential(userAccessToken);
|
||||
|
||||
ChatClient chatClient = new ChatClientBuilder()
|
||||
.endpoint(endpoint)
|
||||
.credential(credential)
|
||||
.buildClient();
|
||||
|
||||
// Async client
|
||||
ChatAsyncClient chatAsyncClient = new ChatClientBuilder()
|
||||
.endpoint(endpoint)
|
||||
.credential(credential)
|
||||
.buildAsyncClient();
|
||||
```
|
||||
|
||||
## Key Concepts
|
||||
|
||||
| Class | Purpose |
|
||||
|-------|---------|
|
||||
| `ChatClient` | Create/delete chat threads, get thread clients |
|
||||
| `ChatThreadClient` | Operations within a thread (messages, participants, receipts) |
|
||||
| `ChatParticipant` | User in a chat thread with display name |
|
||||
| `ChatMessage` | Message content, type, sender info, timestamps |
|
||||
| `ChatMessageReadReceipt` | Read receipt tracking per participant |
|
||||
|
||||
## Create Chat Thread
|
||||
|
||||
```java
|
||||
import com.azure.communication.chat.models.*;
|
||||
import com.azure.communication.common.CommunicationUserIdentifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
// Define participants
|
||||
List<ChatParticipant> participants = new ArrayList<>();
|
||||
|
||||
ChatParticipant participant1 = new ChatParticipant()
|
||||
.setCommunicationIdentifier(new CommunicationUserIdentifier("<user-id-1>"))
|
||||
.setDisplayName("Alice");
|
||||
|
||||
ChatParticipant participant2 = new ChatParticipant()
|
||||
.setCommunicationIdentifier(new CommunicationUserIdentifier("<user-id-2>"))
|
||||
.setDisplayName("Bob");
|
||||
|
||||
participants.add(participant1);
|
||||
participants.add(participant2);
|
||||
|
||||
// Create thread
|
||||
CreateChatThreadOptions options = new CreateChatThreadOptions("Project Discussion")
|
||||
.setParticipants(participants);
|
||||
|
||||
CreateChatThreadResult result = chatClient.createChatThread(options);
|
||||
String threadId = result.getChatThread().getId();
|
||||
|
||||
// Get thread client for operations
|
||||
ChatThreadClient threadClient = chatClient.getChatThreadClient(threadId);
|
||||
```
|
||||
|
||||
## Send Messages
|
||||
|
||||
```java
|
||||
// Send text message
|
||||
SendChatMessageOptions messageOptions = new SendChatMessageOptions()
|
||||
.setContent("Hello, team!")
|
||||
.setSenderDisplayName("Alice")
|
||||
.setType(ChatMessageType.TEXT);
|
||||
|
||||
SendChatMessageResult sendResult = threadClient.sendMessage(messageOptions);
|
||||
String messageId = sendResult.getId();
|
||||
|
||||
// Send HTML message
|
||||
SendChatMessageOptions htmlOptions = new SendChatMessageOptions()
|
||||
.setContent("<strong>Important:</strong> Meeting at 3pm")
|
||||
.setType(ChatMessageType.HTML);
|
||||
|
||||
threadClient.sendMessage(htmlOptions);
|
||||
```
|
||||
|
||||
## Get Messages
|
||||
|
||||
```java
|
||||
import com.azure.core.util.paging.PagedIterable;
|
||||
|
||||
// List all messages
|
||||
PagedIterable<ChatMessage> messages = threadClient.listMessages();
|
||||
|
||||
for (ChatMessage message : messages) {
|
||||
System.out.println("ID: " + message.getId());
|
||||
System.out.println("Type: " + message.getType());
|
||||
System.out.println("Content: " + message.getContent().getMessage());
|
||||
System.out.println("Sender: " + message.getSenderDisplayName());
|
||||
System.out.println("Created: " + message.getCreatedOn());
|
||||
|
||||
// Check if edited or deleted
|
||||
if (message.getEditedOn() != null) {
|
||||
System.out.println("Edited: " + message.getEditedOn());
|
||||
}
|
||||
if (message.getDeletedOn() != null) {
|
||||
System.out.println("Deleted: " + message.getDeletedOn());
|
||||
}
|
||||
}
|
||||
|
||||
// Get specific message
|
||||
ChatMessage message = threadClient.getMessage(messageId);
|
||||
```
|
||||
|
||||
## Update and Delete Messages
|
||||
|
||||
```java
|
||||
// Update message
|
||||
UpdateChatMessageOptions updateOptions = new UpdateChatMessageOptions()
|
||||
.setContent("Updated message content");
|
||||
|
||||
threadClient.updateMessage(messageId, updateOptions);
|
||||
|
||||
// Delete message
|
||||
threadClient.deleteMessage(messageId);
|
||||
```
|
||||
|
||||
## Manage Participants
|
||||
|
||||
```java
|
||||
// List participants
|
||||
PagedIterable<ChatParticipant> participants = threadClient.listParticipants();
|
||||
|
||||
for (ChatParticipant participant : participants) {
|
||||
CommunicationUserIdentifier user =
|
||||
(CommunicationUserIdentifier) participant.getCommunicationIdentifier();
|
||||
System.out.println("User: " + user.getId());
|
||||
System.out.println("Display Name: " + participant.getDisplayName());
|
||||
}
|
||||
|
||||
// Add participants
|
||||
List<ChatParticipant> newParticipants = new ArrayList<>();
|
||||
newParticipants.add(new ChatParticipant()
|
||||
.setCommunicationIdentifier(new CommunicationUserIdentifier("<new-user-id>"))
|
||||
.setDisplayName("Charlie")
|
||||
.setShareHistoryTime(OffsetDateTime.now().minusDays(7))); // Share last 7 days
|
||||
|
||||
threadClient.addParticipants(newParticipants);
|
||||
|
||||
// Remove participant
|
||||
CommunicationUserIdentifier userToRemove = new CommunicationUserIdentifier("<user-id>");
|
||||
threadClient.removeParticipant(userToRemove);
|
||||
```
|
||||
|
||||
## Read Receipts
|
||||
|
||||
```java
|
||||
// Send read receipt
|
||||
threadClient.sendReadReceipt(messageId);
|
||||
|
||||
// Get read receipts
|
||||
PagedIterable<ChatMessageReadReceipt> receipts = threadClient.listReadReceipts();
|
||||
|
||||
for (ChatMessageReadReceipt receipt : receipts) {
|
||||
System.out.println("Message ID: " + receipt.getChatMessageId());
|
||||
System.out.println("Read by: " + receipt.getSenderCommunicationIdentifier());
|
||||
System.out.println("Read at: " + receipt.getReadOn());
|
||||
}
|
||||
```
|
||||
|
||||
## Typing Notifications
|
||||
|
||||
```java
|
||||
import com.azure.communication.chat.models.TypingNotificationOptions;
|
||||
|
||||
// Send typing notification
|
||||
TypingNotificationOptions typingOptions = new TypingNotificationOptions()
|
||||
.setSenderDisplayName("Alice");
|
||||
|
||||
threadClient.sendTypingNotificationWithResponse(typingOptions, Context.NONE);
|
||||
|
||||
// Simple typing notification
|
||||
threadClient.sendTypingNotification();
|
||||
```
|
||||
|
||||
## Thread Operations
|
||||
|
||||
```java
|
||||
// Get thread properties
|
||||
ChatThreadProperties properties = threadClient.getProperties();
|
||||
System.out.println("Topic: " + properties.getTopic());
|
||||
System.out.println("Created: " + properties.getCreatedOn());
|
||||
|
||||
// Update topic
|
||||
threadClient.updateTopic("New Project Discussion Topic");
|
||||
|
||||
// Delete thread
|
||||
chatClient.deleteChatThread(threadId);
|
||||
```
|
||||
|
||||
## List Threads
|
||||
|
||||
```java
|
||||
// List all chat threads for the user
|
||||
PagedIterable<ChatThreadItem> threads = chatClient.listChatThreads();
|
||||
|
||||
for (ChatThreadItem thread : threads) {
|
||||
System.out.println("Thread ID: " + thread.getId());
|
||||
System.out.println("Topic: " + thread.getTopic());
|
||||
System.out.println("Last message: " + thread.getLastMessageReceivedOn());
|
||||
}
|
||||
```
|
||||
|
||||
## Pagination
|
||||
|
||||
```java
|
||||
import com.azure.core.http.rest.PagedResponse;
|
||||
|
||||
// Paginate through messages
|
||||
int maxPageSize = 10;
|
||||
ListChatMessagesOptions listOptions = new ListChatMessagesOptions()
|
||||
.setMaxPageSize(maxPageSize);
|
||||
|
||||
PagedIterable<ChatMessage> pagedMessages = threadClient.listMessages(listOptions);
|
||||
|
||||
pagedMessages.iterableByPage().forEach(page -> {
|
||||
System.out.println("Page status code: " + page.getStatusCode());
|
||||
page.getElements().forEach(msg ->
|
||||
System.out.println("Message: " + msg.getContent().getMessage()));
|
||||
});
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
```java
|
||||
import com.azure.core.exception.HttpResponseException;
|
||||
|
||||
try {
|
||||
threadClient.sendMessage(messageOptions);
|
||||
} catch (HttpResponseException e) {
|
||||
switch (e.getResponse().getStatusCode()) {
|
||||
case 401:
|
||||
System.out.println("Unauthorized - check token");
|
||||
break;
|
||||
case 403:
|
||||
System.out.println("Forbidden - user not in thread");
|
||||
break;
|
||||
case 404:
|
||||
System.out.println("Thread not found");
|
||||
break;
|
||||
default:
|
||||
System.out.println("Error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Message Types
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `TEXT` | Regular chat message |
|
||||
| `HTML` | HTML-formatted message |
|
||||
| `TOPIC_UPDATED` | System message - topic changed |
|
||||
| `PARTICIPANT_ADDED` | System message - participant joined |
|
||||
| `PARTICIPANT_REMOVED` | System message - participant left |
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
AZURE_COMMUNICATION_ENDPOINT=https://<resource>.communication.azure.com
|
||||
AZURE_COMMUNICATION_USER_TOKEN=<user-access-token>
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Token Management** - User tokens expire; implement refresh logic with `CommunicationTokenRefreshOptions`
|
||||
2. **Pagination** - Use `listMessages(options)` with `maxPageSize` for large threads
|
||||
3. **Share History** - Set `shareHistoryTime` when adding participants to control message visibility
|
||||
4. **Message Types** - Filter system messages (`PARTICIPANT_ADDED`, etc.) from user messages
|
||||
5. **Read Receipts** - Send receipts only when messages are actually viewed by user
|
||||
|
||||
## Trigger Phrases
|
||||
|
||||
- "chat application Java", "real-time messaging Java"
|
||||
- "chat thread", "chat participants", "chat messages"
|
||||
- "read receipts", "typing notifications"
|
||||
- "Azure Communication Services chat"
|
||||
304
skills/official/microsoft/java/communication/common/SKILL.md
Normal file
304
skills/official/microsoft/java/communication/common/SKILL.md
Normal file
@@ -0,0 +1,304 @@
|
||||
---
|
||||
name: azure-communication-common-java
|
||||
description: Azure Communication Services common utilities for Java. Use when working with CommunicationTokenCredential, user identifiers, token refresh, or shared authentication across ACS services.
|
||||
package: com.azure:azure-communication-common
|
||||
---
|
||||
|
||||
# Azure Communication Common (Java)
|
||||
|
||||
Shared authentication utilities and data structures for Azure Communication Services.
|
||||
|
||||
## Installation
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.azure</groupId>
|
||||
<artifactId>azure-communication-common</artifactId>
|
||||
<version>1.4.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
## Key Concepts
|
||||
|
||||
| Class | Purpose |
|
||||
|-------|---------|
|
||||
| `CommunicationTokenCredential` | Authenticate users with ACS services |
|
||||
| `CommunicationTokenRefreshOptions` | Configure automatic token refresh |
|
||||
| `CommunicationUserIdentifier` | Identify ACS users |
|
||||
| `PhoneNumberIdentifier` | Identify PSTN phone numbers |
|
||||
| `MicrosoftTeamsUserIdentifier` | Identify Teams users |
|
||||
| `UnknownIdentifier` | Generic identifier for unknown types |
|
||||
|
||||
## CommunicationTokenCredential
|
||||
|
||||
### Static Token (Short-lived Clients)
|
||||
|
||||
```java
|
||||
import com.azure.communication.common.CommunicationTokenCredential;
|
||||
|
||||
// Simple static token - no refresh
|
||||
String userToken = "<user-access-token>";
|
||||
CommunicationTokenCredential credential = new CommunicationTokenCredential(userToken);
|
||||
|
||||
// Use with Chat, Calling, etc.
|
||||
ChatClient chatClient = new ChatClientBuilder()
|
||||
.endpoint("https://<resource>.communication.azure.com")
|
||||
.credential(credential)
|
||||
.buildClient();
|
||||
```
|
||||
|
||||
### Proactive Token Refresh (Long-lived Clients)
|
||||
|
||||
```java
|
||||
import com.azure.communication.common.CommunicationTokenRefreshOptions;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
// Token refresher callback - called when token is about to expire
|
||||
Callable<String> tokenRefresher = () -> {
|
||||
// Call your server to get a fresh token
|
||||
return fetchNewTokenFromServer();
|
||||
};
|
||||
|
||||
// With proactive refresh
|
||||
CommunicationTokenRefreshOptions refreshOptions = new CommunicationTokenRefreshOptions(tokenRefresher)
|
||||
.setRefreshProactively(true) // Refresh before expiry
|
||||
.setInitialToken(currentToken); // Optional initial token
|
||||
|
||||
CommunicationTokenCredential credential = new CommunicationTokenCredential(refreshOptions);
|
||||
```
|
||||
|
||||
### Async Token Refresh
|
||||
|
||||
```java
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
// Async token fetcher
|
||||
Callable<String> asyncRefresher = () -> {
|
||||
CompletableFuture<String> future = fetchTokenAsync();
|
||||
return future.get(); // Block until token is available
|
||||
};
|
||||
|
||||
CommunicationTokenRefreshOptions options = new CommunicationTokenRefreshOptions(asyncRefresher)
|
||||
.setRefreshProactively(true);
|
||||
|
||||
CommunicationTokenCredential credential = new CommunicationTokenCredential(options);
|
||||
```
|
||||
|
||||
## Entra ID (Azure AD) Authentication
|
||||
|
||||
```java
|
||||
import com.azure.identity.InteractiveBrowserCredentialBuilder;
|
||||
import com.azure.communication.common.EntraCommunicationTokenCredentialOptions;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
// For Teams Phone Extensibility
|
||||
InteractiveBrowserCredential entraCredential = new InteractiveBrowserCredentialBuilder()
|
||||
.clientId("<your-client-id>")
|
||||
.tenantId("<your-tenant-id>")
|
||||
.redirectUrl("<your-redirect-uri>")
|
||||
.build();
|
||||
|
||||
String resourceEndpoint = "https://<resource>.communication.azure.com";
|
||||
List<String> scopes = Arrays.asList(
|
||||
"https://auth.msft.communication.azure.com/TeamsExtension.ManageCalls"
|
||||
);
|
||||
|
||||
EntraCommunicationTokenCredentialOptions entraOptions =
|
||||
new EntraCommunicationTokenCredentialOptions(entraCredential, resourceEndpoint)
|
||||
.setScopes(scopes);
|
||||
|
||||
CommunicationTokenCredential credential = new CommunicationTokenCredential(entraOptions);
|
||||
```
|
||||
|
||||
## Communication Identifiers
|
||||
|
||||
### CommunicationUserIdentifier
|
||||
|
||||
```java
|
||||
import com.azure.communication.common.CommunicationUserIdentifier;
|
||||
|
||||
// Create identifier for ACS user
|
||||
CommunicationUserIdentifier user = new CommunicationUserIdentifier("8:acs:resource-id_user-id");
|
||||
|
||||
// Get raw ID
|
||||
String rawId = user.getId();
|
||||
```
|
||||
|
||||
### PhoneNumberIdentifier
|
||||
|
||||
```java
|
||||
import com.azure.communication.common.PhoneNumberIdentifier;
|
||||
|
||||
// E.164 format phone number
|
||||
PhoneNumberIdentifier phone = new PhoneNumberIdentifier("+14255551234");
|
||||
|
||||
String phoneNumber = phone.getPhoneNumber(); // "+14255551234"
|
||||
String rawId = phone.getRawId(); // "4:+14255551234"
|
||||
```
|
||||
|
||||
### MicrosoftTeamsUserIdentifier
|
||||
|
||||
```java
|
||||
import com.azure.communication.common.MicrosoftTeamsUserIdentifier;
|
||||
|
||||
// Teams user identifier
|
||||
MicrosoftTeamsUserIdentifier teamsUser = new MicrosoftTeamsUserIdentifier("<teams-user-id>")
|
||||
.setCloudEnvironment(CommunicationCloudEnvironment.PUBLIC);
|
||||
|
||||
// For anonymous Teams users
|
||||
MicrosoftTeamsUserIdentifier anonymousTeamsUser = new MicrosoftTeamsUserIdentifier("<teams-user-id>")
|
||||
.setAnonymous(true);
|
||||
```
|
||||
|
||||
### UnknownIdentifier
|
||||
|
||||
```java
|
||||
import com.azure.communication.common.UnknownIdentifier;
|
||||
|
||||
// For identifiers of unknown type
|
||||
UnknownIdentifier unknown = new UnknownIdentifier("some-raw-id");
|
||||
```
|
||||
|
||||
## Identifier Parsing
|
||||
|
||||
```java
|
||||
import com.azure.communication.common.CommunicationIdentifier;
|
||||
import com.azure.communication.common.CommunicationIdentifierModel;
|
||||
|
||||
// Parse raw ID to appropriate type
|
||||
public CommunicationIdentifier parseIdentifier(String rawId) {
|
||||
if (rawId.startsWith("8:acs:")) {
|
||||
return new CommunicationUserIdentifier(rawId);
|
||||
} else if (rawId.startsWith("4:")) {
|
||||
String phone = rawId.substring(2);
|
||||
return new PhoneNumberIdentifier(phone);
|
||||
} else if (rawId.startsWith("8:orgid:")) {
|
||||
String teamsId = rawId.substring(8);
|
||||
return new MicrosoftTeamsUserIdentifier(teamsId);
|
||||
} else {
|
||||
return new UnknownIdentifier(rawId);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Type Checking Identifiers
|
||||
|
||||
```java
|
||||
import com.azure.communication.common.CommunicationIdentifier;
|
||||
|
||||
public void processIdentifier(CommunicationIdentifier identifier) {
|
||||
if (identifier instanceof CommunicationUserIdentifier) {
|
||||
CommunicationUserIdentifier user = (CommunicationUserIdentifier) identifier;
|
||||
System.out.println("ACS User: " + user.getId());
|
||||
|
||||
} else if (identifier instanceof PhoneNumberIdentifier) {
|
||||
PhoneNumberIdentifier phone = (PhoneNumberIdentifier) identifier;
|
||||
System.out.println("Phone: " + phone.getPhoneNumber());
|
||||
|
||||
} else if (identifier instanceof MicrosoftTeamsUserIdentifier) {
|
||||
MicrosoftTeamsUserIdentifier teams = (MicrosoftTeamsUserIdentifier) identifier;
|
||||
System.out.println("Teams User: " + teams.getUserId());
|
||||
System.out.println("Anonymous: " + teams.isAnonymous());
|
||||
|
||||
} else if (identifier instanceof UnknownIdentifier) {
|
||||
UnknownIdentifier unknown = (UnknownIdentifier) identifier;
|
||||
System.out.println("Unknown: " + unknown.getId());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Token Access
|
||||
|
||||
```java
|
||||
import com.azure.core.credential.AccessToken;
|
||||
|
||||
// Get current token (for debugging/logging - don't expose!)
|
||||
CommunicationTokenCredential credential = new CommunicationTokenCredential(token);
|
||||
|
||||
// Sync access
|
||||
AccessToken accessToken = credential.getToken();
|
||||
System.out.println("Token expires: " + accessToken.getExpiresAt());
|
||||
|
||||
// Async access
|
||||
credential.getTokenAsync()
|
||||
.subscribe(token -> {
|
||||
System.out.println("Token: " + token.getToken().substring(0, 20) + "...");
|
||||
System.out.println("Expires: " + token.getExpiresAt());
|
||||
});
|
||||
```
|
||||
|
||||
## Dispose Credential
|
||||
|
||||
```java
|
||||
// Clean up when done
|
||||
credential.close();
|
||||
|
||||
// Or use try-with-resources
|
||||
try (CommunicationTokenCredential cred = new CommunicationTokenCredential(options)) {
|
||||
// Use credential
|
||||
chatClient.doSomething();
|
||||
}
|
||||
```
|
||||
|
||||
## Cloud Environments
|
||||
|
||||
```java
|
||||
import com.azure.communication.common.CommunicationCloudEnvironment;
|
||||
|
||||
// Available environments
|
||||
CommunicationCloudEnvironment publicCloud = CommunicationCloudEnvironment.PUBLIC;
|
||||
CommunicationCloudEnvironment govCloud = CommunicationCloudEnvironment.GCCH;
|
||||
CommunicationCloudEnvironment dodCloud = CommunicationCloudEnvironment.DOD;
|
||||
|
||||
// Set on Teams identifier
|
||||
MicrosoftTeamsUserIdentifier teamsUser = new MicrosoftTeamsUserIdentifier("<user-id>")
|
||||
.setCloudEnvironment(CommunicationCloudEnvironment.GCCH);
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
AZURE_COMMUNICATION_ENDPOINT=https://<resource>.communication.azure.com
|
||||
AZURE_COMMUNICATION_USER_TOKEN=<user-access-token>
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Proactive Refresh** - Always use `setRefreshProactively(true)` for long-lived clients
|
||||
2. **Token Security** - Never log or expose full tokens
|
||||
3. **Close Credentials** - Dispose of credentials when no longer needed
|
||||
4. **Error Handling** - Handle token refresh failures gracefully
|
||||
5. **Identifier Types** - Use specific identifier types, not raw strings
|
||||
|
||||
## Common Usage Patterns
|
||||
|
||||
```java
|
||||
// Pattern: Create credential for Chat/Calling client
|
||||
public ChatClient createChatClient(String token, String endpoint) {
|
||||
CommunicationTokenRefreshOptions refreshOptions =
|
||||
new CommunicationTokenRefreshOptions(this::refreshToken)
|
||||
.setRefreshProactively(true)
|
||||
.setInitialToken(token);
|
||||
|
||||
CommunicationTokenCredential credential =
|
||||
new CommunicationTokenCredential(refreshOptions);
|
||||
|
||||
return new ChatClientBuilder()
|
||||
.endpoint(endpoint)
|
||||
.credential(credential)
|
||||
.buildClient();
|
||||
}
|
||||
|
||||
private String refreshToken() {
|
||||
// Call your token endpoint
|
||||
return tokenService.getNewToken();
|
||||
}
|
||||
```
|
||||
|
||||
## Trigger Phrases
|
||||
|
||||
- "ACS authentication", "communication token credential"
|
||||
- "user access token", "token refresh"
|
||||
- "CommunicationUserIdentifier", "PhoneNumberIdentifier"
|
||||
- "Azure Communication Services authentication"
|
||||
274
skills/official/microsoft/java/communication/sms/SKILL.md
Normal file
274
skills/official/microsoft/java/communication/sms/SKILL.md
Normal file
@@ -0,0 +1,274 @@
|
||||
---
|
||||
name: azure-communication-sms-java
|
||||
description: Send SMS messages with Azure Communication Services SMS Java SDK. Use when implementing SMS notifications, alerts, OTP delivery, bulk messaging, or delivery reports.
|
||||
package: com.azure:azure-communication-sms
|
||||
---
|
||||
|
||||
# Azure Communication SMS (Java)
|
||||
|
||||
Send SMS messages to single or multiple recipients with delivery reporting.
|
||||
|
||||
## Installation
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.azure</groupId>
|
||||
<artifactId>azure-communication-sms</artifactId>
|
||||
<version>1.2.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
## Client Creation
|
||||
|
||||
```java
|
||||
import com.azure.communication.sms.SmsClient;
|
||||
import com.azure.communication.sms.SmsClientBuilder;
|
||||
import com.azure.identity.DefaultAzureCredentialBuilder;
|
||||
|
||||
// With DefaultAzureCredential (recommended)
|
||||
SmsClient smsClient = new SmsClientBuilder()
|
||||
.endpoint("https://<resource>.communication.azure.com")
|
||||
.credential(new DefaultAzureCredentialBuilder().build())
|
||||
.buildClient();
|
||||
|
||||
// With connection string
|
||||
SmsClient smsClient = new SmsClientBuilder()
|
||||
.connectionString("<connection-string>")
|
||||
.buildClient();
|
||||
|
||||
// With AzureKeyCredential
|
||||
import com.azure.core.credential.AzureKeyCredential;
|
||||
|
||||
SmsClient smsClient = new SmsClientBuilder()
|
||||
.endpoint("https://<resource>.communication.azure.com")
|
||||
.credential(new AzureKeyCredential("<access-key>"))
|
||||
.buildClient();
|
||||
|
||||
// Async client
|
||||
SmsAsyncClient smsAsyncClient = new SmsClientBuilder()
|
||||
.connectionString("<connection-string>")
|
||||
.buildAsyncClient();
|
||||
```
|
||||
|
||||
## Send SMS to Single Recipient
|
||||
|
||||
```java
|
||||
import com.azure.communication.sms.models.SmsSendResult;
|
||||
|
||||
// Simple send
|
||||
SmsSendResult result = smsClient.send(
|
||||
"+14255550100", // From (your ACS phone number)
|
||||
"+14255551234", // To
|
||||
"Your verification code is 123456");
|
||||
|
||||
System.out.println("Message ID: " + result.getMessageId());
|
||||
System.out.println("To: " + result.getTo());
|
||||
System.out.println("Success: " + result.isSuccessful());
|
||||
|
||||
if (!result.isSuccessful()) {
|
||||
System.out.println("Error: " + result.getErrorMessage());
|
||||
System.out.println("Status: " + result.getHttpStatusCode());
|
||||
}
|
||||
```
|
||||
|
||||
## Send SMS to Multiple Recipients
|
||||
|
||||
```java
|
||||
import com.azure.communication.sms.models.SmsSendOptions;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
List<String> recipients = Arrays.asList(
|
||||
"+14255551111",
|
||||
"+14255552222",
|
||||
"+14255553333"
|
||||
);
|
||||
|
||||
// With options
|
||||
SmsSendOptions options = new SmsSendOptions()
|
||||
.setDeliveryReportEnabled(true)
|
||||
.setTag("marketing-campaign-001");
|
||||
|
||||
Iterable<SmsSendResult> results = smsClient.sendWithResponse(
|
||||
"+14255550100", // From
|
||||
recipients, // To list
|
||||
"Flash sale! 50% off today only.",
|
||||
options,
|
||||
Context.NONE
|
||||
).getValue();
|
||||
|
||||
for (SmsSendResult result : results) {
|
||||
if (result.isSuccessful()) {
|
||||
System.out.println("Sent to " + result.getTo() + ": " + result.getMessageId());
|
||||
} else {
|
||||
System.out.println("Failed to " + result.getTo() + ": " + result.getErrorMessage());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Send Options
|
||||
|
||||
```java
|
||||
SmsSendOptions options = new SmsSendOptions();
|
||||
|
||||
// Enable delivery reports (sent via Event Grid)
|
||||
options.setDeliveryReportEnabled(true);
|
||||
|
||||
// Add custom tag for tracking
|
||||
options.setTag("order-confirmation-12345");
|
||||
```
|
||||
|
||||
## Response Handling
|
||||
|
||||
```java
|
||||
import com.azure.core.http.rest.Response;
|
||||
|
||||
Response<Iterable<SmsSendResult>> response = smsClient.sendWithResponse(
|
||||
"+14255550100",
|
||||
Arrays.asList("+14255551234"),
|
||||
"Hello!",
|
||||
new SmsSendOptions().setDeliveryReportEnabled(true),
|
||||
Context.NONE
|
||||
);
|
||||
|
||||
// Check HTTP response
|
||||
System.out.println("Status code: " + response.getStatusCode());
|
||||
System.out.println("Headers: " + response.getHeaders());
|
||||
|
||||
// Process results
|
||||
for (SmsSendResult result : response.getValue()) {
|
||||
System.out.println("Message ID: " + result.getMessageId());
|
||||
System.out.println("Successful: " + result.isSuccessful());
|
||||
|
||||
if (!result.isSuccessful()) {
|
||||
System.out.println("HTTP Status: " + result.getHttpStatusCode());
|
||||
System.out.println("Error: " + result.getErrorMessage());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Async Operations
|
||||
|
||||
```java
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
SmsAsyncClient asyncClient = new SmsClientBuilder()
|
||||
.connectionString("<connection-string>")
|
||||
.buildAsyncClient();
|
||||
|
||||
// Send single message
|
||||
asyncClient.send("+14255550100", "+14255551234", "Async message!")
|
||||
.subscribe(
|
||||
result -> System.out.println("Sent: " + result.getMessageId()),
|
||||
error -> System.out.println("Error: " + error.getMessage())
|
||||
);
|
||||
|
||||
// Send to multiple with options
|
||||
SmsSendOptions options = new SmsSendOptions()
|
||||
.setDeliveryReportEnabled(true);
|
||||
|
||||
asyncClient.sendWithResponse(
|
||||
"+14255550100",
|
||||
Arrays.asList("+14255551111", "+14255552222"),
|
||||
"Bulk async message",
|
||||
options)
|
||||
.subscribe(response -> {
|
||||
for (SmsSendResult result : response.getValue()) {
|
||||
System.out.println("Result: " + result.getTo() + " - " + result.isSuccessful());
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
```java
|
||||
import com.azure.core.exception.HttpResponseException;
|
||||
|
||||
try {
|
||||
SmsSendResult result = smsClient.send(
|
||||
"+14255550100",
|
||||
"+14255551234",
|
||||
"Test message"
|
||||
);
|
||||
|
||||
// Individual message errors don't throw exceptions
|
||||
if (!result.isSuccessful()) {
|
||||
handleMessageError(result);
|
||||
}
|
||||
|
||||
} catch (HttpResponseException e) {
|
||||
// Request-level failures (auth, network, etc.)
|
||||
System.out.println("Request failed: " + e.getMessage());
|
||||
System.out.println("Status: " + e.getResponse().getStatusCode());
|
||||
} catch (RuntimeException e) {
|
||||
System.out.println("Unexpected error: " + e.getMessage());
|
||||
}
|
||||
|
||||
private void handleMessageError(SmsSendResult result) {
|
||||
int status = result.getHttpStatusCode();
|
||||
String error = result.getErrorMessage();
|
||||
|
||||
if (status == 400) {
|
||||
System.out.println("Invalid phone number: " + result.getTo());
|
||||
} else if (status == 429) {
|
||||
System.out.println("Rate limited - retry later");
|
||||
} else {
|
||||
System.out.println("Error " + status + ": " + error);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Delivery Reports
|
||||
|
||||
Delivery reports are sent via Azure Event Grid. Configure an Event Grid subscription for your ACS resource.
|
||||
|
||||
```java
|
||||
// Event Grid webhook handler (in your endpoint)
|
||||
public void handleDeliveryReport(String eventJson) {
|
||||
// Parse Event Grid event
|
||||
// Event type: Microsoft.Communication.SMSDeliveryReportReceived
|
||||
|
||||
// Event data contains:
|
||||
// - messageId: correlates to SmsSendResult.getMessageId()
|
||||
// - from: sender number
|
||||
// - to: recipient number
|
||||
// - deliveryStatus: "Delivered", "Failed", etc.
|
||||
// - deliveryStatusDetails: detailed status
|
||||
// - receivedTimestamp: when status was received
|
||||
// - tag: your custom tag from SmsSendOptions
|
||||
}
|
||||
```
|
||||
|
||||
## SmsSendResult Properties
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| `getMessageId()` | String | Unique message identifier |
|
||||
| `getTo()` | String | Recipient phone number |
|
||||
| `isSuccessful()` | boolean | Whether send succeeded |
|
||||
| `getHttpStatusCode()` | int | HTTP status for this recipient |
|
||||
| `getErrorMessage()` | String | Error details if failed |
|
||||
| `getRepeatabilityResult()` | RepeatabilityResult | Idempotency result |
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
AZURE_COMMUNICATION_ENDPOINT=https://<resource>.communication.azure.com
|
||||
AZURE_COMMUNICATION_CONNECTION_STRING=endpoint=https://...;accesskey=...
|
||||
SMS_FROM_NUMBER=+14255550100
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Phone Number Format** - Use E.164 format: `+[country code][number]`
|
||||
2. **Delivery Reports** - Enable for critical messages (OTP, alerts)
|
||||
3. **Tagging** - Use tags to correlate messages with business context
|
||||
4. **Error Handling** - Check `isSuccessful()` for each recipient individually
|
||||
5. **Rate Limiting** - Implement retry with backoff for 429 responses
|
||||
6. **Bulk Sending** - Use batch send for multiple recipients (more efficient)
|
||||
|
||||
## Trigger Phrases
|
||||
|
||||
- "send SMS Java", "text message Java"
|
||||
- "SMS notification", "OTP SMS", "bulk SMS"
|
||||
- "delivery report SMS", "Azure Communication Services SMS"
|
||||
Reference in New Issue
Block a user