public async override Task RunAsync(AuthorizationData authorizationData) { try { AdInsightExampleHelper AdInsightExampleHelper = new AdInsightExampleHelper(this.OutputStatusMessage); AdInsightExampleHelper.AdInsightService = new ServiceClient <IAdInsightService>(authorizationData); CampaignManagementExampleHelper CampaignManagementExampleHelper = new CampaignManagementExampleHelper(this.OutputStatusMessage); CampaignManagementExampleHelper.CampaignManagementService = new ServiceClient <ICampaignManagementService>(authorizationData); var campaigns = (await CampaignManagementExampleHelper.GetCampaignsByAccountIdAsync( authorizationData.AccountId, AllCampaignTypes, false)).Campaigns; 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 AdInsightExampleHelper.GetBudgetOpportunitiesAsync((long)campaign.Id)).Opportunities; AdInsightExampleHelper.OutputArrayOfBudgetOpportunity(opportunities); } } } // 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.V11.AdInsight.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V11.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.V11.CampaignManagement.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V11.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); } }
private async Task <List <LabelAssociation> > GetLabelAssociationsByLabelIdsHelperAsync( CampaignManagementExampleHelper CampaignManagementExampleHelper, EntityType entityType, IList <long> labelIds ) { var labelAssociations = new List <LabelAssociation>(); var labelIdsPageIndex = 0; while (labelIdsPageIndex * MaxLabelIdsForGetLabelAssociations < labelIds.Count) { var getLabelIds = labelIds.Skip(labelIdsPageIndex++ *MaxLabelIdsForGetLabelAssociations).Take(MaxLabelIdsForGetLabelAssociations).ToList(); var labelAssociationsPageIndex = 0; var foundLastPage = false; while (!foundLastPage) { var getLabelAssociationsByLabelIds = await CampaignManagementExampleHelper.GetLabelAssociationsByLabelIdsAsync( entityType : entityType, labelIds : getLabelIds, pageInfo : new Paging { Index = labelAssociationsPageIndex++, Size = MaxPagingSize } ).ConfigureAwait(continueOnCapturedContext: false); labelAssociations.AddRange(getLabelAssociationsByLabelIds.LabelAssociations); foundLastPage = MaxPagingSize > getLabelAssociationsByLabelIds.LabelAssociations.Count; } } return(labelAssociations); }
private async Task <List <LabelAssociation> > GetLabelAssociationsByEntityIdsHelperAsync( CampaignManagementExampleHelper CampaignManagementExampleHelper, EntityType entityType, IList <long> entityIds ) { var labelAssociations = new List <LabelAssociation>(); var entityIdsPageIndex = 0; while (entityIdsPageIndex * MaxEntityIdsForGetLabelAssociations < entityIds.Count) { var getEntityIds = entityIds.Skip(entityIdsPageIndex++ *MaxEntityIdsForGetLabelAssociations).Take(MaxEntityIdsForGetLabelAssociations).ToList(); var getLabelAssociationsByEntityIds = await CampaignManagementExampleHelper.GetLabelAssociationsByEntityIdsAsync( entityIds : getEntityIds, entityType : entityType ).ConfigureAwait(continueOnCapturedContext: false); labelAssociations.AddRange(getLabelAssociationsByEntityIds.LabelAssociations); } return(labelAssociations); }
public async override Task RunAsync(AuthorizationData authorizationData) { try { ApiEnvironment environment = ((OAuthDesktopMobileAuthCodeGrant)authorizationData.Authentication).Environment; CampaignManagementExampleHelper CampaignManagementExampleHelper = new CampaignManagementExampleHelper(this.OutputStatusMessage); CampaignManagementExampleHelper.CampaignManagementService = new ServiceClient <ICampaignManagementService>(authorizationData, environment); // 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.", // 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", } }; AddCampaignsResponse addCampaignsResponse = await CampaignManagementExampleHelper.AddCampaignsAsync(authorizationData.AccountId, campaigns); long?[] campaignIds = addCampaignsResponse.CampaignIds.ToArray(); BatchError[] campaignErrors = addCampaignsResponse.PartialErrors.ToArray(); CampaignManagementExampleHelper.OutputArrayOfLong(campaignIds); CampaignManagementExampleHelper.OutputArrayOfBatchError(campaignErrors); 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 CampaignManagementExampleHelper.AddNegativeKeywordsToEntitiesAsync(entityNegativeKeywords); CampaignManagementExampleHelper.OutputArrayOfIdCollection(addNegativeKeywordsToEntitiesResponse.NegativeKeywordIds); CampaignManagementExampleHelper.OutputArrayOfBatchErrorCollection(addNegativeKeywordsToEntitiesResponse.NestedPartialErrors); if (addNegativeKeywordsToEntitiesResponse.NestedPartialErrors == null || addNegativeKeywordsToEntitiesResponse.NestedPartialErrors.Count == 0) { OutputStatusMessage("Added an exclusive set of negative keywords to the Campaign.\n"); CampaignManagementExampleHelper.OutputArrayOfIdCollection(addNegativeKeywordsToEntitiesResponse.NegativeKeywordIds); } else { CampaignManagementExampleHelper.OutputArrayOfBatchErrorCollection(addNegativeKeywordsToEntitiesResponse.NestedPartialErrors); } GetNegativeKeywordsByEntityIdsResponse getNegativeKeywordsByEntityIdsResponse = await CampaignManagementExampleHelper.GetNegativeKeywordsByEntityIdsAsync(new[] { campaignId }, "Campaign", authorizationData.AccountId); CampaignManagementExampleHelper.OutputArrayOfEntityNegativeKeyword(getNegativeKeywordsByEntityIdsResponse.EntityNegativeKeywords); CampaignManagementExampleHelper.OutputArrayOfBatchError(getNegativeKeywordsByEntityIdsResponse.PartialErrors); if (getNegativeKeywordsByEntityIdsResponse.PartialErrors == null || getNegativeKeywordsByEntityIdsResponse.PartialErrors.Count == 0) { OutputStatusMessage("Retrieved an exclusive set of negative keywords for the Campaign.\n"); CampaignManagementExampleHelper.OutputArrayOfEntityNegativeKeyword(getNegativeKeywordsByEntityIdsResponse.EntityNegativeKeywords); } else { CampaignManagementExampleHelper.OutputArrayOfBatchError(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 CampaignManagementExampleHelper.DeleteNegativeKeywordsFromEntitiesAsync(entityNegativeKeywords)).NestedPartialErrors; 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."); CampaignManagementExampleHelper.OutputArrayOfBatchErrorCollection(nestedPartialErrors); } // Delete the negative keywords with identifiers that were returned above. nestedPartialErrors = (BatchErrorCollection[])(await CampaignManagementExampleHelper.DeleteNegativeKeywordsFromEntitiesAsync( getNegativeKeywordsByEntityIdsResponse.EntityNegativeKeywords)).NestedPartialErrors; if (nestedPartialErrors == null || nestedPartialErrors.Length == 0) { OutputStatusMessage("Deleted an exclusive set of negative keywords from the Campaign.\n"); } else { CampaignManagementExampleHelper.OutputArrayOfBatchErrorCollection(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 CampaignManagementExampleHelper.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)); negativeKeywordList.Id = addSharedEntityResponse.SharedEntityId; CampaignManagementExampleHelper.OutputSharedEntity(negativeKeywordList); CampaignManagementExampleHelper.OutputArrayOfSharedListItem(negativeKeywords.ToList()); CampaignManagementExampleHelper.OutputArrayOfBatchError(addSharedEntityResponse.PartialErrors); OutputStatusMessage("Negative keywords currently in NegativeKeywordList:"); negativeKeywords = (SharedListItem[])(await CampaignManagementExampleHelper.GetListItemsBySharedListAsync(new NegativeKeywordList { Id = sharedEntityId })).ListItems; if (negativeKeywords == null || negativeKeywords.Length == 0) { OutputStatusMessage("None\n"); } else { CampaignManagementExampleHelper.OutputArrayOfNegativeKeyword(negativeKeywords.Cast <NegativeKeyword>().ToList()); } // 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 CampaignManagementExampleHelper.DeleteListItemsFromSharedListAsync(listItemIds, new NegativeKeywordList { Id = sharedEntityId }))?.PartialErrors; if (partialErrors == null || !partialErrors.Any()) { OutputStatusMessage("Deleted most recently added negative keywords from negative keyword list.\n"); } else { CampaignManagementExampleHelper.OutputArrayOfBatchError(partialErrors); } OutputStatusMessage("Negative keywords currently in NegativeKeywordList:"); negativeKeywords = (SharedListItem[])(await CampaignManagementExampleHelper.GetListItemsBySharedListAsync(new NegativeKeywordList { Id = sharedEntityId })).ListItems; if (negativeKeywords == null || negativeKeywords.Length == 0) { OutputStatusMessage("None\n"); } else { CampaignManagementExampleHelper.OutputArrayOfNegativeKeyword(negativeKeywords.Cast <NegativeKeyword>().ToList()); } // 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 CampaignManagementExampleHelper.AddListItemsToSharedListAsync( negativeKeywords, new NegativeKeywordList { Id = sharedEntityId }); listItemIds = addListItemsToSharedListResponse.ListItemIds.ToArray(); CampaignManagementExampleHelper.OutputArrayOfSharedListItem(negativeKeywords.ToList()); CampaignManagementExampleHelper.OutputArrayOfBatchError(addListItemsToSharedListResponse.PartialErrors); OutputStatusMessage("Negative keywords currently in NegativeKeywordList:"); negativeKeywords = (SharedListItem[])(await CampaignManagementExampleHelper.GetListItemsBySharedListAsync(new NegativeKeywordList { Id = sharedEntityId })).ListItems; if (negativeKeywords == null || negativeKeywords.Length == 0) { OutputStatusMessage("None\n"); } else { CampaignManagementExampleHelper.OutputArrayOfNegativeKeyword(negativeKeywords.Cast <NegativeKeyword>().ToList()); } // 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 CampaignManagementExampleHelper.UpdateSharedEntitiesAsync(new SharedEntity[] { negativeKeywordList })).PartialErrors; if (partialErrors == null || !partialErrors.Any()) { OutputStatusMessage(string.Format("Updated Negative Keyword List Name to {0}.\n", negativeKeywordList.Name)); } else { CampaignManagementExampleHelper.OutputArrayOfBatchError(partialErrors); } // Get and output the negative keyword lists and store the list of identifiers. const string sharedEntityType = "NegativeKeywordList"; var sharedEntities = (await CampaignManagementExampleHelper.GetSharedEntitiesByAccountIdAsync(sharedEntityType)).SharedEntities; CampaignManagementExampleHelper.OutputArrayOfSharedEntity(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 CampaignManagementExampleHelper.SetSharedEntityAssociationsAsync(associations)).PartialErrors; if (partialErrors == null || !partialErrors.Any()) { OutputStatusMessage(string.Format("Associated CampaignId {0} with Negative Keyword List Id {1}.\n", campaignId, sharedEntityId)); } else { CampaignManagementExampleHelper.OutputArrayOfBatchError(partialErrors); } // Get and output the associations either by Campaign or NegativeKeywordList identifier. GetSharedEntityAssociationsByEntityIdsResponse getSharedEntityAssociationsByEntityIdsResponse = await CampaignManagementExampleHelper.GetSharedEntityAssociationsByEntityIdsAsync(new[] { campaignId }, "Campaign", "NegativeKeywordList"); CampaignManagementExampleHelper.OutputArrayOfSharedEntityAssociation(getSharedEntityAssociationsByEntityIdsResponse.Associations); CampaignManagementExampleHelper.OutputArrayOfBatchError(getSharedEntityAssociationsByEntityIdsResponse.PartialErrors); // Currently the GetSharedEntityAssociationsBySharedEntityIds operation accepts only one shared entity identifier in the list. GetSharedEntityAssociationsBySharedEntityIdsResponse getSharedEntityAssociationsBySharedEntityIdsResponse = await CampaignManagementExampleHelper.GetSharedEntityAssociationsBySharedEntityIdsAsync("Campaign", new[] { sharedEntityIds[sharedEntityIds.Length - 1] }, "NegativeKeywordList"); CampaignManagementExampleHelper.OutputArrayOfSharedEntityAssociation(getSharedEntityAssociationsBySharedEntityIdsResponse.Associations); CampaignManagementExampleHelper.OutputArrayOfBatchError(getSharedEntityAssociationsBySharedEntityIdsResponse.PartialErrors); // Explicitly delete the association between the campaign and the negative keyword list. partialErrors = (await CampaignManagementExampleHelper.DeleteSharedEntityAssociationsAsync(associations)).PartialErrors; if (partialErrors == null || !partialErrors.Any()) { OutputStatusMessage("Deleted NegativeKeywordList associations\n"); } else { CampaignManagementExampleHelper.OutputArrayOfBatchError(partialErrors); } // Delete the campaign and any remaining assocations. await CampaignManagementExampleHelper.DeleteCampaignsAsync(authorizationData.AccountId, new[] { campaignId }); OutputStatusMessage(string.Format("Deleted Campaign Id {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 CampaignManagementExampleHelper.DeleteSharedEntitiesAsync(new SharedEntity[] { new NegativeKeywordList { Id = sharedEntityId } }))?.PartialErrors; if (partialErrors == null || !partialErrors.Any()) { OutputStatusMessage(string.Format("Deleted Negative Keyword List (SharedEntity) Id {0}\n", sharedEntityId)); } else { CampaignManagementExampleHelper.OutputArrayOfBatchError(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.V12.CampaignManagement.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V12.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.V12.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 { ApiEnvironment environment = ((OAuthDesktopMobileAuthCodeGrant)authorizationData.Authentication).Environment; CampaignManagementExampleHelper CampaignManagementExampleHelper = new CampaignManagementExampleHelper( OutputStatusMessageDefault: this.OutputStatusMessage); CampaignManagementExampleHelper.CampaignManagementService = new ServiceClient <ICampaignManagementService>( authorizationData: authorizationData, environment: environment); // Get a list of all Bing Merchant Center stores associated with your CustomerId. OutputStatusMessage("-----\nGetBMCStoresByCustomerId:"); IList <BMCStore> stores = (await CampaignManagementExampleHelper.GetBMCStoresByCustomerIdAsync())?.BMCStores; if (stores == null || stores.Count <= 0) { OutputStatusMessage( string.Format("You do not have any BMC stores registered for CustomerId {0}.", authorizationData.CustomerId) ); return; } OutputStatusMessage("BMCStores:"); CampaignManagementExampleHelper.OutputArrayOfBMCStore(stores); // Create a Shopping campaign with product conditions. var campaigns = new[] { new Campaign { CampaignType = CampaignType.Shopping, Languages = new string[] { "All" }, Name = "Women's Shoes " + DateTime.UtcNow, Description = "Red shoes line.", DailyBudget = 50, BudgetType = BudgetLimitType.DailyBudgetStandard, Settings = new[] { new ShoppingSetting() { Priority = 0, SalesCountryCode = "US", StoreId = (int)stores[0].Id } }, TimeZone = "PacificTimeUSCanadaTijuana", } }; OutputStatusMessage("-----\nAddCampaigns:"); AddCampaignsResponse addCampaignsResponse = await CampaignManagementExampleHelper.AddCampaignsAsync( accountId : authorizationData.AccountId, campaigns : campaigns, includeDynamicSearchAdsSource : false); long?[] campaignIds = addCampaignsResponse.CampaignIds.ToArray(); BatchError[] campaignErrors = addCampaignsResponse.PartialErrors.ToArray(); OutputStatusMessage("CampaignIds:"); CampaignManagementExampleHelper.OutputArrayOfLong(campaignIds); OutputStatusMessage("PartialErrors:"); CampaignManagementExampleHelper.OutputArrayOfBatchError(campaignErrors); long campaignId = (long)campaignIds[0]; // Optionally, you can create a ProductScope criterion that will be associated with your Bing Shopping campaign. // You'll also be able to add more specific product conditions for each ad group. var campaignCriterions = new BiddableCampaignCriterion[] { new BiddableCampaignCriterion() { CampaignId = campaignId, CriterionBid = null, // Not applicable for product scope Criterion = new ProductScope() { Conditions = new ProductCondition[] { new ProductCondition { Operand = "Condition", Attribute = "New" }, new ProductCondition { Operand = "CustomLabel0", Attribute = "MerchantDefinedCustomLabel" }, } }, Status = CampaignCriterionStatus.Active } }; OutputStatusMessage("-----\nAddCampaignCriterions:"); var addCampaignCriterionsResponse = await CampaignManagementExampleHelper.AddCampaignCriterionsAsync( campaignCriterions : campaignCriterions, criterionType : CampaignCriterionType.ProductScope); long?[] campaignCriterionIds = addCampaignCriterionsResponse.CampaignCriterionIds.ToArray(); OutputStatusMessage("CampaignCriterionIds:"); CampaignManagementExampleHelper.OutputArrayOfLong(campaignCriterionIds); BatchErrorCollection[] campaignCriterionErrors = addCampaignCriterionsResponse.NestedPartialErrors.ToArray(); OutputStatusMessage("NestedPartialErrors:"); CampaignManagementExampleHelper.OutputArrayOfBatchErrorCollection(campaignCriterionErrors); // Create the ad group that will have the product partitions. var adGroups = new[] { new AdGroup { Name = "Women's Red Shoe Sale", StartDate = null, EndDate = new Date { Month = 12, Day = 31, Year = DateTime.UtcNow.Year + 1 }, CpcBid = new Bid { Amount = 0.09 }, } }; OutputStatusMessage("-----\nAddAdGroups:"); AddAdGroupsResponse addAdGroupsResponse = await CampaignManagementExampleHelper.AddAdGroupsAsync( campaignId : (long)campaignIds[0], adGroups : adGroups, returnInheritedBidStrategyTypes : false); long?[] adGroupIds = addAdGroupsResponse.AdGroupIds.ToArray(); BatchError[] adGroupErrors = addAdGroupsResponse.PartialErrors.ToArray(); OutputStatusMessage("AdGroupIds:"); CampaignManagementExampleHelper.OutputArrayOfLong(adGroupIds); OutputStatusMessage("PartialErrors:"); CampaignManagementExampleHelper.OutputArrayOfBatchError(adGroupErrors); long adGroupId = (long)adGroupIds[0]; // Bid all products var helper = new PartitionActionHelper(adGroupId); var root = helper.AddUnit( parent: null, condition: new ProductCondition { Operand = "All", Attribute = null }, bidAmount: 0.35, isNegative: false); OutputStatusMessage("-----\nApplyProductPartitionActions:"); OutputStatusMessage("Applying only the root as a Unit with a bid..."); var applyProductPartitionActionsResponse = await CampaignManagementExampleHelper.ApplyProductPartitionActionsAsync( criterionActions : helper.PartitionActions); OutputStatusMessage("-----\nGetAdGroupCriterionsByIds:"); var adGroupCriterions = await CampaignManagementExampleHelper.GetAdGroupCriterionsByIdsAsync( adGroupCriterionIds : null, adGroupId : adGroupId, criterionType : AdGroupCriterionType.ProductPartition, returnAdditionalFields : AdGroupCriterionAdditionalField.FinalUrlSuffix); OutputStatusMessage("The ad group's product partition only has a tree root node: \n"); OutputProductPartitions(adGroupCriterions?.AdGroupCriterions); // Let's update the bid of the root Unit we just added. BiddableAdGroupCriterion updatedRoot = new BiddableAdGroupCriterion { Id = applyProductPartitionActionsResponse.AdGroupCriterionIds[0], CriterionBid = new FixedBid { Amount = 0.45 } }; helper = new PartitionActionHelper(adGroupId); helper.UpdatePartition(updatedRoot); OutputStatusMessage("-----\nApplyProductPartitionActions:"); OutputStatusMessage("Updating the bid for the tree root node..."); await CampaignManagementExampleHelper.ApplyProductPartitionActionsAsync( criterionActions : helper.PartitionActions); OutputStatusMessage("-----\nGetAdGroupCriterionsByIds:"); adGroupCriterions = await CampaignManagementExampleHelper.GetAdGroupCriterionsByIdsAsync( adGroupCriterionIds : null, adGroupId : adGroupId, criterionType : AdGroupCriterionType.ProductPartition, returnAdditionalFields : AdGroupCriterionAdditionalField.FinalUrlSuffix); OutputStatusMessage("Updated the bid for the tree root node: \n"); OutputProductPartitions(adGroupCriterions?.AdGroupCriterions); // Initialize and 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. helper = new PartitionActionHelper(adGroupId); // Check whether a root node exists already. OutputStatusMessage("-----\nGetAdGroupCriterionsByIds:"); adGroupCriterions = await CampaignManagementExampleHelper.GetAdGroupCriterionsByIdsAsync( adGroupCriterionIds : null, adGroupId : adGroupId, criterionType : AdGroupCriterionType.ProductPartition, returnAdditionalFields : AdGroupCriterionAdditionalField.FinalUrlSuffix); var existingRoot = GetRootNode(adGroupCriterions?.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://go.microsoft.com/fwlink?LinkId=507666 var animalsSubdivision = helper.AddSubdivision( parent: root, condition: 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( parent: animalsSubdivision, condition: new ProductCondition { Operand = "CategoryL2", Attribute = "Pet Supplies" }); var brandA = helper.AddUnit( parent: petSuppliesSubdivision, condition: new ProductCondition { Operand = "Brand", Attribute = "Brand A" }, bidAmount: 0.35, isNegative: 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( parent: petSuppliesSubdivision, condition: new ProductCondition { Operand = "Brand", Attribute = "Brand B" }, bidAmount: 0, isNegative: true); var otherBrands = helper.AddUnit( parent: petSuppliesSubdivision, condition: new ProductCondition { Operand = "Brand", Attribute = null }, bidAmount: 0.35, isNegative: false); var otherPetSupplies = helper.AddUnit( parent: animalsSubdivision, condition: new ProductCondition { Operand = "CategoryL2", Attribute = null }, bidAmount: 0.35, isNegative: false); var electronics = helper.AddUnit( parent: root, condition: new ProductCondition { Operand = "CategoryL1", Attribute = "Electronics" }, bidAmount: 0.35, isNegative: false); var otherCategoryL1 = helper.AddUnit( root, new ProductCondition { Operand = "CategoryL1", Attribute = null }, 0.35, false ); OutputStatusMessage("-----\nApplyProductPartitionActions:"); OutputStatusMessage("Applying product partitions to the ad group..."); applyProductPartitionActionsResponse = await CampaignManagementExampleHelper.ApplyProductPartitionActionsAsync( criterionActions : helper.PartitionActions); // To retrieve product partitions after they have been applied, call GetAdGroupCriterionsByIds. // The product partition with ParentCriterionId set to null is the root node. OutputStatusMessage("-----\nGetAdGroupCriterionsByIds:"); adGroupCriterions = await CampaignManagementExampleHelper.GetAdGroupCriterionsByIdsAsync( adGroupCriterionIds : null, adGroupId : adGroupId, criterionType : AdGroupCriterionType.ProductPartition, returnAdditionalFields : AdGroupCriterionAdditionalField.FinalUrlSuffix); // 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?.AdGroupCriterions); // 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(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), 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: parent, condition: new ProductCondition { Operand = "CategoryL1", Attribute = "Electronics" } ); var brandC = helper.AddUnit( parent: electronicsSubdivision, condition: new ProductCondition { Operand = "Brand", Attribute = "Brand C" }, bidAmount: 0.35, isNegative: false); var brandD = helper.AddUnit( parent: electronicsSubdivision, condition: new ProductCondition { Operand = "Brand", Attribute = "Brand D" }, bidAmount: 0.35, isNegative: false); var otherElectronicsBrands = helper.AddUnit( parent: electronicsSubdivision, condition: new ProductCondition { Operand = "Brand", Attribute = null }, bidAmount: 0.35, isNegative: false); OutputStatusMessage("-----\nApplyProductPartitionActions:"); OutputStatusMessage( "Updating the product partition group to refine Electronics (CategoryL1) with 3 child nodes..." ); applyProductPartitionActionsResponse = await CampaignManagementExampleHelper.ApplyProductPartitionActionsAsync( criterionActions : helper.PartitionActions); OutputStatusMessage("-----\nGetAdGroupCriterionsByIds:"); adGroupCriterions = await CampaignManagementExampleHelper.GetAdGroupCriterionsByIdsAsync( adGroupCriterionIds : null, adGroupId : adGroupId, criterionType : AdGroupCriterionType.ProductPartition, returnAdditionalFields : AdGroupCriterionAdditionalField.FinalUrlSuffix); // 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?.AdGroupCriterions); // Create a product ad. You must add at least one product ad to the ad group. // The product ad identifier can be used for reporting analytics. // Use Merchant Promotions if you want tags to appear at the bottom of your product ad // as "special offer" links, helping to increase customer engagement. For details // on Merchant Promotions see https://help.bingads.microsoft.com/#apex/3/en/56805/0. var ads = new Ad[] { new ProductAd { } }; OutputStatusMessage("-----\nAddAds:"); AddAdsResponse addAdsResponse = await CampaignManagementExampleHelper.AddAdsAsync( adGroupId : (long)adGroupIds[0], ads : ads); long?[] adIds = addAdsResponse.AdIds.ToArray(); BatchError[] adErrors = addAdsResponse.PartialErrors.ToArray(); OutputStatusMessage("AdIds:"); CampaignManagementExampleHelper.OutputArrayOfLong(adIds); OutputStatusMessage("PartialErrors:"); CampaignManagementExampleHelper.OutputArrayOfBatchError(adErrors); // Delete the campaign and everything it contains e.g., ad groups and ads. OutputStatusMessage("-----\nDeleteCampaigns:"); await CampaignManagementExampleHelper.DeleteCampaignsAsync( accountId : authorizationData.AccountId, campaignIds : new[] { (long)campaignIds[0] }); OutputStatusMessage(string.Format("Deleted Campaign Id {0}", 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.V12.CampaignManagement.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V12.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.V12.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 { ApiEnvironment environment = ((OAuthDesktopMobileAuthCodeGrant)authorizationData.Authentication).Environment; CampaignManagementExampleHelper CampaignManagementExampleHelper = new CampaignManagementExampleHelper( OutputStatusMessageDefault: this.OutputStatusMessage); CampaignManagementExampleHelper.CampaignManagementService = new ServiceClient <ICampaignManagementService>( authorizationData: authorizationData, environment: environment); // A conversion goal cannot be deleted, so even if this is a test // please choose an appropriate name accordingly. var offlineConversionGoalName = "My Offline Conversion Goal"; var conversionGoals = new ConversionGoal[] { new OfflineConversionGoal { // Determines how long after a click that you want to count offline conversions. ConversionWindowInMinutes = 43200, // If the count type is 'Unique' then only the first offline conversion will be counted. // By setting the count type to 'All', then all offline conversions for the same // MicrosoftClickId with different conversion times will be added cumulatively. CountType = ConversionGoalCountType.All, Name = offlineConversionGoalName, // The default conversion currency code and value. Each offline conversion can override it. Revenue = new ConversionGoalRevenue { CurrencyCode = null, Type = ConversionGoalRevenueType.FixedValue, Value = 5.00m, }, Scope = EntityScope.Account, Status = ConversionGoalStatus.Active, TagId = null }, }; OutputStatusMessage("-----\nAddConversionGoals:"); var addConversionGoalsResponse = await CampaignManagementExampleHelper.AddConversionGoalsAsync( conversionGoals : conversionGoals); var conversionGoalIds = addConversionGoalsResponse.ConversionGoalIds.ToArray(); BatchError[] conversionGoalErrors = addConversionGoalsResponse.PartialErrors.ToArray(); OutputStatusMessage("ConversionGoalIds:"); CampaignManagementExampleHelper.OutputArrayOfLong(conversionGoalIds); OutputStatusMessage("PartialErrors:"); CampaignManagementExampleHelper.OutputArrayOfBatchError(conversionGoalErrors); List <long> goalIds = GetNonNullableIds(conversionGoalIds); var conversionGoalTypes = ConversionGoalType.OfflineConversion; OutputStatusMessage("-----\nGetConversionGoalsByIds:"); var getConversionGoalsResponse = (await CampaignManagementExampleHelper.GetConversionGoalsByIdsAsync( conversionGoalIds: goalIds, conversionGoalTypes: conversionGoalTypes)); var getConversionGoals = getConversionGoalsResponse.ConversionGoals; conversionGoalErrors = getConversionGoalsResponse.PartialErrors.ToArray(); OutputStatusMessage("ConversionGoals:"); CampaignManagementExampleHelper.OutputArrayOfConversionGoal(getConversionGoals); OutputStatusMessage("PartialErrors:"); CampaignManagementExampleHelper.OutputArrayOfBatchError(conversionGoalErrors); // Every time you create a new OfflineConversionGoal via either the Bing Ads web application or Campaign Management API, // the MSCLKIDAutoTaggingEnabled value of the corresponding AccountProperty is set to 'true' automatically. // We can confirm the setting now. var accountPropertyNames = new List <AccountPropertyName>(); accountPropertyNames.Add(AccountPropertyName.MSCLKIDAutoTaggingEnabled); OutputStatusMessage("-----\nGetAccountProperties:"); var getAccountPropertiesResponse = await CampaignManagementExampleHelper.GetAccountPropertiesAsync( accountPropertyNames : accountPropertyNames); var accountProperties = getAccountPropertiesResponse.AccountProperties; BatchError[] accountPropertiesErrors = getAccountPropertiesResponse.PartialErrors.ToArray(); OutputStatusMessage("AccountProperties:"); CampaignManagementExampleHelper.OutputArrayOfAccountProperty(accountProperties); OutputStatusMessage("PartialErrors:"); CampaignManagementExampleHelper.OutputArrayOfBatchError(accountPropertiesErrors); var offlineConversions = new[] { new OfflineConversion { // If you do not specify an offline conversion currency code, // then the 'CurrencyCode' element of the goal's 'ConversionGoalRevenue' is used. ConversionCurrencyCode = "USD", // The conversion name must match the 'Name' of the 'OfflineConversionGoal'. // If it does not match you won't observe any error, although the offline // conversion will not be counted. ConversionName = offlineConversionGoalName, // The date and time must be in UTC, should align to the date and time of the // recorded click (MicrosoftClickId), and cannot be in the future. ConversionTime = DateTime.UtcNow, // If you do not specify an offline conversion value, // then the 'Value' element of the goal's 'ConversionGoalRevenue' is used. ConversionValue = 10, MicrosoftClickId = "f894f652ea334e739002f7167ab8f8e3" } }; // After the OfflineConversionGoal is set up, wait two hours before submitting the offline conversions. // This example would not succeed in production because we created the goal very recently i.e., // please see above call to AddConversionGoalsAsync. OutputStatusMessage("-----\nApplyOfflineConversions:"); var applyOfflineConversionsResponse = await CampaignManagementExampleHelper.ApplyOfflineConversionsAsync( offlineConversions : offlineConversions); OutputStatusMessage("PartialErrors:"); CampaignManagementExampleHelper.OutputArrayOfBatchError(applyOfflineConversionsResponse.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.V12.CampaignManagement.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V12.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.V12.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 { ApiEnvironment environment = ((OAuthDesktopMobileAuthCodeGrant)authorizationData.Authentication).Environment; // 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. CampaignManagementExampleHelper = new CampaignManagementExampleHelper(this.OutputStatusMessage); CampaignManagementExampleHelper.CampaignManagementService = new ServiceClient <ICampaignManagementService>(authorizationData, environment); BulkServiceManager = new BulkServiceManager(authorizationData, environment); var progress = new Progress <BulkOperationProgressInfo>(x => OutputStatusMessage(string.Format("{0} % Complete", x.PercentComplete.ToString(CultureInfo.InvariantCulture)))); #region CampaignThroughAdGroupSetup // Get a list of all Bing Merchant Center stores associated with your CustomerId IList <BMCStore> stores = (await CampaignManagementExampleHelper.GetBMCStoresByCustomerIdAsync())?.BMCStores; if (stores == null) { OutputStatusMessage( string.Format("You do not have any BMC stores registered for CustomerId {0}.\n", authorizationData.CustomerId) ); return; } 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.", // 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, TimeZone = "PacificTimeUSCanadaTijuana", // Used with CustomParameters defined in lower level entities such as ad group criterion. TrackingUrlTemplate = "http://tracker.example.com/?season={_season}&promocode={_promocode}&u={lpurl}" } }; /* 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 { BiddableCampaignCriterion = new BiddableCampaignCriterion() { CampaignId = campaignIdKey, CriterionBid = null, // Not applicable for product scope 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", StartDate = null, EndDate = new Microsoft.BingAds.V12.CampaignManagement.Date { Month = 12, Day = 31, Year = DateTime.UtcNow.Year + 1 }, Language = "English", Status = AdGroupStatus.Active }, }; /* * Create a product ad. You must add at least one product ad to the ad group. * The product ad identifier can be used for reporting analytics. * Use Merchant Promotions if you want tags to appear at the bottom of your product ad * as "special offer" links, helping to increase customer engagement. For details * on Merchant Promotions see https://help.bingads.microsoft.com/#apex/3/en/56805/0. */ var bulkProductAd = new BulkProductAd { AdGroupId = adGroupIdKey, ProductAd = new ProductAd { } }; uploadEntities.Add(bulkCampaign); uploadEntities.Add(bulkAdGroup); uploadEntities.Add(bulkCampaignProductScope); uploadEntities.Add(bulkProductAd); // Upload and write the output var Reader = await WriteEntitiesAndUploadFileAsync(uploadEntities); var downloadEntities = Reader.ReadEntities().ToList(); var campaignResults = downloadEntities.OfType <BulkCampaign>().ToList(); OutputBulkCampaigns(campaignResults); var adGroupResults = downloadEntities.OfType <BulkAdGroup>().ToList(); OutputBulkAdGroups(adGroupResults); var productAdResults = downloadEntities.OfType <BulkProductAd>().ToList(); OutputBulkProductAds(productAdResults); var campaignProductScopeResults = downloadEntities.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 { 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 Product Ads technical guide: * https://docs.microsoft.com/en-us/bingads/guides/product-ads */ 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://go.microsoft.com/fwlink?LinkId=507666 */ 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 #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. //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 BulkAdGroup and BulkAdGroupProductPartition //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( "Deleting the campaign, product conditions, ad group, product partitions, and product ad... \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.V12.Bulk.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V12.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.V12.CampaignManagement.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V12.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(); } } }
public async override Task RunAsync(AuthorizationData authorizationData) { try { ApiEnvironment environment = ((OAuthDesktopMobileAuthCodeGrant)authorizationData.Authentication).Environment; // Used to output the Campaign Management objects within Bulk entities. CampaignManagementExampleHelper = new CampaignManagementExampleHelper( OutputStatusMessageDefault: this.OutputStatusMessage); BulkServiceManager = new BulkServiceManager( authorizationData: authorizationData, apiEnvironment: environment); var progress = new Progress <BulkOperationProgressInfo>(x => OutputStatusMessage(string.Format("{0} % Complete", x.PercentComplete.ToString(CultureInfo.InvariantCulture)))); var tokenSource = new CancellationTokenSource(); tokenSource.CancelAfter(TimeoutInMilliseconds); // In this example we will download all ad groups in the account. var downloadParameters = new DownloadParameters { DataScope = DataScope.EntityData, DownloadEntities = new[] { DownloadEntity.AdGroups }, FileType = FileType, LastSyncTimeInUTC = null, ResultFileDirectory = FileDirectory, ResultFileName = DownloadFileName, OverwriteResultFile = true }; OutputStatusMessage("-----\nDownloading all ad groups in the account."); var bulkFilePath = await BulkServiceManager.DownloadFileAsync( parameters : downloadParameters, progress : progress, cancellationToken : tokenSource.Token); OutputStatusMessage("Download results:"); Reader = new BulkFileReader( filePath: bulkFilePath, resultFileType: ResultFileType.FullDownload, fileFormat: FileType); var bulkAdGroups = Reader.ReadEntities().ToList().OfType <BulkAdGroup>().ToList(); OutputBulkAdGroups(bulkAdGroups); Writer = new BulkFileWriter(FileDirectory + UploadFileName); // We will activate ad groups for one month starting from today as an example. var nextMonth = DateTime.UtcNow.AddMonths(1); // Within the downloaded records, find all ad groups that you want to update. foreach (var bulkAdGroup in bulkAdGroups) { var adGroup = bulkAdGroup.AdGroup; if (adGroup != null && bulkAdGroup.AdGroup.Status.ToString().CompareTo("Active") != 0) { // For best performance, only upload the properties that you want to update. Writer.WriteEntity(new BulkAdGroup { CampaignId = bulkAdGroup.CampaignId, AdGroup = new AdGroup { Id = adGroup.Id, EndDate = new Microsoft.BingAds.V13.CampaignManagement.Date { Month = nextMonth.Month, Day = nextMonth.Day, Year = nextMonth.Year }, Status = AdGroupStatus.Active, } }); } } Reader.Dispose(); Writer.Dispose(); // Upload the local file that we already prepared var fileUploadParameters = new FileUploadParameters { ResultFileDirectory = FileDirectory, CompressUploadFile = true, ResultFileName = ResultFileName, OverwriteResultFile = true, UploadFilePath = FileDirectory + UploadFileName, ResponseMode = ResponseMode.ErrorsAndResults }; OutputStatusMessage("-----\nActivating the ad groups..."); var resultFilePath = await BulkServiceManager.UploadFileAsync( parameters : fileUploadParameters, progress : progress, cancellationToken : tokenSource.Token); Reader = new BulkFileReader( filePath: resultFilePath, resultFileType: ResultFileType.Upload, fileFormat: FileType); OutputStatusMessage("Upload results:"); bulkAdGroups = Reader.ReadEntities().ToList().OfType <BulkAdGroup>().ToList(); OutputBulkAdGroups(bulkAdGroups); Reader.Dispose(); } // 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.V13.Bulk.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V13.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 { ApiEnvironment environment = ((OAuthDesktopMobileAuthCodeGrant)authorizationData.Authentication).Environment; CampaignManagementExampleHelper CampaignManagementExampleHelper = new CampaignManagementExampleHelper( OutputStatusMessageDefault: this.OutputStatusMessage); CampaignManagementExampleHelper.CampaignManagementService = new ServiceClient <ICampaignManagementService>( authorizationData: authorizationData, environment: environment); // Add an ad group in a campaign. Later we will create labels for them. // Although not included in this example you can also create labels for ads and keywords. var campaigns = new[] { new Campaign { BudgetType = BudgetLimitType.DailyBudgetStandard, DailyBudget = 50, CampaignType = CampaignType.Search, Description = "Red shoes line.", Languages = new string[] { "All" }, Name = "Women's Shoes " + DateTime.UtcNow, TimeZone = "PacificTimeUSCanadaTijuana", }, }; OutputStatusMessage("-----\nAddCampaigns:"); AddCampaignsResponse addCampaignsResponse = await CampaignManagementExampleHelper.AddCampaignsAsync( accountId : authorizationData.AccountId, campaigns : campaigns, includeDynamicSearchAdsSource : false); long?[] campaignIds = addCampaignsResponse.CampaignIds.ToArray(); BatchError[] campaignErrors = addCampaignsResponse.PartialErrors.ToArray(); OutputStatusMessage("CampaignIds:"); CampaignManagementExampleHelper.OutputArrayOfLong(campaignIds); OutputStatusMessage("PartialErrors:"); CampaignManagementExampleHelper.OutputArrayOfBatchError(campaignErrors); var adGroups = new[] { new AdGroup { Name = "Women's Red Shoe Sale", StartDate = null, EndDate = new Date { Month = 12, Day = 31, Year = DateTime.UtcNow.Year + 1 }, CpcBid = new Bid { Amount = 0.09 }, } }; OutputStatusMessage("-----\nAddAdGroups:"); AddAdGroupsResponse addAdGroupsResponse = await CampaignManagementExampleHelper.AddAdGroupsAsync( campaignId : (long)campaignIds[0], adGroups : adGroups, returnInheritedBidStrategyTypes : false); long?[] adGroupIds = addAdGroupsResponse.AdGroupIds.ToArray(); BatchError[] adGroupErrors = addAdGroupsResponse.PartialErrors.ToArray(); OutputStatusMessage("AdGroupIds:"); CampaignManagementExampleHelper.OutputArrayOfLong(adGroupIds); OutputStatusMessage("PartialErrors:"); CampaignManagementExampleHelper.OutputArrayOfBatchError(adGroupErrors); // Add labels and associate them with the campaign and ad group. var random = new Random(); var labels = new List <Label>(); for (var labelIndex = 0; labelIndex < 5; labelIndex++) { var color = string.Format("#{0:X6}", random.Next(0x100000)); labels.Add(new Label { ColorCode = color, Description = "Label Description", Name = "Label Name " + color + " " + DateTime.UtcNow }); } OutputStatusMessage("-----\nAddLabels:"); AddLabelsResponse addLabelsResponse = await CampaignManagementExampleHelper.AddLabelsAsync(labels); long?[] nullableLabelIds = addLabelsResponse.LabelIds.ToArray(); BatchError[] labelErrors = addLabelsResponse.PartialErrors.ToArray(); OutputStatusMessage("LabelIds:"); CampaignManagementExampleHelper.OutputArrayOfLong(nullableLabelIds); OutputStatusMessage("PartialErrors:"); CampaignManagementExampleHelper.OutputArrayOfBatchError(labelErrors); var labelIds = GetNonNullableIds(nullableLabelIds); OutputStatusMessage("-----\nGetLabelsByIds:"); var getLabelsByIdsResponse = await CampaignManagementExampleHelper.GetLabelsByIdsAsync( labelIds : labelIds, pageInfo : new Paging { Index = 0, Size = MaxGetLabelsByIds } ); var getLabels = getLabelsByIdsResponse.Labels; labelErrors = getLabelsByIdsResponse.PartialErrors.ToArray(); OutputStatusMessage("Labels:"); CampaignManagementExampleHelper.OutputArrayOfLabel(getLabels); OutputStatusMessage("PartialErrors:"); CampaignManagementExampleHelper.OutputArrayOfBatchError(labelErrors); var campaignLabelAssociations = CreateExampleLabelAssociationsByEntityId((long)campaignIds[0], labelIds); OutputStatusMessage("-----\nAssociating all of the labels with a campaign..."); CampaignManagementExampleHelper.OutputArrayOfLabelAssociation(campaignLabelAssociations); var setLabelAssociationsResponse = await CampaignManagementExampleHelper.SetLabelAssociationsAsync( entityType : EntityType.Campaign, labelAssociations : campaignLabelAssociations); var adGroupLabelAssociations = CreateExampleLabelAssociationsByEntityId((long)adGroupIds[0], labelIds); OutputStatusMessage("-----\nAssociating all of the labels with an ad group..."); CampaignManagementExampleHelper.OutputArrayOfLabelAssociation(adGroupLabelAssociations); setLabelAssociationsResponse = await CampaignManagementExampleHelper.SetLabelAssociationsAsync( entityType : EntityType.AdGroup, labelAssociations : adGroupLabelAssociations); OutputStatusMessage("-----\nUse paging to get all campaign label associations..."); var getLabelAssociationsByLabelIds = await GetLabelAssociationsByLabelIdsHelperAsync( CampaignManagementExampleHelper, entityType : EntityType.Campaign, labelIds : labelIds); CampaignManagementExampleHelper.OutputArrayOfLabelAssociation(getLabelAssociationsByLabelIds); OutputStatusMessage("-----\nUse paging to get all ad group label associations..."); getLabelAssociationsByLabelIds = await GetLabelAssociationsByLabelIdsHelperAsync( CampaignManagementExampleHelper : CampaignManagementExampleHelper, entityType : EntityType.AdGroup, labelIds : labelIds); CampaignManagementExampleHelper.OutputArrayOfLabelAssociation(getLabelAssociationsByLabelIds); OutputStatusMessage("-----\nGet label associations for the campaigns..."); var getLabelAssociationsByEntityIds = await GetLabelAssociationsByEntityIdsHelperAsync( CampaignManagementExampleHelper : CampaignManagementExampleHelper, entityType : EntityType.Campaign, entityIds : GetNonNullableIds(campaignIds) ); CampaignManagementExampleHelper.OutputArrayOfLabelAssociation(getLabelAssociationsByEntityIds); OutputStatusMessage("-----\nGet label associations for the ad groups..."); getLabelAssociationsByEntityIds = await GetLabelAssociationsByEntityIdsHelperAsync( CampaignManagementExampleHelper : CampaignManagementExampleHelper, entityType : EntityType.AdGroup, entityIds : GetNonNullableIds(adGroupIds) ); CampaignManagementExampleHelper.OutputArrayOfLabelAssociation(getLabelAssociationsByEntityIds); OutputStatusMessage("-----\nDelete all label associations that we set above..."); // Deleting the associations is not necessary if you are deleting the corresponding campaign(s), as the // contained ad groups, ads, and associations would also be deleted. var deleteLabelAssociationsResponse = await CampaignManagementExampleHelper.DeleteLabelAssociationsAsync( entityType : EntityType.Campaign, labelAssociations : campaignLabelAssociations); deleteLabelAssociationsResponse = await CampaignManagementExampleHelper.DeleteLabelAssociationsAsync( entityType : EntityType.AdGroup, labelAssociations : adGroupLabelAssociations); // Delete the account's labels. OutputStatusMessage("-----\nDeleteLabels:"); var deleteLabelsResponse = await CampaignManagementExampleHelper.DeleteLabelsAsync( labelIds : labelIds); foreach (var id in labelIds) { OutputStatusMessage(string.Format("Deleted Label Id {0}", id)); } // Delete the campaign and everything it contains e.g., ad groups and ads. OutputStatusMessage("-----\nDeleteCampaigns:"); await CampaignManagementExampleHelper.DeleteCampaignsAsync( accountId : authorizationData.AccountId, campaignIds : new[] { (long)campaignIds[0] }); OutputStatusMessage(string.Format("Deleted Campaign Id {0}", 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.V12.CampaignManagement.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V12.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.V12.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 { ApiEnvironment environment = ((OAuthDesktopMobileAuthCodeGrant)authorizationData.Authentication).Environment; AdInsightExampleHelper AdInsightExampleHelper = new AdInsightExampleHelper( OutputStatusMessageDefault: this.OutputStatusMessage); AdInsightExampleHelper.AdInsightService = new ServiceClient <IAdInsightService>( authorizationData: authorizationData, environment: environment); // Used to output the Campaign Management objects within Bulk entities. CampaignManagementExampleHelper = new CampaignManagementExampleHelper( OutputStatusMessageDefault: this.OutputStatusMessage); BulkServiceManager = new BulkServiceManager( authorizationData: authorizationData, apiEnvironment: environment); var uploadEntities = new List <BulkEntity>(); // Setup a page feed that can be associated with one or more campaigns. var bulkPageFeed = new BulkFeed { CustomAttributes = new[] { new FeedCustomAttributeContract { FeedAttributeType = "Url", Name = "Page Url" }, new FeedCustomAttributeContract { FeedAttributeType = "StringList", Name = "Custom Label" } }, Id = feedIdKey, Name = "My PageFeed " + DateTime.UtcNow, Status = Status.Active, SubType = "PageFeed" }; uploadEntities.Add(bulkPageFeed); var pageFeedItemCustomAttributes = new Dictionary <string, object>(); pageFeedItemCustomAttributes.Add( "Page Url", "https://" + DOMAIN_NAME + "/3001"); pageFeedItemCustomAttributes.Add( "Custom Label", new string[] { "Label_1_3001", "Label_1_3002" }); var serializerSettings = new JsonSerializerSettings(); serializerSettings.NullValueHandling = NullValueHandling.Ignore; var pageFeedItemCustomAttributesJson = JsonConvert.SerializeObject( pageFeedItemCustomAttributes, serializerSettings); var bulkPageFeedItem = new BulkFeedItem { FeedId = feedIdKey, CustomAttributes = pageFeedItemCustomAttributesJson, Status = Status.Active }; uploadEntities.Add(bulkPageFeedItem); // To get started with dynamic search ads, first you'll need to add a new Campaign // with its type set to DynamicSearchAds. When you create the campaign, you'll need to // include a DynamicSearchAdsSetting that specifies the target website domain and language. // Page feeds can be associated at the campaign level via 'Source' and 'Page Feed Ids'. var bulkCampaign = new BulkCampaign { Campaign = new Campaign { Id = campaignIdKey, BudgetType = Microsoft.BingAds.V13.CampaignManagement.BudgetLimitType.DailyBudgetStandard, DailyBudget = 50, CampaignType = CampaignType.DynamicSearchAds, Languages = new string[] { "All" }, Name = "Women's Shoes " + DateTime.UtcNow, TimeZone = "PacificTimeUSCanadaTijuana", Settings = new[] { // Set the target website domain and language. // Be sure to set the Source to AdvertiserSuppliedUrls or All, // otherwise the PageFeedIds will be ignored. new DynamicSearchAdsSetting { DomainName = DOMAIN_NAME, Language = LANGUAGE, Source = DynamicSearchAdsSource.All, PageFeedIds = new [] { feedIdKey } } }, }, }; uploadEntities.Add(bulkCampaign); // Create a new ad group within the dynamic search ads campaign. var bulkAdGroup = new BulkAdGroup { CampaignId = campaignIdKey, AdGroup = new AdGroup { Id = adGroupIdKey, Name = "Women's Red Shoe Sale", StartDate = null, EndDate = new Microsoft.BingAds.V13.CampaignManagement.Date { Month = 12, Day = 31, Year = DateTime.UtcNow.Year + 1 }, CpcBid = new Bid { Amount = 0.09 }, }, }; uploadEntities.Add(bulkAdGroup); // Create an auto target based on the custom label feed items created above e.g., "Label_1_3001". var adGroupWebpagePositiveCustomLabel = new BulkAdGroupDynamicSearchAdTarget { BiddableAdGroupCriterion = new BiddableAdGroupCriterion { AdGroupId = adGroupIdKey, CriterionBid = new FixedBid { Amount = 0.50 }, Criterion = new Webpage { Parameter = new WebpageParameter { Conditions = new[] { new WebpageCondition { Argument = "Label_1_3001", Operand = WebpageConditionOperand.CustomLabel, }, }, CriterionName = "Ad Group Webpage Positive Custom Label Criterion" }, }, } }; uploadEntities.Add(adGroupWebpagePositiveCustomLabel); // To discover the categories that you can use for Webpage criterion (positive or negative), // use the GetDomainCategories operation with the Ad Insight service. OutputStatusMessage("-----\nGetDomainCategories:"); var getDomainCategoriesResponse = await AdInsightExampleHelper.GetDomainCategoriesAsync( categoryName : null, domainName : DOMAIN_NAME, language : LANGUAGE); var categories = getDomainCategoriesResponse.Categories; AdInsightExampleHelper.OutputArrayOfDomainCategory(categories); // If any categories are available let's use one as a condition. if (categories.Count > 0) { var adGroupWebpagePositiveCategory = new BulkAdGroupDynamicSearchAdTarget { BiddableAdGroupCriterion = new BiddableAdGroupCriterion { AdGroupId = adGroupIdKey, CriterionBid = new FixedBid { Amount = 0.50 }, Criterion = new Webpage { Parameter = new WebpageParameter { Conditions = new[] { new WebpageCondition { Argument = categories[0].CategoryName, Operand = WebpageConditionOperand.Category, } }, CriterionName = "Ad Group Webpage Positive Category Criterion" }, } } }; uploadEntities.Add(adGroupWebpagePositiveCategory); } // If you want to exclude certain portions of your website, you can add negative Webpage // criterion at the campaign and ad group level. var adGroupWebpageNegativeUrl = new BulkAdGroupNegativeDynamicSearchAdTarget { NegativeAdGroupCriterion = new NegativeAdGroupCriterion { AdGroupId = adGroupIdKey, Criterion = new Webpage { Parameter = new WebpageParameter { // You can choose whether you want the criterion argument to match partial URLs, // page content, page title, or categories that Bing thinks applies to your website. Conditions = new[] { new WebpageCondition { Argument = "https://" + DOMAIN_NAME + "/3001", Operand = WebpageConditionOperand.Url, } }, // If you do not specify any name, then it will be set to a concatenated list of conditions. CriterionName = null } } } }; uploadEntities.Add(adGroupWebpageNegativeUrl); // Finally you must add at least one Dynamic Search Ad into the ad group. The ad title and display URL // are generated automatically based on the website domain and language that you want to target. var bulkDynamicSearchAd = new BulkDynamicSearchAd { AdGroupId = adGroupIdKey, DynamicSearchAd = new DynamicSearchAd { Text = "Find New Customers & Increase Sales!", TextPart2 = "Start Advertising on Contoso Today.", Path1 = "seattle", Path2 = "shoe sale", // You cannot set FinalUrls for dynamic search ads. // The Final URL will be a dynamically selected landing page. // The final URL is distinct from the path that customers will see and click on in your ad. FinalUrls = null }, }; uploadEntities.Add(bulkDynamicSearchAd); // Upload and write the output OutputStatusMessage("-----\nAdding page feed, campaign, ad group, criterions, and ads..."); var Reader = await WriteEntitiesAndUploadFileAsync(uploadEntities); var downloadEntities = Reader.ReadEntities().ToList(); OutputStatusMessage("Upload results:"); var feedResults = downloadEntities.OfType <BulkFeed>().ToList(); OutputBulkFeeds(feedResults); var feedItemResults = downloadEntities.OfType <BulkFeedItem>().ToList(); OutputBulkFeedItems(feedItemResults); var campaignResults = downloadEntities.OfType <BulkCampaign>().ToList(); OutputBulkCampaigns(campaignResults); var adGroupResults = downloadEntities.OfType <BulkAdGroup>().ToList(); OutputBulkAdGroups(adGroupResults); var adGroupDynamicSearchAdTargetResults = downloadEntities.OfType <BulkAdGroupDynamicSearchAdTarget>().ToList(); OutputBulkAdGroupDynamicSearchAdTargets(adGroupDynamicSearchAdTargetResults); var adGroupNegativeDynamicSearchAdTargetResults = downloadEntities.OfType <BulkAdGroupNegativeDynamicSearchAdTarget>().ToList(); OutputBulkAdGroupNegativeDynamicSearchAdTargets(adGroupNegativeDynamicSearchAdTargetResults); var dynamicSearchAdResults = downloadEntities.OfType <BulkDynamicSearchAd>().ToList(); OutputBulkDynamicSearchAds(dynamicSearchAdResults); Reader.Dispose(); // Delete the campaign and everything it contains e.g., ad groups and ads. uploadEntities = new List <BulkEntity>(); foreach (var feedResult in feedResults) { feedResult.Status = Status.Deleted; uploadEntities.Add(feedResult); } foreach (var campaignResult in campaignResults) { campaignResult.Campaign.Status = CampaignStatus.Deleted; uploadEntities.Add(campaignResult); } OutputStatusMessage("-----\nDeleting page feed, DSA campaign, and all contained entities..."); Reader = await WriteEntitiesAndUploadFileAsync(uploadEntities); downloadEntities = Reader.ReadEntities().ToList(); OutputStatusMessage("Upload results:"); feedResults = downloadEntities.OfType <BulkFeed>().ToList(); OutputBulkFeeds(feedResults); campaignResults = downloadEntities.OfType <BulkCampaign>().ToList(); OutputBulkCampaigns(campaignResults); Reader.Dispose(); } // 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.V13.Bulk.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V13.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 { ApiEnvironment environment = ((OAuthDesktopMobileAuthCodeGrant)authorizationData.Authentication).Environment; // Used to output the Campaign Management objects within Bulk entities. CampaignManagementExampleHelper = new CampaignManagementExampleHelper( OutputStatusMessageDefault: this.OutputStatusMessage); BulkServiceManager = new BulkServiceManager( authorizationData: authorizationData, apiEnvironment: environment); var progress = new Progress <BulkOperationProgressInfo>(x => OutputStatusMessage(string.Format("{0} % Complete", x.PercentComplete.ToString(CultureInfo.InvariantCulture)))); var uploadEntities = new List <BulkEntity>(); // Define a campaign var bulkCampaign = new BulkCampaign { ClientId = "YourClientIdGoesHere", Campaign = new Campaign { Id = campaignIdKey, BudgetType = BudgetLimitType.DailyBudgetStandard, DailyBudget = 50, CampaignType = CampaignType.Search, Languages = new string[] { "All" }, Name = "Women's Shoes " + DateTime.UtcNow, TimeZone = "PacificTimeUSCanadaTijuana", } }; uploadEntities.Add(bulkCampaign); // Define a set of negative sites that can be applied to the campaign. // You can set one negative site via the BulkCampaignNegativeSite (singular) bulk entity, // or multiple negative sites via the BulkCampaignNegativeSites (plural) bulk entity. // // If you upload a BulkCampaignNegativeSites bulk entity, then you are effectively replacing any existing // negative sites assigned to the campaign. // // When the SDK writes a BulkCampaignNegativeSites entity to the bulk upload file, // an extra Campaign Negative Site record is included where the Status is Deleted and the // Website field is empty. (This is the record that deletes any existing campaign negative sites.) // // If you include additional BulkCampaignNegativeSite or BulkCampaignNegativeSites in the same upload, // they will also be included in the set of negative sites applied to the campaign. var bulkCampaignNegativeSite = new BulkCampaignNegativeSite[] { new BulkCampaignNegativeSite { CampaignId = campaignIdKey, Website = "contoso.com/negativesite1" }, new BulkCampaignNegativeSite { CampaignId = campaignIdKey, Website = "contoso.com/negativesite2" }, }; foreach (var campaignNegativeSite in bulkCampaignNegativeSite) { uploadEntities.Add(campaignNegativeSite); } var bulkCampaignNegativeSites = new BulkCampaignNegativeSites { CampaignNegativeSites = new CampaignNegativeSites { CampaignId = campaignIdKey, NegativeSites = new string[] { "contoso.com/negativesite3", "contoso.com/negativesite4", } } }; uploadEntities.Add(bulkCampaignNegativeSites); // Upload and write the output OutputStatusMessage("-----\nApplying negative sites to a new campaign..."); Reader = await WriteEntitiesAndUploadFileAsync(uploadEntities); var downloadEntities = Reader.ReadEntities().ToList(); OutputStatusMessage("Upload results:"); var campaignResults = downloadEntities.OfType <BulkCampaign>().ToList(); OutputBulkCampaigns(campaignResults); // If the upload result file contains a Campaign Negative Site record where the Status is Deleted // and the Website field is empty, the SDK represents all negative sites for the campaign // via a BulkCampaignNegativeSites (plural) object. Otherwise the SDK represents negative sites // for the campaign via one or more BulkCampaignNegativeSite (singlular) objects. var campaignNegativeSiteResults = downloadEntities.OfType <BulkCampaignNegativeSite>().ToList(); OutputBulkCampaignNegativeSite(campaignNegativeSiteResults); var campaignNegativeSitesResults = downloadEntities.OfType <BulkCampaignNegativeSites>().ToList(); OutputBulkCampaignNegativeSites(campaignNegativeSitesResults); Reader.Dispose(); // Delete the campaign and everything it contains e.g., ad groups and ads. uploadEntities = new List <BulkEntity>(); foreach (var campaignResult in campaignResults) { campaignResult.Campaign.Status = CampaignStatus.Deleted; uploadEntities.Add(campaignResult); } // Upload and write the output OutputStatusMessage("-----\nDeleting the campaign and everything it contains e.g., ad groups and ads..."); Reader = await WriteEntitiesAndUploadFileAsync(uploadEntities); downloadEntities = Reader.ReadEntities().ToList(); OutputStatusMessage("Upload results:"); OutputBulkCampaigns(downloadEntities.OfType <BulkCampaign>().ToList()); OutputBulkNegativeKeywordLists(downloadEntities.OfType <BulkNegativeKeywordList>().ToList()); Reader.Dispose(); } // 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.V13.Bulk.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V13.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 { CampaignManagementExampleHelper CampaignManagementExampleHelper = new CampaignManagementExampleHelper(this.OutputStatusMessage); CampaignManagementExampleHelper.CampaignManagementService = new ServiceClient <ICampaignManagementService>(authorizationData); // 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", // 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 CampaignManagementExampleHelper.AddCampaignsAsync(authorizationData.AccountId, campaigns); long?[] campaignIds = addCampaignsResponse.CampaignIds.ToArray(); CampaignManagementExampleHelper.OutputArrayOfLong(campaignIds); CampaignManagementExampleHelper.OutputArrayOfBatchError(addCampaignsResponse?.PartialErrors); // 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.V11.CampaignManagement.Date { Month = 12, Day = 31, Year = DateTime.UtcNow.Year + 1 }, } }, new CalloutAdExtension { Text = "Callout Text" }, new LocationAdExtension { PhoneNumber = "206-555-0100", CompanyName = "Contoso Shoes", Address = new Microsoft.BingAds.V11.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.V11.CampaignManagement.Date { Month = 12, Day = 31, Year = DateTime.UtcNow.Year + 1 }, } }, new PriceAdExtension { Language = "English", TableRows = new PriceTableRow[] { new PriceTableRow { CurrencyCode = "USD", Description = "Come to the event", FinalUrls = new string[] { "https://contoso.com" }, Header = "New Event", Price = 9.99, PriceQualifier = PriceQualifier.From, PriceUnit = PriceUnit.PerDay, }, new PriceTableRow { CurrencyCode = "USD", Description = "Come to the next event", FinalUrls = new string[] { "https://contoso.com" }, Header = "Next Event", Price = 9.99, PriceQualifier = PriceQualifier.From, PriceUnit = PriceUnit.PerDay, }, new PriceTableRow { CurrencyCode = "USD", Description = "Come to the final event", FinalUrls = new string[] { "https://contoso.com" }, Header = "Final Event", Price = 9.99, PriceQualifier = PriceQualifier.From, PriceUnit = PriceUnit.PerDay, }, }, PriceExtensionType = PriceExtensionType.Events, TrackingUrlTemplate = "http://tracker.com?url={lpurl}&matchtype={matchtype}", UrlCustomParameters = new CustomParameters { // Each custom parameter is delimited by a semicolon (;) in the Bulk file Parameters = new[] { new CustomParameter() { Key = "promoCode", Value = "PROMO1" }, new CustomParameter() { Key = "season", Value = "summer" }, } }, }, 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" } } }; // Get adExtensions = adExtensions.Concat(GetSampleSitelink2AdExtensions()).ToArray(); // Add all extensions to the account's ad extension library var addAdExtensionsResponse = (await CampaignManagementExampleHelper.AddAdExtensionsAsync( authorizationData.AccountId, adExtensions )); var adExtensionIdentities = addAdExtensionsResponse?.AdExtensionIdentities; CampaignManagementExampleHelper.OutputArrayOfBatchErrorCollection(addAdExtensionsResponse?.NestedPartialErrors); OutputStatusMessage("Added ad extensions.\n"); // DeleteAdExtensionsAssociations, SetAdExtensionsAssociations, and GetAdExtensionsEditorialReasons // operations each require a list of type AdExtensionIdToEntityIdAssociation. var adExtensionIdToEntityIdAssociations = new List <AdExtensionIdToEntityIdAssociation>(); // GetAdExtensionsByIds requires a list of type long. var adExtensionIds = new List <long>(); // Loop through the list of extension IDs and build any required data structures // for subsequent operations. foreach (var adExtensionIdentity in adExtensionIdentities) { if (adExtensionIdentity != null) { adExtensionIdToEntityIdAssociations.Add(new AdExtensionIdToEntityIdAssociation { AdExtensionId = adExtensionIdentity.Id, EntityId = (long)campaignIds[0] }); adExtensionIds.Add(adExtensionIdentity.Id); } } // Associate the specified ad extensions with the respective campaigns or ad groups. await CampaignManagementExampleHelper.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 getAdExtensionsEditorialReasonsResponse = (await CampaignManagementExampleHelper.GetAdExtensionsEditorialReasonsAsync( authorizationData.AccountId, adExtensionIdToEntityIdAssociations, AssociationType.Campaign )); var adExtensionEditorialReasonCollection = (AdExtensionEditorialReasonCollection[])getAdExtensionsEditorialReasonsResponse?.EditorialReasons; CampaignManagementExampleHelper.OutputArrayOfBatchError(getAdExtensionsEditorialReasonsResponse?.PartialErrors); AdExtensionsTypeFilter adExtensionsTypeFilter = AdExtensionsTypeFilter.AppAdExtension | AdExtensionsTypeFilter.CallAdExtension | AdExtensionsTypeFilter.CalloutAdExtension | AdExtensionsTypeFilter.ImageAdExtension | AdExtensionsTypeFilter.LocationAdExtension | AdExtensionsTypeFilter.Sitelink2AdExtension | // You should remove this flag if your customer is not enabled for price ad extensions. AdExtensionsTypeFilter.PriceAdExtension | AdExtensionsTypeFilter.ReviewAdExtension | AdExtensionsTypeFilter.StructuredSnippetAdExtension; // Get all ad extensions added above. var getAdExtensionsByIdsResponse = (await CampaignManagementExampleHelper.GetAdExtensionsByIdsAsync( authorizationData.AccountId, adExtensionIds, adExtensionsTypeFilter )); adExtensions = getAdExtensionsByIdsResponse?.AdExtensions.ToArray(); CampaignManagementExampleHelper.OutputArrayOfBatchError(getAdExtensionsByIdsResponse?.PartialErrors); OutputStatusMessage("List of ad extensions that were added above:\n"); CampaignManagementExampleHelper.OutputArrayOfAdExtension(adExtensions); // Get only the location extensions and remove scheduling. adExtensionsTypeFilter = AdExtensionsTypeFilter.LocationAdExtension; getAdExtensionsByIdsResponse = (await CampaignManagementExampleHelper.GetAdExtensionsByIdsAsync( authorizationData.AccountId, adExtensionIds, adExtensionsTypeFilter )); adExtensions = getAdExtensionsByIdsResponse?.AdExtensions.ToArray(); // In this example partial errors will be returned for indices where the ad extensions // are not location ad extensions because we only requested AdExtensionsTypeFilter.LocationAdExtension. // This is an example, and ideally you would only send the required ad extension IDs. CampaignManagementExampleHelper.OutputArrayOfBatchError(getAdExtensionsByIdsResponse?.PartialErrors); 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 CampaignManagementExampleHelper.UpdateAdExtensionsAsync(authorizationData.AccountId, updateExtensions); // Get only the location extensions to output the result. getAdExtensionsByIdsResponse = (await CampaignManagementExampleHelper.GetAdExtensionsByIdsAsync( authorizationData.AccountId, updateExtensionIds, adExtensionsTypeFilter )); adExtensions = getAdExtensionsByIdsResponse?.AdExtensions.ToArray(); CampaignManagementExampleHelper.OutputArrayOfBatchError(getAdExtensionsByIdsResponse?.PartialErrors); OutputStatusMessage("List of ad extensions that were updated above:\n"); CampaignManagementExampleHelper.OutputArrayOfAdExtension(adExtensions); // Delete the ad extension associations, ad extensions, and campaign, that were previously added. // At this point the ad extensions are still available in the account's ad extensions library. await CampaignManagementExampleHelper.DeleteAdExtensionsAssociationsAsync( authorizationData.AccountId, adExtensionIdToEntityIdAssociations, AssociationType.Campaign ); OutputStatusMessage("Deleted ad extension associations.\n"); // Delete the ad extensions from the account’s ad extension library. await CampaignManagementExampleHelper.DeleteAdExtensionsAsync( authorizationData.AccountId, adExtensionIds ); OutputStatusMessage("Deleted ad extensions.\n"); await CampaignManagementExampleHelper.DeleteCampaignsAsync(authorizationData.AccountId, new[] { (long)campaignIds[0] }); OutputStatusMessage(string.Format("Deleted Campaign Id {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.V11.CampaignManagement.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V11.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.V11.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.V11.CustomerManagement.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V11.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) { Stream responseStream = null; FileStream fileStream = null; var fileInfo = new FileInfo(LocalFile); try { ApiEnvironment environment = ((OAuthDesktopMobileAuthCodeGrant)authorizationData.Authentication).Environment; CampaignManagementExampleHelper CampaignManagementExampleHelper = new CampaignManagementExampleHelper( OutputStatusMessageDefault: this.OutputStatusMessage); CampaignManagementExampleHelper.CampaignManagementService = new ServiceClient <ICampaignManagementService>( authorizationData: authorizationData, environment: environment); OutputStatusMessage("-----\nGetGeoLocationsFileUrl:"); var getGeoLocationsFileUrlResponse = await CampaignManagementExampleHelper.GetGeoLocationsFileUrlAsync( version : Version, languageLocale : 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(2017, 8, 10, 0, 0, 0, DateTimeKind.Utc); var fileUrl = getGeoLocationsFileUrlResponse.FileUrl; var fileUrlExpiryTimeUtc = getGeoLocationsFileUrlResponse.FileUrlExpiryTimeUtc; var lastModifiedTimeUtc = getGeoLocationsFileUrlResponse.LastModifiedTimeUtc; OutputStatusMessage(string.Format("FileUrl: {0}", fileUrl)); OutputStatusMessage(string.Format("FileUrlExpiryTimeUtc: {0}", fileUrlExpiryTimeUtc)); OutputStatusMessage(string.Format("LastModifiedTimeUtc: {0}", 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.V13.CampaignManagement.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V13.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.V13.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(); } } }
public async override Task RunAsync(AuthorizationData authorizationData) { try { ApiEnvironment environment = ((OAuthDesktopMobileAuthCodeGrant)authorizationData.Authentication).Environment; // Used to output the Campaign Management objects within Bulk entities. CampaignManagementExampleHelper = new CampaignManagementExampleHelper( OutputStatusMessageDefault: this.OutputStatusMessage); BulkServiceManager = new BulkServiceManager( authorizationData: authorizationData, apiEnvironment: environment); var uploadEntities = new List <BulkEntity>(); // Add a new campaign and associate it with ad extensions. uploadEntities.Add(GetBulkCampaign()); uploadEntities.Add(GetBulkActionAdExtension()); uploadEntities.Add(GetBulkAppAdExtension()); uploadEntities.Add(GetBulkCallAdExtension()); uploadEntities.Add(GetBulkCalloutAdExtension()); uploadEntities.Add(GetBulkLocationAdExtension()); uploadEntities.Add(GetBulkPriceAdExtension()); uploadEntities.Add(GetBulkReviewAdExtension()); uploadEntities.Add(GetBulkSitelinkAdExtension()); uploadEntities.Add(GetBulkStructuredSnippetAdExtension()); uploadEntities.Add(GetBulkCampaignActionAdExtension()); uploadEntities.Add(GetBulkCampaignAppAdExtension()); uploadEntities.Add(GetBulkCampaignCallAdExtension()); uploadEntities.Add(GetBulkCampaignCalloutAdExtension()); uploadEntities.Add(GetBulkCampaignLocationAdExtension()); uploadEntities.Add(GetBulkCampaignPriceAdExtension()); uploadEntities.Add(GetBulkCampaignReviewAdExtension()); uploadEntities.Add(GetBulkCampaignSitelinkAdExtension()); uploadEntities.Add(GetBulkCampaignStructuredSnippetAdExtension()); OutputStatusMessage("-----\nAdding campaign, ad extensions, and associations..."); var Reader = await WriteEntitiesAndUploadFileAsync(uploadEntities); var downloadEntities = Reader.ReadEntities().ToList(); OutputStatusMessage("Upload results:"); var campaignResults = downloadEntities.OfType <BulkCampaign>().ToList(); OutputBulkCampaigns(campaignResults); var actionAdExtensionResults = downloadEntities.OfType <BulkActionAdExtension>().ToList(); OutputBulkActionAdExtensions(actionAdExtensionResults); var appAdExtensionResults = downloadEntities.OfType <BulkAppAdExtension>().ToList(); OutputBulkAppAdExtensions(appAdExtensionResults); var callAdExtensionResults = downloadEntities.OfType <BulkCallAdExtension>().ToList(); OutputBulkCallAdExtensions(callAdExtensionResults); var calloutAdExtensionResults = downloadEntities.OfType <BulkCalloutAdExtension>().ToList(); OutputBulkCalloutAdExtensions(calloutAdExtensionResults); var imageAdExtensionResults = downloadEntities.OfType <BulkImageAdExtension>().ToList(); OutputBulkImageAdExtensions(imageAdExtensionResults); var locationAdExtensionResults = downloadEntities.OfType <BulkLocationAdExtension>().ToList(); OutputBulkLocationAdExtensions(locationAdExtensionResults); var priceAdExtensionResults = downloadEntities.OfType <BulkPriceAdExtension>().ToList(); OutputBulkPriceAdExtensions(priceAdExtensionResults); var reviewAdExtensionResults = downloadEntities.OfType <BulkReviewAdExtension>().ToList(); OutputBulkReviewAdExtensions(reviewAdExtensionResults); var structuredSnippetAdExtensionResults = downloadEntities.OfType <BulkStructuredSnippetAdExtension>().ToList(); OutputBulkStructuredSnippetAdExtensions(structuredSnippetAdExtensionResults); var sitelinkAdExtensionResults = downloadEntities.OfType <BulkSitelinkAdExtension>().ToList(); OutputBulkSitelinkAdExtensions(sitelinkAdExtensionResults); OutputBulkCampaignAdExtensionAssociations(downloadEntities.OfType <BulkCampaignAdExtensionAssociation>().ToList()); Reader.Dispose(); // Delete the campaign and ad extensions that were previously added. uploadEntities = new List <BulkEntity>(); foreach (var campaignResult in campaignResults) { if (campaignResult.Campaign != null) { campaignResult.Campaign.Status = CampaignStatus.Deleted; uploadEntities.Add(campaignResult); } } foreach (var actionAdExtensionResult in actionAdExtensionResults) { if (actionAdExtensionResult.ActionAdExtension.Id > 0) { actionAdExtensionResult.ActionAdExtension.Status = AdExtensionStatus.Deleted; uploadEntities.Add(actionAdExtensionResult); } } foreach (var appAdExtensionResult in appAdExtensionResults) { //By default the sample does not successfully create any app ad extensions, //because you need to provide details above such as the AppStoreId. if (appAdExtensionResult.AppAdExtension.Id > 0) { appAdExtensionResult.AppAdExtension.Status = AdExtensionStatus.Deleted; uploadEntities.Add(appAdExtensionResult); } } foreach (var callAdExtensionResult in callAdExtensionResults) { if (callAdExtensionResult.CallAdExtension.Id > 0) { callAdExtensionResult.CallAdExtension.Status = AdExtensionStatus.Deleted; uploadEntities.Add(callAdExtensionResult); } } foreach (var calloutAdExtensionResult in calloutAdExtensionResults) { if (calloutAdExtensionResult.CalloutAdExtension.Id > 0) { calloutAdExtensionResult.CalloutAdExtension.Status = AdExtensionStatus.Deleted; uploadEntities.Add(calloutAdExtensionResult); } } foreach (var imageAdExtensionResult in imageAdExtensionResults) { if (imageAdExtensionResult.ImageAdExtension.Id > 0) { imageAdExtensionResult.ImageAdExtension.Status = AdExtensionStatus.Deleted; uploadEntities.Add(imageAdExtensionResult); } } foreach (var locationAdExtensionResult in locationAdExtensionResults) { if (locationAdExtensionResult.LocationAdExtension.Id > 0) { locationAdExtensionResult.LocationAdExtension.Status = AdExtensionStatus.Deleted; uploadEntities.Add(locationAdExtensionResult); } } foreach (var priceAdExtensionResult in priceAdExtensionResults) { if (priceAdExtensionResult.PriceAdExtension.Id > 0) { priceAdExtensionResult.PriceAdExtension.Status = AdExtensionStatus.Deleted; uploadEntities.Add(priceAdExtensionResult); } } foreach (var reviewAdExtensionResult in reviewAdExtensionResults) { if (reviewAdExtensionResult.ReviewAdExtension.Id > 0) { reviewAdExtensionResult.ReviewAdExtension.Status = AdExtensionStatus.Deleted; uploadEntities.Add(reviewAdExtensionResult); } } foreach (var sitelinkAdExtensionResult in sitelinkAdExtensionResults) { if (sitelinkAdExtensionResult.SitelinkAdExtension.Id > 0) { sitelinkAdExtensionResult.SitelinkAdExtension.Status = AdExtensionStatus.Deleted; uploadEntities.Add(sitelinkAdExtensionResult); } } foreach (var structuredSnippetAdExtensionResult in structuredSnippetAdExtensionResults) { if (structuredSnippetAdExtensionResult.StructuredSnippetAdExtension.Id > 0) { structuredSnippetAdExtensionResult.StructuredSnippetAdExtension.Status = AdExtensionStatus.Deleted; uploadEntities.Add(structuredSnippetAdExtensionResult); } } // Upload and write the output OutputStatusMessage("-----\nDeleting campaign and ad extensions..."); Reader = await WriteEntitiesAndUploadFileAsync(uploadEntities); downloadEntities = Reader.ReadEntities().ToList(); OutputStatusMessage("Upload results:"); OutputBulkCampaigns(downloadEntities.OfType <BulkCampaign>().ToList()); OutputBulkActionAdExtensions(downloadEntities.OfType <BulkActionAdExtension>().ToList()); OutputBulkAppAdExtensions(downloadEntities.OfType <BulkAppAdExtension>().ToList()); OutputBulkCallAdExtensions(downloadEntities.OfType <BulkCallAdExtension>().ToList()); OutputBulkCalloutAdExtensions(downloadEntities.OfType <BulkCalloutAdExtension>().ToList()); OutputBulkImageAdExtensions(downloadEntities.OfType <BulkImageAdExtension>().ToList()); OutputBulkLocationAdExtensions(downloadEntities.OfType <BulkLocationAdExtension>().ToList()); OutputBulkPriceAdExtensions(downloadEntities.OfType <BulkPriceAdExtension>().ToList()); OutputBulkReviewAdExtensions(downloadEntities.OfType <BulkReviewAdExtension>().ToList()); OutputBulkSitelinkAdExtensions(downloadEntities.OfType <BulkSitelinkAdExtension>().ToList()); OutputBulkStructuredSnippetAdExtensions(downloadEntities.OfType <BulkStructuredSnippetAdExtension>().ToList()); Reader.Dispose(); } // 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.V13.Bulk.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V13.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 { ApiEnvironment environment = ((OAuthDesktopMobileAuthCodeGrant)authorizationData.Authentication).Environment; CampaignManagementExampleHelper = new CampaignManagementExampleHelper(this.OutputStatusMessage); BulkServiceManager = new BulkServiceManager(authorizationData, environment); var progress = new Progress <BulkOperationProgressInfo>(x => OutputStatusMessage(string.Format("{0} % Complete", x.PercentComplete.ToString(CultureInfo.InvariantCulture)))); OutputStatusMessage("Add and Update Criterions . . .\n"); await AddUpdateDeleteCriterions().ConfigureAwait(continueOnCapturedContext: false); } // 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 Campaign Management service exceptions catch (FaultException <Microsoft.BingAds.V12.CampaignManagement.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V12.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 Bulk service exceptions catch (FaultException <Microsoft.BingAds.V12.Bulk.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V12.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 { ApiEnvironment environment = ((OAuthDesktopMobileAuthCodeGrant)authorizationData.Authentication).Environment; // The Bing Merchant Center Store Id cannot be retrieved via the Bulk service, // so we'll use the Campaign Management service i.e., the GetBMCStoresByCustomerId service operation below. CampaignManagementExampleHelper = new CampaignManagementExampleHelper( OutputStatusMessageDefault: this.OutputStatusMessage); CampaignManagementExampleHelper.CampaignManagementService = new ServiceClient <ICampaignManagementService>( authorizationData: authorizationData, environment: environment); BulkServiceManager = new BulkServiceManager( authorizationData: authorizationData, apiEnvironment: environment); var progress = new Progress <BulkOperationProgressInfo>(x => OutputStatusMessage(string.Format("{0} % Complete", x.PercentComplete.ToString(CultureInfo.InvariantCulture)))); // Get a list of all Bing Merchant Center stores associated with your CustomerId. OutputStatusMessage("-----\nGetBMCStoresByCustomerId:"); IList <BMCStore> stores = (await CampaignManagementExampleHelper.GetBMCStoresByCustomerIdAsync())?.BMCStores; if (stores == null) { OutputStatusMessage( string.Format("You do not have any BMC stores registered for CustomerId {0}.", authorizationData.CustomerId) ); return; } var uploadEntities = new List <BulkEntity>(); // Create a Shopping campaign with product conditions. var bulkCampaign = new BulkCampaign { Campaign = new Campaign { Id = campaignIdKey, CampaignType = CampaignType.Shopping, Languages = new string[] { "All" }, Name = "Women's Shoes " + DateTime.UtcNow, DailyBudget = 50, BudgetType = BudgetLimitType.DailyBudgetStandard, Settings = new[] { new ShoppingSetting() { Priority = 0, SalesCountryCode = "US", StoreId = (int)stores[0].Id } }, TimeZone = "PacificTimeUSCanadaTijuana", } }; uploadEntities.Add(bulkCampaign); // Optionally, you can create a ProductScope criterion that will be associated with your Bing Shopping campaign. // You'll also be able to add more specific product conditions for each ad group. var bulkCampaignProductScope = new BulkCampaignProductScope { BiddableCampaignCriterion = new BiddableCampaignCriterion() { CampaignId = campaignIdKey, CriterionBid = null, // Not applicable for product scope Criterion = new ProductScope() { Conditions = new ProductCondition[] { new ProductCondition { Operand = "Condition", Attribute = "New" }, new ProductCondition { Operand = "CustomLabel0", Attribute = "MerchantDefinedCustomLabel" }, } }, Status = CampaignCriterionStatus.Active }, }; uploadEntities.Add(bulkCampaignProductScope); // Create the ad group that will have the product partitions. var bulkAdGroup = new BulkAdGroup { CampaignId = campaignIdKey, AdGroup = new AdGroup { Id = adGroupIdKey, Name = "Women's Red Shoe Sale", StartDate = null, EndDate = new Microsoft.BingAds.V13.CampaignManagement.Date { Month = 12, Day = 31, Year = DateTime.UtcNow.Year + 1 }, CpcBid = new Bid { Amount = 0.09 }, }, }; uploadEntities.Add(bulkAdGroup); // Create a product ad. You must add at least one product ad to the ad group. // The product ad identifier can be used for reporting analytics. // Use Merchant Promotions if you want tags to appear at the bottom of your product ad // as "special offer" links, helping to increase customer engagement. For details // on Merchant Promotions see https://help.bingads.microsoft.com/#apex/3/en/56805/0. var bulkProductAd = new BulkProductAd { AdGroupId = adGroupIdKey, ProductAd = new ProductAd { } }; uploadEntities.Add(bulkProductAd); // Upload and write the output OutputStatusMessage("-----\nAdding the campaign, product scope, ad group, and ad..."); var Reader = await WriteEntitiesAndUploadFileAsync(uploadEntities); var downloadEntities = Reader.ReadEntities().ToList(); OutputStatusMessage("Upload results:"); var campaignResults = downloadEntities.OfType <BulkCampaign>().ToList(); OutputBulkCampaigns(campaignResults); var adGroupResults = downloadEntities.OfType <BulkAdGroup>().ToList(); OutputBulkAdGroups(adGroupResults); var campaignProductScopeResults = downloadEntities.OfType <BulkCampaignProductScope>().ToList(); OutputBulkCampaignProductScopes(campaignProductScopeResults); var productAdResults = downloadEntities.OfType <BulkProductAd>().ToList(); OutputBulkProductAds(productAdResults); Reader.Dispose(); // Bid all products 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("-----\nApplying only the root as a Unit with a bid..."); var applyBulkProductPartitionActionsResults = await ApplyBulkProductPartitionActions(helper.PartitionActions); var productPartitions = await GetBulkAdGroupProductPartitionTree(adGroupId); OutputStatusMessage("The ad group's product partition only has a tree root node:"); OutputProductPartitions(productPartitions); // Let's update the bid of the root Unit we just added. var updatedRoot = GetNodeByClientId(applyBulkProductPartitionActionsResults, "root"); var bid = new FixedBid { Amount = 0.45 }; ((BiddableAdGroupCriterion)(updatedRoot.AdGroupCriterion)).CriterionBid = bid; helper = new ProductPartitionHelper(adGroupId); helper.UpdatePartition(updatedRoot); OutputStatusMessage("Updating the bid for the tree root node..."); await ApplyBulkProductPartitionActions(helper.PartitionActions); productPartitions = await GetBulkAdGroupProductPartitionTree(adGroupId); OutputStatusMessage("Updated the bid for the tree root node:"); OutputProductPartitions(productPartitions); // Initialize and 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. 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://go.microsoft.com/fwlink?LinkId=507666 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("-----\nApplying product partitions to the ad group..."); 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:"); OutputProductPartitions(productPartitions); // 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); // Delete the campaign and everything it contains e.g., ad groups and ads. uploadEntities = new List <BulkEntity>(); foreach (var campaignResult in campaignResults) { campaignResult.Campaign.Status = CampaignStatus.Deleted; uploadEntities.Add(campaignResult); } // Upload and write the output OutputStatusMessage("-----\nDeleting the campaign and everything it contains e.g., ad groups and ads..."); Reader = await WriteEntitiesAndUploadFileAsync(uploadEntities); downloadEntities = Reader.ReadEntities().ToList(); OutputStatusMessage("Upload results:"); campaignResults = downloadEntities.OfType <BulkCampaign>().ToList(); OutputBulkCampaigns(campaignResults); Reader.Dispose(); } // 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.V13.Bulk.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V13.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.V13.CampaignManagement.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V13.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(); } } }
public async override Task RunAsync(AuthorizationData authorizationData) { try { ApiEnvironment environment = ((OAuthDesktopMobileAuthCodeGrant)authorizationData.Authentication).Environment; CampaignManagementExampleHelper CampaignManagementExampleHelper = new CampaignManagementExampleHelper( OutputStatusMessageDefault: this.OutputStatusMessage); CampaignManagementExampleHelper.CampaignManagementService = new ServiceClient <ICampaignManagementService>( authorizationData: authorizationData, environment: environment); // Add a budget that can be shared by campaigns in the same account. var budgets = new[] { new Budget { Amount = 50, BudgetType = BudgetLimitType.DailyBudgetStandard, Name = "My Shared Budget " + DateTime.UtcNow, } }; OutputStatusMessage("-----\nAddBudgets:"); AddBudgetsResponse addBudgetsResponse = await CampaignManagementExampleHelper.AddBudgetsAsync( budgets : budgets); long?[] budgetIds = addBudgetsResponse.BudgetIds.ToArray(); BatchError[] budgetErrors = addBudgetsResponse.PartialErrors.ToArray(); OutputStatusMessage("BudgetIds:"); CampaignManagementExampleHelper.OutputArrayOfLong(budgetIds); OutputStatusMessage("PartialErrors:"); CampaignManagementExampleHelper.OutputArrayOfBatchError(budgetErrors); // Add one or more campaigns. var campaigns = new[] { new Campaign { // We'll use the shared budget instead of defining a daily amount for this campaign. BudgetId = budgetIds[0], DailyBudget = null, BudgetType = null, Languages = new string[] { "All" }, Name = "Women's Shoes " + DateTime.UtcNow, TimeZone = "PacificTimeUSCanadaTijuana", }, }; OutputStatusMessage("-----\nAddCampaigns:"); AddCampaignsResponse addCampaignsResponse = await CampaignManagementExampleHelper.AddCampaignsAsync( accountId : authorizationData.AccountId, campaigns : campaigns); long?[] campaignIds = addCampaignsResponse.CampaignIds.ToArray(); BatchError[] campaignErrors = addCampaignsResponse.PartialErrors.ToArray(); OutputStatusMessage("CampaignIds:"); CampaignManagementExampleHelper.OutputArrayOfLong(campaignIds); OutputStatusMessage("PartialErrors:"); CampaignManagementExampleHelper.OutputArrayOfBatchError(campaignErrors); // Delete the campaign and everything it contains e.g., ad groups and ads. OutputStatusMessage("-----\nDeleteCampaigns:"); await CampaignManagementExampleHelper.DeleteCampaignsAsync( accountId : authorizationData.AccountId, campaignIds : new[] { (long)campaignIds[0] }); OutputStatusMessage(string.Format("Deleted Campaign Id {0}", campaignIds[0])); // Delete the account's shared budget. OutputStatusMessage("-----\nDeleteBudgets:"); await CampaignManagementExampleHelper.DeleteBudgetsAsync( budgetIds : new[] { (long)budgetIds[0] }); OutputStatusMessage(string.Format("Deleted Budget Id {0}", 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 <AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <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 <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 { CampaignManagementExampleHelper CampaignManagementExampleHelper = new CampaignManagementExampleHelper(this.OutputStatusMessage); CampaignManagementExampleHelper.CampaignManagementService = 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://docs.microsoft.com/en-us/bingads/guides/universal-event-tracking. // 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 CampaignManagementExampleHelper.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 CampaignManagementExampleHelper.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) { CampaignManagementExampleHelper.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://docs.microsoft.com/en-us/bingads/guides/universal-event-tracking. // We will use the same UET tag for the remainder of this example. var tagId = uetTags[0].Id; // Add a remarketing list that depend on the UET Tag Id retreived above. var addAudiences = new[] { new RemarketingList { Description = "New list with CustomEventsRule", MembershipDuration = 30, Name = "Remarketing List with CustomEventsRule " + DateTime.UtcNow, ParentId = authorizationData.AccountId, Rule = new CustomEventsRule { // The type of user interaction you want to track. Action = "play", ActionOperator = StringOperator.Contains, // The category of event you want to track. Category = "video", CategoryOperator = StringOperator.Contains, // The name of the element that caused the action. Label = "trailer", LabelOperator = StringOperator.Contains, // A numerical value associated with that event. // Could be length of the video played etc. Value = 5.00m, ValueOperator = NumberOperator.Equals, }, Scope = EntityScope.Account, TagId = tagId }, new RemarketingList { Description = "New list with PageVisitorsRule", MembershipDuration = 30, Name = "Remarketing List with PageVisitorsRule " + DateTime.UtcNow, ParentId = authorizationData.AccountId, // The rule definition is translated to the following logical expression: // ((Url Contains X) and (ReferrerUrl DoesNotContain Z)) or ((Url DoesNotBeginWith Y)) // or ((ReferrerUrl Equals Z)) Rule = new PageVisitorsRule { RuleItemGroups = new [] { new RuleItemGroup { Items = new [] { new StringRuleItem { Operand = "Url", Operator = StringOperator.Contains, Value = "X" }, new StringRuleItem { Operand = "ReferrerUrl", Operator = StringOperator.DoesNotContain, Value = "Z" }, } }, new RuleItemGroup { Items = new [] { new StringRuleItem { Operand = "Url", Operator = StringOperator.DoesNotBeginWith, Value = "Y" }, } }, new RuleItemGroup { Items = new [] { new StringRuleItem { Operand = "ReferrerUrl", Operator = StringOperator.Equals, Value = "Z" }, } }, }, }, Scope = EntityScope.Account, TagId = tagId }, new RemarketingList { Description = "New list with PageVisitorsWhoDidNotVisitAnotherPageRule", MembershipDuration = 30, Name = "Remarketing List with PageVisitorsWhoDidNotVisitAnotherPageRule " + DateTime.UtcNow, ParentId = authorizationData.AccountId, // The rule definition is translated to the following logical expression: // (((Url Contains X) and (ReferrerUrl DoesNotContain Z)) or ((Url DoesNotBeginWith Y)) // or ((ReferrerUrl Equals Z))) // and not (((Url BeginsWith A) and (ReferrerUrl BeginsWith B)) or ((Url Contains C))) Rule = new PageVisitorsWhoDidNotVisitAnotherPageRule { ExcludeRuleItemGroups = new [] { new RuleItemGroup { Items = new [] { new StringRuleItem { Operand = "Url", Operator = StringOperator.BeginsWith, Value = "A" }, new StringRuleItem { Operand = "ReferrerUrl", Operator = StringOperator.BeginsWith, Value = "B" }, } }, new RuleItemGroup { Items = new [] { new StringRuleItem { Operand = "Url", Operator = StringOperator.Contains, Value = "C" }, } }, }, IncludeRuleItemGroups = new [] { new RuleItemGroup { Items = new [] { new StringRuleItem { Operand = "Url", Operator = StringOperator.Contains, Value = "X" }, new StringRuleItem { Operand = "ReferrerUrl", Operator = StringOperator.DoesNotContain, Value = "Z" }, } }, new RuleItemGroup { Items = new [] { new StringRuleItem { Operand = "Url", Operator = StringOperator.DoesNotBeginWith, Value = "Y" }, } }, new RuleItemGroup { Items = new [] { new StringRuleItem { Operand = "ReferrerUrl", Operator = StringOperator.Equals, Value = "Z" }, } }, }, }, Scope = EntityScope.Account, TagId = tagId }, new RemarketingList { Description = "New list with PageVisitorsWhoVisitedAnotherPageRule", MembershipDuration = 30, Name = "Remarketing List with PageVisitorsWhoVisitedAnotherPageRule " + DateTime.UtcNow, ParentId = authorizationData.AccountId, // The rule definition is translated to the following logical expression: // (((Url Contains X) and (ReferrerUrl NotEquals Z)) or ((Url DoesNotBeginWith Y)) or // ((ReferrerUrl Equals Z))) // and (((Url BeginsWith A) and (ReferrerUrl BeginsWith B)) or ((Url Contains C))) Rule = new PageVisitorsWhoVisitedAnotherPageRule { AnotherRuleItemGroups = new [] { new RuleItemGroup { Items = new [] { new StringRuleItem { Operand = "Url", Operator = StringOperator.BeginsWith, Value = "A" }, new StringRuleItem { Operand = "ReferrerUrl", Operator = StringOperator.BeginsWith, Value = "B" }, } }, new RuleItemGroup { Items = new [] { new StringRuleItem { Operand = "Url", Operator = StringOperator.Contains, Value = "C" }, } }, }, RuleItemGroups = new [] { new RuleItemGroup { Items = new [] { new StringRuleItem { Operand = "Url", Operator = StringOperator.Contains, Value = "X" }, new StringRuleItem { Operand = "ReferrerUrl", Operator = StringOperator.DoesNotContain, Value = "Z" }, } }, new RuleItemGroup { Items = new [] { new StringRuleItem { Operand = "Url", Operator = StringOperator.DoesNotBeginWith, Value = "Y" }, } }, new RuleItemGroup { Items = new [] { new StringRuleItem { Operand = "ReferrerUrl", Operator = StringOperator.Equals, Value = "Z" }, } }, }, }, Scope = EntityScope.Account, TagId = tagId }, }; // RemarketingList extends the Audience base class. // We manage remarketing lists with Audience operations. var addAudiencesResponse = await CampaignManagementExampleHelper.AddAudiencesAsync(addAudiences); var audienceIds = addAudiencesResponse.AudienceIds; // You must already have at least one remarketing list for the remainder of this example. if (audienceIds.Count < 1) { return; } var updateAudiences = new[] { new RemarketingList { Id = audienceIds[0], // The ParentId cannot be updated, even if you update the rule type. // You can either send the same value or leave ParentId empty. ParentId = authorizationData.AccountId, Rule = new CustomEventsRule { // For both add and update remarketing list operations, you must include one or more // of the following events: // Action, Category, Label, or Value. // For example if you do not include Action during update, // any existing ActionOperator and Action settings will be deleted. Action = null, //ActionOperator = null, Category = "video", CategoryOperator = StringOperator.Equals, // You cannot update the operator unless you also include the expression. // The following attempt to update LabelOperator will result in an error. Label = null, LabelOperator = StringOperator.Equals, // You must specify the previous settings unless you want // them replaced during the update conversion goal operation. Value = 5.00m, ValueOperator = NumberOperator.Equals, }, // The Scope cannot be updated, even if you update the rule type. // You can either send the same value or leave Scope empty. Scope = EntityScope.Account, // 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 RemarketingList { // You can change the remarketing rule type e.g. in this example a remarketing list // with the PageVisitorsRule had been created above at index 1. // Now we are using the returned identifier at index 1 to update the type from // PageVisitorsRule to PageVisitorsWhoDidNotVisitAnotherPageRule. Id = audienceIds[1], Rule = new PageVisitorsWhoDidNotVisitAnotherPageRule { // If you want to keep any of the previous rule items, // then you must explicitly set them again during update. ExcludeRuleItemGroups = new [] { new RuleItemGroup { Items = new [] { new StringRuleItem { Operand = "Url", Operator = StringOperator.Contains, Value = "C" }, } }, }, // If you leave the entire list of rule item groups null, // then previous settings will be retained. IncludeRuleItemGroups = null, }, }, new RemarketingList { Id = audienceIds[2], Rule = new PageVisitorsRule { // If you want to keep any of the previous rule items, // then you must explicitly set them again during update. RuleItemGroups = new [] { new RuleItemGroup { Items = new [] { new StringRuleItem { Operand = "Url", Operator = StringOperator.Contains, Value = "X" }, new StringRuleItem { Operand = "ReferrerUrl", Operator = StringOperator.DoesNotContain, Value = "Z" }, } }, }, }, }, new RemarketingList { Id = audienceIds[3], MembershipDuration = 20, // If not specified during update, the previous rule settings are retained. Rule = null, }, }; var updateAudiencesResponse = await CampaignManagementExampleHelper.UpdateAudiencesAsync(updateAudiences); OutputStatusMessage("Updated audiences. List of errors (if applicable):\n"); CampaignManagementExampleHelper.OutputArrayOfBatchError(updateAudiencesResponse.PartialErrors); // Add an ad group in a campaign. The ad group will later be associated with remarketing lists. 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", }, }; var adGroups = new[] { new AdGroup { Name = "Women's Red Shoe Sale", AdDistribution = AdDistribution.Search, PricingModel = PricingModel.Cpc, StartDate = null, EndDate = new Date { Month = 12, Day = 31, Year = DateTime.UtcNow.Year + 1 }, SearchBid = new Bid { Amount = 0.09 }, Language = "English", TrackingUrlTemplate = null, // Applicable for all remarketing lists that are associated with this ad group. TargetAndBid indicates // that you want to show ads only to people included in the remarketing list, with the option to change // the bid amount. Ads in this ad group will only show to people included in the remarketing list. RemarketingTargetingSetting = RemarketingTargetingSetting.TargetAndBid } }; AddCampaignsResponse addCampaignsResponse = await CampaignManagementExampleHelper.AddCampaignsAsync(authorizationData.AccountId, campaigns); long?[] campaignIds = addCampaignsResponse.CampaignIds.ToArray(); BatchError[] campaignErrors = addCampaignsResponse.PartialErrors.ToArray(); CampaignManagementExampleHelper.OutputArrayOfLong(campaignIds); CampaignManagementExampleHelper.OutputArrayOfBatchError(campaignErrors); AddAdGroupsResponse addAdGroupsResponse = await CampaignManagementExampleHelper.AddAdGroupsAsync((long)campaignIds[0], adGroups); long?[] adGroupIds = addAdGroupsResponse.AdGroupIds.ToArray(); BatchError[] adGroupErrors = addAdGroupsResponse.PartialErrors.ToArray(); CampaignManagementExampleHelper.OutputArrayOfLong(adGroupIds); CampaignManagementExampleHelper.OutputArrayOfBatchError(adGroupErrors); // If the campaign or ad group add operations failed then we cannot continue this example. if (adGroupIds == null || adGroupIds.Length < 1) { return; } var adGroupRemarketingListAssociations = new List <AdGroupCriterion>(); // This example associates all of the remarketing lists with the new ad group. var getAudienceIds = new List <long>(); foreach (var listId in audienceIds) { getAudienceIds.Add((long)listId); } var remarketingLists = (await CampaignManagementExampleHelper.GetAudiencesByIdsAsync( getAudienceIds, AudienceType.RemarketingList, AudienceAdditionalField.AudienceNetworkSize | AudienceAdditionalField.SearchSize, true)).Audiences; foreach (var remarketingList in remarketingLists) { if (remarketingList.Id != null) { var biddableAdGroupCriterion = new BiddableAdGroupCriterion { AdGroupId = (long)adGroupIds[0], Criterion = new AudienceCriterion { AudienceId = (long)remarketingList.Id, AudienceType = AudienceType.RemarketingList, }, CriterionBid = new BidMultiplier { Multiplier = 20.00, }, Status = AdGroupCriterionStatus.Active, }; adGroupRemarketingListAssociations.Add(biddableAdGroupCriterion); OutputStatusMessage(string.Format("Associating the following remarketing list with AdGroup Id {0}.\n", (long)adGroupIds[0])); CampaignManagementExampleHelper.OutputRemarketingList((RemarketingList)remarketingList); } } var addAdGroupCriterionsResponse = await CampaignManagementExampleHelper.AddAdGroupCriterionsAsync( adGroupRemarketingListAssociations, AdGroupCriterionType.Audience); var adGroupCriterionIds = new List <long>(); foreach (long id in addAdGroupCriterionsResponse.AdGroupCriterionIds) { adGroupCriterionIds.Add(id); } var getAdGroupCriterionsByIdsResponse = await CampaignManagementExampleHelper.GetAdGroupCriterionsByIdsAsync( adGroupCriterionIds, true, (long)adGroupIds[0], AdGroupCriterionType.RemarketingList); foreach (var adGroupRemarketingListAssociation in getAdGroupCriterionsByIdsResponse.AdGroupCriterions) { OutputStatusMessage("The following ad group remarketing list association was added.\n"); CampaignManagementExampleHelper.OutputArrayOfAdGroupCriterion(new AdGroupCriterion[] { adGroupRemarketingListAssociation }); } // You can store the association IDs which can be used to update or delete associations later. var nullableAdGroupCriterionIds = addAdGroupCriterionsResponse.AdGroupCriterionIds; // If the associations were added and retrieved successfully let's practice updating and deleting one of them. if (nullableAdGroupCriterionIds != null && nullableAdGroupCriterionIds.Count > 0) { var updateAdGroupRemarketingListAssociation = new BiddableAdGroupCriterion { AdGroupId = (long)adGroupIds[0], Criterion = new AudienceCriterion { AudienceType = AudienceType.RemarketingList, }, CriterionBid = new BidMultiplier { Multiplier = 10.00, }, Id = nullableAdGroupCriterionIds[0], Status = AdGroupCriterionStatus.Active, }; var updateAdGroupCriterionsResponse = await CampaignManagementExampleHelper.UpdateAdGroupCriterionsAsync( new BiddableAdGroupCriterion[] { updateAdGroupRemarketingListAssociation }, AdGroupCriterionType.Audience ); var deleteAdGroupCriterionsResponse = await CampaignManagementExampleHelper.DeleteAdGroupCriterionsAsync( adGroupCriterionIds, (long)adGroupIds[0], AdGroupCriterionType.Audience ); } // Delete the campaign, ad group, and ad group remarketing list associations that were previously added. var deleteCampaignsResponse = (await CampaignManagementExampleHelper.DeleteCampaignsAsync(authorizationData.AccountId, new[] { (long)campaignIds[0] })); OutputStatusMessage(string.Format("Deleted Campaign Id {0}\n", campaignIds[0])); // Delete the remarketing list. var deleteAudiencesResponse = (await CampaignManagementExampleHelper.DeleteAudiencesAsync(new[] { (long)audienceIds[0] })); OutputStatusMessage(string.Format("Deleted Audience Id {0}\n", audienceIds[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.V11.CampaignManagement.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V11.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.V11.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 { ApiEnvironment environment = ((OAuthDesktopMobileAuthCodeGrant)authorizationData.Authentication).Environment; CampaignManagementExampleHelper CampaignManagementExampleHelper = new CampaignManagementExampleHelper( OutputStatusMessageDefault: this.OutputStatusMessage); CampaignManagementExampleHelper.CampaignManagementService = new ServiceClient <ICampaignManagementService>( authorizationData: authorizationData, environment: environment); // Add an image to your media library. // The image asset is needed later to create the responsive ad. var landscapeImageMedia = GetImageMedia( "Image191x100", MediaFilePath + ResponsiveAdMediaFileName, System.Drawing.Imaging.ImageFormat.Png); var media = new Media[] { landscapeImageMedia, }; OutputStatusMessage("-----\nAddMedia:"); AddMediaResponse addMediaResponse = await CampaignManagementExampleHelper.AddMediaAsync( accountId : authorizationData.AccountId, media : media); long[] mediaIds = addMediaResponse.MediaIds.ToArray(); OutputStatusMessage("MediaIds:"); CampaignManagementExampleHelper.OutputArrayOfLong(mediaIds); // Create an Audience campaign with one ad group and a responsive ad. var campaigns = new[] { new Campaign { BudgetType = BudgetLimitType.DailyBudgetStandard, // CampaignType must be set for Audience campaigns CampaignType = CampaignType.Audience, DailyBudget = 50, Description = "Red shoes line.", // Languages must be set for Audience campaigns Languages = new string[] { "All" }, Name = "Women's Shoes " + DateTime.UtcNow, TimeZone = "PacificTimeUSCanadaTijuana", }, }; OutputStatusMessage("-----\nAddCampaigns:"); AddCampaignsResponse addCampaignsResponse = await CampaignManagementExampleHelper.AddCampaignsAsync( accountId : authorizationData.AccountId, campaigns : campaigns, includeDynamicSearchAdsSource : false); long?[] campaignIds = addCampaignsResponse.CampaignIds.ToArray(); BatchError[] campaignErrors = addCampaignsResponse.PartialErrors.ToArray(); OutputStatusMessage("CampaignIds:"); CampaignManagementExampleHelper.OutputArrayOfLong(campaignIds); OutputStatusMessage("PartialErrors:"); CampaignManagementExampleHelper.OutputArrayOfBatchError(campaignErrors); // Add an ad group within the campaign. var adGroups = new[] { new AdGroup { Name = "Women's Red Shoe Sale", StartDate = null, EndDate = new Date { Month = 12, Day = 31, Year = DateTime.UtcNow.Year + 1 }, CpcBid = new Bid { Amount = 0.09 }, // Network cannot be set for ad groups in Audience campaigns Network = null, } }; OutputStatusMessage("-----\nAddAdGroups:"); AddAdGroupsResponse addAdGroupsResponse = await CampaignManagementExampleHelper.AddAdGroupsAsync( campaignId : (long)campaignIds[0], adGroups : adGroups, returnInheritedBidStrategyTypes : false); long?[] adGroupIds = addAdGroupsResponse.AdGroupIds.ToArray(); BatchError[] adGroupErrors = addAdGroupsResponse.PartialErrors.ToArray(); OutputStatusMessage("AdGroupIds:"); CampaignManagementExampleHelper.OutputArrayOfLong(adGroupIds); OutputStatusMessage("PartialErrors:"); CampaignManagementExampleHelper.OutputArrayOfBatchError(adGroupErrors); // Add a responsive ad within the ad group. var ads = new Ad[] { new ResponsiveAd { BusinessName = "Contoso", CallToAction = CallToAction.AddToCart, FinalUrls = new[] { "http://www.contoso.com/womenshoesale" }, Headline = "Fast & Easy Setup", Images = new [] { // You are only required to provide a landscape image asset. // Optionally you can include additional asset links, i.e., one image asset for each supported sub type. // For any image asset sub types that you do not explicitly set, // the service will automatically create image asset links by cropping the LandscapeImageMedia. new AssetLink { Asset = new ImageAsset { CropHeight = null, CropWidth = null, CropX = null, CropY = null, Id = mediaIds[0], Name = "My LandscapeImageMedia", SubType = "LandscapeImageMedia", }, }, }, LongHeadline = "Find New Customers & Increase Sales!", Text = "Find New Customers & Increase Sales! Start Advertising on Contoso Today.", }, }; OutputStatusMessage("-----\nAddAds:"); AddAdsResponse addAdsResponse = await CampaignManagementExampleHelper.AddAdsAsync( adGroupId : (long)adGroupIds[0], ads : ads); long?[] adIds = addAdsResponse.AdIds.ToArray(); BatchError[] adErrors = addAdsResponse.PartialErrors.ToArray(); OutputStatusMessage("AdIds:"); CampaignManagementExampleHelper.OutputArrayOfLong(adIds); OutputStatusMessage("PartialErrors:"); CampaignManagementExampleHelper.OutputArrayOfBatchError(adErrors); // Delete the account's media. OutputStatusMessage("-----\nDeleteMedia:"); await CampaignManagementExampleHelper.DeleteMediaAsync( accountId : authorizationData.AccountId, mediaIds : mediaIds); foreach (var id in mediaIds) { OutputStatusMessage(string.Format("Deleted Media Id {0}", id)); } // Delete the campaign and everything it contains e.g., ad groups and ads. OutputStatusMessage("-----\nDeleteCampaigns:"); await CampaignManagementExampleHelper.DeleteCampaignsAsync( accountId : authorizationData.AccountId, campaignIds : new[] { (long)campaignIds[0] }); OutputStatusMessage(string.Format("Deleted Campaign Id {0}", 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.V12.CampaignManagement.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V12.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.V12.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 { ApiEnvironment environment = ((OAuthDesktopMobileAuthCodeGrant)authorizationData.Authentication).Environment; AdInsightExampleHelper AdInsightExampleHelper = new AdInsightExampleHelper( OutputStatusMessageDefault: this.OutputStatusMessage); AdInsightExampleHelper.AdInsightService = new ServiceClient <IAdInsightService>( authorizationData: authorizationData, environment: environment); CampaignManagementExampleHelper CampaignManagementExampleHelper = new CampaignManagementExampleHelper( OutputStatusMessageDefault: this.OutputStatusMessage); CampaignManagementExampleHelper.CampaignManagementService = new ServiceClient <ICampaignManagementService>( authorizationData: authorizationData, environment: environment); OutputStatusMessage("-----\nGetCampaignsByAccountId:"); var campaigns = (await CampaignManagementExampleHelper.GetCampaignsByAccountIdAsync( accountId: authorizationData.AccountId, campaignType: AllCampaignTypes, returnAdditionalFields: CampaignAdditionalField.AdScheduleUseSearcherTimeZone)).Campaigns; OutputStatusMessage("Campaigns:"); CampaignManagementExampleHelper.OutputArrayOfCampaign(campaigns); IList <BudgetOpportunity> opportunities = null; // Get the budget opportunities for each campaign in the current account. foreach (var campaign in campaigns) { if (campaign.Id != null) { OutputStatusMessage("-----\nGetBudgetOpportunities:"); opportunities = (await AdInsightExampleHelper.GetBudgetOpportunitiesAsync( campaignId: (long)campaign.Id)).Opportunities; OutputStatusMessage("Opportunities:"); AdInsightExampleHelper.OutputArrayOfBudgetOpportunity(opportunities); } } } // 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.V13.AdInsight.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V13.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.V13.CampaignManagement.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V13.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 async override Task RunAsync(AuthorizationData authorizationData) { try { ApiEnvironment environment = ((OAuthDesktopMobileAuthCodeGrant)authorizationData.Authentication).Environment; // Used to output the Campaign Management objects within Bulk entities. CampaignManagementExampleHelper = new CampaignManagementExampleHelper( OutputStatusMessageDefault: this.OutputStatusMessage); BulkServiceManager = new BulkServiceManager( authorizationData: authorizationData, apiEnvironment: environment); var progress = new Progress<BulkOperationProgressInfo>(x => OutputStatusMessage(string.Format("{0} % Complete", x.PercentComplete.ToString(CultureInfo.InvariantCulture)))); var uploadEntities = new List<BulkEntity>(); // Add a search campaign. var bulkCampaign = new BulkCampaign { Campaign = new Campaign { BudgetType = BudgetLimitType.DailyBudgetStandard, DailyBudget = 50, CampaignType = CampaignType.Search, Id = campaignIdKey, Languages = new string[] { "All" }, Name = "Women's Shoes " + DateTime.UtcNow, TimeZone = "PacificTimeUSCanadaTijuana", }, }; uploadEntities.Add(bulkCampaign); // Add an ad group within the campaign. var bulkAdGroup = new BulkAdGroup { CampaignId = campaignIdKey, AdGroup = new AdGroup { Id = adGroupIdKey, Name = "Women's Red Shoe Sale", StartDate = null, EndDate = new Microsoft.BingAds.V12.CampaignManagement.Date { Month = 12, Day = 31, Year = DateTime.UtcNow.Year + 1 }, CpcBid = new Bid { Amount = 0.09 }, }, }; uploadEntities.Add(bulkAdGroup); // Add keywords and ads within the ad group. var bulkKeyword = new BulkKeyword{ AdGroupId = adGroupIdKey, Keyword = new Keyword { Bid = new Bid { Amount = 0.47 }, Param2 = "10% Off", MatchType = MatchType.Phrase, Text = "Brand-A Shoes", }, }; uploadEntities.Add(bulkKeyword); var bulkExpandedTextAd = new BulkExpandedTextAd { AdGroupId = adGroupIdKey, ExpandedTextAd = new ExpandedTextAd { TitlePart1 = "Contoso", TitlePart2 = "Quick & Easy Setup", TitlePart3 = "Seemless Integration", Text = "Find New Customers & Increase Sales!", TextPart2 = "Start Advertising on Contoso Today.", Path1 = "seattle", Path2 = "shoe sale", FinalUrls = new[] { "http://www.contoso.com/womenshoesale" }, }, }; uploadEntities.Add(bulkExpandedTextAd); // Upload and write the output OutputStatusMessage("-----\nAdding campaign, ad group, keyword, and ad..."); var Reader = await WriteEntitiesAndUploadFileAsync(uploadEntities); var downloadEntities = Reader.ReadEntities().ToList(); OutputStatusMessage("Upload results:"); var campaignResults = downloadEntities.OfType<BulkCampaign>().ToList(); OutputBulkCampaigns(campaignResults); var adGroupResults = downloadEntities.OfType<BulkAdGroup>().ToList(); OutputBulkAdGroups(adGroupResults); var keywordResults = downloadEntities.OfType<BulkKeyword>().ToList(); OutputBulkKeywords(keywordResults); var expandedTextAdResults = downloadEntities.OfType<BulkExpandedTextAd>().ToList(); OutputBulkExpandedTextAds(expandedTextAdResults); Reader.Dispose(); // Delete the campaign and everything it contains e.g., ad groups and ads. uploadEntities = new List<BulkEntity>(); foreach (var campaignResult in campaignResults) { campaignResult.Campaign.Status = CampaignStatus.Deleted; uploadEntities.Add(campaignResult); } // Upload and write the output OutputStatusMessage("-----\nDeleting the campaign and everything it contains e.g., ad groups and ads..."); Reader = await WriteEntitiesAndUploadFileAsync(uploadEntities); downloadEntities = Reader.ReadEntities().ToList(); OutputStatusMessage("Upload results:"); campaignResults = downloadEntities.OfType<BulkCampaign>().ToList(); OutputBulkCampaigns(campaignResults); Reader.Dispose(); } // 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.V12.Bulk.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.V12.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 { ApiEnvironment environment = ((OAuthDesktopMobileAuthCodeGrant)authorizationData.Authentication).Environment; // Used to output the Campaign Management objects within Bulk entities. CampaignManagementExampleHelper = new CampaignManagementExampleHelper( OutputStatusMessageDefault: this.OutputStatusMessage); BulkServiceManager = new BulkServiceManager( authorizationData: authorizationData, apiEnvironment: environment); var progress = new Progress <BulkOperationProgressInfo>(x => OutputStatusMessage(string.Format("{0} % Complete", x.PercentComplete.ToString(CultureInfo.InvariantCulture)))); var uploadEntities = new List <BulkEntity>(); // Setup an ad customizer feed that can be referenced later in the ad copy. var bulkAdCustomizerFeed = new BulkFeed { CustomAttributes = new[] { new FeedCustomAttributeContract { FeedAttributeType = "String", Name = "Product" }, new FeedCustomAttributeContract { FeedAttributeType = "String", Name = "Materials_Lightweight" }, new FeedCustomAttributeContract { FeedAttributeType = "String", Name = "Description_Lightweight" }, new FeedCustomAttributeContract { FeedAttributeType = "Int64", Name = "Finishes" }, new FeedCustomAttributeContract { FeedAttributeType = "Price", Name = "StartPrice" }, }, Id = feedIdKey, Name = "My AdCustomizerFeed " + DateTime.UtcNow, Status = Status.Active, SubType = "AdCustomizerFeed", }; uploadEntities.Add(bulkAdCustomizerFeed); var adCustomizerFeedItemCustomAttributes = new Dictionary <string, object>(); adCustomizerFeedItemCustomAttributes.Add("Product", "Contoso 900"); adCustomizerFeedItemCustomAttributes.Add("Materials_Lightweight", "titanium or acetate"); adCustomizerFeedItemCustomAttributes.Add("Description_Lightweight", "Stylish, lightweight shades"); adCustomizerFeedItemCustomAttributes.Add("Finishes", 8); adCustomizerFeedItemCustomAttributes.Add("StartPrice", "$24.99"); var serializerSettings = new JsonSerializerSettings(); serializerSettings.NullValueHandling = NullValueHandling.Ignore; var adCustomizerFeedItemCustomAttributesJson = JsonConvert.SerializeObject( adCustomizerFeedItemCustomAttributes, serializerSettings); var bulkAdCustomizerFeedItem = new BulkFeedItem { FeedId = feedIdKey, CustomAttributes = adCustomizerFeedItemCustomAttributesJson, Id = null, AdGroupName = null, AudienceId = null, CampaignName = null, DayTimeRanges = new[] { new DayTime { Day = Day.Monday, StartHour = 9, StartMinute = Minute.Zero, EndHour = 21, EndMinute = Minute.Zero, }, }, EndDate = null, StartDate = DateTime.UtcNow, IntentOption = IntentOption.PeopleIn, Keyword = "lightweight sunglasses", LocationId = 190, MatchType = MatchType.Broad, DevicePreference = null, Status = Status.Active }; uploadEntities.Add(bulkAdCustomizerFeedItem); // Add a search campaign. var bulkCampaign = new BulkCampaign { Campaign = new Campaign { BudgetType = BudgetLimitType.DailyBudgetStandard, DailyBudget = 50, CampaignType = CampaignType.Search, Id = campaignIdKey, Languages = new string[] { "All" }, Name = "Summer Sunglasses " + DateTime.UtcNow, TimeZone = "PacificTimeUSCanadaTijuana", }, }; uploadEntities.Add(bulkCampaign); // Add an ad group within the campaign. var bulkAdGroup = new BulkAdGroup { CampaignId = campaignIdKey, AdGroup = new AdGroup { Id = adGroupIdKey, Name = "Sunglasses Sale", StartDate = null, EndDate = new Microsoft.BingAds.V13.CampaignManagement.Date { Month = 12, Day = 31, Year = DateTime.UtcNow.Year + 1 }, CpcBid = new Bid { Amount = 0.09 }, }, }; uploadEntities.Add(bulkAdGroup); // Add keywords and ads within the ad group. var bulkKeyword = new BulkKeyword { AdGroupId = adGroupIdKey, Keyword = new Keyword { Bid = new Bid { Amount = 0.47 }, Param2 = "10% Off", MatchType = MatchType.Phrase, Text = "Brand-A Sunglasses", }, }; uploadEntities.Add(bulkKeyword); var bulkExpandedTextAd = new BulkExpandedTextAd { AdGroupId = adGroupIdKey, ExpandedTextAd = new ExpandedTextAd { TitlePart1 = "The latest {=Sunglasses.Product}s", TitlePart2 = "In {=Sunglasses.Materials_Lightweight}", TitlePart3 = null, Text = "{=Sunglasses.Description_Lightweight} in {=Sunglasses.Finishes} finishes.", TextPart2 = "Starting at only {=Sunglasses.StartPrice}!", Path1 = "deals", Path2 = null, FinalUrls = new[] { "https://www.contoso.com" }, }, }; uploadEntities.Add(bulkExpandedTextAd); // Upload and write the output OutputStatusMessage("-----\nAdding the ad customizer feed, campaign, ad group, keyword, and ad..."); var Reader = await WriteEntitiesAndUploadFileAsync(uploadEntities); var downloadEntities = Reader.ReadEntities().ToList(); OutputStatusMessage("Upload results:"); var feedResults = downloadEntities.OfType <BulkFeed>().ToList(); OutputBulkFeeds(feedResults); var feedItemResults = downloadEntities.OfType <BulkFeedItem>().ToList(); OutputBulkFeedItems(feedItemResults); var campaignResults = downloadEntities.OfType <BulkCampaign>().ToList(); OutputBulkCampaigns(campaignResults); var adGroupResults = downloadEntities.OfType <BulkAdGroup>().ToList(); OutputBulkAdGroups(adGroupResults); var keywordResults = downloadEntities.OfType <BulkKeyword>().ToList(); OutputBulkKeywords(keywordResults); var expandedTextAdResults = downloadEntities.OfType <BulkExpandedTextAd>().ToList(); OutputBulkExpandedTextAds(expandedTextAdResults); Reader.Dispose(); // Delete the feed and campaign and everything it contains e.g., ad groups and ads. uploadEntities = new List <BulkEntity>(); foreach (var feedResult in feedResults) { feedResult.Status = Status.Deleted; uploadEntities.Add(feedResult); } foreach (var campaignResult in campaignResults) { campaignResult.Campaign.Status = CampaignStatus.Deleted; uploadEntities.Add(campaignResult); } // Upload and write the output OutputStatusMessage("-----\nDeleting the feed and campaign and everything it contains e.g., ad groups and ads..."); Reader = await WriteEntitiesAndUploadFileAsync(uploadEntities); downloadEntities = Reader.ReadEntities().ToList(); OutputStatusMessage("Upload results:"); campaignResults = downloadEntities.OfType <BulkCampaign>().ToList(); OutputBulkCampaigns(campaignResults); Reader.Dispose(); } // 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.V13.Bulk.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V13.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 { ApiEnvironment environment = ((OAuthDesktopMobileAuthCodeGrant)authorizationData.Authentication).Environment; CampaignManagementExampleHelper CampaignManagementExampleHelper = new CampaignManagementExampleHelper(this.OutputStatusMessage); CampaignManagementExampleHelper.CampaignManagementService = new ServiceClient <ICampaignManagementService>(authorizationData, environment); #region MediaRepresentations var landscapeImageMedia = GetImageMedia( "Image191x100", MediaFilePath + LandscapeImageMediaFileName, System.Drawing.Imaging.ImageFormat.Png); var landscapeLogoMedia = GetImageMedia( "Image4x1", MediaFilePath + LandscapeLogoMediaFileName, System.Drawing.Imaging.ImageFormat.Png); var squareImageMedia = GetImageMedia( "Image1x1", MediaFilePath + SquareImageMediaFileName, System.Drawing.Imaging.ImageFormat.Png); var squareLogoMedia = GetImageMedia( "Image1x1", MediaFilePath + SquareLogoMediaFileName, System.Drawing.Imaging.ImageFormat.Png); var imageAdExtensionMedia = GetImageMedia( "Image15x10", MediaFilePath + ImageAdExtensionMediaFileName, System.Drawing.Imaging.ImageFormat.Png); var addMedia = new Media[] { landscapeImageMedia, landscapeLogoMedia, squareImageMedia, squareLogoMedia, imageAdExtensionMedia }; CampaignManagementExampleHelper.OutputArrayOfMedia(addMedia); var imageMediaIds = (await CampaignManagementExampleHelper.AddMediaAsync( authorizationData.AccountId, addMedia)).MediaIds; // The index of returned IDs is consistent with the order you submitted them in the request; // however, the sequence of the IDs themselves is not guaranteed. For example you might observe: // - Landscape Image Media Id == imageMediaIds[0] == 1 // - Landscape Logo Media Id == imageMediaIds[1] == 4 // - Square Image Media Id == imageMediaIds[2] == 3 // - Square Logo Media Id == imageMediaIds[3] == 2 // - Image Ad Extension Media Id == imageMediaIds[4] == 0 // You can use the first four Media Ids when you add or update a Responsive Ad // in an Audience campaign e.g., see AudienceCampaigns.cs. var adMediaIds = new List <long> { imageMediaIds[0], imageMediaIds[1], imageMediaIds[2], imageMediaIds[3] }; OutputStatusMessage("Media Ids for Responsive Ad:"); CampaignManagementExampleHelper.OutputArrayOfLong(adMediaIds); // You can use the fifth Media Id when you add or update an Image Ad Extension // in a Search campaign e.g., see AdExtensions.cs. var extensionMediaIds = new List <long> { imageMediaIds[4] }; OutputStatusMessage("Media Ids for Image Ad Extension:"); CampaignManagementExampleHelper.OutputArrayOfLong(extensionMediaIds); // Get the media representations to confirm the stored dimensions // and get the Url where you can later view or download the media. var getResponsiveAdMediaMetaData = (await CampaignManagementExampleHelper.GetMediaMetaDataByAccountIdAsync( MediaEnabledEntityFilter.ResponsiveAd)).MediaMetaData; CampaignManagementExampleHelper.OutputArrayOfMediaMetaData(getResponsiveAdMediaMetaData); var getImageAdExtensionMediaMetaData = (await CampaignManagementExampleHelper.GetMediaMetaDataByAccountIdAsync( MediaEnabledEntityFilter.ImageAdExtension)).MediaMetaData; CampaignManagementExampleHelper.OutputArrayOfMediaMetaData(getImageAdExtensionMediaMetaData); var getMediaMetaData = (await CampaignManagementExampleHelper.GetMediaMetaDataByIdsAsync(imageMediaIds)).MediaMetaData; CampaignManagementExampleHelper.OutputArrayOfMediaMetaData(getMediaMetaData); #endregion MediaRepresentations #region Delete // Comment out (disable) the delete operation if you want to use the media IDs // in another example e.g., AudienceCampaigns.cs. var deleteMediaResponse = (await CampaignManagementExampleHelper.DeleteMediaAsync( authorizationData.AccountId, imageMediaIds)); foreach (var id in imageMediaIds) { OutputStatusMessage(string.Format("Deleted Media Id {0}\n", id)); } #endregion Delete } // 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.V12.CampaignManagement.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V12.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.V12.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 { ApiEnvironment environment = ((OAuthDesktopMobileAuthCodeGrant)authorizationData.Authentication).Environment; // Media cannot be added via the Bulk service, so we'll use the Campaign Management service // i.e., the AddMedia service operation below. CampaignManagementExampleHelper = new CampaignManagementExampleHelper( OutputStatusMessageDefault: this.OutputStatusMessage); CampaignManagementExampleHelper.CampaignManagementService = new ServiceClient <ICampaignManagementService>( authorizationData: authorizationData, environment: environment); BulkServiceManager = new BulkServiceManager( authorizationData: authorizationData, apiEnvironment: environment); var progress = new Progress <BulkOperationProgressInfo>(x => OutputStatusMessage(string.Format("{0} % Complete", x.PercentComplete.ToString(CultureInfo.InvariantCulture)))); // Add an image to your media library. // The image asset is needed later to create the responsive ad. var landscapeImageMedia = GetImageMedia( "Image191x100", MediaFilePath + ResponsiveAdMediaFileName, System.Drawing.Imaging.ImageFormat.Png); var media = new Media[] { landscapeImageMedia, }; OutputStatusMessage("-----\nAddMedia:"); AddMediaResponse addMediaResponse = await CampaignManagementExampleHelper.AddMediaAsync( accountId : authorizationData.AccountId, media : media); long[] mediaIds = addMediaResponse.MediaIds.ToArray(); OutputStatusMessage("MediaIds:"); CampaignManagementExampleHelper.OutputArrayOfLong(mediaIds); var uploadEntities = new List <BulkEntity>(); // Create an Audience campaign with one ad group and a responsive ad. var bulkCampaign = new BulkCampaign { Campaign = new Campaign { Id = campaignIdKey, // CampaignType must be set for Audience campaigns CampaignType = CampaignType.Audience, // Languages must be set for Audience campaigns Languages = new string[] { "All" }, Name = "Women's Shoes " + DateTime.UtcNow, DailyBudget = 50, BudgetType = BudgetLimitType.DailyBudgetStandard, Settings = null, TimeZone = "PacificTimeUSCanadaTijuana", } }; uploadEntities.Add(bulkCampaign); // Specify one or more ad groups. var bulkAdGroup = new BulkAdGroup { CampaignId = campaignIdKey, AdGroup = new AdGroup { Id = adGroupIdKey, Name = "Women's Red Shoe Sale", StartDate = null, EndDate = new Microsoft.BingAds.V13.CampaignManagement.Date { Month = 12, Day = 31, Year = DateTime.UtcNow.Year + 1 }, CpcBid = new Bid { Amount = 0.09 }, // Language cannot be set for ad groups in Audience campaigns Language = null, // Network cannot be set for ad groups in Audience campaigns Network = null, }, }; uploadEntities.Add(bulkAdGroup); var bulkResponsiveAd = new BulkResponsiveAd { AdGroupId = adGroupIdKey, ResponsiveAd = new ResponsiveAd { Id = responsiveAdIdKey, BusinessName = "Contoso", CallToAction = CallToAction.AddToCart, FinalUrls = new[] { "https://www.contoso.com/womenshoesale" }, ForwardCompatibilityMap = null, Headline = "Fast & Easy Setup", Images = new[] { // You are only required to provide a landscape image asset. // Optionally you can include additional asset links, i.e., one image asset for each supported sub type. // For any image asset sub types that you do not explicitly set, // the service will automatically create image asset links by cropping the LandscapeImageMedia. new AssetLink { Asset = new ImageAsset { CropHeight = null, CropWidth = null, CropX = null, CropY = null, Id = mediaIds[0], Name = "My LandscapeImageMedia", SubType = "LandscapeImageMedia", Type = null }, }, }, LongHeadlineString = "Find New Customers & Increase Sales!", Text = "Find New Customers & Increase Sales! Start Advertising on Contoso Today.", }, }; uploadEntities.Add(bulkResponsiveAd); // Upload and write the output OutputStatusMessage("-----\nAdding campaign, ad group, and ad..."); var Reader = await WriteEntitiesAndUploadFileAsync(uploadEntities); var downloadEntities = Reader.ReadEntities().ToList(); OutputStatusMessage("Upload results:"); var campaignResults = downloadEntities.OfType <BulkCampaign>().ToList(); OutputBulkCampaigns(campaignResults); var adGroupResults = downloadEntities.OfType <BulkAdGroup>().ToList(); OutputBulkAdGroups(adGroupResults); var responsiveAdResults = downloadEntities.OfType <BulkResponsiveAd>().ToList(); OutputBulkResponsiveAds(responsiveAdResults); Reader.Dispose(); // Delete the campaign and everything it contains e.g., ad groups and ads. uploadEntities = new List <BulkEntity>(); foreach (var campaignResult in campaignResults) { campaignResult.Campaign.Status = CampaignStatus.Deleted; uploadEntities.Add(campaignResult); } OutputStatusMessage("-----\nDeleting campaign, ad group, and ad..."); Reader = await WriteEntitiesAndUploadFileAsync(uploadEntities); downloadEntities = Reader.ReadEntities().ToList(); OutputStatusMessage("Upload results:"); campaignResults = downloadEntities.OfType <BulkCampaign>().ToList(); OutputBulkCampaigns(campaignResults); Reader.Dispose(); } // 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.V13.Bulk.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V13.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 { ApiEnvironment environment = ((OAuthDesktopMobileAuthCodeGrant)authorizationData.Authentication).Environment; CampaignManagementExampleHelper CampaignManagementExampleHelper = new CampaignManagementExampleHelper( OutputStatusMessageDefault: this.OutputStatusMessage); CampaignManagementExampleHelper.CampaignManagementService = new ServiceClient <ICampaignManagementService>( authorizationData: authorizationData, environment: environment); // Add a search campaign. var campaigns = new[] { new Campaign { BudgetType = BudgetLimitType.DailyBudgetStandard, DailyBudget = 50, CampaignType = CampaignType.Search, Languages = new string[] { "All" }, Name = "Women's Shoes " + DateTime.UtcNow, TimeZone = "PacificTimeUSCanadaTijuana", }, }; OutputStatusMessage("-----\nAddCampaigns:"); AddCampaignsResponse addCampaignsResponse = await CampaignManagementExampleHelper.AddCampaignsAsync( accountId : authorizationData.AccountId, campaigns : campaigns); long?[] campaignIds = addCampaignsResponse.CampaignIds.ToArray(); BatchError[] campaignErrors = addCampaignsResponse.PartialErrors.ToArray(); OutputStatusMessage("CampaignIds:"); CampaignManagementExampleHelper.OutputArrayOfLong(campaignIds); OutputStatusMessage("PartialErrors:"); CampaignManagementExampleHelper.OutputArrayOfBatchError(campaignErrors); // Add an ad group within the campaign. var adGroups = new[] { new AdGroup { Name = "Women's Red Shoe Sale", StartDate = null, EndDate = new Date { Month = 12, Day = 31, Year = DateTime.UtcNow.Year + 1 }, CpcBid = new Bid { Amount = 0.09 }, } }; OutputStatusMessage("-----\nAddAdGroups:"); AddAdGroupsResponse addAdGroupsResponse = await CampaignManagementExampleHelper.AddAdGroupsAsync( campaignId : (long)campaignIds[0], adGroups : adGroups, returnInheritedBidStrategyTypes : false); long?[] adGroupIds = addAdGroupsResponse.AdGroupIds.ToArray(); BatchError[] adGroupErrors = addAdGroupsResponse.PartialErrors.ToArray(); OutputStatusMessage("AdGroupIds:"); CampaignManagementExampleHelper.OutputArrayOfLong(adGroupIds); OutputStatusMessage("PartialErrors:"); CampaignManagementExampleHelper.OutputArrayOfBatchError(adGroupErrors); // Add keywords and ads within the ad group. var keywords = new[] { new Keyword { Bid = new Bid { Amount = 0.47 }, Param2 = "10% Off", MatchType = MatchType.Phrase, Text = "Brand-A Shoes", }, }; OutputStatusMessage("-----\nAddKeywords:"); AddKeywordsResponse addKeywordsResponse = await CampaignManagementExampleHelper.AddKeywordsAsync( adGroupId : (long)adGroupIds[0], keywords : keywords, returnInheritedBidStrategyTypes : false); long?[] keywordIds = addKeywordsResponse.KeywordIds.ToArray(); BatchError[] keywordErrors = addKeywordsResponse.PartialErrors.ToArray(); OutputStatusMessage("KeywordIds:"); CampaignManagementExampleHelper.OutputArrayOfLong(keywordIds); OutputStatusMessage("PartialErrors:"); CampaignManagementExampleHelper.OutputArrayOfBatchError(keywordErrors); var ads = new Ad[] { new ExpandedTextAd { TitlePart1 = "Contoso", TitlePart2 = "Quick & Easy Setup", TitlePart3 = "Seemless Integration", Text = "Find New Customers & Increase Sales!", TextPart2 = "Start Advertising on Contoso Today.", Path1 = "seattle", Path2 = "shoe sale", FinalUrls = new[] { "http://www.contoso.com/womenshoesale" }, }, }; OutputStatusMessage("-----\nAddAds:"); AddAdsResponse addAdsResponse = await CampaignManagementExampleHelper.AddAdsAsync( adGroupId : (long)adGroupIds[0], ads : ads); long?[] adIds = addAdsResponse.AdIds.ToArray(); BatchError[] adErrors = addAdsResponse.PartialErrors.ToArray(); OutputStatusMessage("AdIds:"); CampaignManagementExampleHelper.OutputArrayOfLong(adIds); OutputStatusMessage("PartialErrors:"); CampaignManagementExampleHelper.OutputArrayOfBatchError(adErrors); // Delete the campaign and everything it contains e.g., ad groups and ads. OutputStatusMessage("-----\nDeleteCampaigns:"); await CampaignManagementExampleHelper.DeleteCampaignsAsync( accountId : authorizationData.AccountId, campaignIds : new[] { (long)campaignIds[0] }); OutputStatusMessage(string.Format("Deleted Campaign Id {0}", 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 <AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <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 <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 { CampaignManagementExampleHelper = new CampaignManagementExampleHelper(this.OutputStatusMessage); BulkServiceManager = new BulkServiceManager(authorizationData); var progress = new Progress <BulkOperationProgressInfo>(x => OutputStatusMessage(string.Format("{0} % Complete", x.PercentComplete.ToString(CultureInfo.InvariantCulture)))); var downloadParameters = new DownloadParameters { DownloadEntities = new[] { DownloadEntity.RemarketingLists }, ResultFileDirectory = FileDirectory, ResultFileName = DownloadFileName, OverwriteResultFile = true, LastSyncTimeInUTC = null }; var bulkFilePath = await BulkServiceManager.DownloadFileAsync(downloadParameters); OutputStatusMessage("Downloaded all remarketing lists that the current user can associate with ad groups.\n"); Reader = new BulkFileReader(bulkFilePath, ResultFileType.FullDownload, FileType); var downloadEntities = Reader.ReadEntities().ToList(); var remarketingListResults = downloadEntities.OfType <BulkRemarketingList>().ToList(); OutputBulkRemarketingLists(remarketingListResults); Reader.Dispose(); // You must already have at least one remarketing list. if (remarketingListResults.Count < 1) { OutputStatusMessage("You do not have any remarketing lists that the current user can associate with ad groups.\n"); return; } var uploadEntities = new List <BulkEntity>(); #region Add // Prepare the bulk entities that you want to upload. var bulkCampaign = new BulkCampaign { Campaign = new Campaign { 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", TrackingUrlTemplate = null } }; // Specify one or more ad groups. var bulkAdGroup = new BulkAdGroup { // 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", CampaignId = campaignIdKey, AdGroup = new AdGroup { // When using the Campaign Management service, the Id cannot be set. In the context of a BulkAdGroup, 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 // ad group Id will be used when associating this new ad group with a new ad group remarketing list association // in the BulkAdGroupRemarketingListAssociation object below. Id = adGroupIdKey, Name = "Women's Red Shoe Sale", AdDistribution = AdDistribution.Search, BiddingScheme = new InheritFromParentBiddingScheme(), PricingModel = PricingModel.Cpc, StartDate = null, EndDate = new Microsoft.BingAds.V11.CampaignManagement.Date { Month = 12, Day = 31, Year = DateTime.UtcNow.Year + 1 }, Language = "English", Status = AdGroupStatus.Active, TrackingUrlTemplate = null, // Applicable for all remarketing lists that are associated with this ad group. TargetAndBid indicates // that you want to show ads only to people included in the remarketing list, with the option to change // the bid amount. Ads in this ad group will only show to people included in the remarketing list. RemarketingTargetingSetting = RemarketingTargetingSetting.TargetAndBid, }, }; uploadEntities.Add(bulkCampaign); uploadEntities.Add(bulkAdGroup); // This example associates all of the remarketing lists with the new ad group. foreach (var remarketingList in remarketingListResults) { if (remarketingList.RemarketingList != null && remarketingList.RemarketingList.Id != null) { var bulkAdGroupRemarketingListAssociation = new BulkAdGroupRemarketingListAssociation { ClientId = "MyBulkAdGroupRemarketingListAssociation " + remarketingList.RemarketingList.Id, BiddableAdGroupCriterion = new BiddableAdGroupCriterion { AdGroupId = adGroupIdKey, Criterion = new AudienceCriterion { AudienceId = (long)remarketingList.RemarketingList.Id, AudienceType = AudienceType.RemarketingList, }, CriterionBid = new BidMultiplier { Multiplier = 20.00, }, Status = AdGroupCriterionStatus.Paused, }, // Read-only properties AdGroupName = null, CampaignName = null, RemarketingListName = null, }; uploadEntities.Add(bulkAdGroupRemarketingListAssociation); } } // Upload and write the output OutputStatusMessage("\nAdding campaign, ad group, and ad group remarketing list associations...\n"); Reader = await WriteEntitiesAndUploadFileAsync(uploadEntities); downloadEntities = Reader.ReadEntities().ToList(); var campaignResults = downloadEntities.OfType <BulkCampaign>().ToList(); OutputBulkCampaigns(campaignResults); var adGroupResults = downloadEntities.OfType <BulkAdGroup>().ToList(); OutputBulkAdGroups(adGroupResults); var adGroupRemarketingListResults = downloadEntities.OfType <BulkAdGroupRemarketingListAssociation>().ToList(); OutputBulkAdGroupRemarketingListAssociations(adGroupRemarketingListResults); Reader.Dispose(); #endregion Add #region CleanUp // Delete the campaign, ad group, and ad group remarketing list associations that were previously added. // The remarketing lists will not be deleted. // 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 BulkAdGroup and BulkAdGroupRemarketingListAssociation // 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, ad group, and ad group remarketing list associations . . .\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.V11.Bulk.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V11.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 { ApiEnvironment environment = ((OAuthDesktopMobileAuthCodeGrant)authorizationData.Authentication).Environment; AdInsightExampleHelper AdInsightExampleHelper = new AdInsightExampleHelper( OutputStatusMessageDefault: this.OutputStatusMessage); AdInsightExampleHelper.AdInsightService = new ServiceClient <IAdInsightService>( authorizationData: authorizationData, environment: environment); // Used to output the Campaign Management objects within Bulk entities. CampaignManagementExampleHelper = new CampaignManagementExampleHelper( OutputStatusMessageDefault: this.OutputStatusMessage); BulkServiceManager = new BulkServiceManager( authorizationData: authorizationData, apiEnvironment: environment); var uploadEntities = new List <BulkEntity>(); // To get started with dynamic search ads, first you'll need to add a new Campaign // with its type set to DynamicSearchAds. When you create the campaign, you'll need to // include a DynamicSearchAdsSetting that specifies the target website domain and language. var bulkCampaign = new BulkCampaign { ClientId = "YourClientIdGoesHere", Campaign = new Campaign { Id = campaignIdKey, BudgetType = Microsoft.BingAds.V12.CampaignManagement.BudgetLimitType.DailyBudgetStandard, DailyBudget = 50, CampaignType = CampaignType.DynamicSearchAds, Languages = new string[] { "All" }, Name = "Women's Shoes " + DateTime.UtcNow, TimeZone = "PacificTimeUSCanadaTijuana", Settings = new[] { new DynamicSearchAdsSetting { DomainName = "contoso.com", Language = "English" } }, }, }; uploadEntities.Add(bulkCampaign); // Create a new ad group within the dynamic search ads campaign. var bulkAdGroup = new BulkAdGroup { CampaignId = campaignIdKey, AdGroup = new AdGroup { Id = adGroupIdKey, Name = "Women's Red Shoe Sale", StartDate = null, EndDate = new Microsoft.BingAds.V12.CampaignManagement.Date { Month = 12, Day = 31, Year = DateTime.UtcNow.Year + 1 }, CpcBid = new Bid { Amount = 0.09 }, }, }; uploadEntities.Add(bulkAdGroup); // You can add one or more Webpage criteria to each ad group that helps determine // whether or not to serve dynamic search ads. var adGroupWebpagePositivePageContent = new BulkAdGroupDynamicSearchAdTarget { BiddableAdGroupCriterion = new BiddableAdGroupCriterion { AdGroupId = adGroupIdKey, CriterionBid = new FixedBid { Amount = 0.50 }, Criterion = new Webpage { Parameter = new WebpageParameter { Conditions = new[] { new WebpageCondition { Argument = "flowers", Operand = WebpageConditionOperand.PageContent, } }, CriterionName = "Ad Group Webpage Positive Page Content Criterion" }, }, } }; uploadEntities.Add(adGroupWebpagePositivePageContent); // To discover the categories that you can use for Webpage criterion (positive or negative), // use the GetDomainCategories operation with the Ad Insight service. OutputStatusMessage("-----\nGetDomainCategories:"); var getDomainCategoriesResponse = await AdInsightExampleHelper.GetDomainCategoriesAsync( categoryName : null, domainName : DOMAIN_NAME, language : LANGUAGE); var categories = getDomainCategoriesResponse.Categories; AdInsightExampleHelper.OutputArrayOfDomainCategory(categories); // If any categories are available let's use one as a condition. if (categories.Count > 0) { var adGroupWebpagePositiveCategory = new BulkAdGroupDynamicSearchAdTarget { BiddableAdGroupCriterion = new BiddableAdGroupCriterion { AdGroupId = adGroupIdKey, CriterionBid = new FixedBid { Amount = 0.50 }, Criterion = new Webpage { Parameter = new WebpageParameter { Conditions = new[] { new WebpageCondition { Argument = categories[0].CategoryName, Operand = WebpageConditionOperand.Category, } }, CriterionName = "Ad Group Webpage Positive Category Criterion" }, } } }; uploadEntities.Add(adGroupWebpagePositiveCategory); } // If you want to exclude certain portions of your website, you can add negative Webpage // criterion at the campaign and ad group level. var adGroupWebpageNegativeUrl = new BulkAdGroupNegativeDynamicSearchAdTarget { NegativeAdGroupCriterion = new NegativeAdGroupCriterion { AdGroupId = adGroupIdKey, Criterion = new Webpage { Parameter = new WebpageParameter { // You can choose whether you want the criterion argument to match partial URLs, // page content, page title, or categories that Bing thinks applies to your website. Conditions = new[] { new WebpageCondition { Argument = DOMAIN_NAME, Operand = WebpageConditionOperand.Url, } }, // If you do not specify any name, then it will be set to a concatenated list of conditions. CriterionName = null } } } }; uploadEntities.Add(adGroupWebpageNegativeUrl); // The negative Webpage criterion at the campaign level applies to all ad groups // within the campaign; however, if you define ad group level negative Webpage criterion, // the campaign criterion is ignored for that ad group. var campaignWebpageNegative = new BulkCampaignNegativeDynamicSearchAdTarget { NegativeCampaignCriterion = new NegativeCampaignCriterion { CampaignId = campaignIdKey, Criterion = new Webpage { Parameter = new WebpageParameter { Conditions = new[] { new WebpageCondition { Argument = DOMAIN_NAME + "\\seattle", Operand = WebpageConditionOperand.Url, } }, CriterionName = "Campaign Negative Webpage Url Criterion" } } } }; uploadEntities.Add(campaignWebpageNegative); // Finally you must add at least one DynamicSearchAd into the ad group. The ad title and display URL // are generated automatically based on the website domain and language that you want to target. var bulkDynamicSearchAd = new BulkDynamicSearchAd { ClientId = "here", AdGroupId = adGroupIdKey, DynamicSearchAd = new DynamicSearchAd { Text = "Find New Customers & Increase Sales! Start Advertising on Contoso Today.", Path1 = "seattle", Path2 = "shoe sale", // You cannot set FinalUrls for dynamic search ads. // The Final URL will be a dynamically selected landing page. // The final URL is distinct from the path that customers will see and click on in your ad. FinalUrls = null, }, }; uploadEntities.Add(bulkDynamicSearchAd); // Upload and write the output OutputStatusMessage("-----\nAdding campaign, ad group, criterions, and ads..."); var Reader = await WriteEntitiesAndUploadFileAsync(uploadEntities); var downloadEntities = Reader.ReadEntities().ToList(); OutputStatusMessage("Upload results:"); var campaignResults = downloadEntities.OfType <BulkCampaign>().ToList(); OutputBulkCampaigns(campaignResults); var adGroupResults = downloadEntities.OfType <BulkAdGroup>().ToList(); OutputBulkAdGroups(adGroupResults); var campaignNegativeDynamicSearchAdTargetResults = downloadEntities.OfType <BulkCampaignNegativeDynamicSearchAdTarget>().ToList(); OutputBulkCampaignNegativeDynamicSearchAdTargets(campaignNegativeDynamicSearchAdTargetResults); var adGroupDynamicSearchAdTargetResults = downloadEntities.OfType <BulkAdGroupDynamicSearchAdTarget>().ToList(); OutputBulkAdGroupDynamicSearchAdTargets(adGroupDynamicSearchAdTargetResults); var adGroupNegativeDynamicSearchAdTargetResults = downloadEntities.OfType <BulkAdGroupNegativeDynamicSearchAdTarget>().ToList(); OutputBulkAdGroupNegativeDynamicSearchAdTargets(adGroupNegativeDynamicSearchAdTargetResults); var dynamicSearchAdResults = downloadEntities.OfType <BulkDynamicSearchAd>().ToList(); OutputBulkDynamicSearchAds(dynamicSearchAdResults); Reader.Dispose(); // Delete the campaign and everything it contains e.g., ad groups and ads. uploadEntities = new List <BulkEntity>(); foreach (var campaignResult in campaignResults) { campaignResult.Campaign.Status = CampaignStatus.Deleted; uploadEntities.Add(campaignResult); } OutputStatusMessage("-----\nDeleting DSA campaign, criterions, and ad..."); Reader = await WriteEntitiesAndUploadFileAsync(uploadEntities); downloadEntities = Reader.ReadEntities().ToList(); OutputStatusMessage("Upload results:"); campaignResults = downloadEntities.OfType <BulkCampaign>().ToList(); OutputBulkCampaigns(campaignResults); Reader.Dispose(); } // 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.V12.Bulk.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V12.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 { CampaignManagementExampleHelper = new CampaignManagementExampleHelper(this.OutputStatusMessage); BulkServiceManager = 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", } }; // 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.V11.Bulk.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V11.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 { ApiEnvironment environment = ((OAuthDesktopMobileAuthCodeGrant)authorizationData.Authentication).Environment; CampaignManagementExampleHelper = new CampaignManagementExampleHelper(this.OutputStatusMessage); BulkServiceManager = new BulkServiceManager(authorizationData, environment); BulkServiceManager.StatusPollIntervalInMilliseconds = 5000; var progress = new Progress <BulkOperationProgressInfo>(x => OutputStatusMessage(string.Format("{0} % Complete", x.PercentComplete.ToString(CultureInfo.InvariantCulture)))); #region Download // In this example we will download all ad groups in the account. var entities = new[] { DownloadEntity.AdGroups, }; // You can limit by specific campaign IDs and request performance data. var downloadParameters = new DownloadParameters { CampaignIds = null, DataScope = DataScope.EntityData, PerformanceStatsDateRange = null, DownloadEntities = entities, FileType = FileType, LastSyncTimeInUTC = null, ResultFileDirectory = FileDirectory, ResultFileName = DownloadFileName, OverwriteResultFile = true }; // 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. var bulkFilePath = await BulkServiceManager.DownloadFileAsync(downloadParameters); OutputStatusMessage("Downloaded all ad groups in the account.\n"); #endregion Download #region Parse Reader = new BulkFileReader(bulkFilePath, ResultFileType.FullDownload, FileType); var bulkAdGroups = Reader.ReadEntities().ToList().OfType <BulkAdGroup>().ToList(); OutputBulkAdGroups(bulkAdGroups); Writer = new BulkFileWriter(FileDirectory + UploadFileName); // We will activate ad groups for one month starting from today as an example. var nextMonth = DateTime.UtcNow.AddMonths(1); // Within the downloaded records, find all ad groups that you want to update. foreach (var bulkAdGroup in bulkAdGroups) { var adGroup = bulkAdGroup.AdGroup; if (adGroup != null && bulkAdGroup.IsExpired) { // For best performance, only upload the properties that you want to update. Writer.WriteEntity(new BulkAdGroup { CampaignId = bulkAdGroup.CampaignId, AdGroup = new AdGroup { Id = adGroup.Id, EndDate = new Microsoft.BingAds.V12.CampaignManagement.Date { Month = nextMonth.Month, Day = nextMonth.Day, Year = nextMonth.Year }, Status = AdGroupStatus.Active, } }); } } Reader.Dispose(); Writer.Dispose(); #endregion Parse #region Upload // Upload the local file that we already prepared var fileUploadParameters = new FileUploadParameters { ResultFileDirectory = FileDirectory, CompressUploadFile = true, ResultFileName = ResultFileName, OverwriteResultFile = true, UploadFilePath = FileDirectory + UploadFileName, ResponseMode = ResponseMode.ErrorsAndResults }; var resultFilePath = await BulkServiceManager.UploadFileAsync(fileUploadParameters, progress, CancellationToken.None); OutputStatusMessage("Updated ad groups.\n"); Reader = new BulkFileReader(resultFilePath, ResultFileType.Upload, FileType); bulkAdGroups = Reader.ReadEntities().ToList().OfType <BulkAdGroup>().ToList(); OutputBulkAdGroups(bulkAdGroups); Reader.Dispose(); #endregion Upload #region Entities // We can make the same update without explicitly reading or writing a local file. // When working with entities a file is downloaded to the temp directory, // although you don't need to manage it. var downloadEntities = await BulkServiceManager.DownloadEntitiesAsync(downloadParameters); OutputStatusMessage("Downloaded all ad groups in the account.\n"); bulkAdGroups = downloadEntities.ToList().OfType <BulkAdGroup>().ToList(); OutputBulkAdGroups(bulkAdGroups); var uploadEntities = new List <BulkEntity>(); foreach (var bulkAdGroup in bulkAdGroups) { var adGroup = bulkAdGroup.AdGroup; if (adGroup != null && bulkAdGroup.IsExpired) { // Instead of Writer.WriteEntity, we will add to the in-memory list uploadEntities.Add(new BulkAdGroup { CampaignId = bulkAdGroup.CampaignId, AdGroup = new AdGroup { Id = adGroup.Id, EndDate = new Microsoft.BingAds.V12.CampaignManagement.Date { Month = nextMonth.Month, Day = nextMonth.Day, Year = nextMonth.Year }, Status = AdGroupStatus.Active, } }); } } var entityUploadParameters = new EntityUploadParameters { Entities = uploadEntities, ResponseMode = ResponseMode.ErrorsAndResults, }; var resultEntities = await BulkServiceManager.UploadEntitiesAsync(entityUploadParameters, progress, CancellationToken.None); OutputStatusMessage("Updated ad groups.\n"); bulkAdGroups = resultEntities.ToList().OfType <BulkAdGroup>().ToList(); OutputBulkAdGroups(bulkAdGroups); #endregion Entities } // 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.V12.Bulk.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V12.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 { ApiEnvironment environment = ((OAuthDesktopMobileAuthCodeGrant)authorizationData.Authentication).Environment; CampaignManagementExampleHelper = new CampaignManagementExampleHelper(this.OutputStatusMessage); BulkServiceManager = new BulkServiceManager(authorizationData, environment); 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 negative keyword in the BulkCampaignNegativeKeyword 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", } }; var bulkCampaignNegativeKeywords = new BulkCampaignNegativeKeyword[] { new BulkCampaignNegativeKeyword { CampaignId = campaignIdKey, NegativeKeyword = new NegativeKeyword { MatchType = MatchType.Phrase, Text = "auto", } }, new BulkCampaignNegativeKeyword { CampaignId = campaignIdKey, NegativeKeyword = new NegativeKeyword { MatchType = MatchType.Exact, Text = "auto", } }, new BulkCampaignNegativeKeyword { CampaignId = campaignIdKey, NegativeKeyword = new NegativeKeyword { MatchType = MatchType.Phrase, Text = "car", } }, new BulkCampaignNegativeKeyword { CampaignId = campaignIdKey, NegativeKeyword = new NegativeKeyword { MatchType = MatchType.Exact, Text = "car", } }, }; // Negative keywords can be added and deleted from a shared negative keyword list. The negative keyword list can be shared or associated with multiple campaigns. // You can create up to 20 negative keyword lists per account and share or associate them with any campaign in the same account. // To create a negative keyword list, upload a BulkNegativeKeywordList (Negative Keyword List record type). // For each negative keyword that you want to add to the list, upload a BulkSharedNegativeKeyword (Shared Negative Keyword record type). // To associate the negative keyword list with a campaign, also upload a BulkCampaignNegativeKeywordList (Campaign Negative Keyword List Association record type). var bulkNegativeKeywordList = new BulkNegativeKeywordList { NegativeKeywordList = new NegativeKeywordList { // Since we are adding the list and the negative keywords during the same upload, // we will use a reference key to the negative keyword list identifier. Id = negativeKeywordListIdKey, Name = "My NKW List", }, }; var bulkSharedNegativeKeywords = new BulkSharedNegativeKeyword[] { new BulkSharedNegativeKeyword { NegativeKeyword = new NegativeKeyword { MatchType = MatchType.Phrase, Text = "mobile", }, NegativeKeywordListId = negativeKeywordListIdKey, }, new BulkSharedNegativeKeyword { NegativeKeyword = new NegativeKeyword { MatchType = MatchType.Exact, Text = "mobile", }, NegativeKeywordListId = negativeKeywordListIdKey, }, }; var bulkCampaignNegativeKeywordList = new BulkCampaignNegativeKeywordList { SharedEntityAssociation = new SharedEntityAssociation { EntityId = campaignIdKey, EntityType = "Campaign", SharedEntityId = negativeKeywordListIdKey, SharedEntityType = "NegativeKeywordList", } }; var uploadEntities = new List <BulkEntity>(); uploadEntities.Add(bulkCampaign); foreach (var bulkCampaignNegativeKeyword in bulkCampaignNegativeKeywords) { uploadEntities.Add(bulkCampaignNegativeKeyword); } uploadEntities.Add(bulkNegativeKeywordList); foreach (var bulkSharedNegativeKeyword in bulkSharedNegativeKeywords) { uploadEntities.Add(bulkSharedNegativeKeyword); } uploadEntities.Add(bulkCampaignNegativeKeywordList); // Upload and write the output Reader = await WriteEntitiesAndUploadFileAsync(uploadEntities); var downloadEntities = Reader.ReadEntities().ToList(); var campaignResults = downloadEntities.OfType <BulkCampaign>().ToList(); OutputBulkCampaigns(campaignResults); var campaignNegativeKeywordResults = downloadEntities.OfType <BulkCampaignNegativeKeyword>().ToList(); OutputBulkCampaignNegativeKeywords(campaignNegativeKeywordResults); var negativeKeywordListResults = downloadEntities.OfType <BulkNegativeKeywordList>().ToList(); OutputBulkNegativeKeywordLists(negativeKeywordListResults); var sharedNegativeKeywordListResults = downloadEntities.OfType <BulkSharedNegativeKeyword>().ToList(); OutputBulkSharedNegativeKeywords(sharedNegativeKeywordListResults); var campaignNegativeKeywordListResults = downloadEntities.OfType <BulkCampaignNegativeKeywordList>().ToList(); OutputBulkCampaignNegativeKeywordLists(campaignNegativeKeywordListResults); Reader.Dispose(); #endregion Add #region CleanUp //Delete the campaign and negative keywords 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 BulkCampaignNegativeKeyword //are deleted without being specified explicitly. //When you delete a BulkNegativeKeywordList, the dependent entities such as BulkSharedNegativeKeyword //are deleted without being specified explicitly. uploadEntities = new List <BulkEntity>(); foreach (var campaignResult in campaignResults) { campaignResult.Campaign.Status = CampaignStatus.Deleted; uploadEntities.Add(campaignResult); } foreach (var negativeKeywordListResult in negativeKeywordListResults) { negativeKeywordListResult.Status = Status.Deleted; uploadEntities.Add(negativeKeywordListResult); } // Upload and write the output OutputStatusMessage("\nDeleting campaign and negative keywords . . .\n"); Reader = await WriteEntitiesAndUploadFileAsync(uploadEntities); downloadEntities = Reader.ReadEntities().ToList(); OutputBulkCampaigns(downloadEntities.OfType <BulkCampaign>().ToList()); OutputBulkNegativeKeywordLists(downloadEntities.OfType <BulkNegativeKeywordList>().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.V12.Bulk.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V12.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(); } } }