Get all emails from Oulook using GgraphServiceClient api - c#

I want all emails from outlook with date filter and if has attachment.
GraphServiceClient _graphServiceClient = new GraphServiceClient(
new DelegateAuthenticationProvider((requestMessage) =>
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", externalUserLogin.AccessToken);
return Task.FromResult(0);
}));
IUserMessagesCollectionPage pagedMessages;
var queryOptions = new List<QueryOption>()
{
new QueryOption("ReceivedDateTime", $"2022-01-01..2022-10-12"),
new QueryOption("hasattachment", "true")
};
pagedMessages = await _graphServiceClient.Me.Messages
.Request(queryOptions)
.Select(msg => new
{
msg.Subject,
msg.BodyPreview,
msg.ReceivedDateTime,
msg.HasAttachments,
msg.Attachments,
msg.Id
})
.Expand("attachments")
.Top(50)
.OrderBy("receivedDateTime desc")
.GetAsync();
foreach (var item in pagedMessages.CurrentPage)
{
if (item.Attachments != null && item.Attachments.Any())
{
foreach (var attachment in item.Attachments)
{
if (attachment.ODataType == "#microsoft.graph.fileAttachment")
{
FileAttachment fileAttachment = attachment as FileAttachment;
byte[] contentBytes = fileAttachment.ContentBytes;
}
}
}
}
I already tried, but the filter is not working as expected. I got all emails with attachment and without attachment.
I want all emails without take.
I added ".Expand("attachments")", So I have to call the attachment API separately or I get attachments?

if your trying to use $search as a Query option then your code isn't correct it should be
var queryOptions = new List<QueryOption>()
{
new QueryOption("$search", $"received>=2022-01-01 AND received<=2022-10-12 AND hasattachment:true"),
};
the KQL keyword for is ReceivedDateTime is received https://learn.microsoft.com/en-us/microsoft-365/compliance/keyword-queries-and-search-conditions?view=o365-worldwide while the between ... operator should be supported I think its discouraged and didn't work for me (although i have used it in the past in EWS)

Finally I got the solutions. I am getting all the emails using Filter. I used query options, but it is not working. Below is complete solutions for is read, has attachment, filter by date and update email after inserting your local db.
/// <summary>
/// Gets a mail from Outlook
/// </summary>
/// <param name="token">Token</param>
/// <returns></returns>
public async Task<IList<Message>> GetEMails(string token)
{
IUserMessagesCollectionPage pagedMessages;
List<Message> emailMessages = new List<Message>();
GraphServiceClient _graphServiceClient = new GraphServiceClient(
new DelegateAuthenticationProvider((requestMessage) =>
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", token);
return Task.FromResult(0);
}));
pagedMessages = await _graphServiceClient.Me.Messages
.Request()
.Filter($"ReceivedDateTime ge 2022-01-01 and receivedDateTime lt 2022-01-31 and hasAttachments eq true and isRead eq false")
.Select(msg => new
{
msg.Body,
msg.Subject,
msg.BodyPreview,
msg.ReceivedDateTime,
msg.Attachments,
msg.Id,
msg.CcRecipients,
msg.BccRecipients,
msg.From,
msg.ToRecipients
})
.Expand("attachments")
.Top(10)
.OrderBy("receivedDateTime desc")
.GetAsync();
emailMessages.AddRange(pagedMessages.CurrentPage);
while (pagedMessages.NextPageRequest != null)
{
pagedMessages = await pagedMessages.NextPageRequest.GetAsync();
emailMessages.AddRange(pagedMessages.CurrentPage);
}
return messages;
}
/// <summary>
/// Update is Message Mark as Read
/// </summary>
/// <param name="id">Message ID</param>
/// <param name="_graphServiceClient">Graph Service Client</param>
/// <returns></returns>
private async Task MarkAsRead(string id, GraphServiceClient _graphServiceClient)
{
try
{
await _graphServiceClient.Me
.Messages[id]
.Request()
.Select("IsRead")
.UpdateAsync(new Message { IsRead = true });
}
catch (Exception ex)
{
_customLogger.Error(ex.Message, ex);
}
}

Related

