public async override Task RunAsync(AuthorizationData authorizationData) { try { Service = new ServiceClient<ICustomerManagementService>(authorizationData); var getUserResponse = await GetUserAsync(null); var user = getUserResponse.User; // Search for the Bing Ads accounts that the user can access. var accounts = await SearchAccountsByUserIdAsync(user.Id); // Optionally if you are enabled for Final Urls, you can update each account with a tracking template. var accountFCM = new List<KeyValuePair<string, string>>(); accountFCM.Add(new KeyValuePair<string, string>( "TrackingUrlTemplate", "http://tracker.example.com/?season={_season}&promocode={_promocode}&u={lpurl}")); OutputStatusMessage("The user can access the following Bing Ads accounts: \n"); foreach (var account in accounts) { OutputAccount(account); // Optionally you can find out which pilot features the customer is able to use. // Each account could belong to a different customer, so use the customer ID in each account. var featurePilotFlags = await GetCustomerPilotFeaturesAsync((long)account.ParentCustomerId); OutputStatusMessage("Customer Pilot flags:"); OutputStatusMessage(string.Join("; ", featurePilotFlags.Select(flag => string.Format("{0}", flag)))); // Optionally if you are enabled for Final Urls, you can update each account with a tracking template. // The pilot flag value for Final Urls is 194. if (featurePilotFlags.Any(pilotFlag => pilotFlag == 194)) { account.ForwardCompatibilityMap = accountFCM; await UpdateAccountAsync(account); OutputStatusMessage(string.Format("Updated the account with a TrackingUrlTemplate: {0}\n", accountFCM.ToArray().SingleOrDefault(keyValuePair => keyValuePair.Key == "TrackingUrlTemplate").Value)); } } } // Catch authentication exceptions catch (OAuthTokenRequestException ex) { OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description)); } // Catch Customer Management service exceptions catch (FaultException<Microsoft.BingAds.CustomerManagement.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.CustomerManagement.ApiFault> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (Exception ex) { OutputStatusMessage(ex.Message); } }
public async override Task RunAsync(AuthorizationData authorizationData) { try { Service = new ServiceClient<ICustomerManagementService>(authorizationData); var user = await GetUserAsync(null); // Search for the accounts that matches the specified criteria. var accounts = await SearchAccountsByUserIdAsync(user.Id); PrintAccounts(accounts); } // Catch authentication exceptions catch (OAuthTokenRequestException ex) { OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description)); } // Catch Customer Management service exceptions catch (FaultException<Microsoft.BingAds.CustomerManagement.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.CustomerManagement.ApiFault> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (Exception ex) { OutputStatusMessage(ex.Message); } }
public async override Task RunAsync(AuthorizationData authorizationData) { try { AdInsightService = new ServiceClient<IAdInsightService>(authorizationData); CampaignService = new ServiceClient<ICampaignManagementService>(authorizationData); var campaigns = (Campaign[])await GetCampaignsByAccountIdAsync( authorizationData.AccountId, CampaignType.SearchAndContent | CampaignType.Shopping); IList<BudgetOpportunity> opportunities = null; // Get the budget opportunities for each campaign in the current authenticated account. foreach (var campaign in campaigns) { if (campaign.Id != null) { opportunities = await GetBudgetOpportunitiesAsync((long)campaign.Id); OutputBudgetOpportunities(opportunities, (long) campaign.Id); } } } // Catch authentication exceptions catch (OAuthTokenRequestException ex) { OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description)); } // Catch AdInsight service exceptions catch (FaultException<Microsoft.BingAds.V10.AdInsight.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.V10.AdInsight.ApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } // Catch Campaign Management service exceptions catch (FaultException<Microsoft.BingAds.V10.CampaignManagement.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.V10.CampaignManagement.ApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (Exception ex) { OutputStatusMessage(ex.Message); } }
public static AuthorizationData GetAuthorizationData() { //var authentication = GetOAuthDesktopMobileImplicitGrant(); //var authentication = GetOAuthDesktopMobileAuthCodeGrantUsingCode(); var authentication = GetOAuthDesktopMobileAuthCodeGrantUsingToken(); var authorizationData = new AuthorizationData { Authentication = authentication, CustomerId = GlobalConfig.CustomerId, AccountId = GlobalConfig.AccountId, DeveloperToken = GlobalConfig.DevToken }; return authorizationData; }
/// <summary> /// Initializes a new instance of this class with the specified <see cref="AuthorizationData"/>. /// </summary> /// <param name="authorizationData">Represents a user who intends to access the corresponding customer and account.</param> public ServiceClient(AuthorizationData authorizationData) : this(authorizationData, null) { }
/// <summary> /// If for any reason you have to resume from a previous application state, /// you can use an existing download request identifier and use it /// to download the result file. Use TrackAsync to indicate that the application /// should wait to ensure that the download status is completed. /// </summary> /// <param name="requestId"></param> /// <param name="authorizationData"></param> /// <returns></returns> private async Task DownloadResultsAsync( string requestId, AuthorizationData authorizationData) { // You may optionally cancel the TrackAsync operation after a specified time interval. var tokenSource = new CancellationTokenSource(); tokenSource.CancelAfter(TimeoutInMilliseconds); var bulkDownloadOperation = new BulkDownloadOperation(requestId, authorizationData); // Use TrackAsync to indicate that the application should wait to ensure that // the download status is completed. var bulkOperationStatus = await bulkDownloadOperation.TrackAsync(null, tokenSource.Token); var resultFilePath = await bulkDownloadOperation.DownloadResultFileAsync( FileDirectory, ResultFileName, decompress: true, overwrite: true); // Set this value true if you want to overwrite the same file. OutputStatusMessage(String.Format("Download result file: {0}", resultFilePath)); OutputStatusMessage(String.Format("Status: {0}", bulkOperationStatus.Status)); OutputStatusMessage(String.Format("TrackingId: {0}\n", bulkOperationStatus.TrackingId)); }
public async override Task RunAsync(AuthorizationData authorizationData) { try { CampaignService = new ServiceClient<ICampaignManagementService>(authorizationData); CustomerService = new ServiceClient<ICustomerManagementService>(authorizationData); #region MigrationStatus // To prepare for the sitelink ad extensions migration in 2017, you will need to determine // whether the account has been migrated from SiteLinksAdExtension to Sitelink2AdExtension. // All ad extension service operations available for both types of sitelinks; however you will // need to determine which type to add, update, and retrieve. bool sitelinkMigrationIsCompleted = false; // Optionally you can find out which pilot features the customer is able to use. Even if the customer // is in pilot for sitelink migrations, the accounts that it contains might not be migrated. var featurePilotFlags = await GetCustomerPilotFeaturesAsync(authorizationData.CustomerId); // The pilot flag value for Sitelink ad extension migration is 253. // Pilot flags apply to all accounts within a given customer; however, // each account goes through migration individually and has its own migration status. if (featurePilotFlags.Any(pilotFlag => pilotFlag == 253)) { // Account migration status below will be either NotStarted, InProgress, or Completed. OutputStatusMessage("Customer is in pilot for Sitelink migration.\n"); } else { // Account migration status below will be NotInPilot. OutputStatusMessage("Customer is not in pilot for Sitelink migration.\n"); } // Even if you have multiple accounts per customer, each account will have its own // migration status. This example checks one account using the provided AuthorizationData. var accountMigrationStatusesInfos = (await GetAccountMigrationStatusesAsync( new long[] { authorizationData.AccountId }, SITELINK_MIGRATION )).ToArray(); foreach (var accountMigrationStatusesInfo in accountMigrationStatusesInfos) { OutputAccountMigrationStatusesInfo(accountMigrationStatusesInfo); if (accountMigrationStatusesInfo.MigrationStatusInfo.Any( statusInfo => statusInfo.Status == MigrationStatus.Completed && SITELINK_MIGRATION.CompareTo(statusInfo.MigrationType) == 0)) { sitelinkMigrationIsCompleted = true; } } #endregion MigrationStatus // Add a campaign that will later be associated with ad extensions. var campaigns = new[] { new Campaign { Name = "Women's Shoes " + DateTime.UtcNow, Description = "Red shoes line.", // You must choose to set either the shared budget ID or daily amount. // You can set one or the other, but you may not set both. BudgetId = null, DailyBudget = 50, BudgetType = BudgetLimitType.DailyBudgetStandard, BiddingScheme = new EnhancedCpcBiddingScheme(), TimeZone = "PacificTimeUSCanadaTijuana", DaylightSaving = true, // Used with FinalUrls shown in the sitelinks that we will add below. TrackingUrlTemplate = "http://tracker.example.com/?season={_season}&promocode={_promocode}&u={lpurl}" } }; AddCampaignsResponse addCampaignsResponse = await AddCampaignsAsync(authorizationData.AccountId, campaigns); long?[] campaignIds = addCampaignsResponse.CampaignIds.ToArray(); // Specify the extensions. var adExtensions = new AdExtension[] { //new AppAdExtension //{ // AppPlatform = "Windows", // AppStoreId = "AppStoreIdGoesHere", // DestinationUrl = "DestinationUrlGoesHere", // DisplayText = "Contoso", //}, new CallAdExtension { CountryCode = "US", PhoneNumber = "2065550100", IsCallOnly = false, Scheduling = new Schedule { // For this example assume the call center is open Monday - Friday from 9am - 9pm // in the account's time zone. UseSearcherTimeZone = false, DayTimeRanges = new[] { new DayTime { Day = Day.Monday, StartHour = 9, StartMinute = Minute.Zero, EndHour = 21, EndMinute = Minute.Zero, }, new DayTime { Day = Day.Tuesday, StartHour = 9, StartMinute = Minute.Zero, EndHour = 21, EndMinute = Minute.Zero, }, new DayTime { Day = Day.Wednesday, StartHour = 9, StartMinute = Minute.Zero, EndHour = 21, EndMinute = Minute.Zero, }, new DayTime { Day = Day.Thursday, StartHour = 9, StartMinute = Minute.Zero, EndHour = 21, EndMinute = Minute.Zero, }, new DayTime { Day = Day.Friday, StartHour = 9, StartMinute = Minute.Zero, EndHour = 21, EndMinute = Minute.Zero, }, }, StartDate = null, EndDate = new Microsoft.BingAds.V10.CampaignManagement.Date { Month = 12, Day = 31, Year = DateTime.UtcNow.Year + 1 }, } }, new CalloutAdExtension { Text = "Callout Text" }, //new ImageAdExtension //{ // AlternativeText = "Image Extension Alt Text", // ImageMediaIds = new long[] { await AddImageAsync(authorizationData) } //}, new LocationAdExtension { PhoneNumber = "206-555-0100", CompanyName = "Contoso Shoes", IconMediaId = null, ImageMediaId = null, Address = new Microsoft.BingAds.V10.CampaignManagement.Address { StreetAddress = "1234 Washington Place", StreetAddress2 = "Suite 1210", CityName = "Woodinville", ProvinceName = "WA", CountryCode = "US", PostalCode = "98608" }, Scheduling = new Schedule { // For this example assume you want to drive traffic every Saturday morning // in the search user's time zone. UseSearcherTimeZone = true, DayTimeRanges = new[] { new DayTime { Day = Day.Saturday, StartHour = 9, StartMinute = Minute.Zero, EndHour = 12, EndMinute = Minute.Zero, }, }, StartDate = null, EndDate = new Microsoft.BingAds.V10.CampaignManagement.Date { Month = 12, Day = 31, Year = DateTime.UtcNow.Year + 1 }, } }, new ReviewAdExtension { IsExact = true, Source = "Review Source Name", Text = "Review Text", Url = "http://review.contoso.com" // The Url of the third-party review. This is not your business Url. }, new StructuredSnippetAdExtension { Header = "Brands", Values = new [] { "Windows", "Xbox", "Skype"} } }; // Before migration only the deprecated SiteLinksAdExtension type can be added, // and after migration only the new Sitelink2AdExtension type can be added. adExtensions = adExtensions.Concat(sitelinkMigrationIsCompleted ? (AdExtension[]) GetSampleSitelink2AdExtensions() : GetSampleSiteLinksAdExtensions()).ToArray(); // Add all extensions to the account's ad extension library var adExtensionIdentities = await AddAdExtensionsAsync( authorizationData.AccountId, adExtensions ); OutputStatusMessage("Added ad extensions.\n"); // DeleteAdExtensionsAssociations, SetAdExtensionsAssociations, and GetAdExtensionsEditorialReasons // operations each require a list of type AdExtensionIdToEntityIdAssociation. var adExtensionIdToEntityIdAssociations = new AdExtensionIdToEntityIdAssociation[adExtensionIdentities.Count]; // GetAdExtensionsByIds requires a list of type long. var adExtensionIds = new long[adExtensionIdentities.Count]; // Loop through the list of extension IDs and build any required data structures // for subsequent operations. for (int i = 0; i < adExtensionIdentities.Count; i++) { adExtensionIdToEntityIdAssociations[i] = new AdExtensionIdToEntityIdAssociation { AdExtensionId = adExtensionIdentities[i].Id, EntityId = (long)campaignIds[0] }; adExtensionIds[i] = adExtensionIdentities[i].Id; } // Associate the specified ad extensions with the respective campaigns or ad groups. await SetAdExtensionsAssociationsAsync( authorizationData.AccountId, adExtensionIdToEntityIdAssociations, AssociationType.Campaign ); OutputStatusMessage("Set ad extension associations.\n"); // Get editorial rejection reasons for the respective ad extension and entity associations. var adExtensionEditorialReasonCollection = (AdExtensionEditorialReasonCollection[])await GetAdExtensionsEditorialReasons( authorizationData.AccountId, adExtensionIdToEntityIdAssociations, AssociationType.Campaign ); // If migration has been completed, then you should request the Sitelink2AdExtension objects. // You can always request both types; however, before migration only the deprecated SiteLinksAdExtension // type will be returned, and after migration only the new Sitelink2AdExtension type will be returned. AdExtensionsTypeFilter adExtensionsTypeFilter = (sitelinkMigrationIsCompleted ? AdExtensionsTypeFilter.Sitelink2AdExtension : AdExtensionsTypeFilter.SiteLinksAdExtension) | AdExtensionsTypeFilter.AppAdExtension | AdExtensionsTypeFilter.CallAdExtension | AdExtensionsTypeFilter.CalloutAdExtension | AdExtensionsTypeFilter.ImageAdExtension | AdExtensionsTypeFilter.LocationAdExtension | AdExtensionsTypeFilter.ReviewAdExtension | AdExtensionsTypeFilter.StructuredSnippetAdExtension; // Get all ad extensions added above. adExtensions = (AdExtension[]) await GetAdExtensionsByIdsAsync( authorizationData.AccountId, adExtensionIds, adExtensionsTypeFilter, AdExtensionAdditionalField.Scheduling ); OutputStatusMessage("List of ad extensions that were added above:\n"); OutputAdExtensionsWithEditorialReasons(adExtensions, adExtensionEditorialReasonCollection); // Get only the location extensions and remove scheduling. adExtensionsTypeFilter = AdExtensionsTypeFilter.LocationAdExtension; adExtensions = (AdExtension[])await GetAdExtensionsByIdsAsync( authorizationData.AccountId, adExtensionIds, adExtensionsTypeFilter, AdExtensionAdditionalField.Scheduling ); var updateExtensions = new List<AdExtension>(); var updateExtensionIds = new List<long>(); foreach (var extension in adExtensions) { // GetAdExtensionsByIds will return a nil element if the request filters / conditions were not met. if(extension != null && extension.Id != null) { // Remove read-only elements that would otherwise cause the update operation to fail. var updateExtension = SetReadOnlyAdExtensionElementsToNull(extension); // If you set the Scheduling element null, any existing scheduling set for the ad extension will remain unchanged. // If you set this to any non-null Schedule object, you are effectively replacing existing scheduling // for the ad extension. In this example, we will remove any existing scheduling by setting this element // to an empty Schedule object. updateExtension.Scheduling = new Schedule { }; updateExtensions.Add(updateExtension); updateExtensionIds.Add((long)updateExtension.Id); } } OutputStatusMessage("Removing scheduling from the location ad extensions..\n"); await UpdateAdExtensionsAsync(authorizationData.AccountId, updateExtensions); // Get only the location extension to output the result. adExtensions = (AdExtension[])await GetAdExtensionsByIdsAsync( authorizationData.AccountId, updateExtensionIds, adExtensionsTypeFilter, AdExtensionAdditionalField.Scheduling ); OutputStatusMessage("List of ad extensions that were updated above:\n"); OutputAdExtensionsWithEditorialReasons(adExtensions, null); // Delete the ad extension associations, ad extensions, and campaign, that were previously added. // You should remove these lines if you want to view the added entities in the // Bing Ads web application or another tool. // Remove the specified associations from the respective campaigns or ad groups. // At this point the ad extensions are still available in the account's ad extensions library. await DeleteAdExtensionsAssociationsAsync( authorizationData.AccountId, adExtensionIdToEntityIdAssociations, AssociationType.Campaign ); OutputStatusMessage("Deleted ad extension associations.\n"); // Deletes the ad extensions from the account’s ad extension library. await DeleteAdExtensionsAsync( authorizationData.AccountId, adExtensionIds ); OutputStatusMessage("Deleted ad extensions.\n"); await DeleteCampaignsAsync(authorizationData.AccountId, new[] { (long)campaignIds[0] }); OutputStatusMessage(String.Format("Deleted CampaignId {0}\n", (long)campaignIds[0])); } // Catch authentication exceptions catch (OAuthTokenRequestException ex) { OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description)); } // Catch Campaign Management service exceptions catch (FaultException<Microsoft.BingAds.V10.CampaignManagement.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.V10.CampaignManagement.ApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.V10.CampaignManagement.EditorialApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } // Catch Customer Management service exceptions catch (FaultException<Microsoft.BingAds.CustomerManagement.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.CustomerManagement.ApiFault> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (Exception ex) { OutputStatusMessage(ex.Message); } }
public async override Task RunAsync(AuthorizationData authorizationData) { try { BulkService = new BulkServiceManager(authorizationData); var progress = new Progress<BulkOperationProgressInfo>(x => OutputStatusMessage(String.Format("{0} % Complete", x.PercentComplete.ToString(CultureInfo.InvariantCulture)))); var uploadEntities = new List<BulkEntity>(); #region Add // Prepare the bulk entities that you want to upload. Each bulk entity contains the corresponding campaign management object, // and additional elements needed to read from and write to a bulk file. var bulkCampaign = new BulkCampaign { // ClientId may be used to associate records in the bulk upload file with records in the results file. The value of this field // is not used or stored by the server; it is simply copied from the uploaded record to the corresponding result record. // Note: This bulk file Client Id is not related to an application Client Id for OAuth. ClientId = "YourClientIdGoesHere", Campaign = new Campaign { // When using the Campaign Management service, the Id cannot be set. In the context of a BulkCampaign, the Id is optional // and may be used as a negative reference key during bulk upload. For example the same negative value set for the campaign Id // will be used when associating this new campaign with a new call ad extension in the BulkCampaignCallAdExtension object below. Id = campaignIdKey, Name = "Women's Shoes " + DateTime.UtcNow, Description = "Red shoes line.", BudgetType = BudgetLimitType.MonthlyBudgetSpendUntilDepleted, MonthlyBudget = 1000.00, TimeZone = "PacificTimeUSCanadaTijuana", // DaylightSaving is not supported in the Bulk file schema. Whether or not you specify it in a BulkCampaign, // the value is not written to the Bulk file, and by default DaylightSaving is set to true. DaylightSaving = true, // Used with FinalUrls shown in the text ads that we will add below. TrackingUrlTemplate = "http://tracker.example.com/?season={_season}&promocode={_promocode}&u={lpurl}" } }; // Specify one or more ad groups. var bulkAdGroup = new BulkAdGroup { CampaignId = campaignIdKey, AdGroup = new AdGroup { Id = adGroupIdKey, Name = "Women's Red Shoe Sale", AdDistribution = AdDistribution.Search, BiddingModel = BiddingModel.Keyword, PricingModel = PricingModel.Cpc, StartDate = null, EndDate = new Microsoft.BingAds.V10.CampaignManagement.Date { Month = 12, Day = 31, Year = 2016 }, Language = "English", Status = AdGroupStatus.Active, // You could use a tracking template which would override the campaign level // tracking template. Tracking templates defined for lower level entities // override those set for higher level entities. // In this example we are using the campaign level tracking template. TrackingUrlTemplate = null, }, }; // In this example only the second keyword should succeed. The Text of the first keyword exceeds the limit, // and the third keyword is a duplicate of the second keyword. var bulkKeywords = new [] { new BulkKeyword{ AdGroupId = adGroupIdKey, Keyword = new Keyword { Bid = new Bid { Amount = 0.47 }, Param2 = "10% Off", MatchType = MatchType.Broad, Text = "Brand-A Shoes Brand-A Shoes Brand-A Shoes Brand-A Shoes Brand-A Shoes " + "Brand-A Shoes Brand-A Shoes Brand-A Shoes Brand-A Shoes Brand-A Shoes " + "Brand-A Shoes Brand-A Shoes Brand-A Shoes Brand-A Shoes Brand-A Shoes" }, }, new BulkKeyword{ AdGroupId = adGroupIdKey, Keyword = new Keyword { Bid = new Bid { Amount = 0.47 }, Param2 = "10% Off", MatchType = MatchType.Phrase, Text = "Brand-A Shoes" }, }, new BulkKeyword{ AdGroupId = adGroupIdKey, Keyword = new Keyword { Bid = new Bid { Amount = 0.47 }, Param2 = "10% Off", MatchType = MatchType.Phrase, Text = "Brand-A Shoes" }, }, }; // In this example only the first 3 ads should succeed. // The Title of the fourth ad is empty and not valid, // and the fifth ad is a duplicate of the second ad. var bulkTextAds = new [] { new BulkTextAd { AdGroupId = adGroupIdKey, TextAd = new TextAd { Title = "Women's Shoe Sale", Text = "Huge Savings on red shoes.", DisplayUrl = "Contoso.com", // If you are currently using Destination URLs, you must replace them with Final URLs. // Here is an example of a DestinationUrl you might have used previously. // DestinationUrl = "http://www.contoso.com/womenshoesale/?season=spring&promocode=PROMO123", // To migrate from DestinationUrl to FinalUrls for existing ads, you can set DestinationUrl // to an empty string when updating the ad. If you are removing DestinationUrl, // then FinalUrls is required. // DestinationUrl = "", // With FinalUrls you can separate the tracking template, custom parameters, and // landing page URLs. FinalUrls = new[] { "http://www.contoso.com/womenshoesale", }, // Final Mobile URLs can also be used if you want to direct the user to a different page // for mobile devices. FinalMobileUrls = new[] { "http://mobile.contoso.com/womenshoesale", }, // You could use a tracking template which would override the campaign level // tracking template. Tracking templates defined for lower level entities // override those set for higher level entities. // In this example we are using the campaign level tracking template. TrackingUrlTemplate = null, // Set custom parameters that are specific to this ad, // and can be used by the ad, ad group, campaign, or account level tracking template. // In this example we are using the campaign level tracking template. UrlCustomParameters = new CustomParameters { Parameters = new[] { new CustomParameter(){ Key = "promoCode", Value = "PROMO1" }, new CustomParameter(){ Key = "season", Value = "summer" }, } } }, }, new BulkTextAd { AdGroupId = adGroupIdKey, TextAd = new TextAd { Title = "Women's Super Shoe Sale", Text = "Huge Savings on red shoes.", DisplayUrl = "Contoso.com", // If you are currently using Destination URLs, you must replace them with Final URLs. // Here is an example of a DestinationUrl you might have used previously. // DestinationUrl = "http://www.contoso.com/womenshoesale/?season=spring&promocode=PROMO123", // To migrate from DestinationUrl to FinalUrls for existing ads, you can set DestinationUrl // to an empty string when updating the ad. If you are removing DestinationUrl, // then FinalUrls is required. // DestinationUrl = "", // With FinalUrls you can separate the tracking template, custom parameters, and // landing page URLs. FinalUrls = new[] { "http://www.contoso.com/womenshoesale" }, // Final Mobile URLs can also be used if you want to direct the user to a different page // for mobile devices. FinalMobileUrls = new[] { "http://mobile.contoso.com/womenshoesale" }, // You could use a tracking template which would override the campaign level // tracking template. Tracking templates defined for lower level entities // override those set for higher level entities. // In this example we are using the campaign level tracking template. TrackingUrlTemplate = null, // Set custom parameters that are specific to this ad, // and can be used by the ad, ad group, campaign, or account level tracking template. // In this example we are using the campaign level tracking template. UrlCustomParameters = new CustomParameters { Parameters = new[] { new CustomParameter(){ Key = "promoCode", Value = "PROMO2" }, new CustomParameter(){ Key = "season", Value = "summer" }, } }, }, }, new BulkTextAd { AdGroupId = adGroupIdKey, TextAd = new TextAd { Title = "Women's Red Shoe Sale", Text = "Huge Savings on red shoes.", DisplayUrl = "Contoso.com", // If you are currently using Destination URLs, you must replace them with Final URLs. // Here is an example of a DestinationUrl you might have used previously. // DestinationUrl = "http://www.contoso.com/womenshoesale/?season=spring&promocode=PROMO123", // To migrate from DestinationUrl to FinalUrls for existing ads, you can set DestinationUrl // to an empty string when updating the ad. If you are removing DestinationUrl, // then FinalUrls is required. // DestinationUrl = "", // With FinalUrls you can separate the tracking template, custom parameters, and // landing page URLs. FinalUrls = new[] { "http://www.contoso.com/womenshoesale" }, // Final Mobile URLs can also be used if you want to direct the user to a different page // for mobile devices. FinalMobileUrls = new[] { "http://mobile.contoso.com/womenshoesale" }, // You could use a tracking template which would override the campaign level // tracking template. Tracking templates defined for lower level entities // override those set for higher level entities. // In this example we are using the campaign level tracking template. TrackingUrlTemplate = null, // Set custom parameters that are specific to this ad, // and can be used by the ad, ad group, campaign, or account level tracking template. // In this example we are using the campaign level tracking template. UrlCustomParameters = new CustomParameters { Parameters = new[] { new CustomParameter(){ Key = "promoCode", Value = "PROMO3" }, new CustomParameter(){ Key = "season", Value = "summer" }, } }, }, }, new BulkTextAd { AdGroupId = adGroupIdKey, TextAd = new TextAd { Title = "", Text = "Huge Savings on red shoes.", DisplayUrl = "Contoso.com", // If you are currently using Destination URLs, you must replace them with Final URLs. // Here is an example of a DestinationUrl you might have used previously. // DestinationUrl = "http://www.contoso.com/womenshoesale/?season=spring&promocode=PROMO123", // To migrate from DestinationUrl to FinalUrls for existing ads, you can set DestinationUrl // to an empty string when updating the ad. If you are removing DestinationUrl, // then FinalUrls is required. // DestinationUrl = "", // With FinalUrls you can separate the tracking template, custom parameters, and // landing page URLs. FinalUrls = new[] { "http://www.contoso.com/womenshoesale" }, // Final Mobile URLs can also be used if you want to direct the user to a different page // for mobile devices. FinalMobileUrls = new[] { "http://mobile.contoso.com/womenshoesale" }, // You could use a tracking template which would override the campaign level // tracking template. Tracking templates defined for lower level entities // override those set for higher level entities. // In this example we are using the campaign level tracking template. TrackingUrlTemplate = null, // Set custom parameters that are specific to this ad, // and can be used by the ad, ad group, campaign, or account level tracking template. // In this example we are using the campaign level tracking template. UrlCustomParameters = new CustomParameters { Parameters = new[] { new CustomParameter(){ Key = "promoCode", Value = "PROMO4" }, new CustomParameter(){ Key = "season", Value = "summer" }, } }, }, }, new BulkTextAd { AdGroupId = adGroupIdKey, TextAd = new TextAd { Title = "Women's Super Shoe Sale", Text = "Huge Savings on red shoes.", DisplayUrl = "Contoso.com", // If you are currently using Destination URLs, you must replace them with Final URLs. // Here is an example of a DestinationUrl you might have used previously. // DestinationUrl = "http://www.contoso.com/womenshoesale/?season=spring&promocode=PROMO123", // To migrate from DestinationUrl to FinalUrls for existing ads, you can set DestinationUrl // to an empty string when updating the ad. If you are removing DestinationUrl, // then FinalUrls is required. // DestinationUrl = "", // With FinalUrls you can separate the tracking template, custom parameters, and // landing page URLs. FinalUrls = new[] { "http://www.contoso.com/womenshoesale" }, // Final Mobile URLs can also be used if you want to direct the user to a different page // for mobile devices. FinalMobileUrls = new[] { "http://mobile.contoso.com/womenshoesale" }, // You could use a tracking template which would override the campaign level // tracking template. Tracking templates defined for lower level entities // override those set for higher level entities. // In this example we are using the campaign level tracking template. TrackingUrlTemplate = null, // Set custom parameters that are specific to this ad, // and can be used by the ad, ad group, campaign, or account level tracking template. // In this example we are using the campaign level tracking template. UrlCustomParameters = new CustomParameters { Parameters = new[] { new CustomParameter(){ Key = "promoCode", Value = "PROMO5" }, new CustomParameter(){ Key = "season", Value = "summer" }, } }, }, }, }; uploadEntities.Add(bulkCampaign); uploadEntities.Add(bulkAdGroup); foreach (var bulkKeyword in bulkKeywords) { uploadEntities.Add(bulkKeyword); } foreach (var bulkTextAd in bulkTextAds) { uploadEntities.Add(bulkTextAd); } // Write the upload output var Reader = await UploadEntities(uploadEntities); var bulkEntities = Reader.ReadEntities().ToList(); var campaignResults = bulkEntities.OfType<BulkCampaign>().ToList(); OutputBulkCampaigns(campaignResults); var adGroupResults = bulkEntities.OfType<BulkAdGroup>().ToList(); OutputBulkAdGroups(adGroupResults); var keywordResults = bulkEntities.OfType<BulkKeyword>().ToList(); OutputBulkKeywords(keywordResults); var textAdResults = bulkEntities.OfType<BulkTextAd>().ToList(); OutputBulkTextAds(textAdResults); Reader.Dispose(); #endregion Add #region CleanUp /* Delete the campaign, ad group, keywords, and ads that were previously added. * You should remove this region if you want to view the added entities in the * Bing Ads web application or another tool. */ var campaignId = campaignResults[0].Campaign.Id; bulkCampaign = new BulkCampaign { Campaign = new Campaign { Id = campaignId, Status = CampaignStatus.Deleted } }; uploadEntities = new List<BulkEntity>(); uploadEntities.Add(bulkCampaign); // Write the upload output Reader = await UploadEntities(uploadEntities); bulkEntities = Reader.ReadEntities().ToList(); campaignResults = bulkEntities.OfType<BulkCampaign>().ToList(); OutputBulkCampaigns(campaignResults); Reader.Dispose(); OutputStatusMessage(String.Format("Deleted CampaignId {0}\n", campaignResults[0].Campaign.Id)); #endregion Cleanup } // Catch Microsoft Account authorization exceptions. catch (OAuthTokenRequestException ex) { OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description)); } // Catch Bulk service exceptions catch (FaultException<Microsoft.BingAds.V10.Bulk.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.V10.Bulk.ApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (BulkOperationInProgressException ex) { OutputStatusMessage("The result file for the bulk operation is not yet available for download."); OutputStatusMessage(ex.Message); } catch (BulkOperationCouldNotBeCompletedException<DownloadStatus> ex) { OutputStatusMessage(string.Join("; ", ex.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (BulkOperationCouldNotBeCompletedException<UploadStatus> ex) { OutputStatusMessage(string.Join("; ", ex.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (Exception ex) { OutputStatusMessage(ex.Message); } finally { if (Reader != null) { Reader.Dispose(); } if (Writer != null) { Writer.Dispose(); } } }
public async override Task RunAsync(AuthorizationData authorizationData) { Stream responseStream = null; FileStream fileStream = null; var fileInfo = new FileInfo(LocalFile); try { var request = (HttpWebRequest) WebRequest.Create(FileUrl); request.AutomaticDecompression = DecompressionMethods.GZip; // You can set a request condition on either the last modified time or ETag of the file at the Url. request.Headers.Add(string.Format(@"If-None-Match: ""{0}""", ETag)); var response = (HttpWebResponse) request.GetResponse(); PrintETag(response); if (response.StatusCode == HttpStatusCode.OK) { fileStream = new FileStream(fileInfo.FullName, FileMode.Create); responseStream = response.GetResponseStream(); if (responseStream != null) { responseStream.CopyTo(fileStream); } OutputStatusMessage(string.Format("Downloaded the geographical locations to {0}.\n", LocalFile)); } } catch (WebException e) { if (e.Response != null) { if (((HttpWebResponse) e.Response).StatusCode == HttpStatusCode.NotModified) { PrintETag((HttpWebResponse) e.Response); OutputStatusMessage("The locations file has not been modified since last download.\n"); } else { OutputStatusMessage("Unexpected status code = " + ((HttpWebResponse)e.Response).StatusCode); } } else OutputStatusMessage("Unexpected Web Exception " + e.Message); } catch (IOException ex) { OutputStatusMessage(ex.Message); } catch (Exception ex) { OutputStatusMessage(ex.Message); } finally { if (fileStream != null) fileStream.Close(); if (responseStream != null) responseStream.Close(); } }
public async override Task RunAsync(AuthorizationData authorizationData) { try { Service = new ServiceClient<ICampaignManagementService>(authorizationData); // Add a campaign that will later be associated with ad extensions. var campaign = GetExampleCampaign(); var campaignIds = await AddCampaignsAsync(authorizationData.AccountId, new[] { campaign }); // Specify the extensions. var adExtensions = new AdExtension[] { new AppAdExtension { AppPlatform="Windows", AppStoreId="AppStoreIdGoesHere", DestinationUrl="DestinationUrlGoesHere", DisplayText="Contoso", }, new CallAdExtension { CountryCode = "US", PhoneNumber = "2065550100", IsCallOnly = false }, new LocationAdExtension { PhoneNumber = "206-555-0100", CompanyName = "Contoso Shoes", IconMediaId = null, ImageMediaId = null, Address = new Address { StreetAddress = "1234 Washington Place", StreetAddress2 = "Suite 1210", CityName = "Woodinville", ProvinceName = "WA", CountryCode = "US", PostalCode = "98608" } }, new SiteLinksAdExtension { SiteLinks = new [] { new SiteLink { DestinationUrl = "Contoso.com/WomenShoeSale", DisplayText = "Women's Shoe Sale" } } } }; // Add all extensions to the account's ad extension library var adExtensionIdentities = await AddAdExtensionsAsync( authorizationData.AccountId, adExtensions ); OutputStatusMessage("Added ad extensions.\n\n"); // DeleteAdExtensionsAssociations, SetAdExtensionsAssociations, and GetAdExtensionsEditorialReasons // operations each require a list of type AdExtensionIdToEntityIdAssociation. var adExtensionIdToEntityIdAssociations = new AdExtensionIdToEntityIdAssociation[adExtensionIdentities.Count]; // GetAdExtensionsByIds requires a list of type long. var adExtensionIds = new long[adExtensionIdentities.Count]; // Loop through the list of extension IDs and build any required data structures // for subsequent operations. for (int i = 0; i < adExtensionIdentities.Count; i++) { adExtensionIdToEntityIdAssociations[i] = new AdExtensionIdToEntityIdAssociation { AdExtensionId = adExtensionIdentities[i].Id, EntityId = campaignIds[0] }; adExtensionIds[i] = adExtensionIdentities[i].Id; } // Associate the specified ad extensions with the respective campaigns or ad groups. SetAdExtensionsAssociationsAsync( authorizationData.AccountId, adExtensionIdToEntityIdAssociations, AssociationType.Campaign ); OutputStatusMessage("Set ad extension associations.\n\n"); // Get editorial rejection reasons for the respective ad extension and entity associations. var adExtensionEditorialReasonCollection = (AdExtensionEditorialReasonCollection[])await GetAdExtensionsEditorialReasons( authorizationData.AccountId, adExtensionIdToEntityIdAssociations, AssociationType.Campaign ); const AdExtensionsTypeFilter adExtensionsTypeFilter = AdExtensionsTypeFilter.AppAdExtension | AdExtensionsTypeFilter.CallAdExtension | AdExtensionsTypeFilter.LocationAdExtension | AdExtensionsTypeFilter.SiteLinksAdExtension; // Get the specified ad extensions from the account’s ad extension library. adExtensions = (AdExtension[])await GetAdExtensionsByIdsAsync( authorizationData.AccountId, adExtensionIds, adExtensionsTypeFilter ); PrintAdExtensions(adExtensions, adExtensionEditorialReasonCollection); // Remove the specified associations from the respective campaigns or ad groups. // The extesions are still available in the account's extensions library. DeleteAdExtensionsAssociationsAsync( authorizationData.AccountId, adExtensionIdToEntityIdAssociations, AssociationType.Campaign ); OutputStatusMessage("Deleted ad extension associations.\n\n"); // Deletes the ad extensions from the account’s ad extension library. DeleteAdExtensionsAsync( authorizationData.AccountId, adExtensionIds ); OutputStatusMessage("Deleted ad extensions.\n\n"); } // Catch authentication exceptions catch (OAuthTokenRequestException ex) { OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description)); } // Catch Campaign Management service exceptions catch (FaultException<Microsoft.BingAds.CampaignManagement.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.CampaignManagement.ApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.CampaignManagement.EditorialApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (Exception ex) { OutputStatusMessage(ex.Message); } }
public async override Task RunAsync(AuthorizationData authorizationData) { try { OutputStatusMessage("You must edit the ManageClient.cs file to provide the ClientAccountId for " + "the client link."); OutputStatusMessage("When adding a client link, the client link's ManagingCustomerId is set to the CustomerId of the current " + "authenticated user, who must be a Super Admin of the agency."); OutputStatusMessage("Login as an agency Super Admin user to send a client link invitation, " + "or unlink an existing client link."); OutputStatusMessage("Login as a client Super Admin user to accept a client link invitation.\n"); Service = new ServiceClient<ICustomerManagementService>(authorizationData); UpdateClientLinksResponse updateClientLinksResponse = null; // Specify the client link search criteria var pageInfo = new Paging { Index = 0, // The first page Size = 100 // The first 100 client links for this page of results }; var ordering = new OrderBy { Field = OrderByField.Number, Order = SortOrder.Ascending }; var predicate = new Predicate { Field = "ClientAccountId", Operator = PredicateOperator.In, Value = ClientAccountId.ToString(CultureInfo.InvariantCulture) }; // Search for client links that match the specified criteria. var clientLinks = await SearchClientLinksAsync( new[] { ordering }, pageInfo, new[] { predicate }); // Determine whether the agency is already managing the specified client account. // If a link exists with status either Active, LinkInProgress, LinkPending, // UnlinkInProgress, or UnlinkPending, the agency may not initiate a duplicate client link. ClientLink clientLink; var newLinkRequired = true; if (clientLinks.Count > 0) { clientLink = clientLinks[0]; OutputStatusMessage(String.Format("Current ClientLink Status: {0}.\n", clientLink.Status)); switch (clientLink.Status) { // The agency may choose to initiate the unlink process, // which would terminate the existing relationship with the client. case ClientLinkStatus.Active: clientLink.Status = ClientLinkStatus.UnlinkRequested; updateClientLinksResponse = await UpdateClientLinksAsync(new[] { clientLink }); OutputStatusMessage("UpdateClientLinks : UnlinkRequested.\n"); newLinkRequired = false; break; // Waiting on a system status transition or waiting for the StartDate. case ClientLinkStatus.LinkAccepted: OutputStatusMessage("The status is transitioning towards LinkInProgress.\n"); newLinkRequired = false; break; // Waiting on a system status transition. case ClientLinkStatus.LinkInProgress: OutputStatusMessage("The status is transitioning towards Active.\n"); newLinkRequired = false; break; // When the status is LinkPending, either the agency or client may update the status. // The agency may choose to cancel the client link invitation; however, in this example // the client will accept the invitation. // If the client does not accept or decline the invitation within 30 days, and if the agency // does not update the status to LinkCanceled, the system updates the status to LinkExpired. case ClientLinkStatus.LinkPending: /* clientLink.Status = ClientLinkStatus.LinkCanceled; updateClientLinksResponse = UpdateClientLinks(new[] { clientLink }); WriteMessage(String.Format("The agency updated status: LinkCanceled.\n"); */ clientLink.Status = ClientLinkStatus.LinkAccepted; updateClientLinksResponse = await UpdateClientLinksAsync(new[] { clientLink }); OutputStatusMessage("UpdateClientLinks: LinkAccepted.\n"); newLinkRequired = false; break; // Waiting on a system status transition. case ClientLinkStatus.UnlinkInProgress: OutputStatusMessage("The status is transitioning towards Inactive.\n"); newLinkRequired = false; break; // Waiting on a system status transition. case ClientLinkStatus.UnlinkPending: OutputStatusMessage("The status is transitioning towards Inactive.\n"); newLinkRequired = false; break; // The link lifecycle has ended. default: OutputStatusMessage("A new client link invitation is required.\n"); break; } // Print errors if any occurred when updating the client link. if (updateClientLinksResponse != null) { PrintPartialErrors(updateClientLinksResponse.OperationErrors, updateClientLinksResponse.PartialErrors); } } // If no links exist between the agency and specified client account, or a link exists with status // either Inactive, LinkCanceled, LinkDeclined, LinkExpired, or LinkFailed, then the agency must // initiate a new client link. if (newLinkRequired) { clientLink = new ClientLink { ClientAccountId = ClientAccountId, ManagingCustomerId = authorizationData.CustomerId, IsBillToClient = true, Name = "My Client Link", StartDate = null, SuppressNotification = true }; var addClientLinksResponse = await AddClientLinksAsync(new[] { clientLink }); // Print errors if any occurred when adding the client link. PrintPartialErrors(addClientLinksResponse.OperationErrors.ToArray(), addClientLinksResponse.PartialErrors.ToArray()); OutputStatusMessage(string.Format("The user attempted to add a new ClientLink.\n")); OutputStatusMessage(string.Format("Login as the client Super Admin to accept the agency's request to manage AccountId {0}.\n", ClientAccountId)); } // Get and print the current client link clientLinks = await SearchClientLinksAsync( new[] { ordering }, pageInfo, new[] { predicate }); PrintClientLinks(clientLinks); } // Catch authentication exceptions catch (OAuthTokenRequestException ex) { OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description)); } // Catch Customer Management service exceptions catch (FaultException<Microsoft.BingAds.CustomerManagement.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.CustomerManagement.ApiFault> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (Exception ex) { OutputStatusMessage(ex.Message); } }
public async override Task RunAsync(AuthorizationData authorizationData) { try { BulkService = new BulkServiceManager(authorizationData); var progress = new Progress<BulkOperationProgressInfo>(x => OutputStatusMessage(String.Format("{0} % Complete", x.PercentComplete.ToString(CultureInfo.InvariantCulture)))); #region Add const int appAdExtensionIdKey = -11; const int callAdExtensionIdKey = -12; const int locationAdExtensionIdKey = -13; const int siteLinksAdExtensionIdKey = -14; const int campaignIdKey = -123; // Prepare the bulk entities that you want to upload. Each bulk entity contains the corresponding campaign management object, // and additional elements needed to read from and write to a bulk file. var bulkCampaign = new BulkCampaign { // ClientId may be used to associate records in the bulk upload file with records in the results file. The value of this field // is not used or stored by the server; it is simply copied from the uploaded record to the corresponding result record. // Note: This bulk file Client Id is not related to an application Client Id for OAuth. ClientId = "YourClientIdGoesHere", Campaign = new Campaign { // When using the Campaign Management service, the Id cannot be set. In the context of a BulkCampaign, the Id is optional // and may be used as a negative reference key during bulk upload. For example the same negative value set for the campaign Id // will be used when associating this new campaign with a new call ad extension in the BulkCampaignCallAdExtension object below. Id = campaignIdKey, Name = "Women's Shoes " + DateTime.UtcNow, Description = "Red shoes line.", BudgetType = BudgetLimitType.MonthlyBudgetSpendUntilDepleted, MonthlyBudget = 1000.00, TimeZone = "PacificTimeUSCanadaTijuana", } }; // Prepare ad extensions for upload var bulkAppAdExtension = new BulkAppAdExtension { AccountId = authorizationData.AccountId, AppAdExtension = new AppAdExtension { AppPlatform = "Windows", AppStoreId = "AppStoreIdGoesHere", DestinationUrl = "DestinationUrlGoesHere", DisplayText = "Contoso", Id = appAdExtensionIdKey, } }; var bulkCallAdExtension = new BulkCallAdExtension { AccountId = authorizationData.AccountId, CallAdExtension = new CallAdExtension { CountryCode = "US", PhoneNumber = "2065550100", IsCallOnly = false, Id = callAdExtensionIdKey } }; var bulkLocationAdExtension = new BulkLocationAdExtension { AccountId = authorizationData.AccountId, LocationAdExtension = new LocationAdExtension { Id = locationAdExtensionIdKey, PhoneNumber = "206-555-0100", CompanyName = "Contoso Shoes", IconMediaId = null, ImageMediaId = null, Address = new Address { StreetAddress = "1234 Washington Place", StreetAddress2 = "Suite 1210", CityName = "Woodinville", ProvinceName = "WA", CountryCode = "US", PostalCode = "98608" } } }; // Note that when written to file using the BulkFileWriter, an extra Sitelink Ad Extension record with Deleted // status precedes the actual site link record or records that you want to upload. All bulk entities // that are derived from MultiRecordBulkEntiy are preceded with a Deleted record using the BulkFileWriter. // In this example it is a moot point because we are creating a new ad extension. If the specified // ad extension Id already exists in your account, the Deleted record effectively deletes the existing // extension and replaces it with the SiteLinksAdExtension specified below. var bulkSiteLinkAdExtension = new BulkSiteLinkAdExtension { AccountId = authorizationData.AccountId, SiteLinksAdExtension = new SiteLinksAdExtension { // Note that if you do not specify a negative Id as reference key, each of SiteLinks items will // be split during upload into separate sitelink ad extensions with unique ad extension identifiers. Id = siteLinksAdExtensionIdKey, SiteLinks = new List<SiteLink> { new SiteLink { DestinationUrl = "Contoso.com", DisplayText = "Women's Shoe Sale 1" }, new SiteLink { DestinationUrl = "Contoso.com/WomenShoeSale/2", DisplayText = "Women's Shoe Sale 2" } } } // Note that BulkSiteLinkAdExtension.SiteLinks is read only and only // accessible when reading results from the download or upload results file. // To upload new site links for a new site links ad extension, you should specify // BulkSiteLinkAdExtension.SiteLinksAdExtension.SiteLinks as shown above. }; // Prepare ad extension associations for upload var bulkCampaignAppAdExtension = new BulkCampaignAppAdExtension { AdExtensionIdToEntityIdAssociation = new AdExtensionIdToEntityIdAssociation { AdExtensionId = appAdExtensionIdKey, EntityId = campaignIdKey } }; var bulkCampaignCallAdExtension = new BulkCampaignCallAdExtension { AdExtensionIdToEntityIdAssociation = new AdExtensionIdToEntityIdAssociation { AdExtensionId = callAdExtensionIdKey, EntityId = campaignIdKey } }; var bulkCampaignLocationAdExtension = new BulkCampaignLocationAdExtension { AdExtensionIdToEntityIdAssociation = new AdExtensionIdToEntityIdAssociation { AdExtensionId = locationAdExtensionIdKey, EntityId = campaignIdKey } }; var bulkCampaignSiteLinkAdExtension = new BulkCampaignSiteLinkAdExtension { AdExtensionIdToEntityIdAssociation = new AdExtensionIdToEntityIdAssociation { AdExtensionId = siteLinksAdExtensionIdKey, EntityId = campaignIdKey } }; // Write the entities created above, to the specified file. // Dependent entities such as BulkCampaignCallAdExtension must be written after any dependencies, // for example the BulkCampaign and BulkCallAdExtension. Writer = new BulkFileWriter(FileDirectory + UploadFileName); Writer.WriteEntity(bulkCampaign); Writer.WriteEntity(bulkAppAdExtension); Writer.WriteEntity(bulkCallAdExtension); Writer.WriteEntity(bulkLocationAdExtension); Writer.WriteEntity(bulkSiteLinkAdExtension); Writer.WriteEntity(bulkCampaignAppAdExtension); Writer.WriteEntity(bulkCampaignCallAdExtension); Writer.WriteEntity(bulkCampaignLocationAdExtension); Writer.WriteEntity(bulkCampaignSiteLinkAdExtension); Writer.Dispose(); var fileUploadParameters = new FileUploadParameters { ResultFileDirectory = FileDirectory, ResultFileName = ResultFileName, OverwriteResultFile = true, UploadFilePath = FileDirectory + UploadFileName, ResponseMode = ResponseMode.ErrorsAndResults }; // UploadFileAsync will upload the file you finished writing and will download the results file OutputStatusMessage("\nAdding campaign, ad extensions, and associations . . .\n"); var bulkFilePath = await BulkService.UploadFileAsync(fileUploadParameters, progress, CancellationToken.None); Reader = new BulkFileReader(bulkFilePath, ResultFileType.Upload, FileType); OutputStatusMessage("Upload Results Bulk File Path: " + Reader.BulkFilePath + "\n"); // Write the upload output var bulkEntities = Reader.ReadEntities().ToList(); var campaignResults = bulkEntities.OfType<BulkCampaign>().ToList(); OutputBulkCampaigns(campaignResults); var appAdExtensionResults = bulkEntities.OfType<BulkAppAdExtension>().ToList(); OutputBulkAppAdExtensions(appAdExtensionResults); var callAdExtensionResults = bulkEntities.OfType<BulkCallAdExtension>().ToList(); OutputBulkCallAdExtensions(callAdExtensionResults); var locationAdExtensionResults = bulkEntities.OfType<BulkLocationAdExtension>().ToList(); OutputBulkLocationAdExtensions(locationAdExtensionResults); var siteLinkAdExtensionResults = bulkEntities.OfType<BulkSiteLinkAdExtension>().ToList(); OutputBulkSiteLinkAdExtensions(siteLinkAdExtensionResults); OutputBulkCampaignAppAdExtensions(bulkEntities.OfType<BulkCampaignAppAdExtension>().ToList()); OutputBulkCampaignCallAdExtensions(bulkEntities.OfType<BulkCampaignCallAdExtension>().ToList()); OutputBulkCampaignLocationAdExtensions(bulkEntities.OfType<BulkCampaignLocationAdExtension>().ToList()); OutputBulkCampaignSiteLinkAdExtensions(bulkEntities.OfType<BulkCampaignSiteLinkAdExtension>().ToList()); Reader.Dispose(); #endregion Add #region Update // Update the site links ad extension. // Do not create a BulkSiteLinkAdExtension for update, unless you want to replace all existing SiteLinks // with the specified SiteLinks for the specified ad extension. // Instead you should upload one or more site links as a list of BulkSiteLink. var bulkSiteLinks = new List<BulkSiteLink> { new BulkSiteLink { SiteLink = new SiteLink { DestinationUrl = "Contoso.com", DisplayText = "Red Shoe Sale" } } }; // Add an additional site link, and update an existing site link if (siteLinkAdExtensionResults.ToArray().Any() && siteLinkAdExtensionResults.ToArray()[0].SiteLinks.ToArray().Any()) { var existingSiteLink = siteLinkAdExtensionResults.ToArray()[0].SiteLinks[0]; existingSiteLink.SiteLink.DisplayText = "Red Shoes Super Sale"; // Associate the new site links with the identifier of the existing site links ad extension foreach (var bulkSiteLink in bulkSiteLinks) { bulkSiteLink.AdExtensionId = existingSiteLink.AdExtensionId; } bulkSiteLinks.Add(existingSiteLink); } // Write the new site link and updated site link to the file Writer = new BulkFileWriter(FileDirectory + UploadFileName); foreach (var bulkSiteLink in bulkSiteLinks) { Writer.WriteEntity(bulkSiteLink); } Writer.Dispose(); fileUploadParameters = new FileUploadParameters { ResultFileDirectory = FileDirectory, ResultFileName = ResultFileName, OverwriteResultFile = true, UploadFilePath = FileDirectory + UploadFileName, ResponseMode = ResponseMode.ErrorsAndResults }; // UploadFileAsync will upload the file you finished writing and will download the results file OutputStatusMessage("\nUpdating sitelinks . . .\n"); bulkFilePath = await BulkService.UploadFileAsync(fileUploadParameters, progress, CancellationToken.None); Reader = new BulkFileReader(bulkFilePath, ResultFileType.Upload, FileType); OutputStatusMessage("Upload Results Bulk File Path: " + Reader.BulkFilePath + "\n"); // Write any upload errors bulkEntities = Reader.ReadEntities().ToList(); var siteLinkResults = bulkEntities.OfType<BulkSiteLink>().ToList(); OutputBulkSiteLinks(siteLinkResults); Reader.Dispose(); #endregion Update #region Delete // Prepare the bulk entities that you want to delete. You must set the Id field to the corresponding // entity identifier, and the Status field to Deleted. var campaignId = campaignResults[0].Campaign.Id; bulkCampaign = new BulkCampaign { Campaign = new Campaign { Id = campaignId, Status = CampaignStatus.Deleted } }; var appAdExtensionId = appAdExtensionResults[0].AppAdExtension.Id; bulkAppAdExtension = new BulkAppAdExtension { AppAdExtension = new AppAdExtension { Id = appAdExtensionId, Status = AdExtensionStatus.Deleted } }; var callAdExtensionId = callAdExtensionResults[0].CallAdExtension.Id; bulkCallAdExtension = new BulkCallAdExtension { CallAdExtension = new CallAdExtension { Id = callAdExtensionId, Status = AdExtensionStatus.Deleted } }; var locationAdExtensionId = locationAdExtensionResults[0].LocationAdExtension.Id; bulkLocationAdExtension = new BulkLocationAdExtension { LocationAdExtension = new LocationAdExtension { Id = locationAdExtensionId, Status = AdExtensionStatus.Deleted } }; var siteLinkAdExtensionId = siteLinkAdExtensionResults[0].SiteLinksAdExtension.Id; bulkSiteLinkAdExtension = new BulkSiteLinkAdExtension { SiteLinksAdExtension = new SiteLinksAdExtension { Id = siteLinkAdExtensionId, Status = AdExtensionStatus.Deleted } }; // Write the entities that you want deleted, to the specified file. // Dependent entities such as BulkCampaignCallAdExtension are deleted without being specified explicitly. // For example, if you delete either BulkCampaign or BulkCallAdExtension, then the equivalent of // BulkCampaignCallAdExtension is effectively deleted. Writer = new BulkFileWriter(FileDirectory + UploadFileName); Writer.WriteEntity(bulkCampaign); Writer.WriteEntity(bulkAppAdExtension); Writer.WriteEntity(bulkCallAdExtension); Writer.WriteEntity(bulkLocationAdExtension); Writer.WriteEntity(bulkSiteLinkAdExtension); Writer.Dispose(); fileUploadParameters = new FileUploadParameters { ResultFileDirectory = FileDirectory, ResultFileName = ResultFileName, OverwriteResultFile = true, UploadFilePath = FileDirectory + UploadFileName, ResponseMode = ResponseMode.ErrorsAndResults }; // UploadFileAsync will upload the file you finished writing and will download the results file OutputStatusMessage("\nDeleting campaign and ad extensions . . .\n"); bulkFilePath = await BulkService.UploadFileAsync(fileUploadParameters, progress, CancellationToken.None); Reader = new BulkFileReader(bulkFilePath, ResultFileType.Upload, FileType); OutputStatusMessage("Upload Results Bulk File Path: " + Reader.BulkFilePath + "\n"); // Write the upload output bulkEntities = Reader.ReadEntities().ToList(); campaignResults = bulkEntities.OfType<BulkCampaign>().ToList(); OutputBulkCampaigns(campaignResults); appAdExtensionResults = bulkEntities.OfType<BulkAppAdExtension>().ToList(); OutputBulkAppAdExtensions(appAdExtensionResults); callAdExtensionResults = bulkEntities.OfType<BulkCallAdExtension>().ToList(); OutputBulkCallAdExtensions(callAdExtensionResults); locationAdExtensionResults = bulkEntities.OfType<BulkLocationAdExtension>().ToList(); OutputBulkLocationAdExtensions(locationAdExtensionResults); siteLinkAdExtensionResults = bulkEntities.OfType<BulkSiteLinkAdExtension>().ToList(); OutputBulkSiteLinkAdExtensions(siteLinkAdExtensionResults); OutputBulkCampaignAppAdExtensions(bulkEntities.OfType<BulkCampaignAppAdExtension>().ToList()); OutputBulkCampaignCallAdExtensions(bulkEntities.OfType<BulkCampaignCallAdExtension>().ToList()); OutputBulkCampaignLocationAdExtensions(bulkEntities.OfType<BulkCampaignLocationAdExtension>().ToList()); OutputBulkCampaignSiteLinkAdExtensions(bulkEntities.OfType<BulkCampaignSiteLinkAdExtension>().ToList()); Reader.Dispose(); #endregion Delete } // Catch Microsoft Account authorization exceptions. catch (OAuthTokenRequestException ex) { OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description)); } // Catch Bulk service exceptions catch (FaultException<Microsoft.BingAds.Bulk.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.Bulk.ApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (BulkOperationInProgressException ex) { OutputStatusMessage("The result file for the bulk operation is not yet available for download."); OutputStatusMessage(ex.Message); } catch (BulkOperationCouldNotBeCompletedException<DownloadStatus> ex) { OutputStatusMessage(string.Join("; ", ex.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (BulkOperationCouldNotBeCompletedException<UploadStatus> ex) { OutputStatusMessage(string.Join("; ", ex.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (Exception ex) { OutputStatusMessage(ex.Message); } finally { if (Reader != null) { Reader.Dispose(); } if (Writer != null) { Writer.Dispose(); } } }
public async override Task RunAsync(AuthorizationData authorizationData) { try { Service = new ServiceClient<ICampaignManagementService>(authorizationData); // Add a campaign that will later be associated with negative keywords. var campaigns = new[]{ new Campaign { Name = "Women's Shoes" + DateTime.UtcNow, Description = "Red shoes line.", BudgetType = BudgetLimitType.MonthlyBudgetSpendUntilDepleted, MonthlyBudget = 1000.00, TimeZone = "PacificTimeUSCanadaTijuana", DaylightSaving = true, } }; AddCampaignsResponse addCampaignsResponse = await AddCampaignsAsync(authorizationData.AccountId, campaigns); long?[] campaignIds = addCampaignsResponse.CampaignIds.ToArray(); BatchError[] campaignErrors = addCampaignsResponse.PartialErrors.ToArray(); long campaignId = (long)campaignIds[0]; // You may choose to associate an exclusive set of negative keywords to an individual campaign // or ad group. An exclusive set of negative keywords cannot be shared with other campaigns // or ad groups. This example only associates negative keywords with a campaign. var entityNegativeKeywords = new[] { new EntityNegativeKeyword { EntityId = campaignId, EntityType = "Campaign", NegativeKeywords = new[] { new NegativeKeyword { MatchType = MatchType.Phrase, Text = "auto" }, new NegativeKeyword { MatchType = MatchType.Phrase, Text = "auto" }, } } }; AddNegativeKeywordsToEntitiesResponse addNegativeKeywordsToEntitiesResponse = await AddNegativeKeywordsToEntitiesAsync(entityNegativeKeywords); OutputNegativeKeywordIds(addNegativeKeywordsToEntitiesResponse.NegativeKeywordIds); OutputNestedPartialErrors(addNegativeKeywordsToEntitiesResponse.NestedPartialErrors); if (addNegativeKeywordsToEntitiesResponse.NestedPartialErrors == null || addNegativeKeywordsToEntitiesResponse.NestedPartialErrors.Count == 0) { OutputStatusMessage("Added an exclusive set of negative keywords to the Campaign.\n"); OutputNegativeKeywordIds(addNegativeKeywordsToEntitiesResponse.NegativeKeywordIds); } else { OutputNestedPartialErrors(addNegativeKeywordsToEntitiesResponse.NestedPartialErrors); } GetNegativeKeywordsByEntityIdsResponse getNegativeKeywordsByEntityIdsResponse = await GetNegativeKeywordsByEntityIdsAsync(new[] { campaignId }, "Campaign", authorizationData.AccountId); OutputEntityNegativeKeywords(getNegativeKeywordsByEntityIdsResponse.EntityNegativeKeywords); OutputPartialErrors(getNegativeKeywordsByEntityIdsResponse.PartialErrors); if (getNegativeKeywordsByEntityIdsResponse.PartialErrors == null || getNegativeKeywordsByEntityIdsResponse.PartialErrors.Count == 0) { OutputStatusMessage("Retrieved an exclusive set of negative keywords for the Campaign.\n"); OutputEntityNegativeKeywords(getNegativeKeywordsByEntityIdsResponse.EntityNegativeKeywords); } else { OutputPartialErrors(getNegativeKeywordsByEntityIdsResponse.PartialErrors); } // If you attempt to delete a negative keyword without an identifier the operation will // succeed but will return partial errors corresponding to the index of the negative keyword // that was not deleted. var nestedPartialErrors = (BatchErrorCollection[])await DeleteNegativeKeywordsFromEntitiesAsync(entityNegativeKeywords); if (nestedPartialErrors == null || nestedPartialErrors.Length == 0) { OutputStatusMessage("Deleted an exclusive set of negative keywords from the Campaign.\n"); } else { OutputStatusMessage("Attempt to DeleteNegativeKeywordsFromEntities without NegativeKeyword identifier partially fails by design."); OutputNestedPartialErrors(nestedPartialErrors); } // Delete the negative keywords with identifiers that were returned above. nestedPartialErrors = (BatchErrorCollection[])await DeleteNegativeKeywordsFromEntitiesAsync( getNegativeKeywordsByEntityIdsResponse.EntityNegativeKeywords); if (nestedPartialErrors == null || nestedPartialErrors.Length == 0) { OutputStatusMessage("Deleted an exclusive set of negative keywords from the Campaign.\n"); } else { OutputNestedPartialErrors(nestedPartialErrors); } // Negative keywords can also be added and deleted from a shared negative keyword list. // The negative keyword list can be shared or associated with multiple campaigns. // NegativeKeywordList inherits from SharedList which inherits from SharedEntity. var negativeKeywordList = new NegativeKeywordList { Name = "My Negative Keyword List" + DateTime.UtcNow, Type = "NegativeKeywordList" }; SharedListItem[] negativeKeywords = { new NegativeKeyword { Text = "car", Type = "NegativeKeyword", MatchType = MatchType.Exact }, new NegativeKeyword { Text = "car", Type = "NegativeKeyword", MatchType = MatchType.Phrase } }; // You can create a new list for negative keywords with or without negative keywords. var addSharedEntityResponse = await AddSharedEntityAsync(negativeKeywordList, negativeKeywords); var sharedEntityId = addSharedEntityResponse.SharedEntityId; long[] listItemIds = addSharedEntityResponse.ListItemIds.ToArray(); OutputStatusMessage(String.Format("NegativeKeywordList successfully added to account library and assigned identifer {0}\n", sharedEntityId)); OutputNegativeKeywordsWithPartialErrors( sharedEntityId, negativeKeywords, listItemIds, addSharedEntityResponse.PartialErrors.ToArray()); OutputStatusMessage("Negative keywords currently in NegativeKeywordList:"); negativeKeywords = (SharedListItem[])await GetListItemsBySharedListAsync(new NegativeKeywordList { Id = sharedEntityId }); if (negativeKeywords == null || negativeKeywords.Length == 0) { OutputStatusMessage("None\n"); } else { OutputNegativeKeywords(negativeKeywords.Cast<NegativeKeyword>()); } // To update the list of negative keywords, you must either add or remove from the list // using the respective AddListItemsToSharedList or DeleteListItemsFromSharedList operations. // To remove the negative keywords from the list pass the negative keyword identifers // and negative keyword list (SharedEntity) identifer. var partialErrors = await DeleteListItemsFromSharedListAsync(listItemIds, new NegativeKeywordList { Id = sharedEntityId }); if (partialErrors == null || !partialErrors.Any()) { OutputStatusMessage("Deleted most recently added negative keywords from negative keyword list.\n"); } else { OutputPartialErrors(partialErrors); } OutputStatusMessage("Negative keywords currently in NegativeKeywordList:"); negativeKeywords = (SharedListItem[])await GetListItemsBySharedListAsync(new NegativeKeywordList { Id = sharedEntityId }); if (negativeKeywords == null || negativeKeywords.Length == 0) { OutputStatusMessage("None\n"); } else { OutputNegativeKeywords(negativeKeywords.Cast<NegativeKeyword>()); } // Whether you created the list with or without negative keywords, more can be added // using the AddListItemsToSharedList operation. negativeKeywords = new SharedListItem[] { new NegativeKeyword { Text = "auto", Type = "NegativeKeyword", MatchType = MatchType.Exact }, new NegativeKeyword { Text = "auto", Type = "NegativeKeyword", MatchType = MatchType.Phrase } }; var addListItemsToSharedListResponse = await AddListItemsToSharedListAsync( negativeKeywords, new NegativeKeywordList { Id = sharedEntityId }); listItemIds = addListItemsToSharedListResponse.ListItemIds.ToArray(); OutputNegativeKeywordsWithPartialErrors( sharedEntityId, negativeKeywords, listItemIds, addListItemsToSharedListResponse.PartialErrors.ToArray()); OutputStatusMessage("Negative keywords currently in NegativeKeywordList:"); negativeKeywords = (SharedListItem[])await GetListItemsBySharedListAsync(new NegativeKeywordList { Id = sharedEntityId }); if (negativeKeywords == null || negativeKeywords.Length == 0) { OutputStatusMessage("None\n"); } else { OutputNegativeKeywords(negativeKeywords.Cast<NegativeKeyword>()); } // You can update the name of the negative keyword list. negativeKeywordList = new NegativeKeywordList { Id = sharedEntityId, Name = "My Updated Negative Keyword List", Type = "NegativeKeywordList" }; partialErrors = await UpdateSharedEntitiesAsync(new SharedEntity[] { negativeKeywordList }); if (partialErrors == null || !partialErrors.Any()) { OutputStatusMessage(String.Format("Updated Negative Keyword List Name to {0}.\n", negativeKeywordList.Name)); } else { OutputPartialErrors(partialErrors); } // Get and output the negative keyword lists and store the list of identifiers. const string sharedEntityType = "NegativeKeywordList"; var sharedEntities = await GetSharedEntitiesByAccountIdAsync(sharedEntityType); OutputSharedEntityIdentifiersAsync(sharedEntities); var sharedEntityIds = new long[sharedEntities.Count]; for (int index = 0; index < sharedEntities.Count; index++) { if (sharedEntities[index].Id != null) { sharedEntityIds[index] = (long)sharedEntities[index].Id; } } // Negative keywords were added to the negative keyword list above. You can associate the // shared list of negative keywords with a campaign with or without negative keywords. // Shared negative keyword lists cannot be associated with an ad group. An ad group can only // be assigned an exclusive set of negative keywords. var associations = new[] { new SharedEntityAssociation { EntityId = campaignId, EntityType = "Campaign", SharedEntityId = sharedEntityId, SharedEntityType = "NegativeKeywordList" } }; partialErrors = await SetSharedEntityAssociationsAsync(associations); if (partialErrors == null || !partialErrors.Any()) { OutputStatusMessage(String.Format("Associated CampaignId {0} with Negative Keyword List Id {1}.\n", campaignId, sharedEntityId)); } else { OutputPartialErrors(partialErrors); } // Get and output the associations either by Campaign or NegativeKeywordList identifier. GetSharedEntityAssociationsByEntityIdsResponse getSharedEntityAssociationsByEntityIdsResponse = await GetSharedEntityAssociationsByEntityIdsAsync(new[] { campaignId }, "Campaign", "NegativeKeywordList"); OutputSharedEntityAssociations(getSharedEntityAssociationsByEntityIdsResponse.Associations); OutputPartialErrors(getSharedEntityAssociationsByEntityIdsResponse.PartialErrors); // Currently the GetSharedEntityAssociationsBySharedEntityIds operation accepts only one shared entity identifier in the list. GetSharedEntityAssociationsBySharedEntityIdsResponse getSharedEntityAssociationsBySharedEntityIdsResponse = await GetSharedEntityAssociationsBySharedEntityIdsAsync("Campaign", new[] { sharedEntityIds[sharedEntityIds.Length - 1] }, "NegativeKeywordList"); OutputSharedEntityAssociations(getSharedEntityAssociationsBySharedEntityIdsResponse.Associations); OutputPartialErrors(getSharedEntityAssociationsBySharedEntityIdsResponse.PartialErrors); // Explicitly delete the association between the campaign and the negative keyword list. partialErrors = await DeleteSharedEntityAssociationsAsync(associations); if (partialErrors == null || !partialErrors.Any()) { OutputStatusMessage("Deleted NegativeKeywordList associations\n"); } else { OutputPartialErrors(partialErrors); } // Delete the campaign and any remaining assocations. DeleteCampaigns(authorizationData.AccountId, new[] { campaignId }); OutputStatusMessage(String.Format("Deleted CampaignId {0}\n", campaignId)); // DeleteCampaigns does not delete the negative keyword list from the account's library. // Call the DeleteSharedEntities operation to delete the shared entities. partialErrors = await DeleteSharedEntitiesAsync(new SharedEntity[] { new NegativeKeywordList { Id = sharedEntityId } }); if (partialErrors == null || !partialErrors.Any()) { OutputStatusMessage(String.Format("Deleted Negative Keyword List (SharedEntity) Id {0}\n", sharedEntityId)); } else { OutputPartialErrors(partialErrors); } } // Catch authentication exceptions catch (OAuthTokenRequestException ex) { OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description)); } // Catch Campaign Management service exceptions catch (FaultException<Microsoft.BingAds.V10.CampaignManagement.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.V10.CampaignManagement.ApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.V10.CampaignManagement.EditorialApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (Exception ex) { OutputStatusMessage(ex.Message); } }
public async override Task RunAsync(AuthorizationData authorizationData) { try { BulkService = new BulkServiceManager(authorizationData); var progress = new Progress<BulkOperationProgressInfo>(x => OutputStatusMessage(String.Format("{0} % Complete", x.PercentComplete.ToString(CultureInfo.InvariantCulture)))); #region Add // Prepare the bulk entities that you want to upload. Each bulk entity contains the corresponding campaign management object, // and additional elements needed to read from and write to a bulk file. var bulkCampaign = new BulkCampaign { // ClientId may be used to associate records in the bulk upload file with records in the results file. The value of this field // is not used or stored by the server; it is simply copied from the uploaded record to the corresponding result record. // Note: This bulk file Client Id is not related to an application Client Id for OAuth. ClientId = "YourClientIdGoesHere", Campaign = new Campaign { // When using the Campaign Management service, the Id cannot be set. In the context of a BulkCampaign, the Id is optional // and may be used as a negative reference key during bulk upload. For example the same negative value set for the campaign Id // will be used when associating this new campaign with a new call ad extension in the BulkCampaignCallAdExtension object below. Id = campaignIdKey, Name = "Women's Shoes " + DateTime.UtcNow, Description = "Red shoes line.", // You must choose to set either the shared budget ID or daily amount. // You can set one or the other, but you may not set both. BudgetId = null, DailyBudget = 50, BudgetType = BudgetLimitType.DailyBudgetStandard, BiddingScheme = new EnhancedCpcBiddingScheme(), TimeZone = "PacificTimeUSCanadaTijuana", // DaylightSaving is not supported in the Bulk file schema. Whether or not you specify it in a BulkCampaign, // the value is not written to the Bulk file, and by default DaylightSaving is set to true. DaylightSaving = true, } }; // You can specify one negative site per BulkCampaignNegativeSite (singular), or multiple negative sites // in a BulkCampaignNegativeSites (plural) object. var bulkCampaignNegativeSite = new BulkCampaignNegativeSite[] { new BulkCampaignNegativeSite { // CampaignName will be ignored if you specify CampaignId. CampaignName = null, CampaignId = campaignIdKey, Website = "contoso.com/negativesite1" }, new BulkCampaignNegativeSite { CampaignId = campaignIdKey, Website = "contoso.com/negativesite2" }, }; // If you upload a BulkCampaignNegativeSites bulk entity, then you are effectively replacing any existing // negative sites assigned to the campaign. Thus, when a BulkCampaignNegativeSites entity is written to the // upload file, an extra Campaign Negative Site record is included where the Status is Deleted and the // Website field is empty. // That said, if you include additional BulkCampaignNegativeSite or BulkCampaignNegativeSites in the same upload, // they will be included in the new set of negative sites. var bulkCampaignNegativeSites = new BulkCampaignNegativeSites[] { new BulkCampaignNegativeSites { // CampaignName will be ignored if you specify CampaignId. CampaignName = null, CampaignNegativeSites = new CampaignNegativeSites { CampaignId = campaignIdKey, NegativeSites = new string[] { "contoso.com/negativesite3", "contoso.com/negativesite4", } } }, }; var uploadEntities = new List<BulkEntity>(); uploadEntities.Add(bulkCampaign); foreach (var campaignNegativeSite in bulkCampaignNegativeSite) { uploadEntities.Add(campaignNegativeSite); } foreach (var campaignNegativeSites in bulkCampaignNegativeSites) { uploadEntities.Add(campaignNegativeSites); } // Upload and write the output Reader = await WriteEntitiesAndUploadFileAsync(uploadEntities); var downloadEntities = Reader.ReadEntities().ToList(); var campaignResults = downloadEntities.OfType<BulkCampaign>().ToList(); OutputBulkCampaigns(campaignResults); // If you modify the sample to upload only BulkCampaignNegativeSite entities for a campaign, the SDK will abstract // the results file contents as one or more BulkCampaignNegativeSite. If you upload both BulkCampaignNegativeSite // and BulkCampaignNegativeSites as shown above, then the SDK will abstract the results file contents as a // BulkCampaignNegativeSites object containing all of the negative sites for the campaign, // including those uploaded as a BulkCampaignNegativeSite. // Whether you use the SDK to upload the entities, or only use the SDK to read an upload results file, // the SDK will abstract the results file as follows: // If the file contains an extra Campaign Negative Site record where the Status is Deleted and the // Website field is empty, the SDK returns a BulkCampaignNegativeSites (plural) object. // Otherwise the SDK returns one or more BulkCampaignNegativeSite (singlular) objects. var campaignNegativeSiteResults = downloadEntities.OfType<BulkCampaignNegativeSite>().ToList(); OutputBulkCampaignNegativeSite(campaignNegativeSiteResults); var campaignNegativeSitesResults = downloadEntities.OfType<BulkCampaignNegativeSites>().ToList(); OutputBulkCampaignNegativeSites(campaignNegativeSitesResults); Reader.Dispose(); #endregion Add #region CleanUp //Delete the campaign and negative sites that were previously added. //You should remove this region if you want to view the added entities in the //Bing Ads web application or another tool. //You must set the Id field to the corresponding entity identifier, and the Status field to Deleted. //When you delete a BulkCampaign, the dependent entities such as BulkCampaignNegativeSite //are deleted without being specified explicitly. uploadEntities = new List<BulkEntity>(); foreach (var campaignResult in campaignResults) { campaignResult.Campaign.Status = CampaignStatus.Deleted; uploadEntities.Add(campaignResult); } // Upload and write the output OutputStatusMessage("\nDeleting campaign and negative sites . . .\n"); Reader = await WriteEntitiesAndUploadFileAsync(uploadEntities); downloadEntities = Reader.ReadEntities().ToList(); OutputBulkCampaigns(downloadEntities.OfType<BulkCampaign>().ToList()); Reader.Dispose(); #endregion Cleanup } // Catch Microsoft Account authorization exceptions. catch (OAuthTokenRequestException ex) { OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description)); } // Catch Bulk service exceptions catch (FaultException<Microsoft.BingAds.V10.Bulk.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.V10.Bulk.ApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (BulkOperationInProgressException ex) { OutputStatusMessage("The result file for the bulk operation is not yet available for download."); OutputStatusMessage(ex.Message); } catch (BulkOperationCouldNotBeCompletedException<DownloadStatus> ex) { OutputStatusMessage(string.Join("; ", ex.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (BulkOperationCouldNotBeCompletedException<UploadStatus> ex) { OutputStatusMessage(string.Join("; ", ex.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (Exception ex) { OutputStatusMessage(ex.Message); } finally { if (Reader != null) { Reader.Dispose(); } if (Writer != null) { Writer.Dispose(); } } }
/// <summary> /// Prompt for user credentials and then call the Bing Ads Customer Management service /// with the current authenticated Microsoft account user. /// </summary> private async void AuthenticateUser() { try { Authentication authentication; if (OAuthCheckBox.IsChecked == true) { authentication = await OAuthHelper.AuthorizeDesktopMobileAuthCodeGrant(); var authenticationToken = ((OAuthDesktopMobileAuthCodeGrant) (authentication)).OAuthTokens.AccessToken; } else { authentication = new PasswordAuthentication(UserNameTextBox.Text, UserNamePasswordBox.Password); } ClearUserData(); // Get user's CustomerId and AccountId _authorizationData = new AuthorizationData { Authentication = authentication, DeveloperToken = (SandboxCheckBox.IsChecked == false) ? Settings.Default["DeveloperToken"].ToString() : Settings.Default["DeveloperTokenSandbox"].ToString() }; _customerService = new ServiceClient<ICustomerManagementService>(_authorizationData); var user = await GetUserAsync(null); UserNameTextBox.Text = user.UserName; // Search for the accounts that matches the specified criteria. var accounts = await SearchAccountsByUserIdAsync(user.Id); // Store the parent customer identifier in the second dimension of the array. _accountCustomerIds = new long?[accounts.Length, 2]; for (var i = 0; i < accounts.Length; i++) { AccountIdsComboBox.Items.Add(accounts[i].Id); _accountCustomerIds[i, 0] = accounts[i].Id; _accountCustomerIds[i, 1] = accounts[i].ParentCustomerId; } AccountIdsComboBox.SelectedIndex = 0; SetAuthorizationDataByAccountIndex(AccountIdsComboBox.SelectedIndex); if (accounts.Length > 0) RunButton.IsEnabled = true; } // Catch authentication exceptions catch (OAuthTokenRequestException ex) { OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description)); } // Catch Customer Management service exceptions catch (FaultException<Microsoft.BingAds.CustomerManagement.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => { if ((error.Code == 105) || (error.Code == 106)) { return "Authorization data is missing or incomplete for the specified environment.\n" + "To run the examples switch users or contact support for help with the following error.\n"; } return string.Format("{0}: {1}", error.Code, error.Message); }))); OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.CustomerManagement.ApiFault> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (HttpRequestException ex) { OutputStatusMessage(ex.Message); } finally { SwitchUserButton.IsEnabled = true; } }
private void ClearUserData() { RunButton.IsEnabled = false; OutputScrollViewer.Content = ""; UserNameTextBox.Text = ""; UserNamePasswordBox.Password = ""; CustomerIdLabel.Content = ""; AccountIdsComboBox.Items.Clear(); _authorizationData = null; }
public async override Task RunAsync(AuthorizationData authorizationData) { try { BulkService = new BulkServiceManager(authorizationData); var progress = new Progress<BulkOperationProgressInfo>(x => OutputStatusMessage(String.Format("{0} % Complete", x.PercentComplete.ToString(CultureInfo.InvariantCulture)))); #region Add const int targetIdKey = -1; const int campaignIdKey = -123; var uploadEntities = new List<BulkEntity>(); // Prepare the bulk entities that you want to upload. Each bulk entity contains the corresponding campaign management object, // and additional elements needed to read from and write to a bulk file. var bulkCampaign = new BulkCampaign { // ClientId may be used to associate records in the bulk upload file with records in the results file. The value of this field // is not used or stored by the server; it is simply copied from the uploaded record to the corresponding result record. // Note: This bulk file Client Id is not related to an application Client Id for OAuth. ClientId = "YourClientIdGoesHere", Campaign = new Campaign { // When using the Campaign Management service, the Id cannot be set. In the context of a BulkCampaign, the Id is optional // and may be used as a negative reference key during bulk upload. For example the same negative value set for the campaign Id // will be used when associating this new campaign with a new call ad extension in the BulkCampaignCallAdExtension object below. Id = campaignIdKey, Name = "Women's Shoes " + DateTime.UtcNow, Description = "Red shoes line.", BudgetType = BudgetLimitType.MonthlyBudgetSpendUntilDepleted, MonthlyBudget = 1000.00, TimeZone = "PacificTimeUSCanadaTijuana", // DaylightSaving is not supported in the Bulk file schema. Whether or not you specify it in a BulkCampaign, // the value is not written to the Bulk file, and by default DaylightSaving is set to true. DaylightSaving = true, } }; // Prepare targets for upload var bulkCampaignDayTimeTarget = new BulkCampaignDayTimeTarget { CampaignId = campaignIdKey, TargetId = targetIdKey, DayTimeTarget = new DayTimeTarget { Bids = new List<DayTimeTargetBid> { new DayTimeTargetBid { BidAdjustment = 10, Day = Day.Friday, FromHour = 11, FromMinute = Minute.Zero, ToHour = 13, ToMinute = Minute.Fifteen }, new DayTimeTargetBid { BidAdjustment = 20, Day = Day.Saturday, FromHour = 11, FromMinute = Minute.Zero, ToHour = 13, ToMinute = Minute.Fifteen } } } }; var bulkCampaignLocationTarget = new BulkCampaignLocationTarget { CampaignId = campaignIdKey, TargetId = targetIdKey, IntentOption = IntentOption.PeopleIn, CityTarget = new CityTarget { Bids = new List<CityTargetBid> { new CityTargetBid { BidAdjustment = 15, City = "Toronto, Toronto ON CA", IsExcluded = false } } }, CountryTarget = new CountryTarget { Bids = new List<CountryTargetBid> { new CountryTargetBid { BidAdjustment = 15, CountryAndRegion = "CA", IsExcluded = false } } }, MetroAreaTarget = new MetroAreaTarget { Bids = new List<MetroAreaTargetBid> { new MetroAreaTargetBid { BidAdjustment = 15, MetroArea = "Seattle-Tacoma, WA, WA US", IsExcluded = false } } }, StateTarget = new StateTarget { Bids = new List<StateTargetBid> { new StateTargetBid { BidAdjustment = 15, State = "US-WA", IsExcluded = false } } }, PostalCodeTarget = new PostalCodeTarget { Bids = new List<PostalCodeTargetBid> { new PostalCodeTargetBid { // Bid adjustments are not allowed for location exclusions. // If IsExcluded is true, this element will be ignored. BidAdjustment = 10, PostalCode = "98052, WA US", IsExcluded = false } } } }; var bulkCampaignRadiusTarget = new BulkCampaignRadiusTarget { CampaignId = campaignIdKey, TargetId = targetIdKey, RadiusTarget = new RadiusTarget2 { Bids = new List<RadiusTargetBid2> { new RadiusTargetBid2 { BidAdjustment = 50, LatitudeDegrees = 47.755367, LongitudeDegrees = -122.091827, Radius = 11, RadiusUnit = DistanceUnit.Kilometers, Name = "radius1" }, new RadiusTargetBid2 { BidAdjustment = 20, LatitudeDegrees = 49.755367, LongitudeDegrees = -129.091827, Radius = 12, RadiusUnit = DistanceUnit.Kilometers, Name = "radius2" } } } }; // Write the entities created above, to the specified file. uploadEntities.Add(bulkCampaign); uploadEntities.Add(bulkCampaignDayTimeTarget); uploadEntities.Add(bulkCampaignLocationTarget); uploadEntities.Add(bulkCampaignRadiusTarget); // Write the upload output var Reader = await UploadEntities(uploadEntities); var bulkEntities = Reader.ReadEntities().ToList(); var campaignResults = bulkEntities.OfType<BulkCampaign>().ToList(); OutputBulkCampaigns(campaignResults); var campaignDayTimeTargetResults = bulkEntities.OfType<BulkCampaignDayTimeTarget>().ToList(); OutputBulkCampaignDayTimeTargets(campaignDayTimeTargetResults); var campaignLocationTargetResults = bulkEntities.OfType<BulkCampaignLocationTarget>().ToList(); OutputBulkCampaignLocationTargets(campaignLocationTargetResults); var campaignRadiusTargetResults = bulkEntities.OfType<BulkCampaignRadiusTarget>().ToList(); OutputBulkCampaignRadiusTargets(campaignRadiusTargetResults); Reader.Dispose(); #endregion Add #region Update // Update the day and time target. // Do not create a BulkCampaignDayTimeTarget for update, unless you want to replace all existing DayTime target bids // with the specified day and time target set for the current bulk upload. // Instead you should upload one or more BulkCampaignDayTimeTargetBid. var bulkCampaignDayTimeTargetBids = new List<BulkCampaignDayTimeTargetBid> { new BulkCampaignDayTimeTargetBid { CampaignId = campaignDayTimeTargetResults[0].CampaignId, TargetId = campaignDayTimeTargetResults[0].TargetId, DayTimeTargetBid = new DayTimeTargetBid { BidAdjustment = 15, Day = Day.Friday, FromHour = 11, FromMinute = Minute.Zero, ToHour = 13, ToMinute = Minute.Fifteen } } }; // Write the updated target to the file uploadEntities = new List<BulkEntity>(); foreach (var bulkCampaignDayTimeTargetBid in bulkCampaignDayTimeTargetBids) { uploadEntities.Add(bulkCampaignDayTimeTargetBid); } // Write the upload output Reader = await UploadEntities(uploadEntities); OutputStatusMessage("Upload Results Bulk File Path" + Reader.BulkFilePath + "\n"); OutputStatusMessage("Updated Entities\n"); bulkEntities = Reader.ReadEntities().ToList(); var campaignDayTimeTargetBidResults = bulkEntities.OfType<BulkCampaignDayTimeTargetBid>().ToList(); OutputBulkCampaignDayTimeTargetBids(campaignDayTimeTargetBidResults); Reader.Dispose(); #endregion Update #region CleanUp /* Delete the campaign and target associations that were previously added. * Note that the targets are not deleted. Deleting targets is not supported using the * Bulk service. To delete targets you can use the DeleteTargetsFromLibrary operation * via the Campaign Management service. * You should remove this region if you want to view the added entities in the * Bing Ads web application or another tool. */ var campaignId = campaignResults[0].Campaign.Id; bulkCampaign = new BulkCampaign { Campaign = new Campaign { Id = campaignId, Status = CampaignStatus.Deleted } }; uploadEntities = new List<BulkEntity>(); uploadEntities.Add(bulkCampaign); // Write the upload output Reader = await UploadEntities(uploadEntities); bulkEntities = Reader.ReadEntities().ToList(); campaignResults = bulkEntities.OfType<BulkCampaign>().ToList(); OutputBulkCampaigns(campaignResults); Reader.Dispose(); OutputStatusMessage(String.Format("Deleted CampaignId {0}\n", campaignResults[0].Campaign.Id)); #endregion Cleanup } // Catch Microsoft Account authorization exceptions. catch (OAuthTokenRequestException ex) { OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description)); } // Catch Bulk service exceptions catch (FaultException<Microsoft.BingAds.Bulk.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.Bulk.ApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (BulkOperationInProgressException ex) { OutputStatusMessage("The result file for the bulk operation is not yet available for download."); OutputStatusMessage(ex.Message); } catch (BulkOperationCouldNotBeCompletedException<DownloadStatus> ex) { OutputStatusMessage(string.Join("; ", ex.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (BulkOperationCouldNotBeCompletedException<UploadStatus> ex) { OutputStatusMessage(string.Join("; ", ex.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (Exception ex) { OutputStatusMessage(ex.Message); } finally { if (Reader != null) { Reader.Dispose(); } if (Writer != null) { Writer.Dispose(); } } }
public async override Task RunAsync(AuthorizationData authorizationData) { try { BulkService = new BulkServiceManager(authorizationData); BulkService.StatusPollIntervalInMilliseconds = 5000; var progress = new Progress<BulkOperationProgressInfo>(x => OutputStatusMessage(String.Format("{0} % Complete", x.PercentComplete.ToString(CultureInfo.InvariantCulture)))); // In this example we will download all ads and keywords in the account. var entities = BulkDownloadEntity.Ads | BulkDownloadEntity.Keywords; // DownloadParameters is used for Option A below. var downloadParameters = new DownloadParameters { CampaignIds = null, DataScope = DataScope.EntityData | DataScope.EntityPerformanceData, PerformanceStatsDateRange = new PerformanceStatsDateRange { PredefinedTime = ReportTimePeriod.LastFourWeeks }, Entities = entities, FileType = FileType, LastSyncTimeInUTC = null, ResultFileDirectory = FileDirectory, ResultFileName = DownloadFileName, OverwriteResultFile = true }; // SubmitDownloadParameters is used for Option B and Option C below. var submitDownloadParameters = new SubmitDownloadParameters { CampaignIds = null, DataScope = DataScope.EntityData | DataScope.EntityPerformanceData, PerformanceStatsDateRange = new PerformanceStatsDateRange { PredefinedTime = ReportTimePeriod.LastFourWeeks }, Entities = entities, FileType = FileType, LastSyncTimeInUTC = null }; // Option A - Background Completion with BulkServiceManager // You can submit a download or upload request and the BulkServiceManager will automatically // return results. The BulkServiceManager abstracts the details of checking for result file // completion, and you don't have to write any code for results polling. OutputStatusMessage("Awaiting Background Completion . . ."); await BackgroundCompletionAsync(downloadParameters, progress); // Option B - Submit and Download with BulkServiceManager // Submit the download request and then use the BulkDownloadOperation result to // track status until the download is complete e.g. either using // TrackAsync or GetStatusAsync. //OutputStatusMessage("Awaiting Submit and Download . . ."); //await SubmitAndDownloadAsync(submitDownloadParameters); // Option C - Download Results with BulkServiceManager // If for any reason you have to resume from a previous application state, // you can use an existing download request identifier and use it // to download the result file. // For example you might have previously retrieved a request ID using SubmitDownloadAsync. //var bulkDownloadOperation = await BulkService.SubmitDownloadAsync(submitDownloadParameters); //var requestId = bulkDownloadOperation.RequestId; // Given the request ID above, you can resume the workflow and download the bulk file. // The download request identifier is valid for two days. // If you do not download the bulk file within two days, you must request it again. //OutputStatusMessage("Awaiting Download Results . . ."); //await DownloadResultsAsync(requestId, authorizationData); } // Catch authentication exceptions catch (OAuthTokenRequestException ex) { OutputStatusMessage(String.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description)); } // Catch Bulk service exceptions catch (FaultException<Microsoft.BingAds.V10.Bulk.AdApiFaultDetail> ex) { OutputStatusMessage(String.Join("; ", ex.Detail.Errors.Select(error => String.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.V10.Bulk.ApiFaultDetail> ex) { OutputStatusMessage(String.Join("; ", ex.Detail.OperationErrors.Select(error => String.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(String.Join("; ", ex.Detail.BatchErrors.Select(error => String.Format("{0}: {1}", error.Code, error.Message)))); } catch (BulkOperationInProgressException ex) { OutputStatusMessage("The result file for the bulk operation is not yet available for download."); OutputStatusMessage(ex.Message); } catch (BulkOperationCouldNotBeCompletedException<DownloadStatus> ex) { OutputStatusMessage(string.Join("; ", ex.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (BulkOperationCouldNotBeCompletedException<UploadStatus> ex) { OutputStatusMessage(string.Join("; ", ex.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (Exception ex) { OutputStatusMessage(ex.Message); } }
public async override Task RunAsync(AuthorizationData authorizationData) { try { Service = new ServiceClient<IReportingService>(authorizationData); // Build a keyword performance report request, including Format, ReportName, Aggregation, // Scope, Time, Filter, and Columns. var report = new KeywordPerformanceReportRequest { Format = ReportFormat.Tsv, ReportName = "My Keyword Performance Report", ReturnOnlyCompleteData = false, Aggregation = ReportAggregation.Daily, Scope = new AccountThroughAdGroupReportScope { AccountIds = new[] { authorizationData.AccountId }, AdGroups = null, Campaigns = null }, Time = new ReportTime { // You may either use a custom date range or predefined time. //CustomDateRangeStart = new Date // { // Month = DateTime.Now.Month, // Day = DateTime.Now.Day, // Year = DateTime.Now.Year - 1 // }, //CustomDateRangeEnd = new Date // { // Month = DateTime.Now.Month, // Day = DateTime.Now.Day, // Year = DateTime.Now.Year // }, PredefinedTime = ReportTimePeriod.Yesterday }, // If you specify a filter, results may differ from data you see in the Bing Ads web application Filter = new KeywordPerformanceReportFilter { DeviceType = DeviceTypeReportFilter.Computer | DeviceTypeReportFilter.SmartPhone }, // Specify the attribute and data report columns. Columns = new[] { KeywordPerformanceReportColumn.TimePeriod, KeywordPerformanceReportColumn.AccountId, KeywordPerformanceReportColumn.CampaignId, KeywordPerformanceReportColumn.Keyword, KeywordPerformanceReportColumn.KeywordId, KeywordPerformanceReportColumn.DeviceType, KeywordPerformanceReportColumn.BidMatchType, KeywordPerformanceReportColumn.Clicks, KeywordPerformanceReportColumn.Impressions, KeywordPerformanceReportColumn.Ctr, KeywordPerformanceReportColumn.AverageCpc, KeywordPerformanceReportColumn.Spend, KeywordPerformanceReportColumn.QualityScore }, // You may optionally sort by any KeywordPerformanceReportColumn, and optionally // specify the maximum number of rows to return in the sorted report. Sort = new[] { new KeywordPerformanceReportSort { SortColumn = KeywordPerformanceReportColumn.Clicks, SortOrder = SortOrder.Ascending } }, MaxRows = 10, }; // SubmitGenerateReport helper method calls the corresponding Bing Ads service operation // to request the report identifier. The identifier is used to check report generation status // before downloading the report. var reportRequestId = await SubmitGenerateReportAsync(report); OutputStatusMessage("Report Request ID: " + reportRequestId); var waitTime = new TimeSpan(0, 0, 30); ReportRequestStatus reportRequestStatus = null; // This example polls every 30 seconds up to 5 minutes. // In production you may poll the status every 1 to 2 minutes for up to one hour. // If the call succeeds, stop polling. If the call or // download fails, the call throws a fault. for (int i = 0; i < 10; i++) { OutputStatusMessage(String.Format("Will check if the report is ready in {0} seconds: ", waitTime.Seconds)); Thread.Sleep(waitTime); // PollGenerateReport helper method calls the corresponding Bing Ads service operation // to get the report request status. reportRequestStatus = await PollGenerateReportAsync(reportRequestId); if (reportRequestStatus.Status == ReportRequestStatusType.Success || reportRequestStatus.Status == ReportRequestStatusType.Error) { break; } OutputStatusMessage("The report is not yet ready for download."); } if (reportRequestStatus != null) { if (reportRequestStatus.Status == ReportRequestStatusType.Success) { var reportDownloadUrl = reportRequestStatus.ReportDownloadUrl; OutputStatusMessage(String.Format("Downloading from {0}.", reportDownloadUrl)); OutputStatusMessage("\n"); DownloadFile(reportDownloadUrl, DownloadPath); OutputStatusMessage(String.Format("The report was written to {0}.", DownloadPath)); } else if (reportRequestStatus.Status == ReportRequestStatusType.Error) { OutputStatusMessage("The request failed. Try requesting the report " + "later.\nIf the request continues to fail, contact support."); } else // Pending { OutputStatusMessage(String.Format("The request is taking longer than expected.\n " + "Save the report ID ({0}) and try again later.", reportRequestId)); } } } // Catch authentication exceptions catch (OAuthTokenRequestException ex) { OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description)); } // Catch Reporting service exceptions catch (FaultException<Microsoft.BingAds.Reporting.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.Reporting.ApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (WebException ex) { OutputStatusMessage(ex.Message); if (ex.Response != null) OutputStatusMessage("HTTP status code: " + ((HttpWebResponse)ex.Response).StatusCode); } catch (IOException ex) { OutputStatusMessage(ex.Message); } catch (Exception ex) { OutputStatusMessage(ex.Message); } }
public async override Task RunAsync(AuthorizationData authorizationData) { try { Service = new ServiceClient<IAdIntelligenceService>(authorizationData); // Set the Currency, Keywords, Language, PublisherCountries, and TargetPositionForAds // for the estimated bid by keywords request. const Currency currency = Currency.USDollar; var keywordAndMatchTypes = new[] { new KeywordAndMatchType { KeywordText = "flower", MatchTypes = new[] { MatchType.Broad, MatchType.Exact, MatchType.Phrase } }, new KeywordAndMatchType { KeywordText = "delivery", MatchTypes = new[] { MatchType.Broad, MatchType.Exact, MatchType.Phrase } } }; const string language = "English"; var publisherCountries = new[] { "US" }; const TargetAdPosition targetPositionForAds = TargetAdPosition.SideBar; // GetKeywordEstimatedBidByKeywords helper method calls the corresponding Bing Ads service operation // to request the KeywordEstimatedBids. IEnumerable<KeywordEstimatedBid> keywordEstimatedBids = await GetKeywordEstimatedBidByKeywordsAsync( currency, keywordAndMatchTypes, language, publisherCountries, targetPositionForAds ); // GetAdGroupEstimatedBidByKeywords helper method calls the corresponding Bing Ads service operation // to request the AdGroupEstimatedBid. AdGroupEstimatedBid adGroupEstimatedBid = await GetAdGroupEstimatedBidByKeywordsAsync( currency, keywordAndMatchTypes, language, publisherCountries, targetPositionForAds ); // Print the KeywordEstimatedBids if (keywordEstimatedBids != null) { OutputStatusMessage("KeywordEstimatedBids"); foreach (KeywordEstimatedBid bid in keywordEstimatedBids) { if (bid == null) { OutputStatusMessage("The keyword is not valid."); } else { OutputStatusMessage(bid.Keyword); if (bid.EstimatedBids.Count == 0) { OutputStatusMessage(" There is no bid information available for the keyword.\n"); } else { foreach (EstimatedBidAndTraffic estimatedBidAndTraffic in bid.EstimatedBids) { OutputStatusMessage(" " + estimatedBidAndTraffic.MatchType); OutputStatusMessage(String.Format(" Estimated Minimum Bid: {0:c}", estimatedBidAndTraffic.EstimatedMinBid)); OutputStatusMessage(" Average CPC: " + estimatedBidAndTraffic.AverageCPC); OutputStatusMessage(String.Format(" Estimated clicks per week: {0} to {1}", estimatedBidAndTraffic.MinClicksPerWeek, estimatedBidAndTraffic.MaxClicksPerWeek)); OutputStatusMessage(String.Format(" Estimated impressions per week: {0} to {1}", estimatedBidAndTraffic.MinImpressionsPerWeek, estimatedBidAndTraffic.MaxImpressionsPerWeek)); OutputStatusMessage(String.Format(" Estimated cost per week: {0} to {1}", estimatedBidAndTraffic.MinTotalCostPerWeek, estimatedBidAndTraffic.MaxTotalCostPerWeek)); } } } } } // Print the AdGroupEstimatedBid OutputStatusMessage("AdGroupEstimatedBid"); OutputStatusMessage(" Average CPC: " + adGroupEstimatedBid.AverageCPC); OutputStatusMessage(" CTR: " + adGroupEstimatedBid.CTR); OutputStatusMessage(" Estimated Ad Group Bid: " + adGroupEstimatedBid.EstimatedAdGroupBid); OutputStatusMessage(String.Format(" Estimated clicks per week: {0} to {1}", adGroupEstimatedBid.MinClicksPerWeek, adGroupEstimatedBid.MaxClicksPerWeek)); OutputStatusMessage(String.Format(" Estimated impressions per week: {0} to {1}", adGroupEstimatedBid.MinImpressionsPerWeek, adGroupEstimatedBid.MaxImpressionsPerWeek)); OutputStatusMessage(String.Format(" Estimated cost per week: {0} to {1}", adGroupEstimatedBid.MinTotalCostPerWeek, adGroupEstimatedBid.MaxTotalCostPerWeek)); } // Catch authentication exceptions catch (OAuthTokenRequestException ex) { OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description)); } // Catch Ad Intelligence service exceptions catch (FaultException<Microsoft.BingAds.AdIntelligence.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.AdIntelligence.ApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (Exception ex) { OutputStatusMessage(ex.Message); } }
public async override Task RunAsync(AuthorizationData authorizationData) { try { CampaignService = new ServiceClient<ICampaignManagementService>(authorizationData); var budgetIds = new List<long?>(); var budgets = new List<Budget>(); budgets.Add(new Budget { Amount = 50, BudgetType = BudgetLimitType.DailyBudgetStandard, Name = "My Shared Budget " + DateTime.UtcNow, }); budgetIds = (await AddBudgetsAsync(budgets)).BudgetIds.ToList(); // Specify one or more campaigns. var campaigns = new[]{ new Campaign { Name = "Women's Shoes " + DateTime.UtcNow, Description = "Red shoes line.", // You must choose to set either the shared budget ID or daily amount. // You can set one or the other, but you may not set both. BudgetId = budgetIds.Count > 0 ? budgetIds[0] : null, DailyBudget = budgetIds.Count > 0 ? 0 : 50, BudgetType = BudgetLimitType.DailyBudgetStandard, // You can set your campaign bid strategy to Enhanced CPC (EnhancedCpcBiddingScheme) // and then, at any time, set an individual ad group or keyword bid strategy to // Manual CPC (ManualCpcBiddingScheme). // For campaigns you can use either of the EnhancedCpcBiddingScheme or ManualCpcBiddingScheme objects. // If you do not set this element, then ManualCpcBiddingScheme is used by default. BiddingScheme = new EnhancedCpcBiddingScheme { }, TimeZone = "PacificTimeUSCanadaTijuana", DaylightSaving = true, // Used with FinalUrls shown in the text ads that we will add below. TrackingUrlTemplate = "http://tracker.example.com/?season={_season}&promocode={_promocode}&u={lpurl}" }, }; // Specify one or more ad groups. var adGroups = new[] { new AdGroup { Name = "Women's Red Shoe Sale", AdDistribution = AdDistribution.Search, BiddingModel = BiddingModel.Keyword, PricingModel = PricingModel.Cpc, StartDate = null, EndDate = new Microsoft.BingAds.V10.CampaignManagement.Date { Month = 12, Day = 31, Year = DateTime.UtcNow.Year + 1 }, SearchBid = new Bid { Amount = 0.09 }, Language = "English", // For ad groups you can use either of the InheritFromParentBiddingScheme or ManualCpcBiddingScheme objects. // If you do not set this element, then InheritFromParentBiddingScheme is used by default. BiddingScheme = new ManualCpcBiddingScheme { }, // You could use a tracking template which would override the campaign level // tracking template. Tracking templates defined for lower level entities // override those set for higher level entities. // In this example we are using the campaign level tracking template. TrackingUrlTemplate = null, } }; // In this example only the second keyword should succeed. The Text of the first keyword exceeds the limit, // and the third keyword is a duplicate of the second keyword. var keywords = new[] { new Keyword { Bid = new Bid { Amount = 0.47 }, Param2 = "10% Off", MatchType = MatchType.Broad, Text = "Brand-A Shoes Brand-A Shoes Brand-A Shoes Brand-A Shoes Brand-A Shoes " + "Brand-A Shoes Brand-A Shoes Brand-A Shoes Brand-A Shoes Brand-A Shoes " + "Brand-A Shoes Brand-A Shoes Brand-A Shoes Brand-A Shoes Brand-A Shoes", // For keywords you can use either of the InheritFromParentBiddingScheme or ManualCpcBiddingScheme objects. // If you do not set this element, then InheritFromParentBiddingScheme is used by default. BiddingScheme = new InheritFromParentBiddingScheme { }, }, new Keyword { Bid = new Bid { Amount = 0.47 }, Param2 = "10% Off", MatchType = MatchType.Phrase, Text = "Brand-A Shoes", // For keywords you can use either of the InheritFromParentBiddingScheme or ManualCpcBiddingScheme objects. // If you do not set this element, then InheritFromParentBiddingScheme is used by default. BiddingScheme = new InheritFromParentBiddingScheme { }, }, new Keyword { Bid = new Bid { Amount = 0.47 }, Param2 = "10% Off", MatchType = MatchType.Phrase, Text = "Brand-A Shoes", // For keywords you can use either of the InheritFromParentBiddingScheme or ManualCpcBiddingScheme objects. // If you do not set this element, then InheritFromParentBiddingScheme is used by default. BiddingScheme = new InheritFromParentBiddingScheme { }, } }; // In this example only the first 3 ads should succeed. // The TitlePart2 of the fourth ad is empty and not valid, // and the fifth ad is a duplicate of the second ad. var ads = new Ad[] { new ExpandedTextAd { TitlePart1 = "Contoso", TitlePart2 = "Fast & Easy Setup", Text = "Find New Customers & Increase Sales! Start Advertising on Contoso Today.", Path1 = "seattle", Path2 = "shoe sale", // With FinalUrls you can separate the tracking template, custom parameters, and // landing page URLs. FinalUrls = new[] { "http://www.contoso.com/womenshoesale" }, // Final Mobile URLs can also be used if you want to direct the user to a different page // for mobile devices. FinalMobileUrls = new[] { "http://mobile.contoso.com/womenshoesale" }, // You could use a tracking template which would override the campaign level // tracking template. Tracking templates defined for lower level entities // override those set for higher level entities. // In this example we are using the campaign level tracking template. TrackingUrlTemplate = null, // Set custom parameters that are specific to this ad, // and can be used by the ad, ad group, campaign, or account level tracking template. // In this example we are using the campaign level tracking template. UrlCustomParameters = new CustomParameters { Parameters = new[] { new CustomParameter(){ Key = "promoCode", Value = "PROMO1" }, new CustomParameter(){ Key = "season", Value = "summer" }, } } }, new ExpandedTextAd { TitlePart1 = "Contoso", TitlePart2 = "Quick & Easy Setup", Text = "Find New Customers & Increase Sales! Start Advertising on Contoso Today.", Path1 = "seattle", Path2 = "shoe sale", // With FinalUrls you can separate the tracking template, custom parameters, and // landing page URLs. FinalUrls = new[] { "http://www.contoso.com/womenshoesale" }, // Final Mobile URLs can also be used if you want to direct the user to a different page // for mobile devices. FinalMobileUrls = new[] { "http://mobile.contoso.com/womenshoesale" }, // You could use a tracking template which would override the campaign level // tracking template. Tracking templates defined for lower level entities // override those set for higher level entities. // In this example we are using the campaign level tracking template. TrackingUrlTemplate = null, // Set custom parameters that are specific to this ad, // and can be used by the ad, ad group, campaign, or account level tracking template. // In this example we are using the campaign level tracking template. UrlCustomParameters = new CustomParameters { Parameters = new[] { new CustomParameter(){ Key = "promoCode", Value = "PROMO2" }, new CustomParameter(){ Key = "season", Value = "summer" }, } }, }, new ExpandedTextAd { TitlePart1 = "Contoso", TitlePart2 = "Fast & Simple Setup", Text = "Find New Customers & Increase Sales! Start Advertising on Contoso Today.", Path1 = "seattle", Path2 = "shoe sale", // With FinalUrls you can separate the tracking template, custom parameters, and // landing page URLs. FinalUrls = new[] { "http://www.contoso.com/womenshoesale" }, // Final Mobile URLs can also be used if you want to direct the user to a different page // for mobile devices. FinalMobileUrls = new[] { "http://mobile.contoso.com/womenshoesale" }, // You could use a tracking template which would override the campaign level // tracking template. Tracking templates defined for lower level entities // override those set for higher level entities. // In this example we are using the campaign level tracking template. TrackingUrlTemplate = null, // Set custom parameters that are specific to this ad, // and can be used by the ad, ad group, campaign, or account level tracking template. // In this example we are using the campaign level tracking template. UrlCustomParameters = new CustomParameters { Parameters = new[] { new CustomParameter(){ Key = "promoCode", Value = "PROMO3" }, new CustomParameter(){ Key = "season", Value = "summer" }, } }, }, new ExpandedTextAd { TitlePart1 = "Contoso", TitlePart2 = "", Text = "Find New Customers & Increase Sales! Start Advertising on Contoso Today.", Path1 = "seattle", Path2 = "shoe sale", // With FinalUrls you can separate the tracking template, custom parameters, and // landing page URLs. FinalUrls = new[] { "http://www.contoso.com/womenshoesale" }, // Final Mobile URLs can also be used if you want to direct the user to a different page // for mobile devices. FinalMobileUrls = new[] { "http://mobile.contoso.com/womenshoesale" }, // You could use a tracking template which would override the campaign level // tracking template. Tracking templates defined for lower level entities // override those set for higher level entities. // In this example we are using the campaign level tracking template. TrackingUrlTemplate = null, // Set custom parameters that are specific to this ad, // and can be used by the ad, ad group, campaign, or account level tracking template. // In this example we are using the campaign level tracking template. UrlCustomParameters = new CustomParameters { Parameters = new[] { new CustomParameter(){ Key = "promoCode", Value = "PROMO4" }, new CustomParameter(){ Key = "season", Value = "summer" }, } }, }, new ExpandedTextAd { TitlePart1 = "Contoso", TitlePart2 = "Quick & Easy Setup", Text = "Find New Customers & Increase Sales! Start Advertising on Contoso Today.", Path1 = "seattle", Path2 = "shoe sale", // With FinalUrls you can separate the tracking template, custom parameters, and // landing page URLs. FinalUrls = new[] { "http://www.contoso.com/womenshoesale" }, // Final Mobile URLs can also be used if you want to direct the user to a different page // for mobile devices. FinalMobileUrls = new[] { "http://mobile.contoso.com/womenshoesale" }, // You could use a tracking template which would override the campaign level // tracking template. Tracking templates defined for lower level entities // override those set for higher level entities. // In this example we are using the campaign level tracking template. TrackingUrlTemplate = null, // Set custom parameters that are specific to this ad, // and can be used by the ad, ad group, campaign, or account level tracking template. // In this example we are using the campaign level tracking template. UrlCustomParameters = new CustomParameters { Parameters = new[] { new CustomParameter(){ Key = "promoCode", Value = "PROMO5" }, new CustomParameter(){ Key = "season", Value = "summer" }, } }, }, }; // Add the campaign, ad group, keywords, and ads AddCampaignsResponse addCampaignsResponse = await AddCampaignsAsync(authorizationData.AccountId, campaigns); long?[] campaignIds = addCampaignsResponse.CampaignIds.ToArray(); BatchError[] campaignErrors = addCampaignsResponse.PartialErrors.ToArray(); AddAdGroupsResponse addAdGroupsResponse = await AddAdGroupsAsync((long)campaignIds[0], adGroups); long?[] adGroupIds = addAdGroupsResponse.AdGroupIds.ToArray(); BatchError[] adGroupErrors = addAdGroupsResponse.PartialErrors.ToArray(); AddKeywordsResponse addKeywordsResponse = await AddKeywordsAsync((long)adGroupIds[0], keywords); long?[] keywordIds = addKeywordsResponse.KeywordIds.ToArray(); BatchError[] keywordErrors = addKeywordsResponse.PartialErrors.ToArray(); AddAdsResponse addAdsResponse = await AddAdsAsync((long)adGroupIds[0], ads); long?[] adIds = addAdsResponse.AdIds.ToArray(); BatchError[] adErrors = addAdsResponse.PartialErrors.ToArray(); // Output the new assigned entity identifiers, as well as any partial errors OutputCampaignsWithPartialErrors(campaigns, campaignIds, campaignErrors); OutputAdGroupsWithPartialErrors(adGroups, adGroupIds, adGroupErrors); OutputKeywordsWithPartialErrors(keywords, keywordIds, keywordErrors); OutputAdsWithPartialErrors(ads, adIds, adErrors); // Here is a simple example that updates the campaign budget. // If the campaign has a shared budget you cannot update the Campaign budget amount, // and you must instead update the amount in the Budget object. If you try to update // the budget amount of a campaign that has a shared budget, the service will return // the CampaignServiceCannotUpdateSharedBudget error code. var getCampaigns = (await GetCampaignsByAccountIdAsync( authorizationData.AccountId, CampaignType.SearchAndContent | CampaignType.Shopping, CampaignAdditionalField.BiddingScheme | CampaignAdditionalField.BudgetId )).Campaigns; var updateCampaigns = new List<Campaign>(); var updateBudgets = new List<Budget>(); var getCampaignIds = new List<long>(); var getBudgetIds = new List<long>(); // Increase existing budgets by 20% foreach (var campaign in getCampaigns) { // If the campaign has a shared budget, let's add the budget ID to the list we will update later. if (campaign != null && campaign.BudgetId > 0) { getBudgetIds.Add((long)campaign.BudgetId); } // If the campaign has its own budget, let's add it to the list of campaigns to update later. else if(campaign != null) { updateCampaigns.Add(campaign); } } // Update shared budgets in Budget objects. if (getBudgetIds.Count > 0) { getBudgetIds = getBudgetIds.Distinct().ToList(); var getBudgets = (await GetBudgetsByIdsAsync(getBudgetIds)).Budgets; OutputStatusMessage("List of shared budgets BEFORE update:\n"); foreach (var budget in getBudgets) { OutputStatusMessage("Budget:"); OutputBudget(budget); } OutputStatusMessage("List of campaigns that share each budget:\n"); var getCampaignIdCollection = (await GetCampaignIdsByBudgetIdsAsync(getBudgetIds)).CampaignIdCollection; for(int index = 0; index < getCampaignIdCollection.Count; index++) { OutputStatusMessage(string.Format("BudgetId: {0}", getBudgetIds[index])); OutputStatusMessage("Campaign Ids:"); if(getCampaignIdCollection[index] != null) { foreach (var id in getCampaignIdCollection[index].Ids) { OutputStatusMessage(string.Format("\t{0}", id)); } } } foreach (var budget in getBudgets) { if (budget != null) { // Increase budget by 20 % budget.Amount *= 1.2m; updateBudgets.Add(budget); } } await UpdateBudgetsAsync(updateBudgets); getBudgets = (await GetBudgetsByIdsAsync(getBudgetIds)).Budgets; OutputStatusMessage("List of shared budgets AFTER update:\n"); foreach (var budget in getBudgets) { OutputStatusMessage("Budget:"); OutputBudget(budget); } } // Update unshared budgets in Campaign objects. if(updateCampaigns.Count > 0) { // The UpdateCampaigns operation only accepts 100 Campaign objects per call. // To simply the example we will update the first 100. updateCampaigns = updateCampaigns.Take(100).ToList(); OutputStatusMessage("List of campaigns with unshared budget BEFORE budget update:\n"); foreach (var campaign in updateCampaigns) { OutputStatusMessage("Campaign:"); OutputCampaign(campaign); // Monthly budgets are deprecated and there will be a forced migration to daily budgets in calendar year 2017. // Shared budgets do not support the monthly budget type, so this is only applicable to unshared budgets. // During the migration all campaign level unshared budgets will be rationalized as daily. // The formula that will be used to convert monthly to daily budgets is: Monthly budget amount / 30.4. // Moving campaign monthly budget to daily budget is encouraged before monthly budgets are migrated. if (campaign.BudgetType == BudgetLimitType.MonthlyBudgetSpendUntilDepleted) { // Increase budget by 20 % campaign.BudgetType = BudgetLimitType.DailyBudgetStandard; campaign.DailyBudget = (campaign.MonthlyBudget / 30.4) * 1.2; } else { // Increase budget by 20 % campaign.DailyBudget *= 1.2; } getCampaignIds.Add((long)campaign.Id); } await UpdateCampaignsAsync(authorizationData.AccountId, updateCampaigns); getCampaigns = (await GetCampaignsByIdsAsync( authorizationData.AccountId, getCampaignIds, CampaignType.SearchAndContent | CampaignType.Shopping, CampaignAdditionalField.BiddingScheme | CampaignAdditionalField.BudgetId )).Campaigns; OutputStatusMessage("List of campaigns with unshared budget AFTER budget update:\n"); foreach (var campaign in getCampaigns) { OutputStatusMessage("Campaign:"); OutputCampaign(campaign); } } // Update the Text for the 3 successfully created ads, and update some UrlCustomParameters. var updateAds = new Ad[] { new ExpandedTextAd { Id = adIds[0], Text = "Huge Savings on All Red Shoes.", // Set the UrlCustomParameters element to null or empty to retain any // existing custom parameters. UrlCustomParameters = null, }, new ExpandedTextAd { Id = adIds[1], Text = "Huge Savings on All Red Shoes.", // To remove all custom parameters, set the Parameters element of the // CustomParameters object to null or empty. UrlCustomParameters = new CustomParameters { Parameters = null, }, }, new ExpandedTextAd { Id = adIds[2], Text = "Huge Savings on All Red Shoes.", // To remove a subset of custom parameters, specify the custom parameters that // you want to keep in the Parameters element of the CustomParameters object. UrlCustomParameters = new CustomParameters { Parameters = new[] { new CustomParameter(){ Key = "promoCode", Value = "updatedpromo" }, } } }, }; // As an exercise you can step through using the debugger and view the results. var getAdsByAdGroupIdResponse = await GetAdsByAdGroupIdAsync((long)adGroupIds[0]); var updateAdsResponse = await UpdateAdsAsync((long)adGroupIds[0], updateAds); getAdsByAdGroupIdResponse = await GetAdsByAdGroupIdAsync((long)adGroupIds[0]); // Here is a simple example that updates the keyword bid to use the ad group bid. var updateKeyword = new Keyword { // Set Bid.Amount null (new empty Bid) to use the ad group bid. // If the Bid property is null, your keyword bid will not be updated. Bid = new Bid(), Id = keywordIds[1], }; // As an exercise you can step through using the debugger and view the results. var getKeywordsByAdGroupIdResponse = await GetKeywordsByAdGroupIdAsync((long)adGroupIds[0], KeywordAdditionalField.BiddingScheme); var updateKeywordsResponse = await UpdateKeywordsAsync((long)adGroupIds[0], new[] { updateKeyword }); getKeywordsByAdGroupIdResponse = await GetKeywordsByAdGroupIdAsync((long)adGroupIds[0], KeywordAdditionalField.BiddingScheme); // Delete the campaign, ad group, keyword, and ad that were previously added. // You should remove this line if you want to view the added entities in the // Bing Ads web application or another tool. await DeleteCampaignsAsync(authorizationData.AccountId, new[] { (long)campaignIds[0] }); OutputStatusMessage(String.Format("\nDeleted CampaignId {0}\n", campaignIds[0])); // This sample will attempt to delete the budget that was created above. if (budgetIds.Count > 0) { await DeleteBudgetsAsync(new[] { (long)budgetIds[0] }); OutputStatusMessage(String.Format("\nDeleted Budget Id {0}\n", budgetIds[0])); } } // Catch authentication exceptions catch (OAuthTokenRequestException ex) { OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description)); } // Catch Campaign Management service exceptions catch (FaultException<Microsoft.BingAds.V10.CampaignManagement.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.V10.CampaignManagement.ApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.V10.CampaignManagement.EditorialApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (Exception ex) { OutputStatusMessage(ex.Message); } }
public async override Task RunAsync(AuthorizationData authorizationData) { try { Service = new ServiceClient<ICampaignManagementService>(authorizationData); // Before you can track conversions or target audiences using a remarketing list, // you need to create a UET tag in Bing Ads (web application or API) and then // add the UET tag tracking code to every page of your website. For more information, please see // Universal Event Tracking at https://msdn.microsoft.com/library/bing-ads-universal-event-tracking-guide.aspx. // First you should call the GetUetTagsByIds operation to check whether a tag has already been created. // You can leave the TagIds element null or empty to request all UET tags available for the customer. var uetTags = (await GetUetTagsByIdsAsync(null)).UetTags; // If you do not already have a UET tag that can be used, or if you need another UET tag, // call the AddUetTags service operation to create a new UET tag. If the call is successful, // the tracking script that you should add to your website is included in a corresponding // UetTag within the response message. if (uetTags == null || uetTags.Count < 1) { var uetTag = new UetTag { Description = "My First Uet Tag", Name = "New Uet Tag", }; uetTags = (await AddUetTagsAsync(new[] { uetTag })).UetTags; } if (uetTags == null || uetTags.Count < 1) { OutputStatusMessage( string.Format("You do not have any UET tags registered for CustomerId {0}.\n", authorizationData.CustomerId) ); return; } OutputStatusMessage("List of all UET Tags:\n"); foreach (var uetTag in uetTags) { OutputUetTag(uetTag); } // After you retreive the tracking script from the AddUetTags or GetUetTagsByIds operation, // the next step is to add the UET tag tracking code to your website. We recommend that you, // or your website administrator, add it to your entire website in either the head or body sections. // If your website has a master page, then that is the best place to add it because you add it once // and it is included on all pages. For more information, please see // Universal Event Tracking at https://msdn.microsoft.com/library/bing-ads-universal-event-tracking-guide.aspx. // We will use the same UET tag for the remainder of this example. var tagId = uetTags[0].Id; // Optionally you can update the name and description of a UetTag with the UpdateUetTags operation. OutputStatusMessage("UET Tag BEFORE update:\n"); OutputUetTag(uetTags[0]); uetTags = new[] { new UetTag { Description = "Updated Uet Tag Description", Id = tagId, Name = "Updated Uet Tag Name " + DateTime.UtcNow, } }; await UpdateUetTagsAsync(uetTags); uetTags = (await GetUetTagsByIdsAsync(new[] { (long)tagId })).UetTags; OutputStatusMessage("UET Tag AFTER update:\n"); OutputUetTag(uetTags[0]); // Add conversion goals that depend on the UET Tag Id retreived above. // Please note that you cannot delete conversion goals. If you want to stop // tracking conversions for the goal, you can set the goal status to Paused. var conversionGoals = new ConversionGoal[] { new DurationGoal { ConversionWindowInMinutes = 30, CountType = ConversionGoalCountType.All, MinimumDurationInSeconds = 60, Name = "My Duration Goal " + DateTime.UtcNow, Revenue = new ConversionGoalRevenue { Type = ConversionGoalRevenueType.FixedValue, Value = 5.00m, CurrencyCode = null }, Scope = EntityScope.Account, Status = ConversionGoalStatus.Active, TagId = tagId, }, new EventGoal { // The type of user interaction you want to track. ActionExpression = "play", ActionOperator = ExpressionOperator.Contains, // The category of event you want to track. CategoryExpression = "video", CategoryOperator = ExpressionOperator.Contains, ConversionWindowInMinutes = 30, CountType = ConversionGoalCountType.All, // The name of the element that caused the action. LabelExpression = "trailer", LabelOperator = ExpressionOperator.Contains, Name = "My Event Goal " + DateTime.UtcNow, Revenue = new ConversionGoalRevenue { Type = ConversionGoalRevenueType.FixedValue, Value = 5.00m, CurrencyCode = null }, Scope = EntityScope.Account, Status = ConversionGoalStatus.Active, TagId = tagId, // A numerical value associated with that event. // Could be length of the video played etc. Value = 5.00m, ValueOperator = ValueOperator.Equals, }, new PagesViewedPerVisitGoal { ConversionWindowInMinutes = 30, CountType = ConversionGoalCountType.All, MinimumPagesViewed = 5, Name = "My Pages Viewed Per Visit Goal " + DateTime.UtcNow, Revenue = new ConversionGoalRevenue { Type = ConversionGoalRevenueType.FixedValue, Value = 5.00m, CurrencyCode = null }, Scope = EntityScope.Account, Status = ConversionGoalStatus.Active, TagId = tagId, }, new UrlGoal { ConversionWindowInMinutes = 30, CountType = ConversionGoalCountType.All, Name = "My Url Goal " + DateTime.UtcNow, Revenue = new ConversionGoalRevenue { Type = ConversionGoalRevenueType.FixedValue, Value = 5.00m, CurrencyCode = null }, Scope = EntityScope.Account, Status = ConversionGoalStatus.Active, TagId = tagId, UrlExpression = "contoso", UrlOperator = ExpressionOperator.Contains }, new AppInstallGoal { // You must provide a valid app platform and app store identifier, // otherwise this goal will not be added successfully. AppPlatform = "Windows", AppStoreId = "AppStoreIdGoesHere", ConversionWindowInMinutes = 30, CountType = ConversionGoalCountType.All, Name = "My App Install Goal " + DateTime.UtcNow, Revenue = new ConversionGoalRevenue { Type = ConversionGoalRevenueType.FixedValue, Value = 5.00m, CurrencyCode = null }, // Account scope is not supported for app install goals. You can // set scope to Customer or don't set it for the same result. Scope = EntityScope.Customer, Status = ConversionGoalStatus.Active, // The TagId is inherited from the ConversionGoal base class, // however, App Install goals do not use a UET tag. TagId = null, }, }; var addConversionGoalsResponse = await AddConversionGoalsAsync(conversionGoals); // Find the conversion goals that were added successfully. List<long> conversionGoalIds = new List<long>(); foreach (var goalId in addConversionGoalsResponse.ConversionGoalIds) { if (goalId != null) { conversionGoalIds.Add((long)goalId); } } OutputStatusMessage("List of errors returned from AddConversionGoals (if any):\n"); OutputPartialErrors(addConversionGoalsResponse.PartialErrors); var conversionGoalTypes = ConversionGoalType.AppInstall | ConversionGoalType.Duration | ConversionGoalType.Event | ConversionGoalType.PagesViewedPerVisit | ConversionGoalType.Url; var getConversionGoals = (await GetConversionGoalsByIdsAsync(conversionGoalIds, conversionGoalTypes)).ConversionGoals; OutputStatusMessage("List of conversion goals BEFORE update:\n"); foreach (var conversionGoal in getConversionGoals) { OutputConversionGoal(conversionGoal); } var updateConversionGoals = new ConversionGoal[] { new DurationGoal { ConversionWindowInMinutes = 60, CountType = ConversionGoalCountType.Unique, // You can change the conversion goal type e.g. in this example an event goal // had been created above at index 1. Now we are using the returned identifier // at index 1 to update the type from EventGoal to DurationGoal. Id = conversionGoalIds[1], MinimumDurationInSeconds = 120, Name = "My Updated Duration Goal " + DateTime.UtcNow, Revenue = new ConversionGoalRevenue { Type = ConversionGoalRevenueType.FixedValue, Value = 10.00m, CurrencyCode = null }, // The Scope cannot be updated, even if you update the goal type. // You can either send the same value or leave Scope empty. Scope = EntityScope.Account, Status = ConversionGoalStatus.Paused, // You can update the tag as needed. In this example we will explicitly use the same UET tag. // To keep the UET tag unchanged, you can also leave this element nil or empty. TagId = tagId, }, new EventGoal { // For both add and update conversion goal operations, you must include one or more // of the following events: // ActionExpression, CategoryExpression, LabelExpression, or Value. // For example if you do not include ActionExpression during update, // any existing ActionOperator and ActionExpression settings will be deleted. ActionExpression = null, ActionOperator = null, CategoryExpression = "video", CategoryOperator = ExpressionOperator.Equals, Id = conversionGoalIds[0], // You cannot update the operator unless you also include the expression. // The following attempt to update LabelOperator will result in an error. LabelExpression = null, LabelOperator = ExpressionOperator.Equals, Name = "My Updated Event Goal " + DateTime.UtcNow, Revenue = new ConversionGoalRevenue { Type = ConversionGoalRevenueType.FixedValue, Value = 5.00m, CurrencyCode = null }, // You must specify the previous settings unless you want // them replaced during the update conversion goal operation. Value = 5.00m, ValueOperator = ValueOperator.Equals, }, new PagesViewedPerVisitGoal { Id = conversionGoalIds[2], Name = "My Updated Pages Viewed Per Visit Goal " + DateTime.UtcNow, // When updating a conversion goal, if the Revenue element is nil or empty then none // of the nested properties will be updated. However, if this element is not nil or empty // then you are effectively replacing any existing revenue properties. For example to delete // any previous revenue settings, set the Revenue element to an empty ConversionGoalRevenue object. Revenue = new ConversionGoalRevenue(), }, new UrlGoal { Id = conversionGoalIds[3], Name = "My Updated Url Goal" + DateTime.UtcNow, // If not specified during update, the previous Url settings are retained. UrlExpression = null, UrlOperator = ExpressionOperator.BeginsWith } }; var updateConversionGoalsResponse = await UpdateConversionGoalsAsync(updateConversionGoals); OutputStatusMessage("List of errors returned from UpdateConversionGoals (if any):\n"); OutputPartialErrors(updateConversionGoalsResponse.PartialErrors); getConversionGoals = (await GetConversionGoalsByIdsAsync(conversionGoalIds, conversionGoalTypes)).ConversionGoals; OutputStatusMessage("List of conversion goals AFTER update:\n"); foreach (var conversionGoal in getConversionGoals) { OutputConversionGoal(conversionGoal); } } // Catch authentication exceptions catch (OAuthTokenRequestException ex) { OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description)); } // Catch ConversionGoal Management service exceptions catch (FaultException<Microsoft.BingAds.V10.CampaignManagement.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.V10.CampaignManagement.ApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.V10.CampaignManagement.EditorialApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (Exception ex) { OutputStatusMessage(ex.Message); } }
public async override Task RunAsync(AuthorizationData authorizationData) { try { Service = new ServiceClient<ICampaignManagementService>(authorizationData); // Add a campaign that will later be associated with ad extensions. var campaigns = new[] { new Campaign { Id = null, Name = "Women's Shoes " + DateTime.UtcNow, Description = "Red shoes line.", BudgetType = BudgetLimitType.MonthlyBudgetSpendUntilDepleted, MonthlyBudget = 1000.00, TimeZone = "PacificTimeUSCanadaTijuana", DaylightSaving = true, // Used with FinalUrls shown in the sitelinks that we will add below. TrackingUrlTemplate = "http://tracker.example.com/?season={_season}&promocode={_promocode}&u={lpurl}" } }; AddCampaignsResponse addCampaignsResponse = await AddCampaignsAsync(authorizationData.AccountId, campaigns); long?[] campaignIds = addCampaignsResponse.CampaignIds.ToArray(); // Specify the extensions. var adExtensions = new AdExtension[] { //new AppAdExtension //{ // AppPlatform = "Windows", // AppStoreId = "AppStoreIdGoesHere", // DestinationUrl = "DestinationUrlGoesHere", // DisplayText = "Contoso", //}, new CallAdExtension { CountryCode = "US", PhoneNumber = "2065550100", IsCallOnly = false }, //new ImageAdExtension //{ // AlternativeText = "Image Extension Alt Text", // ImageMediaIds = new long[] { await AddImageAsync(authorizationData) } //}, new LocationAdExtension { PhoneNumber = "206-555-0100", CompanyName = "Contoso Shoes", IconMediaId = null, ImageMediaId = null, Address = new Address { StreetAddress = "1234 Washington Place", StreetAddress2 = "Suite 1210", CityName = "Woodinville", ProvinceName = "WA", CountryCode = "US", PostalCode = "98608" } }, new SiteLinksAdExtension { SiteLinks = new [] { new SiteLink { DisplayText = "Women's Shoe Sale 1", // If you are currently using Destination URLs, you must replace them with Final URLs. // Here is an example of a DestinationUrl you might have used previously. // DestinationUrl = "http://www.contoso.com/womenshoesale/?season=spring&promocode=PROMO123", // To migrate from DestinationUrl to FinalUrls for existing sitelinks, you can set DestinationUrl // to an empty string when updating the sitelink. If you are removing DestinationUrl, // then FinalUrls is required. // DestinationUrl = "", // With FinalUrls you can separate the tracking template, custom parameters, and // landing page URLs. FinalUrls = new[] { "http://www.contoso.com/womenshoesale" }, // Final Mobile URLs can also be used if you want to direct the user to a different page // for mobile devices. FinalMobileUrls = new[] { "http://mobile.contoso.com/womenshoesale" }, // You could use a tracking template which would override the campaign level // tracking template. Tracking templates defined for lower level entities // override those set for higher level entities. // In this example we are using the campaign level tracking template. TrackingUrlTemplate = null, // Set custom parameters that are specific to this sitelink, // and can be used by the sitelink, ad group, campaign, or account level tracking template. // In this example we are using the campaign level tracking template. UrlCustomParameters = new CustomParameters { Parameters = new[] { new CustomParameter(){ Key = "promoCode", Value = "PROMO1" }, new CustomParameter(){ Key = "season", Value = "summer" }, } }, }, new SiteLink { DisplayText = "Women's Shoe Sale 2", // If you are currently using Destination URLs, you must replace them with Final URLs. // Here is an example of a DestinationUrl you might have used previously. // DestinationUrl = "http://www.contoso.com/womenshoesale/?season=spring&promocode=PROMO123", // To migrate from DestinationUrl to FinalUrls for existing sitelinks, you can set DestinationUrl // to an empty string when updating the sitelink. If you are removing DestinationUrl, // then FinalUrls is required. // DestinationUrl = "", // With FinalUrls you can separate the tracking template, custom parameters, and // landing page URLs. FinalUrls = new[] { "http://www.contoso.com/womenshoesale" }, // Final Mobile URLs can also be used if you want to direct the user to a different page // for mobile devices. FinalMobileUrls = new[] { "http://mobile.contoso.com/womenshoesale" }, // You could use a tracking template which would override the campaign level // tracking template. Tracking templates defined for lower level entities // override those set for higher level entities. // In this example we are using the campaign level tracking template. TrackingUrlTemplate = null, // Set custom parameters that are specific to this sitelink, // and can be used by the sitelink, ad group, campaign, or account level tracking template. // In this example we are using the campaign level tracking template. UrlCustomParameters = new CustomParameters { Parameters = new[] { new CustomParameter(){ Key = "promoCode", Value = "PROMO2" }, new CustomParameter(){ Key = "season", Value = "summer" }, } }, } } } }; // Add all extensions to the account's ad extension library var adExtensionIdentities = await AddAdExtensionsAsync( authorizationData.AccountId, adExtensions ); OutputStatusMessage("Added ad extensions.\n\n"); // DeleteAdExtensionsAssociations, SetAdExtensionsAssociations, and GetAdExtensionsEditorialReasons // operations each require a list of type AdExtensionIdToEntityIdAssociation. var adExtensionIdToEntityIdAssociations = new AdExtensionIdToEntityIdAssociation[adExtensionIdentities.Count]; // GetAdExtensionsByIds requires a list of type long. var adExtensionIds = new long[adExtensionIdentities.Count]; // Loop through the list of extension IDs and build any required data structures // for subsequent operations. for (int i = 0; i < adExtensionIdentities.Count; i++) { adExtensionIdToEntityIdAssociations[i] = new AdExtensionIdToEntityIdAssociation { AdExtensionId = adExtensionIdentities[i].Id, EntityId = (long)campaignIds[0] }; adExtensionIds[i] = adExtensionIdentities[i].Id; } // Associate the specified ad extensions with the respective campaigns or ad groups. await SetAdExtensionsAssociationsAsync( authorizationData.AccountId, adExtensionIdToEntityIdAssociations, AssociationType.Campaign ); OutputStatusMessage("Set ad extension associations.\n\n"); // Get editorial rejection reasons for the respective ad extension and entity associations. var adExtensionEditorialReasonCollection = (AdExtensionEditorialReasonCollection[])await GetAdExtensionsEditorialReasons( authorizationData.AccountId, adExtensionIdToEntityIdAssociations, AssociationType.Campaign ); const AdExtensionsTypeFilter adExtensionsTypeFilter = AdExtensionsTypeFilter.AppAdExtension | AdExtensionsTypeFilter.CallAdExtension | AdExtensionsTypeFilter.ImageAdExtension | AdExtensionsTypeFilter.LocationAdExtension | AdExtensionsTypeFilter.SiteLinksAdExtension; // Get the specified ad extensions from the account’s ad extension library. adExtensions = (AdExtension[])await GetAdExtensionsByIdsAsync( authorizationData.AccountId, adExtensionIds, adExtensionsTypeFilter ); OutputAdExtensionsWithEditorialReasons(adExtensions, adExtensionEditorialReasonCollection); // Remove the specified associations from the respective campaigns or ad groups. // The extesions are still available in the account's extensions library. await DeleteAdExtensionsAssociationsAsync( authorizationData.AccountId, adExtensionIdToEntityIdAssociations, AssociationType.Campaign ); OutputStatusMessage("Deleted ad extension associations.\n\n"); // Deletes the ad extensions from the account’s ad extension library. await DeleteAdExtensionsAsync( authorizationData.AccountId, adExtensionIds ); OutputStatusMessage("Deleted ad extensions.\n\n"); } // Catch authentication exceptions catch (OAuthTokenRequestException ex) { OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description)); } // Catch Campaign Management service exceptions catch (FaultException<Microsoft.BingAds.V10.CampaignManagement.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.V10.CampaignManagement.ApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.V10.CampaignManagement.EditorialApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (Exception ex) { OutputStatusMessage(ex.Message); } }
public async override Task RunAsync(AuthorizationData authorizationData) { try { #region CampaignThroughAdGroupSetup // You will need to use the Campaign Management service to get the Bing Merchant Center Store Id. This will be used // when creating a new Bing Shopping Campaign. // For other operations such as adding product conditions, you can manage Bing Shopping Campaigns solely with the Bulk Service. CampaignService = new ServiceClient<ICampaignManagementService>(authorizationData); // Get a list of all Bing Merchant Center stores associated with your CustomerId IList<BMCStore> stores = await GetBMCStoresByCustomerIdAsync(); if (stores == null) { OutputStatusMessage( String.Format("You do not have any BMC stores registered for CustomerId {0}.\n", authorizationData.CustomerId) ); return; } BulkService = new BulkServiceManager(authorizationData); var progress = new Progress<BulkOperationProgressInfo>(x => OutputStatusMessage(String.Format("{0} % Complete", x.PercentComplete.ToString(CultureInfo.InvariantCulture)))); const int campaignIdKey = -123; const int adGroupIdKey = -1234; var uploadEntities = new List<BulkEntity>(); /* Add a new Bing Shopping campaign that will be associated with a ProductScope criterion. * - Set the CampaignType element of the Campaign to Shopping. * - Create a ShoppingSetting instance and set its Priority (0, 1, or 2), SalesCountryCode, and StoreId elements. * Add this shopping setting to the Settings list of the Campaign. */ var bulkCampaign = new BulkCampaign { // ClientId may be used to associate records in the bulk upload file with records in the results file. The value of this field // is not used or stored by the server; it is simply copied from the uploaded record to the corresponding result record. // Note: This bulk file Client Id is not related to an application Client Id for OAuth. ClientId = "YourClientIdGoesHere", Campaign = new Campaign { // When using the Campaign Management service, the Id cannot be set. In the context of a BulkCampaign, the Id is optional // and may be used as a negative reference key during bulk upload. For example the same negative value set for the campaign Id // will be used when associating this new campaign with a new campaign product scope in the BulkCampaignProductScope object below. Id = campaignIdKey, CampaignType = CampaignType.Shopping, Settings = new[] { new ShoppingSetting() { Priority = 0, SalesCountryCode = "US", StoreId = (int)stores[0].Id } }, Name = "Bing Shopping Campaign " + DateTime.UtcNow, Description = "Bing Shopping Campaign Example.", BudgetType = BudgetLimitType.MonthlyBudgetSpendUntilDepleted, MonthlyBudget = 1000.00, TimeZone = "PacificTimeUSCanadaTijuana", // DaylightSaving is not supported in the Bulk file schema. Whether or not you specify it in a BulkCampaign, // the value is not written to the Bulk file, and by default DaylightSaving is set to true. DaylightSaving = true, } }; /* Optionally, you can create a ProductScope criterion that will be associated with your Bing Shopping campaign. * Use the product scope criterion to include a subset of your product catalog, for example a specific brand, * category, or product type. A campaign can only be associated with one ProductScope, which contains a list * of up to 7 ProductCondition. You'll also be able to specify more specific product conditions for each ad group. */ var bulkCampaignProductScope = new BulkCampaignProductScope { CampaignCriterion = new CampaignCriterion() { CampaignId = campaignIdKey, BidAdjustment = null, // Reserved for future use Criterion = new ProductScope() { Conditions = new ProductCondition[] { new ProductCondition { Operand = "Condition", Attribute = "New" }, new ProductCondition { Operand = "CustomLabel0", Attribute = "MerchantDefinedCustomLabel" }, } }, }, Status = Status.Active, }; // Specify one or more ad groups. var bulkAdGroup = new BulkAdGroup { CampaignId = campaignIdKey, AdGroup = new AdGroup { Id = adGroupIdKey, Name = "Product Categories", AdDistribution = AdDistribution.Search, BiddingModel = BiddingModel.Keyword, PricingModel = PricingModel.Cpc, StartDate = null, EndDate = new Microsoft.BingAds.V10.CampaignManagement.Date { Month = 12, Day = 31, Year = 2016 }, Language = "English", Status = AdGroupStatus.Active }, }; /* * Create a product ad. You must add at least one ProductAd to the corresponding ad group. * A ProductAd is not used directly for delivered ad copy. Instead, the delivery engine generates * product ads from the product details that it finds in your Bing Merchant Center store's product catalog. * The primary purpose of the ProductAd object is to provide promotional text that the delivery engine * adds to the product ads that it generates. For example, if the promotional text is set to * “Free shipping on $99 purchases”, the delivery engine will set the product ad’s description to * “Free shipping on $99 purchases.” */ var bulkProductAd = new BulkProductAd { AdGroupId = adGroupIdKey, ProductAd = new ProductAd { PromotionalText = "Free shipping on $99 purchases." } }; uploadEntities.Add(bulkCampaign); uploadEntities.Add(bulkAdGroup); uploadEntities.Add(bulkCampaignProductScope); uploadEntities.Add(bulkProductAd); // Write the upload output var Reader = await UploadEntities(uploadEntities); var bulkEntities = Reader.ReadEntities().ToList(); var campaignResults = bulkEntities.OfType<BulkCampaign>().ToList(); OutputBulkCampaigns(campaignResults); var adGroupResults = bulkEntities.OfType<BulkAdGroup>().ToList(); OutputBulkAdGroups(adGroupResults); var productAdResults = bulkEntities.OfType<BulkProductAd>().ToList(); OutputBulkProductAds(productAdResults); var campaignProductScopeResults = bulkEntities.OfType<BulkCampaignProductScope>().ToList(); OutputBulkCampaignProductScopes(campaignProductScopeResults); Reader.Dispose(); #endregion CampaignThroughAdGroupSetup #region BidAllProducts var adGroupId = (long)adGroupResults[0].AdGroup.Id; var helper = new ProductPartitionHelper(adGroupId); var root = helper.AddUnit( null, new ProductCondition { Operand = "All", Attribute = null }, 0.35, false, "root" ); OutputStatusMessage("Applying only the root as a Unit with a bid . . . \n"); var applyBulkProductPartitionActionsResults = await ApplyBulkProductPartitionActions(helper.PartitionActions); var productPartitions = await GetBulkAdGroupProductPartitionTree(adGroupId); OutputStatusMessage("The ad group's product partition only has a tree root node: \n"); OutputProductPartitions(productPartitions); /* * Let's update the bid of the root Unit we just added. */ var updatedRoot = GetNodeByClientId(applyBulkProductPartitionActionsResults, "root"); var bid = new FixedBid { Bid = new Bid { Amount = 0.45 } }; ((BiddableAdGroupCriterion)(updatedRoot.AdGroupCriterion)).CriterionBid = bid; helper = new ProductPartitionHelper(adGroupId); helper.UpdatePartition(updatedRoot); OutputStatusMessage("Updating the bid for the tree root node . . . \n"); await ApplyBulkProductPartitionActions(helper.PartitionActions); productPartitions = await GetBulkAdGroupProductPartitionTree(adGroupId); OutputStatusMessage("Updated the bid for the tree root node: \n"); OutputProductPartitions(productPartitions); #endregion BidAllProducts #region InitializeTree /* * Now we will overwrite any existing tree root, and build a product partition group tree structure in multiple steps. * You could build the entire tree in a single call since there are less than 20,000 nodes; however, * we will build it in steps to demonstrate how to use the results from bulk upload to update the tree. * * For a list of validation rules, see the Bing Shopping Campaigns technical guide: * https://msdn.microsoft.com/en-US/library/bing-ads-campaign-management-bing-shopping-campaigns.aspx */ helper = new ProductPartitionHelper(adGroupId); /* * Check whether a root node exists already. */ var existingRoot = GetNodeByClientId(applyBulkProductPartitionActionsResults, "root"); if (existingRoot != null) { existingRoot.ClientId = "deletedroot"; helper.DeletePartition(existingRoot); } root = helper.AddSubdivision( null, new ProductCondition { Operand = "All", Attribute = null }, "root" ); /* * The direct children of any node must have the same Operand. * For this example we will use CategoryL1 nodes as children of the root. * For a list of valid CategoryL1 through CategoryL5 values, see the Bing Category Taxonomy: * http://advertise.bingads.microsoft.com/en-us/WWDocs/user/search/en-us/Bing_Category_Taxonomy.txt */ var animalsSubdivision = helper.AddSubdivision( root, new ProductCondition { Operand = "CategoryL1", Attribute = "Animals & Pet Supplies" }, "animalsSubdivision" ); /* * If you use a CategoryL2 node, it must be a descendant (child or later) of a CategoryL1 node. * In other words you cannot have a CategoryL2 node as parent of a CategoryL1 node. * For this example we will a CategoryL2 node as child of the CategoryL1 Animals & Pet Supplies node. */ var petSuppliesSubdivision = helper.AddSubdivision( animalsSubdivision, new ProductCondition { Operand = "CategoryL2", Attribute = "Pet Supplies" }, "petSuppliesSubdivision" ); var brandA = helper.AddUnit( petSuppliesSubdivision, new ProductCondition { Operand = "Brand", Attribute = "Brand A" }, 0.35, false, "brandA" ); /* * If you won't bid on Brand B, set the helper method's bidAmount to '0' and isNegative to true. * The helper method will create a NegativeAdGroupCriterion and apply the condition. */ var brandB = helper.AddUnit( petSuppliesSubdivision, new ProductCondition { Operand = "Brand", Attribute = "Brand B" }, 0, true, "brandB" ); var otherBrands = helper.AddUnit( petSuppliesSubdivision, new ProductCondition { Operand = "Brand", Attribute = null }, 0.35, false, "otherBrands" ); var otherPetSupplies = helper.AddUnit( animalsSubdivision, new ProductCondition { Operand = "CategoryL2", Attribute = null }, 0.35, false, "otherPetSupplies" ); var electronics = helper.AddUnit( root, new ProductCondition { Operand = "CategoryL1", Attribute = "Electronics" }, 0.35, false, "electronics" ); var otherCategoryL1 = helper.AddUnit( root, new ProductCondition { Operand = "CategoryL1", Attribute = null }, 0.35, false, "otherCategoryL1" ); OutputStatusMessage("Applying product partitions to the ad group . . . \n"); applyBulkProductPartitionActionsResults = await ApplyBulkProductPartitionActions(helper.PartitionActions); productPartitions = await GetBulkAdGroupProductPartitionTree(adGroupId); /* * The product partition group tree now has 9 nodes. All other (Root Node) | +-- Animals & Pet Supplies (CategoryL1) | | | +-- Pet Supplies (CategoryL2) | | | | | +-- Brand A | | | | | +-- Brand B | | | | | +-- All other (Brand) | | | +-- All other (CategoryL2) | +-- Electronics (CategoryL1) | +-- All other (CategoryL1) */ OutputStatusMessage("The product partition group tree now has 9 nodes: \n"); OutputProductPartitions(productPartitions); #endregion InitializeTree #region UpdateTree /* * Let's replace the Electronics (CategoryL1) node created above with an Electronics (CategoryL1) node that * has children i.e. Brand C (Brand), Brand D (Brand), and All other (Brand) as follows: Electronics (CategoryL1) | +-- Brand C (Brand) | +-- Brand D (Brand) | +-- All other (Brand) */ helper = new ProductPartitionHelper(adGroupId); /* * To replace a node we must know its Id and its ParentCriterionId. In this case the parent of the node * we are replacing is All other (Root Node). The node that we are replacing is Electronics (CategoryL1). */ var rootId = GetNodeByClientId(applyBulkProductPartitionActionsResults, "root").AdGroupCriterion.Id; electronics.AdGroupCriterion.Id = GetNodeByClientId(applyBulkProductPartitionActionsResults, "electronics").AdGroupCriterion.Id; helper.DeletePartition(electronics); var parent = new BulkAdGroupProductPartition { AdGroupCriterion = new BiddableAdGroupCriterion() { Id = rootId } }; var electronicsSubdivision = helper.AddSubdivision( parent, new ProductCondition { Operand = "CategoryL1", Attribute = "Electronics" }, "electronicsSubdivision" ); var brandC = helper.AddUnit( electronicsSubdivision, new ProductCondition { Operand = "Brand", Attribute = "Brand C" }, 0.35, false, "brandC" ); var brandD = helper.AddUnit( electronicsSubdivision, new ProductCondition { Operand = "Brand", Attribute = "Brand D" }, 0.35, false, "brandD" ); var otherElectronicsBrands = helper.AddUnit( electronicsSubdivision, new ProductCondition { Operand = "Brand", Attribute = null }, 0.35, false, "otherElectronicsBrands" ); OutputStatusMessage( "Updating the product partition group to refine Electronics (CategoryL1) with 3 child nodes . . . \n" ); applyBulkProductPartitionActionsResults = await ApplyBulkProductPartitionActions(helper.PartitionActions); productPartitions = await GetBulkAdGroupProductPartitionTree(adGroupId); /* * The product partition group tree now has 12 nodes, including the children of Electronics (CategoryL1): All other (Root Node) | +-- Animals & Pet Supplies (CategoryL1) | | | +-- Pet Supplies (CategoryL2) | | | | | +-- Brand A | | | | | +-- Brand B | | | | | +-- All other (Brand) | | | +-- All other (CategoryL2) | +-- Electronics (CategoryL1) | | | +-- Brand C (Brand) | | | +-- Brand D (Brand) | | | +-- All other (Brand) | +-- All other (CategoryL1) */ OutputStatusMessage( "The product partition group tree now has 12 nodes, including the children of Electronics (CategoryL1): \n" ); OutputProductPartitions(productPartitions); #endregion UpdateTree var Service = new ServiceClient<ICampaignManagementService>(authorizationData); var getCampaignIds = new List<long>(); getCampaignIds.Add((long)campaignResults[0].Campaign.Id); var request = new GetCampaignsByIdsRequest { AccountId = authorizationData.AccountId, CampaignIds = getCampaignIds, CampaignType = CampaignType.Shopping }; await Service.CallAsync((s, r) => s.GetCampaignsByIdsAsync(r), request); #region CleanUp /* Delete the campaign, ad group, criterion, and ad that were previously added. * You should remove this region if you want to view the added entities in the * Bing Ads web application or another tool. */ var campaignId = campaignResults[0].Campaign.Id; bulkCampaign = new BulkCampaign { Campaign = new Campaign { Id = campaignId, Status = CampaignStatus.Deleted } }; uploadEntities = new List<BulkEntity>(); uploadEntities.Add(bulkCampaign); // Write the upload output Reader = await UploadEntities(uploadEntities); bulkEntities = Reader.ReadEntities().ToList(); campaignResults = bulkEntities.OfType<BulkCampaign>().ToList(); OutputBulkCampaigns(campaignResults); Reader.Dispose(); OutputStatusMessage(String.Format("Deleted CampaignId {0}\n", campaignResults[0].Campaign.Id)); #endregion Cleanup } // Catch Microsoft Account authorization exceptions. catch (OAuthTokenRequestException ex) { OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description)); } // Catch Bulk service exceptions catch (FaultException<Microsoft.BingAds.V10.Bulk.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.V10.Bulk.ApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } // Catch Campaign Management service exceptions catch (FaultException<Microsoft.BingAds.V10.CampaignManagement.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.V10.CampaignManagement.ApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (BulkOperationInProgressException ex) { OutputStatusMessage("The result file for the bulk operation is not yet available for download."); OutputStatusMessage(ex.Message); } catch (BulkOperationCouldNotBeCompletedException<DownloadStatus> ex) { OutputStatusMessage(string.Join("; ", ex.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (BulkOperationCouldNotBeCompletedException<UploadStatus> ex) { OutputStatusMessage(string.Join("; ", ex.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (Exception ex) { OutputStatusMessage(ex.Message); } finally { if (Reader != null) { Reader.Dispose(); } if (Writer != null) { Writer.Dispose(); } } }
/// <summary> /// Each example must implement either Run or RunAsync as the main entry point. /// </summary> /// <param name="authorizationData">Represents a user who intends to access the corresponding customer and account.</param> /// <returns></returns> public virtual async Task RunAsync(AuthorizationData authorizationData) { await Task.Factory.StartNew(() => Run(authorizationData)); }
/// <summary> /// Utility method for setting the customer and account identifiers within the global /// <see cref="_authorizationData"/> instance. /// </summary> /// <param name="authentication">The OAuth or Bing Ads managed (UserName/Password) authentication credentials.</param> /// <returns></returns> private static async Task SetAuthorizationDataAsync(Authentication authentication) { _authorizationData = new AuthorizationData { Authentication = authentication, DeveloperToken = (Settings.Default["DeveloperToken"] != null) ? Settings.Default["DeveloperToken"].ToString() : null }; _customerService = new ServiceClient<ICustomerManagementService>(_authorizationData); var user = await GetUserAsync(null); var accounts = await SearchAccountsByUserIdAsync(user.Id); if (accounts.Length <= 0) return; _authorizationData.AccountId = (long)accounts[0].Id; _authorizationData.CustomerId = (int)accounts[0].ParentCustomerId; return; }
public async override Task RunAsync(AuthorizationData authorizationData) { Stream responseStream = null; FileStream fileStream = null; var fileInfo = new FileInfo(LocalFile); try { CampaignService = new ServiceClient<ICampaignManagementService>(authorizationData); var getGeoLocationsFileUrlResponse = await GetGeoLocationsFileUrlAsync(Version, LanguageLocale); // Going forward you should track the date and time of the previous download, // and compare it with the last modified time provided by the service. var previousSyncTimeUtc = new DateTime(2016, 11, 29, 0, 0, 0, DateTimeKind.Utc); var fileUrl = getGeoLocationsFileUrlResponse.FileUrl; var fileUrlExpiryTimeUtc = getGeoLocationsFileUrlResponse.FileUrlExpiryTimeUtc; var lastModifiedTimeUtc = getGeoLocationsFileUrlResponse.LastModifiedTimeUtc; OutputStatusMessage(string.Format("FileUrl: {0}\n", fileUrl)); OutputStatusMessage(string.Format("FileUrlExpiryTimeUtc: {0}\n", fileUrlExpiryTimeUtc)); OutputStatusMessage(string.Format("LastModifiedTimeUtc: {0}\n", lastModifiedTimeUtc)); // Download the file if it was modified since the previous download. if (DateTime.Compare(previousSyncTimeUtc, lastModifiedTimeUtc) < 0) { DownloadFile(fileUrl, LocalFile); } } // Catch authentication exceptions catch (OAuthTokenRequestException ex) { OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description)); } // Catch Campaign Management service exceptions catch (FaultException<Microsoft.BingAds.V10.CampaignManagement.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.V10.CampaignManagement.ApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.V10.CampaignManagement.EditorialApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (Exception ex) { OutputStatusMessage(ex.Message); } finally { if (fileStream != null) fileStream.Close(); if (responseStream != null) responseStream.Close(); } }
private async Task<long> AddImageAsync(AuthorizationData authorizationData) { var media = new List<Media>(); var image = new Image(); // This example uses an image with 1.5:1 aspect ratio. // For more information about available aspect ratios and min / max dimensions, // see the Image data object reference documentation on MSDN. image.Data = GetImage15x10Data(); image.Type = "Image15x10"; image.MediaType = "Image"; media.Add(image); var request = new AddMediaRequest { Media = media }; var Service = new ServiceClient<ICampaignManagementService>(authorizationData); return (await Service.CallAsync((s, r) => s.AddMediaAsync(r), request)).MediaIds[0]; }
public async override Task RunAsync(AuthorizationData authorizationData) { try { Service = new ServiceClient<ICampaignManagementService>(authorizationData); // Specify one or more campaigns. var campaigns = new[]{ new Campaign { Name = "Women's Shoes" + DateTime.UtcNow, Description = "Red shoes line.", BudgetType = BudgetLimitType.MonthlyBudgetSpendUntilDepleted, MonthlyBudget = 1000.00, TimeZone = "PacificTimeUSCanadaTijuana", DaylightSaving = true, } }; // Specify one or more ad groups. var adGroups = new[] { new AdGroup { Name = "Women's Red Shoe Sale", AdDistribution = AdDistribution.Search, BiddingModel = BiddingModel.Keyword, PricingModel = PricingModel.Cpc, StartDate = null, EndDate = new Date { Month = 12, Day = 31, Year = 2015 }, SearchBid = new Bid { Amount = 0.09 }, Language = "English", } }; // Add the campaign and ad group AddCampaignsResponse addCampaignsResponse = await AddCampaignsAsync(authorizationData.AccountId, campaigns); long?[] campaignIds = addCampaignsResponse.CampaignIds.ToArray(); BatchError[] campaignErrors = addCampaignsResponse.PartialErrors.ToArray(); long campaignId = (long)campaignIds[0]; AddAdGroupsResponse addAdGroupsResponse = await AddAdGroupsAsync((long)campaignId, adGroups); long?[] adGroupIds = addAdGroupsResponse.AdGroupIds.ToArray(); BatchError[] adGroupErrors = addAdGroupsResponse.PartialErrors.ToArray(); long adGroupId = (long)adGroupIds[0]; // Print the new assigned campaign and ad group identifiers OutputCampaignsWithPartialErrors(campaigns, campaignIds, campaignErrors); OutputAdGroupsWithPartialErrors(adGroups, adGroupIds, adGroupErrors); // Create targets to associate with the campaign and ad group. var campaignTarget = new Target { Name = "My Campaign Target", DeviceOS = new DeviceOSTarget { Bids = new[] { new DeviceOSTargetBid { BidAdjustment = 10, DeviceName = "Tablets", }, }, }, DayTime = new DayTimeTarget { Bids = new[] { new DayTimeTargetBid { BidAdjustment = 10, Day = Day.Monday, FromHour = 1, ToHour = 12, FromMinute = Minute.Zero, ToMinute = Minute.FortyFive } } }, Location = new LocationTarget { IntentOption = IntentOption.PeopleIn, MetroAreaTarget = new MetroAreaTarget { Bids = new List<MetroAreaTargetBid> { new MetroAreaTargetBid { BidAdjustment = 15, MetroArea = "Seattle-Tacoma, WA, WA US", IsExcluded = false } } }, RadiusTarget = new RadiusTarget { Bids = new[] { new RadiusTargetBid { BidAdjustment = 50, LatitudeDegrees = 47.755367, LongitudeDegrees = -122.091827, Radius = 5, RadiusUnit = DistanceUnit.Miles } } } } }; var adGroupTarget = new Target { Name = "My Ad Group Target", DayTime = new DayTimeTarget { Bids = new[] { new DayTimeTargetBid { BidAdjustment = 10, Day = Day.Friday, FromHour = 1, ToHour = 12, FromMinute = Minute.Zero, ToMinute = Minute.FortyFive } } }, }; // Each customer has a target library that can be used to set up targeting for any campaign // or ad group within the specified customer. // Add a target to the library and associate it with the campaign. var campaignTargetId = (await AddTargetsToLibraryAsync(new[] { campaignTarget }))[0]; OutputStatusMessage(String.Format("Added Target Id: {0}\n", campaignTargetId)); SetTargetToCampaignAsync(campaignId, campaignTargetId); OutputStatusMessage(String.Format("Associated CampaignId {0} with TargetId {1}.\n", campaignId, campaignTargetId)); // Add a target to the library and associate it with the ad group. var adGroupTargetId = (await AddTargetsToLibraryAsync(new[] { adGroupTarget }))[0]; OutputStatusMessage(String.Format("Added Target Id: {0}\n", adGroupTargetId)); SetTargetToAdGroupAsync(adGroupId, adGroupTargetId); OutputStatusMessage(String.Format("Associated AdGroupId {0} with TargetId {1}.\n", adGroupId, adGroupTargetId)); // Get and print the targets with the GetTargetsByIds operation OutputStatusMessage("Get Campaign and AdGroup targets: \n"); var targets = await GetTargetsByIdsAsync(new[] { campaignTargetId, adGroupTargetId }); foreach (var target in targets) { OutputTarget(target); } // Update the ad group's Target object with additional target types. // Existing target types such as DayTime must be specified // or they will not be included in the updated target. var updateAdGroupTarget = new Target { Id = adGroupTargetId, Name = "My Target", Age = new AgeTarget { Bids = new[] { new AgeTargetBid { BidAdjustment = 10, Age = AgeRange.EighteenToTwentyFive } } }, DayTime = new DayTimeTarget { Bids = new[] { new DayTimeTargetBid { BidAdjustment = 10, Day = Day.Friday, FromHour = 1, ToHour = 12, FromMinute = Minute.Zero, ToMinute = Minute.FortyFive } } }, DeviceOS = new DeviceOSTarget { Bids = new[] { new DeviceOSTargetBid { BidAdjustment = 20, DeviceName = "Tablets", }, }, }, Gender = new GenderTarget { Bids = new[] { new GenderTargetBid { BidAdjustment = 10, Gender = GenderType.Female } } }, Location = new LocationTarget { IntentOption = IntentOption.PeopleSearchingForOrViewingPages, CountryTarget = new CountryTarget { Bids = new[] { new CountryTargetBid { BidAdjustment = 10, CountryAndRegion = "US", IsExcluded = false } } }, MetroAreaTarget = new MetroAreaTarget { Bids = new List<MetroAreaTargetBid> { new MetroAreaTargetBid { BidAdjustment = 15, MetroArea = "Seattle-Tacoma, WA, WA US", IsExcluded = false } } }, PostalCodeTarget = new PostalCodeTarget { Bids = new[] { new PostalCodeTargetBid { // Bid adjustments are not allowed for location exclusions. // If IsExcluded is true, this element will be ignored. BidAdjustment = 10, PostalCode = "98052, WA US", IsExcluded = true } } }, RadiusTarget = new RadiusTarget { Bids = new[] { new RadiusTargetBid { BidAdjustment = 51, LatitudeDegrees = 47.755367, LongitudeDegrees = -122.091827, Radius = 11, RadiusUnit = DistanceUnit.Miles } } } } }; // Update the Target object associated with the ad group. UpdateTargetsInLibraryAsync(new[] { updateAdGroupTarget }); OutputStatusMessage("Updated the ad group level target as a Target object.\n"); // Get and print the targets with the GetTargetsByIds operation OutputStatusMessage("Get Campaign and AdGroup targets: \n"); targets = await GetTargetsByIdsAsync(new[] { campaignTargetId, adGroupTargetId }); foreach (var target in targets) { OutputTarget(target); } // Get all new and existing targets in the customer library, whether or not they are // associated with campaigns or ad groups. var allTargetsInfo = await GetTargetsInfoFromLibraryAsync(); OutputStatusMessage("All target identifiers and names from the customer library: \n"); PrintTargetsInfo(allTargetsInfo); // Delete the campaign, ad group, and targets that were previously added. // DeleteCampaigns would remove the campaign and ad group, as well as the association // between ad groups and campaigns. To explicitly delete the association between an entity // and the target, use DeleteTargetFromCampaign and DeleteTargetFromAdGroup respectively. DeleteTargetFromCampaignAsync(campaignId); DeleteTargetFromAdGroupAsync(adGroupId); DeleteCampaignsAsync(authorizationData.AccountId, new[] { campaignId }); OutputStatusMessage(String.Format("Deleted CampaignId {0}\n", campaignId)); // DeleteCampaigns deletes the association between the campaign and target, but does not // delete the target from the customer library. // Call the DeleteTargetsFromLibrary operation for each target that you want to delete. // You must specify an array with exactly one item. DeleteTargetsFromLibraryAsync(new[] { campaignTargetId }); OutputStatusMessage(String.Format("Deleted TargetId {0}\n", campaignTargetId)); DeleteTargetsFromLibraryAsync(new[] { adGroupTargetId }); OutputStatusMessage(String.Format("Deleted TargetId {0}\n", adGroupTargetId)); } // Catch authentication exceptions catch (OAuthTokenRequestException ex) { OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description)); } // Catch Campaign Management service exceptions catch (FaultException<Microsoft.BingAds.V10.CampaignManagement.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.V10.CampaignManagement.ApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.V10.CampaignManagement.EditorialApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (Exception ex) { OutputStatusMessage(ex.Message); } }
public async override Task RunAsync(AuthorizationData authorizationData) { try { Service = new ServiceClient<ICampaignManagementService>(authorizationData); // Get a list of all Bing Merchant Center stores associated with your CustomerId IList<BMCStore> stores = await GetBMCStoresByCustomerIdAsync(); if (stores == null) { OutputStatusMessage( String.Format("You do not have any BMC stores registered for CustomerId {0}.\n", authorizationData.CustomerId) ); return; } #region ManageCampaign /* Add a new Bing Shopping campaign that will be associated with a ProductScope criterion. * - Set the CampaignType element of the Campaign to Shopping. * - Create a ShoppingSetting instance and set its Priority (0, 1, or 2), SalesCountryCode, and StoreId elements. * Add this shopping setting to the Settings list of the Campaign. */ var campaign = new Campaign { CampaignType = CampaignType.Shopping, Settings = new[] { new ShoppingSetting() { Priority = 0, SalesCountryCode = "US", StoreId = (int)stores[0].Id } }, Name = "Bing Shopping Campaign " + DateTime.UtcNow, Description = "Bing Shopping Campaign Example.", BudgetType = BudgetLimitType.MonthlyBudgetSpendUntilDepleted, MonthlyBudget = 1000.00, TimeZone = "PacificTimeUSCanadaTijuana", DaylightSaving = true, }; var campaignIds = await AddCampaignsAsync(authorizationData.AccountId, new[] { campaign }); OutputCampaignIdentifiers(campaignIds); /* Optionally, you can create a ProductScope criterion that will be associated with your Bing Shopping campaign. * Use the product scope criterion to include a subset of your product catalog, for example a specific brand, * category, or product type. A campaign can only be associated with one ProductScope, which contains a list * of up to 7 ProductCondition. You'll also be able to specify more specific product conditions for each ad group. */ var campaignCriterions = new CampaignCriterion[] { new CampaignCriterion() { CampaignId = campaignIds[0], BidAdjustment = null, // Reserved for future use Criterion = new ProductScope() { Conditions = new ProductCondition[] { new ProductCondition { Operand = "Condition", Attribute = "New" }, new ProductCondition { Operand = "CustomLabel0", Attribute = "MerchantDefinedCustomLabel" }, } }, } }; var addCampaignCriterionsResponse = await (AddCampaignCriterionsAsync( campaignCriterions, CampaignCriterionType.ProductScope) ); #endregion ManageCampaign #region ManageAdGroup // Specify one or more ad groups. var adGroup = new AdGroup { Name = "Product Categories", AdDistribution = AdDistribution.Search, BiddingModel = BiddingModel.Keyword, PricingModel = PricingModel.Cpc, StartDate = null, EndDate = new Date { Month = 12, Day = 31, Year = 2016 }, Language = "English" }; var adGroupIds = (long[])await AddAdGroupsAsync(campaignIds[0], new[] { adGroup }); OutputAdGroupIdentifiers(adGroupIds); #region BidAllProducts var helper = new PartitionActionHelper(adGroupIds[0]); var root = helper.AddUnit( null, new ProductCondition { Operand = "All", Attribute = null }, 0.35, false ); OutputStatusMessage("Applying only the root as a Unit with a bid . . . \n"); var applyProductPartitionActionsResponse = await ApplyProductPartitionActionsAsync(helper.PartitionActions); var adGroupCriterions = await GetAdGroupCriterionsByAdGroupIdAsync( adGroupIds[0], CriterionType.ProductPartition ); OutputStatusMessage("The ad group's product partition only has a tree root node: \n"); OutputProductPartitions(adGroupCriterions); /* * Let's update the bid of the root Unit we just added. */ BiddableAdGroupCriterion updatedRoot = new BiddableAdGroupCriterion { Id = applyProductPartitionActionsResponse.AdGroupCriterionIds[0], CriterionBid = new FixedBid { Bid = new Bid { Amount = 0.45 } } }; helper = new PartitionActionHelper(adGroupIds[0]); helper.UpdatePartition(updatedRoot); OutputStatusMessage("Updating the bid for the tree root node . . . \n"); await ApplyProductPartitionActionsAsync(helper.PartitionActions); adGroupCriterions = await GetAdGroupCriterionsByAdGroupIdAsync( adGroupIds[0], CriterionType.ProductPartition ); OutputStatusMessage("Updated the bid for the tree root node: \n"); OutputProductPartitions(adGroupCriterions); #endregion BidAllProducts #region InitializeTree /* * Now we will overwrite any existing tree root, and build a product partition group tree structure in multiple steps. * You could build the entire tree in a single call since there are less than 5,000 nodes; however, * we will build it in steps to demonstrate how to use the results from ApplyProductPartitionActions to update the tree. * * For a list of validation rules, see the Bing Shopping Campaigns technical guide: * https://msdn.microsoft.com/en-US/library/bing-ads-campaign-management-bing-shopping-campaigns.aspx */ helper = new PartitionActionHelper(adGroupIds[0]); /* * Check whether a root node exists already. */ adGroupCriterions = await GetAdGroupCriterionsByAdGroupIdAsync( adGroupIds[0], CriterionType.ProductPartition ); var existingRoot = GetRootNode(adGroupCriterions); if (existingRoot != null) { helper.DeletePartition(existingRoot); } root = helper.AddSubdivision( null, new ProductCondition { Operand = "All", Attribute = null } ); /* * The direct children of any node must have the same Operand. * For this example we will use CategoryL1 nodes as children of the root. * For a list of valid CategoryL1 through CategoryL5 values, see the Bing Category Taxonomy: * http://advertise.bingads.microsoft.com/en-us/WWDocs/user/search/en-us/Bing_Category_Taxonomy.txt */ var animalsSubdivision = helper.AddSubdivision( root, new ProductCondition { Operand = "CategoryL1", Attribute = "Animals & Pet Supplies" } ); /* * If you use a CategoryL2 node, it must be a descendant (child or later) of a CategoryL1 node. * In other words you cannot have a CategoryL2 node as parent of a CategoryL1 node. * For this example we will a CategoryL2 node as child of the CategoryL1 Animals & Pet Supplies node. */ var petSuppliesSubdivision = helper.AddSubdivision( animalsSubdivision, new ProductCondition { Operand = "CategoryL2", Attribute = "Pet Supplies" } ); var brandA = helper.AddUnit( petSuppliesSubdivision, new ProductCondition { Operand = "Brand", Attribute = "Brand A" }, 0.35, false ); /* * If you won't bid on Brand B, set the helper method's bidAmount to '0' and isNegative to true. * The helper method will create a NegativeAdGroupCriterion and apply the condition. */ var brandB = helper.AddUnit( petSuppliesSubdivision, new ProductCondition { Operand = "Brand", Attribute = "Brand B" }, 0, true ); var otherBrands = helper.AddUnit( petSuppliesSubdivision, new ProductCondition { Operand = "Brand", Attribute = null }, 0.35, false ); var otherPetSupplies = helper.AddUnit( animalsSubdivision, new ProductCondition { Operand = "CategoryL2", Attribute = null }, 0.35, false ); var electronics = helper.AddUnit( root, new ProductCondition { Operand = "CategoryL1", Attribute = "Electronics" }, 0.35, false ); var otherCategoryL1 = helper.AddUnit( root, new ProductCondition { Operand = "CategoryL1", Attribute = null }, 0.35, false ); OutputStatusMessage("Applying product partitions to the ad group . . . \n"); applyProductPartitionActionsResponse = await ApplyProductPartitionActionsAsync(helper.PartitionActions); // To retrieve product partitions after they have been applied, call GetAdGroupCriterionsByAdGroupId. // The product partition with ParentCriterionId set to null is the root node. adGroupCriterions = await GetAdGroupCriterionsByAdGroupIdAsync( adGroupIds[0], CriterionType.ProductPartition ); /* * The product partition group tree now has 9 nodes. All other (Root Node) | +-- Animals & Pet Supplies (CategoryL1) | | | +-- Pet Supplies (CategoryL2) | | | | | +-- Brand A | | | | | +-- Brand B | | | | | +-- All other (Brand) | | | +-- All other (CategoryL2) | +-- Electronics (CategoryL1) | +-- All other (CategoryL1) */ OutputStatusMessage("The product partition group tree now has 9 nodes: \n"); OutputProductPartitions(adGroupCriterions); #endregion InitializeTree #region UpdateTree /* * Let's replace the Electronics (CategoryL1) node created above with an Electronics (CategoryL1) node that * has children i.e. Brand C (Brand), Brand D (Brand), and All other (Brand) as follows: Electronics (CategoryL1) | +-- Brand C (Brand) | +-- Brand D (Brand) | +-- All other (Brand) */ helper = new PartitionActionHelper(adGroupIds[0]); /* * To replace a node we must know its Id and its ParentCriterionId. In this case the parent of the node * we are replacing is All other (Root Node), and was created at Index 1 of the previous ApplyProductPartitionActions call. * The node that we are replacing is Electronics (CategoryL1), and was created at Index 8. */ var rootId = applyProductPartitionActionsResponse.AdGroupCriterionIds[1]; electronics.Id = applyProductPartitionActionsResponse.AdGroupCriterionIds[8]; helper.DeletePartition(electronics); var parent = new BiddableAdGroupCriterion() { Id = rootId }; var electronicsSubdivision = helper.AddSubdivision( parent, new ProductCondition { Operand = "CategoryL1", Attribute = "Electronics" } ); var brandC = helper.AddUnit( electronicsSubdivision, new ProductCondition { Operand = "Brand", Attribute = "Brand C" }, 0.35, false ); var brandD = helper.AddUnit( electronicsSubdivision, new ProductCondition { Operand = "Brand", Attribute = "Brand D" }, 0.35, false ); var otherElectronicsBrands = helper.AddUnit( electronicsSubdivision, new ProductCondition { Operand = "Brand", Attribute = null }, 0.35, false ); OutputStatusMessage( "Updating the product partition group to refine Electronics (CategoryL1) with 3 child nodes . . . \n" ); applyProductPartitionActionsResponse = await ApplyProductPartitionActionsAsync(helper.PartitionActions); adGroupCriterions = await GetAdGroupCriterionsByAdGroupIdAsync( adGroupIds[0], CriterionType.ProductPartition ); /* * The product partition group tree now has 12 nodes, including the children of Electronics (CategoryL1): All other (Root Node) | +-- Animals & Pet Supplies (CategoryL1) | | | +-- Pet Supplies (CategoryL2) | | | | | +-- Brand A | | | | | +-- Brand B | | | | | +-- All other (Brand) | | | +-- All other (CategoryL2) | +-- Electronics (CategoryL1) | | | +-- Brand C (Brand) | | | +-- Brand D (Brand) | | | +-- All other (Brand) | +-- All other (CategoryL1) */ OutputStatusMessage( "The product partition group tree now has 12 nodes, including the children of Electronics (CategoryL1): \n" ); OutputProductPartitions(adGroupCriterions); #endregion UpdateTree #endregion ManageAdGroup #region ManageAds /* * Create a product ad. You must add at least one ProductAd to the corresponding ad group. * A ProductAd is not used directly for delivered ad copy. Instead, the delivery engine generates * product ads from the product details that it finds in your Bing Merchant Center store's product catalog. * The primary purpose of the ProductAd object is to provide promotional text that the delivery engine * adds to the product ads that it generates. For example, if the promotional text is set to * “Free shipping on $99 purchases”, the delivery engine will set the product ad’s description to * “Free shipping on $99 purchases.” */ var ads = new Ad[] { new ProductAd { PromotionalText = "Free shipping on $99 purchases." }, }; AddAdsResponse addAdsResponse = await AddAdsAsync(adGroupIds[0], ads); OutputAdResults(ads, addAdsResponse.AdIds, addAdsResponse.PartialErrors); #endregion ManageAds #region CleanUp /* Delete the campaign, ad group, criterion, and ad that were previously added. * You should remove this region if you want to view the added entities in the * Bing Ads web application or another tool. */ DeleteCampaignsAsync(authorizationData.AccountId, new[] { campaignIds[0] }); OutputStatusMessage(String.Format("Deleted CampaignId {0}\n", campaignIds[0])); #endregion CleanUp } // Catch authentication exceptions catch (OAuthTokenRequestException ex) { OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description)); } // Catch Campaign Management service exceptions catch (FaultException<Microsoft.BingAds.CampaignManagement.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.CampaignManagement.ApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.CampaignManagement.EditorialApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (Exception ex) { OutputStatusMessage(ex.Message); } }
public async override Task RunAsync(AuthorizationData authorizationData) { try { Service = new ServiceClient<ICampaignManagementService>(authorizationData); // Specify one or more campaigns. var campaign = new Campaign { Name = "Women's Shoes" + DateTime.UtcNow, Description = "Red shoes line.", BudgetType = BudgetLimitType.MonthlyBudgetSpendUntilDepleted, MonthlyBudget = 1000.00, TimeZone = "PacificTimeUSCanadaTijuana", DaylightSaving = true }; // Specify one or more ad groups. var adGroup = new AdGroup { Name = "Women's Red Shoe Sale", AdDistribution = AdDistribution.Search, BiddingModel = BiddingModel.Keyword, PricingModel = PricingModel.Cpc, StartDate = null, EndDate = new Date { Month = 12, Day = 31, Year = 2015 }, ExactMatchBid = new Bid { Amount = 0.09 }, PhraseMatchBid = new Bid { Amount = 0.07 }, Language = "English" }; // In this example only the second keyword should succeed. The Text of the first keyword exceeds the limit, // and the third keyword is a duplicate of the second keyword. var keywords = new[] { new Keyword { Bid = new Bid { Amount = 0.47 }, Param2 = "10% Off", MatchType = MatchType.Broad, Text = "Brand-A Shoes Brand-A Shoes Brand-A Shoes Brand-A Shoes Brand-A Shoes " + "Brand-A Shoes Brand-A Shoes Brand-A Shoes Brand-A Shoes Brand-A Shoes " + "Brand-A Shoes Brand-A Shoes Brand-A Shoes Brand-A Shoes Brand-A Shoes" }, new Keyword { Bid = new Bid { Amount = 0.47 }, Param2 = "10% Off", MatchType = MatchType.Phrase, Text = "Brand-A Shoes" }, new Keyword { Bid = new Bid { Amount = 0.47 }, Param2 = "10% Off", MatchType = MatchType.Phrase, Text = "Brand-A Shoes" } }; // In this example only the second ad should succeed. The Title of the first ad is empty and not valid, // and the third ad is a duplicate of the second ad. var ads = new Ad[] { new TextAd { DestinationUrl = "http://www.contoso.com/womenshoesale", DisplayUrl = "Contoso.com", Text = "Huge Savings on red shoes.", Title = "" }, new TextAd { DestinationUrl = "http://www.contoso.com/womenshoesale", DisplayUrl = "Contoso.com", Text = "Huge Savings on red shoes.", Title = "Women's Shoe Sale" }, new TextAd { DestinationUrl = "http://www.contoso.com/womenshoesale", DisplayUrl = "Contoso.com", Text = "Huge Savings on red shoes.", Title = "Women's Shoe Sale" } }; // Add the campaign, ad group, keywords, and ads var campaignIds = (long[]) await AddCampaignsAsync(authorizationData.AccountId, new[] { campaign }); var adGroupIds = (long[])await AddAdGroupsAsync(campaignIds[0], new[] { adGroup }); AddKeywordsResponse addKeywordsResponse = await AddKeywordsAsync(adGroupIds[0], keywords); long?[] keywordIds = addKeywordsResponse.KeywordIds.ToArray(); BatchError[] keywordErrors = addKeywordsResponse.PartialErrors.ToArray(); AddAdsResponse addAdsResponse = await AddAdsAsync(adGroupIds[0], ads); long?[] adIds = addAdsResponse.AdIds.ToArray(); BatchError[] adErrors = addAdsResponse.PartialErrors.ToArray(); // Print the new assigned campaign and ad group identifiers PrintCampaignIdentifiers(campaignIds); PrintAdGroupIdentifiers(adGroupIds); // Print the new assigned keyword and ad identifiers, as well as any partial errors PrintKeywordResults(keywords, keywordIds, keywordErrors); PrintAdResults(ads, adIds, adErrors); // Delete the campaign, ad group, keyword, and ad that were previously added. // You should remove this line if you want to view the added entities in the // Bing Ads web application or another tool. DeleteCampaignsAsync(authorizationData.AccountId, new[] { campaignIds[0] }); OutputStatusMessage(String.Format("Deleted CampaignId {0}\n", campaignIds[0])); } // Catch authentication exceptions catch (OAuthTokenRequestException ex) { OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description)); } // Catch Campaign Management service exceptions catch (FaultException<Microsoft.BingAds.CampaignManagement.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.CampaignManagement.ApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.CampaignManagement.EditorialApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (Exception ex) { OutputStatusMessage(ex.Message); } }