Skip to content

Commit

Permalink
[ads] Add SmartNTT virtual skus pref
Browse files Browse the repository at this point in the history
thud
  • Loading branch information
tmancey committed Oct 29, 2024
1 parent 4705b35 commit feb0165
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 22 deletions.
1 change: 1 addition & 0 deletions browser/brave_ads/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ source_set("impl") {
"//brave/components/brave_ads/browser/application_state",
"//brave/components/l10n/common",
"//brave/components/p3a_utils",
"//brave/components/skus/browser",
"//chrome/browser:browser_process",
"//chrome/browser:browser_public_dependencies",
"//chrome/browser:primitives",
Expand Down
128 changes: 119 additions & 9 deletions browser/brave_ads/ads_service_delegate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@

#include "brave/browser/brave_ads/ads_service_delegate.h"

#include <cstddef>
#include <utility>

#include "base/json/json_reader.h"
#include "base/strings/utf_string_conversions.h"
#include "base/version_info/channel.h"
#include "base/version_info/version_info.h"
Expand All @@ -15,6 +17,7 @@
#include "brave/browser/ui/brave_ads/notification_ad.h"
#include "brave/components/brave_adaptive_captcha/brave_adaptive_captcha_service.h"
#include "brave/components/l10n/common/locale_util.h"
#include "brave/components/skus/browser/pref_names.h"
#include "build/build_config.h"
#include "chrome/browser/notifications/notification_display_service.h"
#include "chrome/browser/profiles/profile.h"
Expand All @@ -34,6 +37,71 @@

namespace brave_ads {

namespace {

constexpr char kSkuEnvironmentPrefix[] = "skus:";
constexpr char kSkuOrdersKey[] = "orders";
constexpr char kSkuOrderLocationKey[] = "location";
constexpr char kSkuOrderCreatedAtKey[] = "created_at";
constexpr char kSkuOrderExpiresAtKey[] = "expires_at";
constexpr char kSkuOrderLastPaidAtKey[] = "last_paid_at";
constexpr char kSkuOrderStatusKey[] = "status";

std::string StripSkuEnvironmentPrefix(const std::string& environment) {
const size_t pos = environment.find(':');
return environment.substr(pos + 1);
}

std::string NormalizeSkuStatus(const std::string& status) {
return status == "cancelled" ? "canceled" : status;
}

base::Value::Dict ParseSkuOrder(const base::Value::Dict& dict) {
base::Value::Dict order;

if (const auto* const created_at = dict.FindString(kSkuOrderCreatedAtKey)) {
order.Set(kSkuOrderCreatedAtKey, *created_at);
}

if (const auto* const expires_at = dict.FindString(kSkuOrderExpiresAtKey)) {
order.Set(kSkuOrderExpiresAtKey, *expires_at);
}

if (const auto* const last_paid_at =
dict.FindString(kSkuOrderLastPaidAtKey)) {
order.Set(kSkuOrderLastPaidAtKey, *last_paid_at);
}

if (const auto* const status = dict.FindString(kSkuOrderStatusKey)) {
const std::string normalized_status = NormalizeSkuStatus(*status);
order.Set(kSkuOrderStatusKey, normalized_status);
}

return order;
}

base::Value::Dict ParseSkuOrders(const base::Value::Dict& dict) {
base::Value::Dict orders;

for (const auto [/*id*/ _, value] : dict) {
const base::Value::Dict* const order = value.GetIfDict();
if (!order) {
continue;
}

const std::string* const location = order->FindString(kSkuOrderLocationKey);
if (!location) {
continue;
}

orders.Set(*location, ParseSkuOrder(*order));
}

return orders;
}

} // namespace

AdsServiceDelegate::AdsServiceDelegate(
Profile* profile,
PrefService* local_state,
Expand Down Expand Up @@ -65,6 +133,39 @@ std::string AdsServiceDelegate::GetDefaultSearchEngineName() {
return base::UTF16ToUTF8(default_search_engine_name);
}

base::Value::Dict AdsServiceDelegate::GetSkus() const {
base::Value::Dict skus;

if (!local_state_->FindPreference(skus::prefs::kSkusState)) {
// No SKUs in local state.
return skus;
}

const base::Value::Dict& skus_state =
local_state_->GetDict(skus::prefs::kSkusState);
for (const auto [environment, value] : skus_state) {
if (!environment.starts_with(kSkuEnvironmentPrefix)) {
continue;
}

// Parse the SKUs JSON because it is stored as a string in local state.
const std::optional<base::Value::Dict> sku_state =
base::JSONReader::ReadDict(value.GetString());
if (!sku_state) {
continue;
}

const base::Value::Dict* const orders = sku_state->FindDict(kSkuOrdersKey);
if (!orders) {
continue;
}

skus.Set(StripSkuEnvironmentPrefix(environment), ParseSkuOrders(*orders));
}

return skus;
}

void AdsServiceDelegate::OpenNewTabWithUrl(const GURL& url) {
#if BUILDFLAG(IS_ANDROID)
// ServiceTabLauncher can currently only launch new tabs
Expand Down Expand Up @@ -158,15 +259,24 @@ bool AdsServiceDelegate::IsFullScreenMode() {

base::Value::Dict AdsServiceDelegate::GetVirtualPrefs() {
return base::Value::Dict()
.Set("[virtual]:operating_system.name", version_info::GetOSType())
.Set("[virtual]:operating_system.locale",
brave_l10n::GetDefaultLocaleString())
.Set("[virtual]:application_locale", application_locale_)
.Set("[virtual]:build_channel.name",
version_info::GetChannelString(chrome::GetChannel()))
.Set("[virtual]:browser_version", version_info::GetVersionNumber())
.Set("[virtual]:default_search_engine.name",
GetDefaultSearchEngineName());
.Set("[virtual]:browser",
base::Value::Dict()
.Set("build_channel",
version_info::GetChannelString(chrome::GetChannel()))
.Set("version", version_info::GetVersionNumber()))
.Set("[virtual]:operating_system",
base::Value::Dict()
.Set("locale",
base::Value::Dict()
.Set("language",
brave_l10n::GetDefaultISOLanguageCodeString())
.Set("region",
brave_l10n::GetDefaultISOCountryCodeString()))
.Set("name", version_info::GetOSType()))
.Set(
"[virtual]:search_engine",
base::Value::Dict().Set("default_name", GetDefaultSearchEngineName()))
.Set("[virtual]:skus", GetSkus());
}

} // namespace brave_ads
2 changes: 2 additions & 0 deletions browser/brave_ads/ads_service_delegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ class AdsServiceDelegate : public AdsService::Delegate {

std::string GetDefaultSearchEngineName();

base::Value::Dict GetSkus() const;

// AdsService::Delegate implementation
void InitNotificationHelper() override;
bool CanShowSystemNotificationsWhileBrowserIsBackgrounded() override;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ using ConditionMatcherMap =
class PrefProviderInterface;

// Matchers are one or more pref paths and conditions, using AND logic, that
// must all be met for an ad to be served. Pref path keys which contain dotted
// paths should be separated by |, where paths may include list indices (e.g.,
// "list|1") or dictionary keys (e.g., "dict|key"). Paths can also be nested.
// Both Brave local state and profile prefs are supported.
// must all be met for an ad to be served. Pref path keys should be separated by
// "|", where paths may include list indices (e.g., "list|1") or dictionary keys
// (e.g., "dict|key"). Paths can also be nested. Both Brave local state and
// profile prefs are supported.
//
// For non-Rewards users, condition matchers should be included in the
// "photo.json" file under the NTP (New Tab Page) sponsored images component,
Expand Down Expand Up @@ -65,9 +65,9 @@ class PrefProviderInterface;
// - 'R≥': Greater than or equal to
// - 'R<': Less than
// - 'R≤': Less than or equal to
// - This matcher triggers an ad based on when a real number stored a1t
// "prefPath". For instance, the example below will serve an ad only if the
// value stored at "foo.bar" is not equal to 3:
// - This matcher triggers an ad based on when a real number (integers or
// fractional) stored at "prefPath". For instance, the example below will
// serve an ad only if the value stored at "foo.bar" is not equal to 3:
//
// "conditionMatchers": [
// {
Expand Down Expand Up @@ -129,22 +129,40 @@ class PrefProviderInterface;
// local state prefs. Virtual pref path keys should be prefixed with
// "[virtual]:".
//
// "[virtual]:operating_system.name" retrieves the operating system, returning
// one of the following values: "Windows", "Mac OS X", "Linux", "Android",
// "iOS", or "Unknown".
// "[virtual]:browser|version" retrieves the browser version, e.g. "72.0.59.3".
//
// "[virtual]:build_channel.name" retrieves the build channel of the browser,
// "[virtual]:browser|build_channel" retrieves the build channel of the browser,
// returning one of the following values: "stable", "beta", "dev", "nightly", or
// "unknown".
//
// "[virtual]:browser_version" retrieves the browser version, e.g. "6.0.490.1".
// "[virtual]:operating_system|locale|language" retrieves the operating system's
// language, e.g., "en", and "[virtual]:operating_system|locale|region"
// retrieves the operating system's region, e.g., "US".
//
// "[virtual]:operating_system|name" retrieves the operating system, returning
// one of the following values: "Windows", "Mac OS X", "Linux", "Android",
// "iOS", or "Unknown".
//
// "[virtual]:default_search_engine.name" retrieves the default search engine
// "[virtual]:search_engine|default_name" retrieves the default search engine
// chosen during browser installation, returning one of the following values:
// "Brave", "Google", "Yandex", "Bing", "Daum", "네이버", "DuckDuckGo", "Qwant",
// "Startpage", or "Ecosia". For the most up-to-date list of possible default
// search engines, see `TemplateURLPrepopulateData::GetDefaultSearchEngine`.
//
// "[virtual]:skus|environment|location|key" retrieves the value from either the
// production or staging environment, the "talk.brave.com", "vpn.brave.com", or
// "leo.brave.com" location, and the "created_at", "expires_at", "last_paid_at",
// or "status" key. Status returns one of the following values: `trial`, `beta`,
// `paid`, or `canceled`. For example, the following will serve an ad if the
// user has canceled their Brave VPN subscription:
//
// "conditionMatchers": [
// {
// "condition": "canceled",
// "prefPath": "[virtual]:skus|production|vpn.brave.com|status"
// }
// ]
//
// NOTE: To identify condition matchers, first create a copy of your pref files.
// Next, change a brave://setting or enable a feature, quit the browser and then
// compare the original and modified versions to determine which key/value pairs
Expand Down

0 comments on commit feb0165

Please sign in to comment.