azure ad list users into combobox graph api C#

I'm currently working on an application where i need to get all the users from the company's azure ad
I manage to make a select and get all users into the ResultText textblock.
i now use the DisplayBasicTokenInfo() to fill a textbox but it only returns 1 username and that's my own. Now what i want to do is make a list of all the users and load these into a combobox via DisplayBasicTokenInfo.
I don't know if this is the best solution but it is what i found. The end goal for this is that i want every Displayname to be a comboboxItem
This is the code i have so far.
public partial class TestGraphApi : Window
{
//Set the API Endpoint to Graph 'users' endpoint
string _graphAPIEndpoint = "https://graph.microsoft.com/v1.0/users?$select=displayName";
//Set the scope for API call to user.read.all
string[] _scopes = new string[] { "user.read.all" };
public TestGraphApi()
{
InitializeComponent();
}
/// <summary>
/// Call AcquireTokenAsync - to acquire a token requiring user to sign-in
/// </summary>
private async void CallGraphButton_Click(object sender, RoutedEventArgs e)
{
AuthenticationResult authResult = null;
var app = App.PublicClientApp;
ResultText.Text = string.Empty;
TokenInfoText.Text = string.Empty;
var accounts = await app.GetAccountsAsync();
var firstAccount = accounts.FirstOrDefault();
try
{
authResult = await app.AcquireTokenSilent(_scopes, firstAccount)
.ExecuteAsync();
}
catch (MsalUiRequiredException ex)
{
// A MsalUiRequiredException happened on AcquireTokenSilent.
// This indicates you need to call AcquireTokenInteractive to acquire a token
System.Diagnostics.Debug.WriteLine($"MsalUiRequiredException: {ex.Message}");
try
{
authResult = await app.AcquireTokenInteractive(_scopes)
.WithAccount(accounts.FirstOrDefault())
.WithPrompt(Prompt.SelectAccount)
.ExecuteAsync();
}
catch (MsalException msalex)
{
ResultText.Text = $"Error Acquiring Token:{System.Environment.NewLine}{msalex}";
}
}
catch (Exception ex)
{
ResultText.Text = $"Error Acquiring Token Silently:{System.Environment.NewLine}{ex}";
return;
}
if (authResult != null)
{
ResultText.Text = await GetHttpContentWithToken(_graphAPIEndpoint, authResult.AccessToken);
DisplayBasicTokenInfo(authResult);
}
}
/// <summary>
/// Perform an HTTP GET request to a URL using an HTTP Authorization header
/// </summary>
/// <param name="url">The URL</param>
/// <param name="token">The token</param>
/// <returns>String containing the results of the GET operation</returns>
public async Task<string> GetHttpContentWithToken(string url, string token)
{
var httpClient = new System.Net.Http.HttpClient();
System.Net.Http.HttpResponseMessage response;
try
{
var request = new System.Net.Http.HttpRequestMessage(System.Net.Http.HttpMethod.Get, url);
//Add the token in Authorization header
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
response = await httpClient.SendAsync(request);
var content = await response.Content.ReadAsStringAsync();
return content;
}
catch (Exception ex)
{
return ex.ToString();
}
}
/// <summary>
/// Display basic information contained in the token
/// </summary>
private void DisplayBasicTokenInfo(AuthenticationResult authResult)
{
TokenInfoText.Text = "";
if (authResult != null)
{
TokenInfoText.Text += $"{authResult.Account.Username}";
}
}
}
You use TokenInfoText.Text += $"{authResult.Account.Username}"; to set TokenInfoText in your case.
authResult.Account.Username just returned the signed-in user which used to get access token, but not the users the API returned.
If you just want to get all displayNames, you could decode the ResultText.Text and get the values. Convert JSON String to JSON Object c#.
Also you can use Microsoft Graph SDK to get the users.
private async Task<List<User>> GetUsersFromGraph()
{
// Create Graph Service Client, refer to https://github.com/microsoftgraph/msgraph-sdk-dotnet-auth
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithRedirectUri(redirectUri)
.WithClientSecret(clientSecret)
.Build();
AuthorizationCodeProvider authenticationProvider = new AuthorizationCodeProvider(confidentialClientApplication, scopes);
GraphServiceClient graphServiceClient = new GraphServiceClient(authenticationProvider);
// Create a bucket to hold the users
List<User> users = new List<User>();
// Get the first page
IGraphServiceUsersCollectionPage usersPage = await graphServiceClient
.Users
.Request()
.Select(u => new {
u.DisplayName
})
.GetAsync();
// Add the first page of results to the user list
users.AddRange(usersPage.CurrentPage);
// Fetch each page and add those results to the list
while (usersPage.NextPageRequest != null)
{
usersPage = await usersPage.NextPageRequest.GetAsync();
users.AddRange(usersPage.CurrentPage);
}
return users;
}

