fix(provider/microsoftads): add provider

This commit is contained in:
Kevin Franklin Kim
2024-11-26 17:29:09 +01:00
parent c75e99a51b
commit dddb3b9201
13 changed files with 1208 additions and 202 deletions

View File

@@ -9,6 +9,7 @@ import (
googleadsprovider "github.com/foomo/sesamy-cli/pkg/provider/googleads"
googleanalyticsprovider "github.com/foomo/sesamy-cli/pkg/provider/googleanalytics"
googletagmanagerprovider "github.com/foomo/sesamy-cli/pkg/provider/googletagmanager"
microsoftadsprovider "github.com/foomo/sesamy-cli/pkg/provider/microsoftads"
tracifyprovider "github.com/foomo/sesamy-cli/pkg/provider/tracify"
umamiprovider "github.com/foomo/sesamy-cli/pkg/provider/umami"
"github.com/foomo/sesamy-cli/pkg/tagmanager"
@@ -109,6 +110,13 @@ func NewServer(root *cobra.Command) {
}
}
if cfg.MicrosoftAds.Enabled && pkgcmd.Tag(microsoftadsprovider.Tag, tags) {
l.Info("🅿️ Running provider", "name", microsoftadsprovider.Name, "tag", microsoftadsprovider.Tag)
if err := microsoftadsprovider.Server(l, tm, cfg.MicrosoftAds); err != nil {
return errors.Wrap(err, "failed to provision microsoftads")
}
}
return nil
},
}

View File

