Skip to content
71 changes: 60 additions & 11 deletions core/samples/SsoBot/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,30 @@

auth.OnSignInComplete(async (context, tokenResponse, ct) =>
{
await context.SendActivityAsync("You're now signed in! Try `profile` or `calendar`.", ct);
await context.SendActivityAsync(new MessageActivity("You're now signed in! Try `profile` or `calendar`.")
.WithSuggestedActions(
new SuggestedActions()
{
Actions = new List<SuggestedAction>()
{
new SuggestedAction() { Title = "Profile", Type = "imBack", Value = "profile" },
new SuggestedAction() { Title = "Calendar", Type = "imBack", Value = "calendar" }
}
}), ct);
Comment on lines +41 to +50
});

auth.OnSignInFailure(async (context, failure, ct) =>
{
string message = failure is not null
? $"Sign-in failed: {failure.Code} — {failure.Message}"
? $"User {context.Activity.From?.Name} Sign-in failed: {failure.Code} — {failure.Message}"
: "Sign-in failed. Please try again.";
await context.SendActivityAsync(message, ct);
var signInFailureMessage = new MessageActivity(message)
{
TextFormat = TextFormats.Markdown
};
signInFailureMessage.Recipient = context.Activity.From;
signInFailureMessage.Recipient?.IsTargeted = context.Activity?.Conversation?.ConversationType == ConversationType.GroupChat; // only set IsTargeted for 1:1 chats to avoid issues in group contexts
await context.SendActivityAsync(signInFailureMessage, ct);
Comment on lines +62 to +64
});

// ==================== MESSAGE HANDLERS ====================
Expand All @@ -57,7 +72,13 @@
string? token = await context.SignIn(cancellationToken: ct);
if (token is not null)
{
await context.SendActivityAsync("You're already signed in.", ct);
var alreadySignedInMessage = new MessageActivity($"You're already signed in, {context.Activity.From?.Name}!")
{
TextFormat = TextFormats.Markdown
};
alreadySignedInMessage.Recipient = context.Activity.From;
alreadySignedInMessage.Recipient?.IsTargeted = context.Activity?.Conversation?.ConversationType == ConversationType.GroupChat; // only set IsTargeted for 1:1 chats to avoid issues in group contexts
await context.SendActivityAsync(alreadySignedInMessage, ct);
Comment on lines +79 to +81
}
// else: OAuthCard sent, SSO flow in progress -- OnSignInComplete will fire
});
Expand All @@ -75,11 +96,16 @@
{
string json = await http.GetStringAsync("https://graph.microsoft.com/v1.0/me", ct);
string indentedJson = JsonSerializer.Serialize(JsonSerializer.Deserialize<JsonObject>(json), new JsonSerializerOptions { WriteIndented = true });
await context.SendActivityAsync(new MessageActivity($" ## Graph Me \n ```json\n{indentedJson}\n```") { TextFormat = TextFormats.Markdown }, ct);

var msgResponse = new MessageActivity($" ## Graph Me [{context.Activity.From?.Name}] \n ```json\n{indentedJson}\n```")
{ TextFormat = TextFormats.Markdown };
msgResponse.Recipient = context.Activity.From;
msgResponse.Recipient?.IsTargeted = context.Activity?.Conversation?.ConversationType == ConversationType.GroupChat; // only set IsTargeted for 1:1 chats to avoid issues in group contexts
await context.SendActivityAsync(msgResponse, ct);
Comment on lines +102 to +104
}
catch (HttpRequestException ex)
{
await context.SendActivityAsync($"Graph call failed: {ex.Message}", ct);
await context.SendActivityAsync($"[{context.Activity.From?.Name}] Graph call failed: {ex.Message}", ct);
}
Comment on lines 106 to 109
});

Expand All @@ -96,24 +122,38 @@
string json = await http.GetStringAsync(
"https://graph.microsoft.com/v1.0/me/events?$top=3&$select=subject,start,end&$orderby=start/dateTime", ct);
string indentedJson = JsonSerializer.Serialize(JsonSerializer.Deserialize<JsonObject>(json), new JsonSerializerOptions { WriteIndented = true });
await context.SendActivityAsync(new MessageActivity($" ## Graph Calendar \n ```json\n{indentedJson}\n```") { TextFormat = TextFormats.Markdown }, ct);
await context.SendActivityAsync(new MessageActivity($" ## Graph Calendar [{context.Activity.From?.Name}] \n ```json\n{indentedJson}\n```") { TextFormat = TextFormats.Markdown }, ct);
}
Comment on lines 122 to 126
catch (HttpRequestException ex)
{
await context.SendActivityAsync($"Graph call failed: {ex.Message}", ct);
await context.SendActivityAsync($"[{context.Activity.From?.Name}] Graph call failed: {ex.Message}", ct);
}
Comment on lines 127 to 130
});