How to get all TeamDrives, Folders, and Files with Google Drive API (using pagination)

I have to go through all team drives, folders, and files and the code I have works perfectly fine:
/// <summary>
/// Get All files inside a folder
/// </summary>
/// <param name="service"></param>
/// <param name="folderId"></param>
/// <param name="td"></param>
/// <returns></returns>
private static async Task<Google.Apis.Drive.v3.Data.FileList> GetAllFilesInsideFolder(DriveService service, string folderId, TeamDrive td)
{
string FolderId = folderId;
// Define parameters of request.
FilesResource.ListRequest listRequest = service.Files.List();
listRequest.Corpora = "drive";
listRequest.SupportsAllDrives = true;
listRequest.DriveId = td.Id;
listRequest.PageSize = 100;
listRequest.IncludeItemsFromAllDrives = true;
listRequest.Q = "'" + FolderId + "' in parents and trashed=false";
listRequest.Fields = "nextPageToken, files(*)";
// List files.
Google.Apis.Drive.v3.Data.FileList files = await listRequest.ExecuteAsync();
return files;
}
/// <summary>
/// Gets all folders from an specific team drive
/// </summary>
/// <param name="service"></param>
/// <param name="td"></param>
/// <returns></returns>
private static async Task<Google.Apis.Drive.v3.Data.FileList> GetAllFoldersFromTeamDriveAsync(DriveService service, TeamDrive td)
{
try
{
var request = service.Files.List();
request.Corpora = "drive";
request.SupportsAllDrives = true;
request.DriveId = td.Id;
request.PageSize = 100;
request.IncludeItemsFromAllDrives = true;
request.Q = "mimeType = 'application/vnd.google-apps.folder'";
var response = await request.ExecuteAsync();
return response;
}
catch (Exception ex )
{
Console.WriteLine("GetAllFoldersFromTeamDriveAsync Error: " + ex.Message);
throw;
}
}
/// <summary>
/// Gets all teamdrives
/// </summary>
/// <param name="service"></param>
/// <returns></returns>
private static async Task<Google.Apis.Drive.v3.Data.TeamDriveList> GetAllTeamDrivesAsync(DriveService service)
{
try
{
var request = service.Teamdrives.List();
request.PageSize = 100;
var response = await request.ExecuteAsync();
return response;
}
catch (Exception ex)
{
Console.WriteLine("GetAllTeamDrivesAsync Error" + ex.Message);
throw;
}
}
Main method:
public async Task RunFix()
{
UserCredential credential;
Directory.CreateDirectory(StorageLocation);
var DataStore = new FileDataStore(StorageLocation);
using var stream = new FileStream(CredentialsString, FileMode.Open, FileAccess.Read);
// Please give consent to the Auth/drive scope
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
Username,
CancellationToken.None,
DataStore).Result;
try
{
var service = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
var clientbillCycleGoogleDriveFilesDEV = new TableClient("...");
// Get All team drives
var allTeamDrives = await GetAllTeamDrivesAsync(service);
// We will use a CSV to copy the unmapped files
var csvUnmappedBillCycleFiles = new StringBuilder();
//Iterate over all Team Drives I have access to:
foreach (TeamDrive td in allTeamDrives.TeamDrives)
{
if (td.Name == "XXX")
{
Console.WriteLine("Team Drive: " + td.Name);
Stopwatch sAllFilesInsideFolder = Stopwatch.StartNew();
// Get All Folders inside this team drive
var allFolders = await GetAllFoldersFromTeamDriveAsync(service, td);
foreach (Google.Apis.Drive.v3.Data.File file in allFolders.Files)
{
Console.WriteLine("--Folder-- " + file.Name);
if (file.Name.StartsWith("xyz"))
{
//some business logic
foreach (Google.Apis.Drive.v3.Data.File googlefile in allFilesInsideFolder.Files)
{
//some business logic
}
}
}
}
}
System.IO.File.WriteAllText("csvUnmappedBillCycleFiles.csv", csvUnmappedBillCycleFiles.ToString());
}
catch (Exception ex)
{
Console.WriteLine("Error" + ex.Message);
}
}
The problem is that I cant set PageSize to something above 100.
How can I change this code to actually to go through everything?
Assuming that you are using drives list and files list. Which is just a guess since you haven't included the code GetAllTeamDrivesAsync? GetAllFoldersFromTeamDriveAsync.
You can set the page size to max 1000 on your file.list method.
var request = service.Files.List();
request.PageSize = 1000;
But thats you are still going to need to paginate over the page. I recommend using the pageStreamer. It will loop over each of the rows for you.
var request = service.Files.List();
request.PageSize = 1000;
var pageStreamer =
new Google.Apis.Requests.PageStreamer<Google.Apis.Drive.v3.Data.File, FilesResource.ListRequest,
Google.Apis.Drive.v3.Data.FileList, string>(
(req, token) => request.PageToken = token,
response => response.NextPageToken,
response => response.Files);
// data storage
var all = new Google.Apis.Drive.v3.Data.FileList();
all.Files = new List<Google.Apis.Drive.v3.Data.File>();
// fetching data and adding it to storage
foreach (var result in await pageStreamer.FetchAllAsync(request, CancellationToken.None))
{
all.Files.Add(result);
}
I have a video up that explains how pagestreamer works How to use the nextpagetoken from the files list method in the Google Drive api v3.
You should be able to use pagestreamer with drives list as well.