@@ -21,6 +21,8 @@ type Config struct {
ConversionLinker ConversionLinker `json:"conversionLinker" yaml:"conversionLinker"`
// Facebook provider settings
Facebook Facebook `json:"facebook" yaml:"facebook"`
// MicrosoftAds provider settings
MicrosoftAds MicrosoftAds `json:"microsoftAds" yaml:"microsoftAds"`
// Emarsys provider settings
Emarsys Emarsys `json:"emarsys" yaml:"emarsys"`
// Hotjar provider settings

View File

@@ -0,0 +1,12 @@
package config
type MicrosoftAds struct {
// Enable provider
Enabled bool `json:"enabled" yaml:"enabled"`
// Google Consent settings
GoogleConsent GoogleConsent `json:"googleConsent" yaml:"googleConsent"`
// Microsoft Ads UET Tag ID
TagID string `json:"tagId" yaml:"tagId"`
// Microsoft Ads Conversion settings
Conversion MicrosoftAdsConversion `json:"conversion" yaml:"conversion"`
}

View File

@@ -0,0 +1,29 @@
package config
import (
"github.com/foomo/gocontemplate/pkg/contemplate"
)
type MicrosoftAdsConversion struct {
// Enable provider
Enabled bool `json:"enabled" yaml:"enabled"`
// Google Tag Manager server container settings
ServerContainer MicrosoftAdsServerContainer `json:"serverContainer" yaml:"serverContainer"`
}
type MicrosoftAdsServerContainer struct {
contemplate.Config `json:",inline" yaml:",squash"`
Settings map[string]MicrosoftAdsConversionTag `json:"settings" yaml:"settings"`
}
type MicrosoftAdsConversionTag struct {
PageType string `json:"pageType" yaml:"pageType"`
EventType string `json:"eventType" yaml:"eventType"`
}
func (s *MicrosoftAdsServerContainer) Setting(eventName string) MicrosoftAdsConversionTag {
if value, ok := s.Settings[eventName]; ok {
return value
}
return MicrosoftAdsConversionTag{}
}

View File

@@ -1,200 +0,0 @@
const sendHttpGet = require('sendHttpGet');
const getEventData = require('getEventData');
const generateRandom = require('generateRandom');
const logToConsole = require('logToConsole');
const Math = require('Math');
const encodeUriComponent = require('encodeUriComponent');
const JSON = require('JSON');
const getCookieValues = require('getCookieValues');
const setCookie = require('setCookie');
const parseUrl = require('parseUrl');
let clickid;
function isNotEmpty(obj) {
if (obj === undefined || obj === null || obj.toString() === "" ) {
return false;
} else {
return obj;
}
}
let msquery = () => {
const url = parseUrl(getEventData('page_location'));
if (url && url.searchParams.msclkid) {
return url.searchParams.msclkid;
} else {
return false;
}
};
function setUETCookie(clickid) {
setCookie('_uetmsclkid', "_uet"+clickid, {"expires":7776000000, httpOnly: false});
logToConsole("Cookie set: " + clickid);
}
if(data.first_click) {
if (isNotEmpty(data.msclkidCookie) || isNotEmpty(getCookieValues('_uetmsclkid'))) {
let clickid_cookie = getCookieValues('_uetmsclkid')[0];
clickid = (isNotEmpty(data.msclkidCookie) || (clickid_cookie.substring(4)));
clickid += "-0";
} else { //cookie not present
if (isNotEmpty(data.msclkidQuery) || msquery()) {
clickid = (isNotEmpty(data.msclkidQuery) || msquery());
setUETCookie(clickid);
clickid += "-1";
} else {
clickid = "N";
}
}
} else {
if (isNotEmpty(data.msclkidQuery) || msquery()) {
clickid = (isNotEmpty(data.msclkidQuery) || msquery());
setUETCookie(clickid);
clickid += "-1";
} else {
if(isNotEmpty(data.msclkidCookie) || isNotEmpty(getCookieValues('_uetmsclkid'))){
let clickid_cookie = getCookieValues('_uetmsclkid')[0];
clickid = (isNotEmpty(data.msclkidCookie) || (clickid_cookie.substring(4)));
clickid += "-0";
} else {
clickid = "N";
}
}
}
const screen = data.screen || getEventData('screen_resolution');
let width, height = "";
if (isNotEmpty(screen)){
width = screen.split('x')[0];
height = screen.split('x')[1];
}
let rn = generateRandom(100000, 999999);
function s4() {
return Math.floor(((1 + (generateRandom(1, 9999999)/10000000))) * 65536)
.toString(16)
.substring(1);
}
let mid = (s4() + s4() + "-" + s4() + "-" + s4() + "-" + s4() + "-" + s4() + s4() + s4());
let items_data;
if(isNotEmpty(getEventData('items'))){
items_data = getEventData('items');
}
let items = () => {
let result = "";
if(isNotEmpty(data.prodid)){
items_data = data.prodid;
}
if (data.itemsGa) {
if(items_data) {
items_data.forEach(function(item, i) {
result += "id=" + item.item_id + 'quantity=' + item.quantity + 'price=' + item.price;
if (i < items_data.length - 1) {
result += ',';
}
});
}
} else {
result = items_data;
}
return result;
};
let items_id = () => {
let result = "";
if(isNotEmpty(data.items)){
items_data = data.items;
}
if(data.prodidGa) {
if (items_data) {
items_data.forEach(function(item, i) {
result += item.item_id;
if (i < items_data.length - 1) {
result += ',';
}
});
}
}
return result;
};
let spa = () => {
if(data.spa) {
return "Y";
} else {
return "N";
}
};
let params = {
rn: rn,
ti: data.ti,
ver: '2.3',
mid: mid,
uid: isNotEmpty(data.userId) || getEventData('user_id'),
evt: data.evt,
p: getEventData('page_location'),
r: getEventData('page_referrer'),
tl: isNotEmpty(data.pageTitle) || getEventData('page_title'),
pagetype: data.pagetype,
items: items(),
prodid: items_id(),
search_term: isNotEmpty(data.searchTerm) || getEventData('search_term'),
transaction_id: isNotEmpty(data.transactionId) || getEventData('transaction_id'),
lg: isNotEmpty(data.lg) || getEventData('language'),
sw: width,
sh: height,
sc: data.sc || getEventData('screen_color_depth'),
spa: spa(),
msclkid: clickid,
sid: isNotEmpty(data.sid) || getEventData('uet_session_id'),
vid: isNotEmpty(data.vid) || getEventData('vid'),
page_path: isNotEmpty(data.pagePath) || getEventData('page_path'),
gc: getEventData('currency'),
gv: getEventData('value'),
ec: isNotEmpty(data.ec) || getEventData('event_category'),
ea: isNotEmpty(data.ea) || getEventData('event_action'),
el: isNotEmpty(data.el) || getEventData('event_label'),
ev: isNotEmpty(data.ev) || getEventData('event_value'),
};
if (data.activateLogs) {
logToConsole("Params: " + JSON.stringify(params));
}
let url = 'https://bat.bing.com/action/0?';
let all_params = "";
if(isNotEmpty(params)){
for (var key in params){
if (params[key] == undefined || params[key] == null) {
continue;
}
all_params += key + "=" + encodeUriComponent(params[key]) + "&";
}
url += all_params;
}
if (data.activateLogs) {
logToConsole('URL: ' + url);
}
return sendHttpGet(url, {
headers: {key: 'value'},
timeout: 500,
}).then((result) => {
if (result.statusCode >= 200 && result.statusCode < 300) {
logToConsole('Result: ' + data.gtmOnSuccess());
data.gtmOnSuccess();
} else {
logToConsole('Error: ' + result.statusCode);
data.gtmOnFailure();
}
});

View File

@@ -0,0 +1,8 @@
package facebook
const (
Tag = "mads"
Name = "Microsoft Ads"
NameTagIDConstant = "Microsoft UET Tag ID"
NameConversionsTagTemplate = "Microsoft Ads Conversions"
)

View File

@@ -0,0 +1,70 @@
package facebook
import (
"log/slog"
"github.com/foomo/sesamy-cli/pkg/config"
"github.com/foomo/sesamy-cli/pkg/provider/googleconsent"
googleconsentvariable "github.com/foomo/sesamy-cli/pkg/provider/googleconsent/server/variable"
servertagx "github.com/foomo/sesamy-cli/pkg/provider/microsoftads/server/tag"
"github.com/foomo/sesamy-cli/pkg/provider/microsoftads/server/template"
"github.com/foomo/sesamy-cli/pkg/provider/microsoftads/server/trigger"
"github.com/foomo/sesamy-cli/pkg/tagmanager"
commonvariable "github.com/foomo/sesamy-cli/pkg/tagmanager/common/variable"
"github.com/foomo/sesamy-cli/pkg/utils"
"github.com/pkg/errors"
)
func Server(l *slog.Logger, tm *tagmanager.TagManager, cfg config.MicrosoftAds) error {
{ // create folder
if folder, err := tm.UpsertFolder("Sesamy - " + Name); err != nil {
return err
} else {
tm.SetFolderName(folder.Name)
}
}
tagID, err := tm.UpsertVariable(commonvariable.NewConstant(NameTagIDConstant, cfg.TagID))
if err != nil {
return err
}
if cfg.Conversion.Enabled {
tagTemplate, err := tm.UpsertCustomTemplate(template.NewConversionTag(NameConversionsTagTemplate))
if err != nil {
return err
}
{ // create tags
eventParameters, err := utils.LoadEventParams(cfg.Conversion.ServerContainer.Config)
if err != nil {
return err
}
for event := range eventParameters {
var eventTriggerOpts []trigger.ConversionEventOption
if cfg.GoogleConsent.Enabled {
if err := googleconsent.ServerEnsure(tm); err != nil {
return err
}
consentVariable, err := tm.LookupVariable(googleconsentvariable.GoogleConsentModeName(cfg.GoogleConsent.Mode))
if err != nil {
return err
}
eventTriggerOpts = append(eventTriggerOpts, trigger.ConversionEventWithConsentMode(consentVariable))
}
eventTrigger, err := tm.UpsertTrigger(trigger.NewConversionEvent(event, eventTriggerOpts...))
if err != nil {
return errors.Wrap(err, "failed to upsert event trigger: "+event)
}
if _, err := tm.UpsertTag(servertagx.NewConversion(event, tagID, tagTemplate, cfg.Conversion.ServerContainer.Setting(event), eventTrigger)); err != nil {
return err
}
}
}
}
return nil
}

View File

@@ -0,0 +1,145 @@
package tag
import (
"github.com/foomo/sesamy-cli/pkg/config"
"github.com/foomo/sesamy-cli/pkg/utils"
"google.golang.org/api/tagmanager/v2"
)
func ConversionName(v string) string {
return "MAds Conversion - " + v
}
func NewConversion(name string, tagID *tagmanager.Variable, template *tagmanager.CustomTemplate, settings config.MicrosoftAdsConversionTag, triggers ...*tagmanager.Trigger) *tagmanager.Tag {
return &tagmanager.Tag{
FiringTriggerId: utils.TriggerIDs(triggers),
Name: ConversionName(name),
TagFiringOption: "oncePerEvent",
Parameter: []*tagmanager.Parameter{
{
Key: "pagetype",
Type: "template",
Value: settings.PageType,
},
{
Key: "activateLogs",
Type: "boolean",
Value: "false",
},
{
Key: "pageTitle",
Type: "template",
},
{
Key: "prodidGa",
Type: "boolean",
Value: "true",
},
{
Key: "vid",
Type: "template",
},
{
Key: "searchTerm",
Type: "template",
},
{
Key: "msclkidCookie",
Type: "template",
},
{
Key: "msclkidCookie",
Type: "template",
},
{
Key: "itemsGa",
Type: "boolean",
Value: "true",
},
{
Key: "ea",
Type: "template",
Value: name,
},
{
Key: "ea",
Type: "template",
Value: name,
},
{
Key: "ecommCategory",
Type: "template",
},
{
Key: "gc",
Type: "template",
},
{
Key: "ec",
Type: "template",
},
{
Key: "evt",
Type: "template",
Value: settings.EventType,
},
{
Key: "firstClick",
Type: "boolean",
Value: "false",
},
{
Key: "spa",
Type: "boolean",
Value: "false",
},
{
Key: "el",
Type: "template",
},
{
Key: "prodid",
Type: "template",
},
{
Key: "pagePath",
Type: "template",
},
{
Key: "gv",
Type: "template",
},
{
Key: "ev",
Type: "template",
},
{
Key: "pageLocation",
Type: "template",
},
{
Key: "msclkidQuery",
Type: "template",
},
{
Key: "createCookie",
Type: "boolean",
Value: "false",
},
{
Key: "ti",
Type: "template",
Value: "{{" + tagID.Name + "}}",
},
{
Key: "user_id",
Type: "template",
},
{
Key: "pageReferrer",
Type: "template",
},
},
Type: utils.TemplateType(template),
}
}

View File

@@ -0,0 +1,14 @@
package template
import (
"fmt"
"google.golang.org/api/tagmanager/v2"
)
func NewConversionTag(name string) *tagmanager.CustomTemplate {
return &tagmanager.CustomTemplate{
Name: name,
TemplateData: fmt.Sprintf(ConversionTagData, name),
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,75 @@
package trigger
import (
"google.golang.org/api/tagmanager/v2"
)
func ConversionEventName(v string) string {
return "MAds Conversion - " + v
}
type (
ConversionEventOptions struct {
consentMode *tagmanager.Variable
}
ConversionEventOption func(*ConversionEventOptions)
)
func ConversionEventWithConsentMode(mode *tagmanager.Variable) ConversionEventOption {
return func(o *ConversionEventOptions) {
o.consentMode = mode
}
}
func NewConversionEvent(name string, opts ...ConversionEventOption) *tagmanager.Trigger {
o := &ConversionEventOptions{}
for _, opt := range opts {
if opt != nil {
opt(o)
}
}
var filter []*tagmanager.Condition
if o.consentMode != nil {
filter = append(filter,
&tagmanager.Condition{
Parameter: []*tagmanager.Parameter{
{
Key: "arg0",
Type: "template",
Value: "{{" + o.consentMode.Name + "}}",
},
{
Key: "arg1",
Type: "template",
Value: "granted",
},
},
Type: "equals",
},
)
}
return &tagmanager.Trigger{
Type: "customEvent",
Name: ConversionEventName(name),
CustomEventFilter: []*tagmanager.Condition{
{
Parameter: []*tagmanager.Parameter{
{
Key: "arg0",
Type: "template",
Value: "{{_event}}",
},
{
Key: "arg1",
Type: "template",
Value: name,
},
},
Type: "equals",
},
},
Filter: filter,
}
}

View File

@@ -78,6 +78,10 @@
"$ref": "#/$defs/github.com.foomo.sesamy-cli.pkg.config.Facebook",
"description": "Facebook provider settings"
},
"microsoftAds": {
"$ref": "#/$defs/github.com.foomo.sesamy-cli.pkg.config.MicrosoftAds",
"description": "MicrosoftAds provider settings"
},
"emarsys": {
"$ref": "#/$defs/github.com.foomo.sesamy-cli.pkg.config.Emarsys",
"description": "Emarsys provider settings"
@@ -419,6 +423,66 @@
"additionalProperties": false,
"type": "object"
},
"github.com.foomo.sesamy-cli.pkg.config.MicrosoftAds": {
"properties": {
"enabled": {
"type": "boolean",
"description": "Enable provider"
},
"googleConsent": {
"$ref": "#/$defs/github.com.foomo.sesamy-cli.pkg.config.GoogleConsent",
"description": "Google Consent settings"
},
"tagId": {
"type": "string",
"description": "Microsoft Ads UET Tag ID"
},
"conversion": {
"$ref": "#/$defs/github.com.foomo.sesamy-cli.pkg.config.MicrosoftAdsConversion",
"description": "Microsoft Ads Conversion settings"
}
},
"additionalProperties": false,
"type": "object"
},
"github.com.foomo.sesamy-cli.pkg.config.MicrosoftAdsConversion": {
"properties": {
"enabled": {
"type": "boolean",
"description": "Enable provider"
},
"serverContainer": {
"$ref": "#/$defs/github.com.foomo.sesamy-cli.pkg.config.MicrosoftAdsServerContainer",
"description": "Google Tag Manager server container settings"
}
},
"additionalProperties": false,
"type": "object"
},
"github.com.foomo.sesamy-cli.pkg.config.MicrosoftAdsConversionTag": {
"properties": {
"pageType": {
"type": "string"
},
"eventType": {
"type": "string"
}
},
"additionalProperties": false,
"type": "object"
},
"github.com.foomo.sesamy-cli.pkg.config.MicrosoftAdsServerContainer": {
"properties": {
"packages": {
"$ref": "#/$defs/[]*contemplate.PackageConfig"
},
"settings": {
"$ref": "#/$defs/map[string]config.MicrosoftAdsConversionTag"
}
},
"additionalProperties": false,
"type": "object"
},
"github.com.foomo.sesamy-cli.pkg.config.ServerContainer": {
"properties": {
"packages": {
@@ -501,6 +565,12 @@
"$ref": "#/$defs/github.com.foomo.sesamy-cli.pkg.config.GoogleAdsConversionTracking"
},
"type": "object"
},
"map[string]config.MicrosoftAdsConversionTag": {
"additionalProperties": {
"$ref": "#/$defs/github.com.foomo.sesamy-cli.pkg.config.MicrosoftAdsConversionTag"
},
"type": "object"
}
}
}

View File

@@ -164,10 +164,14 @@ googleAds:
conversion:
# Enable Google Ads Conversion
enabled: true
# Google Ads Conversion Tracking Label
conversionLabel: ''
# Google Tag Manager server container settings
serverContainer:
# Conversion labels map
settings:
add_to_cart:
label: ''
purchase:
label: ''
# Contemplate package config for generated events
packages:
- path: github.com/foomo/sesamy-go/pkg/event
@@ -291,6 +295,38 @@ tracify:
- ViewItem
- Purchase
# --- Microsoft Ads
microsoftAds:
# Enable provider
enabled: true
# Microsoft UET Tag ID
tagId: '123456789'
# Microsoft Ads Conversion
conversion:
# Enable conversion
enabled: true
# Google Tag Manager server container settings
serverContainer:
# Conversion labels map
settings:
add_to_cart:
pageType: other
eventType: custom
page_view:
pageType: other
eventType: pageLoad
purchase:
pageType: purchase
eventType: custom
# Contemplate package config for generated events
packages:
- path: github.com/foomo/sesamy-go/pkg/event
types:
- AddToCart
- PageView
- Purchase
# --- Hotjar
hotjar:
# Enable provider