bot.OnMessage("(?i)^logout$", async (context, ct) =>
{
await context.SignOut(cancellationToken: ct);
await context.SendActivityAsync("Signed out.", ct);
var signOutMessage = new MessageActivity($"You've been signed out, {context.Activity.From?.Name}.")
{
TextFormat = TextFormats.Markdown
};
signOutMessage.Recipient = context.Activity.From;
signOutMessage.Recipient?.IsTargeted = context.Activity?.Conversation?.ConversationType == ConversationType.GroupChat; // only set IsTargeted for 1:1 chats to avoid issues in group contexts
await context.SendActivityAsync(signOutMessage, ct);
Comment on lines +140 to +142
});

bot.OnMessage("(?i)^status$", async (context, ct) =>
{
bool signedIn = await context.IsSignedInAsync(cancellationToken: ct);
await context.SendActivityAsync(signedIn ? "Signed in." : "Not signed in.", ct);
var signInStatusMessage = new MessageActivity(signedIn
? $"User {context.Activity.From?.Name} is signed in."
: $"User {context.Activity.From?.Name} is not signed in.")
{
TextFormat = TextFormats.Markdown
};
signInStatusMessage.Recipient = context.Activity.From;
signInStatusMessage.Recipient?.IsTargeted = context.Activity?.Conversation?.ConversationType == ConversationType.GroupChat; // only set IsTargeted for 1:1 chats to avoid issues in group contexts
await context.SendActivityAsync(signInStatusMessage, ct);
Comment on lines +154 to +156
});

bot.OnMessage("(?i)^help$", async (context, ct) =>
Expand All @@ -131,7 +171,16 @@
""";

await context.SendActivityAsync(
new MessageActivity(helpText) { TextFormat = TextFormats.Markdown }, ct);
new MessageActivity(helpText) { TextFormat = TextFormats.Markdown }
.WithSuggestedActions(
new SuggestedActions() {
Actions = new List<SuggestedAction>()
{
new SuggestedAction() { Title = "Login", Type = "imBack", Value = "login" },
new SuggestedAction() { Title = "Logout", Type = "imBack", Value = "logout" },
new SuggestedAction() { Title = "Status", Type = "imBack", Value = "status" },
Comment on lines +177 to +181
}
}), ct);
});

// ==================== INSTALL HANDLER ====================
Expand Down
1 change: 1 addition & 0 deletions core/samples/SsoBot/SsoBot.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<NoWarn>$(NoWarn);ExperimentalTeamsTargeted</NoWarn>
</PropertyGroup>

<ItemGroup>
Expand Down
4 changes: 2 additions & 2 deletions core/src/Microsoft.Teams.Apps/OAuth/OAuthFlow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -180,10 +180,10 @@ public OAuthFlow OnSignInFailure(SignInFailureHandler handler)

TeamsActivity oauthActivity = TeamsActivity.CreateBuilder()
.WithConversationReference(context.Activity)
.WithRecipient(context.Activity.From, false)
.WithRecipient(context.Activity.From, context.Activity?.Conversation?.ConversationType == ConversationType.GroupChat)
.WithAttachment(attachment)
.Build();
Comment on lines 181 to 185

await context.SendActivityAsync(oauthActivity, cancellationToken).ConfigureAwait(false);

// Track that this user has a pending sign-in for this flow
Expand Down
2 changes: 1 addition & 1 deletion core/src/Microsoft.Teams.Apps/Schema/TeamsConversation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public TeamsConversation()
[JsonPropertyName("tenantId")] public string? TenantId { get; set; }

/// <summary>
/// Conversation Type. See <see cref="ConversationType"/> for known values.
/// Conversation Type. See <see cref="Schema.ConversationType"/> for known values.
/// </summary>
[JsonPropertyName("conversationType")] public string? ConversationType { get; set; }
Comment on lines 74 to 77

Expand Down
Loading