Get messages which contain an attachment via gmail API

I'm building an app based on the Gmail API. I can see all messages from the current inbox, but I need to limit this to only messages which have an attachment. How can I do this?
This is my GoogleController.cs:
[HttpGet]
[Authorize]
public async Task<IActionResult> GetListEmail(string LabelId, string nameLabel)
{
string UserEmail = User.FindFirst(c => c.Type == ClaimTypes.Email).Value;
var service = GetService();
List<My_Message> listMessages = new List<My_Message>();
List<Message> result = new List<Message>();
var emailListRequest = service.Users.Messages.List(UserEmail);
emailListRequest.LabelIds = LabelId;
emailListRequest.IncludeSpamTrash = false;
emailListRequest.MaxResults = 1000;
var emailListResponse = await emailListRequest.ExecuteAsync();
if (emailListResponse != null && emailListResponse.Messages != null)
{
foreach (var email in emailListResponse.Messages)
{
var emailInfoRequest = service.Users.Messages.Get(UserEmail, email.Id);
var emailInfoResponse = await emailInfoRequest.ExecuteAsync();
if (emailInfoResponse != null)
{
My_Message message = new My_Message();
message.Id = listMessages.Count + 1;
message.EmailId = email.Id;
foreach (var mParts in emailInfoResponse.Payload.Headers)
{
if (mParts.Name == "Date")
message.Date_Received = mParts.Value;
else if (mParts.Name == "From")
message.From = mParts.Value;
else if (mParts.Name == "Subject")
message.Title = mParts.Value;
}
listMessages.Add(message);
}
}
}
ViewBag.Message = nameLabel;
return View("~/Views/Home/Index.cshtml", listMessages);
}
I think a simpler solution would be to query in the Users.messages.list endpoint without the need to create filters.
You can actually use the parameter q to make a query like you would in the GMail searchbox, if not familiar you can look at the whole list of operators.
In fact there is an example to make use of this query parameter in the documentation:
using Google.Apis.Gmail.v1;
using Google.Apis.Gmail.v1.Data;
using System.Collections.Generic;
// ...
public class MyClass {
// ...
/// <summary>
/// List all Messages of the user's mailbox matching the query.
/// </summary>
/// <param name="service">Gmail API service instance.</param>
/// <param name="userId">User's email address. The special value "me"
/// can be used to indicate the authenticated user.</param>
/// <param name="query">String used to filter Messages returned.</param>
public static List<Message> ListMessages(GmailService service, String userId, String query)
{
List<Message> result = new List<Message>();
UsersResource.MessagesResource.ListRequest request = service.Users.Messages.List(userId);
request.Q = query; // inform this with the right query
do
{
try
{
ListMessagesResponse response = request.Execute();
result.AddRange(response.Messages);
request.PageToken = response.NextPageToken;
}
catch (Exception e)
{
Console.WriteLine("An error occurred: " + e.Message);
}
} while (!String.IsNullOrEmpty(request.PageToken));
return result;
}
// ...
}
So for your case you just need to add the line
emailListRequest.Q = "has:attachment";
before executing the request, doing like this will not create a whole filter for your account, so maybe it's more convenient for your case.
use a filter with criteria like this => criteria.query="has:attachment"
see also here => https://developers.google.com/gmail/api/v1/reference/users/settings/filters#resource
For testing any filter you can use GMail:
image

Delete Secret on Azure Keyvault not working

I have a web API method which creates secrets on azure key vault and it works fine, I also have a delete method that deletes an entity and its associated secrets, however, this method is not deleting the key on azure key vault, but it's not throwing an exception either!
Here the helper methods:
public async Task OnCreateSecretAsync(string name, string value)
{
Message = "Your application description page.";
int retries = 0;
bool retry = false;
try
{
/* The below 4 lines of code shows you how to use AppAuthentication library to set secrets from your Key Vault*/
AzureServiceTokenProvider azureServiceTokenProvider = new AzureServiceTokenProvider();
KeyVaultClient keyVaultClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));
var result = await keyVaultClient.SetSecretAsync(ConfigurationManager.AppSettings["VaultUrl"].ToString(), name, value)
.ConfigureAwait(false);
SecretIdentifier = result.Id;
/* The below do while logic is to handle throttling errors thrown by Azure Key Vault. It shows how to do exponential backoff which is the recommended client side throttling*/
do
{
long waitTime = Math.Min(GetWaitTime(retries), 2000000);
result = await keyVaultClient.SetSecretAsync(ConfigurationManager.AppSettings["VaultUrl"].ToString(), name, value)
.ConfigureAwait(false);
Message = result.Id;
retry = false;
}
while (retry && (retries++ < 10));
}
/// <exception cref="KeyVaultErrorException">
/// Thrown when the operation returned an invalid status code
/// </exception>
catch (KeyVaultErrorException keyVaultException)
{
Message = keyVaultException.Message;
if ((int)keyVaultException.Response.StatusCode == 429)
retry = true;
}
}
/// <summary>
/// Deletes secrets
/// </summary>
/// <param name="name">Secret</param>
/// <param name="value">Value</param>
/// <returns></returns>
public async Task OnDeleteSecretAsync(string name)
{
Message = "Your application description page.";
int retries = 0;
bool retry = false;
try
{
/* The below 4 lines of code shows you how to use AppAuthentication library to set secrets from your Key Vault*/
AzureServiceTokenProvider azureServiceTokenProvider = new AzureServiceTokenProvider();
KeyVaultClient keyVaultClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));
var result = await keyVaultClient.DeleteSecretAsync(ConfigurationManager.AppSettings["VaultUrl"].ToString(), name)
.ConfigureAwait(false);
SecretIdentifier = result.Id;
/* The below do while logic is to handle throttling errors thrown by Azure Key Vault. It shows how to do exponential backoff which is the recommended client side throttling*/
do
{
long waitTime = Math.Min(GetWaitTime(retries), 2000000);
result = await keyVaultClient.DeleteSecretAsync(ConfigurationManager.AppSettings["VaultUrl"].ToString(), name)
.ConfigureAwait(false);
Message = result.Id;
retry = false;
}
while (retry && (retries++ < 10));
}
/// <exception cref="KeyVaultErrorException">
/// Thrown when the operation returned an invalid status code
/// </exception>
catch (KeyVaultErrorException keyVaultException)
{
Message = keyVaultException.Message;
if ((int)keyVaultException.Response.StatusCode == 429)
retry = true;
}
}
And here the methods where I call them from:
public async Task<IHttpActionResult> AddGlobalDesignTenant([FromBody]GlobalDesignTenant globaldesigntenant)
{
var telemetry = new TelemetryClient();
try
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
string domainUrl = globaldesigntenant.TestSiteCollectionUrl;
string tenantName = domainUrl.Split('.')[0].Remove(0, 8);
globaldesigntenant.TenantName = tenantName;
var globalDesignTenantStore = CosmosStoreHolder.Instance.CosmosStoreGlobalDesignTenant;
byte[] data = Convert.FromBase64String(globaldesigntenant.base64CertFile);
var cert = new X509Certificate2(
data,
globaldesigntenant.CertificatePassword,
X509KeyStorageFlags.Exportable |
X509KeyStorageFlags.MachineKeySet |
X509KeyStorageFlags.PersistKeySet);
try
{
using (var cc = new AuthenticationManager().GetAzureADAppOnlyAuthenticatedContext(globaldesigntenant.TestSiteCollectionUrl,
globaldesigntenant.Applicationid,
globaldesigntenant.TenantName + ".onmicrosoft.com",
cert, AzureEnvironment.Production))
{
cc.Load(cc.Web, p => p.Title);
cc.ExecuteQuery();
Console.WriteLine(cc.Web.Title);
}
}
catch (Exception ex)
{
return BadRequest("Cant authenticate with those credentials");
}
KeyVaultHelper keyVaultHelperPFX = new KeyVaultHelper();
await keyVaultHelperPFX.OnCreateSecretAsync("GlobalDesignTenantPFXFileBAse64"+ tenantName, globaldesigntenant.base64CertFile);
globaldesigntenant.SecretIdentifierBase64PFXFile = keyVaultHelperPFX.SecretIdentifier;
KeyVaultHelper keyVaultHelperPassword = new KeyVaultHelper();
await keyVaultHelperPassword.OnCreateSecretAsync("GlobalDesignTenantCertPassword" + tenantName, globaldesigntenant.CertificatePassword);
globaldesigntenant.SecretIdentifieCertificatePassword = keyVaultHelperPassword.SecretIdentifier;
globaldesigntenant.CertificatePassword = string.Empty;
globaldesigntenant.base64CertFile = string.Empty;
var added = await globalDesignTenantStore.AddAsync(globaldesigntenant);
return Ok(added);
}
catch (Exception ex)
{
string guid = Guid.NewGuid().ToString();
var dt = new Dictionary<string, string>
{
{ "Error Lulo: ", guid }
};
telemetry.TrackException(ex, dt);
return BadRequest("Error Lulo: " + guid);
}
}
public async Task<IHttpActionResult> DeleteGlobalDesignTenant(string id)
{
var telemetry = new TelemetryClient();
try
{
var globalDesignTenantStore = CosmosStoreHolder.Instance.CosmosStoreGlobalDesignTenant;
var globalDesignTenant = await globalDesignTenantStore.FindAsync(id, "globaldesigntenants");
KeyVaultHelper keyVaultHelperPFX = new KeyVaultHelper();
await keyVaultHelperPFX.OnDeleteSecretAsync("GlobalDesignTenantPFXFileBAse64" + globalDesignTenant.TenantName);
KeyVaultHelper keyVaultHelperPassword = new KeyVaultHelper();
await keyVaultHelperPassword.OnDeleteSecretAsync("GlobalDesignTenantCertPassword" + globalDesignTenant.TenantName);
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var result = await globalDesignTenantStore.RemoveAsync(globalDesignTenant);
return Ok(result);
}
catch (Exception ex)
{
string guid = Guid.NewGuid().ToString();
var dt = new Dictionary<string, string>
{
{ "Error Lulo: ", guid }
};
telemetry.TrackException(ex, dt);
return BadRequest("Error Lulo: " + guid);
}
}
Based on my test, await keyVaultClient.DeleteSecretAsync(ConfigurationManager.AppSettings["VaultUrl"].ToString(), name) will delete the secret with specified name.
So, please set a break-point at the delete calling. Then run your application to see if it hits, and check if the parameters were expected values.

How to send broadcast message using webhook in dialogflow.ai , in c#

I'm creating a bot using dialogflow ( api.ai ) and webhook in c# using visual studio.
I have to create the option of broadcast message for all the user who has request to be subscribe.
When user call intent subscribe , i call the action : subscribe.about where i save psid .
The code below :
public ApiAiResponse Post([FromBody]JObject jsonRequest)
{
using (WebhookReceiverModelDataContext ctx = new
WebhookReceiverModelDataContext())
{
ApiAiRequest request = jsonRequest.ToObject<ApiAiRequest>();
ApiAiResponse response = new ApiAiResponse();
JObject jObject =
JObject.Parse(request.result.parameters.ToString());
if ("subscribe.about".Equals(request.result.action.ToLower()))
{
using (WebhookReceiverModelDataContext cts = new
WebhookReceiverModelDataContext())
{
string messageabout = request.result.resolvedQuery != null ? request.result.resolvedQuery : "";
speechLang = "You are subscribed";
source = "Broadcast";
}
}
I have to create a module to send broadcast message to all this users in the moment that the event will be online .
I have in a table stored data like : Id, Event Name , Event Date
When : Event Date = now => i have to send the message all this users which are subscribed.
I start to use proactive message , but seems that they are created using botframework . I want to use only the webhook .
I created a controller to send message in facebook , but seems not OK :
I got this message when i call this controller :
<Error>
<Message>An error has occurred.</Message>
</Error>
namespace WebhookReceiver.Controllers
{
public class WebhookController : ApiController
{
string pageToken = "xxxxxxx";
string appSecret = "yyyyy";
public HttpResponseMessage Get()
{
var querystrings = Request.GetQueryNameValuePairs().ToDictionary(x
=> x.Key, x => x.Value);
if (querystrings["hub.verify_token"] == "zzzzz")
{
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(querystrings["hub.challenge"],
Encoding.UTF8, "text/plain")
};
}
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
}
[HttpPost]
public async Task<HttpResponseMessage> Post()
{
var signature = Request.Headers.GetValues("X-Hub-
Signature").FirstOrDefault().Replace("sha1=", "");
var body = await Request.Content.ReadAsStringAsync();
if (!VerifySignature(signature, body))
return new HttpResponseMessage(HttpStatusCode.BadRequest);
var value = JsonConvert.DeserializeObject<WebhookModel>(body);
if (value._object != "page")
return new HttpResponseMessage(HttpStatusCode.OK);
foreach (var item in value.entry[0].messaging)
{
if (item.message == null && item.postback == null)
continue;
else
await SendMessage(GetMessageTemplate(item.message.text,
item.sender.id));
}
return new HttpResponseMessage(HttpStatusCode.OK);
}
private bool VerifySignature(string signature, string body)
{
var hashString = new StringBuilder();
using (var crypto = new HMACSHA1(Encoding.UTF8.GetBytes(appSecret)))
{
var hash = crypto.ComputeHash(Encoding.UTF8.GetBytes(body));
foreach (var item in hash)
hashString.Append(item.ToString("X2"));
}
return hashString.ToString().ToLower() == signature.ToLower();
}
/// <summary>
/// get text message template
/// </summary>
/// <param name="text">text</param>
/// <param name="sender">sender id</param>
/// <returns>json</returns>
private JObject GetMessageTemplate(string text, string sender)
{
return JObject.FromObject(new
{
recipient = new { id = sender },
message = new { text = text }
});
}
/// <summary>
/// send message
/// </summary>
/// <param name="json">json</param>
private async Task SendMessage(JObject json)
{
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Add(new
MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage res = await
client.PostAsync($"https://graph.facebook.com/v2.6/me/messages?
access_token={pageToken}", new StringContent(json.ToString(),
Encoding.UTF8,
"application/json"));
}
}
}
}
How can i create this module please . I need some advises . Using postman i have test to send message in by bot using API call of Facebook . I'm new in c# so please help me :(

Categories