moved things around, added storage

This commit is contained in:
Darko Luketic 2017-03-03 22:07:39 +01:00
parent 45981e603f
commit 213efef45a
6 changed files with 876 additions and 503 deletions

523
main.go
View File

@ -1,512 +1,29 @@
package main
import (
"encoding/json"
"github.com/davecgh/go-spew/spew"
"net/http"
"strconv"
"time"
"log"
"net/http"
"github.com/dalu/gopiwik/mongo"
"github.com/dalu/gopiwik/piwik"
"gopkg.in/mgo.v2"
)
const (
database = "piwik"
siteCol = "sites"
visitCol = "visits"
)
func main() {
http.HandleFunc("/piwik.php", func(w http.ResponseWriter, r *http.Request) {
pr := ParseV3(r)
spew.Dump(pr)
})
http.Handle("/", http.FileServer(http.Dir("assets")))
ms, e := mgo.Dial("localhost")
if e != nil {
log.Fatal(e)
}
defer ms.Close()
siteStorage := mongo.NewSiteStorage(ms, database, siteCol)
visitStorage := mongo.NewVisitStorage(ms, database, visitCol)
ph := piwik.NewPiwikHandler(siteStorage, visitStorage)
http.Handle("/", ph)
http.ListenAndServe(":8080", nil)
}
type V3Visit struct {
SiteID string //idsite
Record bool //rec
URL string //url
ActionName string //action_anem
VisitorID string //_id
Referrer string //urlref
CustomVars map[string]interface{} //_cvar
VisitsCount string //_idvc
ViewTime time.Time //_viewts
VisitorFirstVisitTime time.Time //_idts
CampaignName string //_rcn
CampaignKeyword string //_rck
Resolution string //res
LocalHour string //h
LocalMinute string //m
LocalSeconds string //s
LocalTimeUTC time.Time
Plugins struct {
Flash bool //fla
Java bool //java
Director bool //dir
Quicktime bool //qt
RealPlayer bool //realp
PDF bool //pdf
WindowsMedia bool //wma
Gears bool //gears
Silverlight bool //ag
}
SupportsCookies bool //cookie
UserAgent string //ua
Language string //lang
UserID string //uid
CustomUserID string //cid
NewVisit bool //new_visit
PageCustomVars map[string]interface{} //cvar
Link string //link
Download string //download
Search string //search
SearchCategory string //search_cat
SearchCount string //search_count
PageViewID string //pv_id
GoalID string //idgoal
Revenue string //revenue
GenerationTime string //gt_ms
Characterset string //cs
EventCategory string //e_c
EventAction string //e_a
EventName string //e_n
EventValue string //e_v
ContentName string //c_n
ContentPiece string //c_p
ContentTarget string //c_t
ContentInteraction string //c_i
ECommerceID string // ec_id
ECommerceItems [][]string // ec_items
ECommerceSubTotal string // ec_st
ECommerceTax string // ec_tx
ECommerceShipping string // ec_sh
ECommerceDiscount string // ec_dt
ECommerceTime time.Time // ec_ts
TokenAuth string //token_auth
CustomIP string //cip
CustomDateTime string //cdt
CustomCountry string //country
CustomRegion string //region
CustomCity string //city
CustomLatitude string //lat
CustomLongitude string //long
MediaID string // ma_id
MediaTitle string // ma_ti
MediaResource string // ma_re
MediaType string // ma_mt
MediaPlayerName string // ma_pn
MediaSecondsPlayingTime string // ma_st
MediaLength string // ma_le
MediaProgress string // ma_ps
MediaTimeUntilPlay string // ma_ttp
MediaWidth string // ma_w
MediaHeight string // ma_h
MediaFullscreen bool // ma_fs
SendImage string //send_image
}
func ParseV3(r *http.Request) *V3Visit {
/*
https://developer.piwik.org/api-reference/tracking-api
*/
v3 := new(V3Visit)
// (required) — The ID of the website we're tracking a visit/action for.
v3.SiteID = r.URL.Query().Get("idsite")
// (required) — Required for tracking, must be set to one, eg, &rec=1.
if r.URL.Query().Get("rec") == "1" {
v3.Record = true
} else {
v3.Record = false
}
// (required) — The full URL for the current action.
v3.URL = r.URL.Query().Get("url")
// (recommended) — The title of the action being tracked.
// It is possible to use slashes / to set one or several categories for this action.
// For example, Help / Feedback will create the Action Feedback in the category Help.
v3.ActionName = r.URL.Query().Get("action_name")
// (recommended) — The unique visitor ID, must be a 16 characters hexadecimal string.
// Every unique visitor must be assigned a different ID and this ID must not change after it is assigned.
// If this value is not set Piwik will still track visits, but the unique visitors metric might be less accurate.
v3.VisitorID = r.URL.Query().Get("_id")
/*
r.URL.Query().Get("rand") // (recommended) — Meant to hold a random value that is generated before each request. Using it helps avoid the tracking request being cached by the browser or a proxy.
r.URL.Query().Get("apiv") // (recommended) — The parameter &apiv=1 defines the api version to use (currently always set to 1)
*/
// The full HTTP Referrer URL.
// This value is used to determine how someone got to your website (ie, through a website, search engine or campaign).
v3.Referrer = r.URL.Query().Get("urlref")
// Visit scope custom variables.
// This is a JSON encoded string of the custom variable array (see below for an example value).
if e := json.Unmarshal([]byte(r.URL.Query().Get("_cvar")), v3.CustomVars);e != nil {
log.Println(e)
}
// The current count of visits for this visitor.
// To set this value correctly, it would be required to store the value for each visitor in your application (using sessions or persisting in a database).
// Then you would manually increment the counts by one on each new visit or "session", depending on how you choose to define a visit.
// This value is used to populate the report Visitors > Engagement > Visits by visit number.
v3.VisitsCount = r.URL.Query().Get("_idvc")
// The UNIX timestamp of this visitor's previous visit. This parameter is used to populate the report Visitors > Engagement > Visits by days since last visit.
v3.ViewTime = stringToTime(r.URL.Query().Get("_viewts"))
// The UNIX timestamp of this visitor's first visit. This could be set to the date where the user first started using your software/app, or when he/she created an account.
// This parameter is used to populate the Goals > Days to Conversion report.
v3.VisitorFirstVisitTime = stringToTime(r.URL.Query().Get("_idts"))
// The Campaign name (see Tracking Campaigns).
// Used to populate the Referrers > Campaigns report.
// Note: this parameter will only be used for the first pageview of a visit.
v3.CampaignName = r.URL.Query().Get("_rcn")
// The Campaign Keyword (see Tracking Campaigns).
// Used to populate the Referrers > Campaigns report (clicking on a campaign loads all keywords for this campaign).
// Note: this parameter will only be used for the first pageview of a visit.
v3.CampaignKeyword = r.URL.Query().Get("_rck")
// The resolution of the device the visitor is using, eg 1280x1024.
v3.Resolution = r.URL.Query().Get("res")
// The current hour (local time).
v3.LocalHour = r.URL.Query().Get("h")
// The current minute (local time).
v3.LocalMinute = r.URL.Query().Get("m")
// The current second (local time).
v3.LocalSeconds = r.URL.Query().Get("s")
v3.LocalTimeUTC = time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), atoi(r.URL.Query().Get("h")), atoi(r.URL.Query().Get("m")), atoi(r.URL.Query().Get("s")), 0, time.UTC)
// plugins used by the visitor can be specified by setting the following parameters to 1
// Flash
if r.URL.Query().Get("fla") == "1" {
v3.Plugins.Flash = true
}
// Java
if r.URL.Query().Get("java") == "1" {
v3.Plugins.Java = true
}
// Director
if r.URL.Query().Get("dir") == "1" {
v3.Plugins.Director = true
}
// Quicktime
if r.URL.Query().Get("qt") == "1" {
v3.Plugins.Quicktime = true
}
// Real Player
if r.URL.Query().Get("realp") == "1" {
v3.Plugins.RealPlayer = true
}
// PDF
if r.URL.Query().Get("pdf") == "1" {
v3.Plugins.PDF = true
}
// Windows Media
if r.URL.Query().Get("wma") == "1" {
v3.Plugins.WindowsMedia = true
}
// Gears
if r.URL.Query().Get("gears") == "1" {
v3.Plugins.Gears = true
}
// Silverlight
if r.URL.Query().Get("ag") == "1" {
v3.Plugins.Silverlight = true
}
// when set to 1, the visitor's client is known to support cookies.
if r.URL.Query().Get("cookie") == "1" {
v3.SupportsCookies = true
}
// An override value for the User-Agent HTTP header field. The user agent is used to detect the operating system and browser used.
if r.URL.Query().Get("ua") != "" {
v3.UserAgent = r.URL.Query().Get("ua")
} else {
v3.UserAgent = r.UserAgent()
}
// An override value for the Accept-Language HTTP header field. This value is used to detect the visitor's country if GeoIP is not enabled.
if r.URL.Query().Get("lang") != "" {
v3.Language = r.URL.Query().Get("lang")
} else {
v3.Language = r.Header.Get("Accept-Language")
}
// defines the User ID for this request.
// User ID is any non empty unique string identifying the user (such as an email address or a username).
// To access this value, users must be logged-in in your system so you can fetch this user ID from your system, and pass it to Piwik.
// The User ID appears in the visitor log, the Visitor profile, and you can Segment reports for one or several User ID (userId segment).
// When specified, the User ID will be "enforced".
// This means that if there is no recent visit with this User ID, a new one will be created.
// If a visit is found in the last 30 minutes with your specified User ID, then the new action will be recorded to this existing visit.
v3.UserID = r.URL.Query().Get("uid")
// defines the visitor ID for this request.
// You must set this value to exactly a 16 character hexadecimal string (containing only characters 01234567890abcdefABCDEF).
// We recommended to set the User ID via uid rather than use this cid
v3.CustomUserID = r.URL.Query().Get("cid")
// If set to 1, will force a new visit to be created for this action. This feature is also available in JavaScript.
if r.URL.Query().Get("new_visit") == "1" {
v3.NewVisit = true
}
/*
// ? scopes?
dimension := make(map[string]string)
for k, v := range r.URL.Query() {
if strings.HasPrefix(k, "dimension") {
dimension[strings.TrimPrefix(k, "dimension")] = v
}
}
*/
/*
Optional Action info (measure Page view, Outlink, Download, Site search)
*/
// Page scope custom variables. This is a JSON encoded string of the custom variable array (see below for an example value).
json.Unmarshal([]byte(r.URL.Query().Get("cvar")), v3.PageCustomVars)
// An external URL the user has opened.
// Used for tracking outlink clicks.
// We recommend to also set the url parameter to this same value.
v3.Link = r.URL.Query().Get("link")
// URL of a file the user has downloaded.
// Used for tracking downloads.
// We recommend to also set the url parameter to this same value.
v3.Download = r.URL.Query().Get("download")
// The Site Search keyword.
// When specified, the request will not be tracked as a normal pageview but will instead be tracked as a Site Search request.
v3.Search = r.URL.Query().Get("search")
// when search is specified, you can optionally specify a search category with this parameter.
v3.SearchCategory = r.URL.Query().Get("search_cat")
// when search is specified, we also recommend to set the search_count to the number of search results displayed on the results page.
// When keywords are tracked with &search_count=0 they will appear in the "No Result Search Keyword" report.
v3.SearchCount = r.URL.Query().Get("search_count")
// Accepts a six character unique ID that identifies which actions were performed on a specific page view.
// When a page was viewed, all following tracking requests (such as events) during that page view should use the same pageview ID.
// Once another page was viewed a new unique ID should be generated.
// Use [0-9a-Z] as possible characters for the unique ID.
v3.PageViewID = r.URL.Query().Get("pv_id")
// If specified, the tracking request will trigger a conversion for the goal of the website being tracked with this ID.
v3.GoalID = r.URL.Query().Get("idgoal")
// A monetary value that was generated as revenue by this goal conversion.
// Only used if idgoal is specified in the request.
v3.Revenue = r.URL.Query().Get("revenue")
// The amount of time it took the server to generate this action, in milliseconds.
// This value is used to process the Page speed report Avg. generation time column in the Page URL and Page Title reports,
// as well as a site wide running average of the speed of your server.
// Note: when using the JavaScript tracker this value is set to the time for server to generate response + the time for client to download response.
v3.GenerationTime = r.URL.Query().Get("gt_ms")
// The charset of the page being tracked.
// Specify the charset if the data you send to Piwik is encoded in a different character set than the default utf-8.
v3.Characterset = r.URL.Query().Get("cs")
/*
Optional Event Tracking info
*/
// The event category. Must not be empty. (eg. Videos, Music, Games...)
v3.EventCategory = r.URL.Query().Get("e_c")
// The event action. Must not be empty. (eg. Play, Pause, Duration, Add Playlist, Downloaded, Clicked...)
v3.EventAction = r.URL.Query().Get("e_a")
// The event name. (eg. a Movie name, or Song name, or File name...)
v3.EventName = r.URL.Query().Get("e_n")
// The event value. Must be a float or integer value (numeric), not a string.
v3.EventValue = r.URL.Query().Get("e_v")
/*
Optional Content Tracking info
*/
// The name of the content. For instance 'Ad Foo Bar'
v3.ContentName = r.URL.Query().Get("c_n")
// The actual content piece. For instance the path to an image, video, audio, any text
v3.ContentPiece = r.URL.Query().Get("c_p")
// The target of the content. For instance the URL of a landing page
v3.ContentTarget = r.URL.Query().Get("c_t")
// The name of the interaction with the content. For instance a 'click'
v3.ContentInteraction = r.URL.Query().Get("c_i")
// Optional Ecommerce info
// you must set &idgoal=0 in the request to track an ecommerce interaction: cart update or an ecommerce order.
if r.URL.Query().Get("idgoal") == "0" {
// The unique string identifier for the ecommerce order (required when tracking an ecommerce order)
v3.ECommerceID = r.URL.Query().Get("ec_id")
// Items in the Ecommerce order.
// This is a JSON encoded array of items. Each item is an array with the following info in this order: item sku, item name, item category, item price, item quantity.
json.Unmarshal([]byte(r.URL.Query().Get("ec_items")), v3.ECommerceItems)
// The grand total for the ecommerce order (required when tracking an ecommerce order)
v3.Revenue = r.URL.Query().Get("revenue")
// The sub total of the order; excludes shipping.
v3.ECommerceSubTotal = r.URL.Query().Get("ec_st")
// Tax Amount of the order
v3.ECommerceTax = r.URL.Query().Get("ec_tx")
// Shipping cost of the Order
v3.ECommerceShipping = r.URL.Query().Get("ec_sh")
// Discount offered
v3.ECommerceDiscount = r.URL.Query().Get("ec_dt")
// The UNIX timestamp of this customer's last ecommerce order.
// This value is used to process the "Days since last order" report.
v3.ECommerceTime = stringToTime(r.URL.Query().Get("_ects"))
}
/*
Other parameters (require authentication via token_auth)
*/
// 32 character authorization key used to authenticate the API request.
v3.TokenAuth = r.URL.Query().Get("token_auth")
// Override value for the visitor IP (both IPv4 and IPv6 notations supported).
v3.CustomIP = r.URL.Query().Get("cip")
// Override for the datetime of the request (normally the current time is used).
// This can be used to record visits and page views in the past.
// The expected format is either a datetime such as: 2011-04-05 00:11:42 (remember to URL encode the value!), or a valid UNIX timestamp such as 1301919102.
// The datetime must be sent in UTC timezone.
// Note: if you record data in the past, you will need to force Piwik to re-process reports for the past dates.
// If you set cdt to a datetime older than 24 hours then token_auth must be set.
// If you set cdt with a datetime in the last 24 hours then you don't need to pass token_auth.
v3.CustomDateTime = r.URL.Query().Get("cdt")
// An override value for the country. Should be set to the two letter country code of the visitor (lowercase), eg fr, de, us.
v3.CustomCountry = r.URL.Query().Get("country")
// An override value for the region. Should be set to the two letter region code as defined by MaxMind's GeoIP databases. See here for a list of them for every country (the region codes are located in the second column, to the left of the region name and to the right of the country code).
v3.CustomRegion = r.URL.Query().Get("region")
// An override value for the city. The name of the city the visitor is located in, eg, Tokyo.
v3.CustomCity = r.URL.Query().Get("city")
// An override value for the visitor's latitude, eg 22.456.
v3.CustomLatitude = r.URL.Query().Get("lat")
// An override value for the visitor's longitude, eg 22.456.
v3.CustomLongitude = r.URL.Query().Get("long")
/*
Media Analytics parameters
https://developer.piwik.org/guides/media-analytics/custom-player#media-analytics-http-tracking-api-reference
*/
// A unique id that is always the same while playing a media. As soon as the played media changes (new video or audio started), this ID has to change.
v3.MediaID = r.URL.Query().Get("ma_id")
// The name / title of the media.
v3.MediaTitle = r.URL.Query().Get("ma_ti")
// The URL of the media resource.
v3.MediaResource = r.URL.Query().Get("ma_re")
// video or audio depending on the type of the media.
v3.MediaType = r.URL.Query().Get("ma_mt")
// The name of the media player, for example html5.
v3.MediaPlayerName = r.URL.Query().Get("ma_pn")
// The time in seconds for how long a user has been playing this media.
// This number should typically increase when you send a media tracking request.
// It should be 0 if the media was only visible/impressed but not played.
// Do not increase this number when a media is paused.
v3.MediaSecondsPlayingTime = r.URL.Query().Get("ma_st")
// The duration (the length) of the media in seconds. For example if a video is 90 seconds long, the value should be 90.
v3.MediaLength = r.URL.Query().Get("ma_le")
// The progress / current position within the media. Defines basically at which position within the total length the user is currently playing.
v3.MediaProgress = r.URL.Query().Get("ma_ps")
// Defines after how many seconds the user has started playing this media.
// For example a user might have seen the poster of the video for 30 seconds before a user actually pressed the play button.
v3.MediaTimeUntilPlay = r.URL.Query().Get("ma_ttp")
// The resolution width of the media in pixels. Only recommended to be set for videos.
v3.MediaWidth = r.URL.Query().Get("ma_w")
// The resolution height of the media in pixels. Only recommended to be set for videos.
v3.MediaHeight = r.URL.Query().Get("ma_h")
// Should be 0 or 1 and defines whether the media is currently viewed in full screen. Only recommended to be set for videos.
switch r.URL.Query().Get("ma_fs") {
case "0":
v3.MediaFullscreen = false
case "1":
v3.MediaFullscreen = true
default:
v3.MediaFullscreen = false
}
// If set to 0 (send_image=0) Piwik will respond with a HTTP 204 response code instead of a GIF image.
// This improves performance and can fix errors if images are not allowed to be obtained directly (eg Chrome Apps).
// Available since Piwik 2.10.0
v3.SendImage = r.URL.Query().Get("send_image")
return v3
/*
r.URL.Query().Get("") //
*/
}
func stringToTime(s string) time.Time {
i, err := strconv.ParseInt(s, 10, 64)
if err != nil {
return time.Time{}
}
return time.Unix(i, 0)
}
func atoi(a string) int {
i, err := strconv.Atoi(a)
if err != nil {
panic(err)
}
return i
}

87
mongo/site.go Normal file
View File

@ -0,0 +1,87 @@
package mongo
import (
"github.com/dalu/gopiwik/piwik"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
)
func NewSiteStorage(ms *mgo.Session, db, col string) SiteStorage {
return SiteStorage{
ms: ms.Clone(),
db: db,
col: col,
}
}
type SiteStorage struct {
ms *mgo.Session
db, col string
}
func (s SiteStorage) Add(site *piwik.Site) error {
ms := s.ms.Copy()
defer ms.Close()
c := ms.DB(s.db).C(s.col)
site.ID = bson.NewObjectId().Hex()
return c.Insert(site)
}
func (s SiteStorage) Remove(site *piwik.Site) error {
ms := s.ms.Copy()
defer ms.Close()
c := ms.DB(s.db).C(s.col)
return c.RemoveId(site.ID)
}
func (s SiteStorage) Update(site *piwik.Site) error {
ms := s.ms.Copy()
defer ms.Close()
c := ms.DB(s.db).C(s.col)
return c.UpdateId(site.ID, site)
}
func (s SiteStorage) List() ([]*piwik.Site, error) {
ms := s.ms.Copy()
defer ms.Close()
c := ms.DB(s.db).C(s.col)
sites := []*piwik.Site{}
if e := c.Find(nil).All(&sites); e != nil {
return sites, e
}
return sites, nil
}
func (s SiteStorage) FindId(id string) (*piwik.Site, error) {
ms := s.ms.Copy()
defer ms.Close()
site := new(piwik.Site)
c := ms.DB(s.db).C(s.col)
if e := c.FindId(id).One(site); e != nil {
return nil, e
}
return site, nil
}
func (s SiteStorage) Find(query map[string]interface{}) (*piwik.Site, error) {
ms := s.ms.Copy()
defer ms.Close()
c := ms.DB(s.db).C(s.col)
site := new(piwik.Site)
if e := c.Find(query).One(site); e != nil {
return nil, e
}
return site, nil
}
func (s SiteStorage) FindAll(query map[string]interface{}) ([]*piwik.Site, error) {
ms := s.ms.Copy()
defer ms.Close()
c := ms.DB(s.db).C(s.col)
sites := []*piwik.Site{}
if e := c.Find(query).All(&sites); e != nil {
return sites, e
}
return sites, nil
}

51
mongo/visit.go Normal file
View File

@ -0,0 +1,51 @@
package mongo
import (
"github.com/dalu/gopiwik/piwik"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
)
func NewVisitStorage(ms *mgo.Session, db, col string) VisitStorage {
return VisitStorage{
ms: ms.Clone(),
db: db,
col: col,
}
}
type VisitStorage struct {
ms *mgo.Session
db, col string
}
func (s VisitStorage) Add(visit *piwik.Visit) error {
ms := s.ms.Copy()
defer ms.Close()
c := ms.DB(s.db).C(s.col)
visit.ID = bson.NewObjectId().Hex()
return c.Insert(visit)
}
func (s VisitStorage) Find(query map[string]interface{}) (*piwik.Visit, error) {
ms := s.ms.Copy()
defer ms.Close()
c := ms.DB(s.db).C(s.col)
visit := new(piwik.Visit)
if e := c.Find(query).One(visit); e != nil {
return nil, e
}
return visit, nil
}
func (s VisitStorage) FindAll(query map[string]interface{}) ([]*piwik.Visit, error) {
ms := s.ms.Copy()
defer ms.Close()
c := ms.DB(s.db).C(s.col)
visits := []*piwik.Visit{}
if e := c.Find(query).All(visits); e != nil {
return nil, e
}
return visits, nil
}

120
piwik/handler.go Normal file
View File

@ -0,0 +1,120 @@
package piwik
import (
"log"
"net/http"
"encoding/json"
"os"
)
type PiwikHandler struct {
fileServer http.Handler
siteStorage SiteStorageInterface
visitStorage VisitStorageInterface
}
func NewPiwikHandler(ss SiteStorageInterface, vs VisitStorageInterface) *PiwikHandler {
return &PiwikHandler{
fileServer: http.FileServer(http.Dir("assets")),
siteStorage: ss,
visitStorage: vs,
}
}
func (h *PiwikHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
switch r.URL.Path {
case "/piwik.php":
h.handlePiwik(w, r)
default:
h.fileServer.ServeHTTP(w, r)
}
default:
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
}
}
func (h *PiwikHandler) handlePiwik(w http.ResponseWriter, r *http.Request) {
pr := ParseV3(r)
v := &Visit{
SiteID: pr.SiteID,
URL: pr.URL,
ActionName: pr.ActionName,
VisitorID: pr.VisitorID,
Referrer: pr.Referrer,
CustomVars: pr.CustomVars,
VisitsCount: pr.VisitsCount,
ViewTime: pr.ViewTime,
VisitorFirstVisitTime: pr.VisitorFirstVisitTime,
CampaignName: pr.CampaignName,
CampaignKeyword: pr.CampaignKeyword,
Resolution: pr.Resolution,
LocalHour: pr.LocalHour,
LocalMinute: pr.LocalMinute,
LocalSeconds: pr.LocalSeconds,
LocalTimeUTC: pr.LocalTimeUTC,
Plugins: pr.Plugins,
SupportsCookies: pr.SupportsCookies,
UserAgent: pr.UserAgent,
Language: pr.Language,
UserID: pr.UserID,
CustomUserID: pr.CustomUserID,
NewVisit: pr.NewVisit,
PageCustomVars: pr.PageCustomVars,
Link: pr.Link,
Download: pr.Download,
Search: pr.Search,
SearchCategory: pr.SearchCategory,
SearchCount: pr.SearchCount,
PageViewID: pr.PageViewID,
GoalID: pr.GoalID,
Revenue: pr.Revenue,
GenerationTime: pr.GenerationTime,
Characterset: pr.Characterset,
EventCategory: pr.EventCategory,
EventAction: pr.EventAction,
EventName: pr.EventName,
EventValue: pr.EventValue,
ContentName: pr.ContentName,
ContentPiece: pr.ContentPiece,
ContentTarget: pr.ContentTarget,
ContentInteraction: pr.ContentInteraction,
ECommerceID: pr.ECommerceID,
ECommerceItems: pr.ECommerceItems,
ECommerceSubTotal: pr.ECommerceSubTotal,
ECommerceTax: pr.ECommerceTax,
ECommerceShipping: pr.ECommerceShipping,
ECommerceDiscount: pr.ECommerceDiscount,
ECommerceTime: pr.ECommerceTime,
TokenAuth: pr.TokenAuth,
CustomIP: pr.CustomIP,
CustomDateTime: pr.CustomDateTime,
CustomCountry: pr.CustomCountry,
CustomRegion: pr.CustomRegion,
CustomCity: pr.CustomCity,
CustomLatitude: pr.CustomLatitude,
CustomLongitude: pr.CustomLongitude,
MediaID: pr.MediaID,
MediaTitle: pr.MediaTitle,
MediaResource: pr.MediaResource,
MediaType: pr.MediaType,
MediaPlayerName: pr.MediaPlayerName,
MediaSecondsPlayingTime: pr.MediaSecondsPlayingTime,
MediaLength: pr.MediaLength,
MediaProgress: pr.MediaProgress,
MediaTimeUntilPlay: pr.MediaTimeUntilPlay,
MediaWidth: pr.MediaWidth,
MediaHeight: pr.MediaHeight,
MediaFullscreen: pr.MediaFullscreen,
}
if e := h.visitStorage.Add(v); e != nil {
log.Println(e)
}
if pr.SendImage == "0" {
w.WriteHeader(204)
}
json.NewEncoder(os.Stdout).Encode(v)
}

500
piwik/parse.go Normal file
View File

@ -0,0 +1,500 @@
package piwik
import (
"encoding/json"
"log"
"net/http"
"strconv"
"time"
)
type V3Visit struct {
SiteID string `json:"site_id"` //idsite
Record bool `json:"record"` //rec
URL string `json:"url"` //url
ActionName string `json:"action_name"` //action_anem
VisitorID string `json:"visitor_id"` //_id
Referrer string `json:"referrer"` //urlref
CustomVars map[string]interface{} `json:"custom_vars"` //_cvar
VisitsCount string `json:"visits_count"` //_idvc
ViewTime time.Time `json:"view_time"` //_viewts
VisitorFirstVisitTime time.Time `json:"visitor_first_visit_time"` //_idts
CampaignName string `json:"campaign_name"` //_rcn
CampaignKeyword string `json:"campaign_keyword"` //_rck
Resolution string `json:"resolution"` //res
LocalHour string `json:"local_hour"` //h
LocalMinute string `json:"local_minute"` //m
LocalSeconds string `json:"local_seconds"` //s
LocalTimeUTC time.Time `json:"local_time_utc"`
Plugins *Plugins `json:"plugins"`
SupportsCookies bool `json:"supports_cookies"` //cookie
UserAgent string `json:"user_agent"` //ua
Language string `json:"language"` //lang
UserID string `json:"user_id"` //uid
CustomUserID string `json:"custom_user_id"` //cid
NewVisit bool `json:"new_visit"` //new_visit
PageCustomVars map[string]interface{} `json:"page_custom_vars"` //cvar
Link string `json:"link"` //link
Download string `json:"download"` //download
Search string `json:"search"` //search
SearchCategory string `json:"search_category"` //search_cat
SearchCount string `json:"search_count"` //search_count
PageViewID string `json:"page_view_id"` //pv_id
GoalID string `json:"goal_id"` //idgoal
Revenue string `json:"revenue"` //revenue
GenerationTime string `json:"generation_time"` //gt_ms
Characterset string `json:"characterset"` //cs
EventCategory string `json:"event_category"` //e_c
EventAction string `json:"event_action"` //e_a
EventName string `json:"event_name"` //e_n
EventValue string `json:"event_value"` //e_v
ContentName string `json:"content_name"` //c_n
ContentPiece string `json:"content_piece"` //c_p
ContentTarget string `json:"content_target"` //c_t
ContentInteraction string `json:"content_interaction"` //c_i
ECommerceID string `json:"ecommerce_id"` // ec_id
ECommerceItems [][]string `json:"ecommerce_items"` // ec_items
ECommerceSubTotal string `json:"ecommerce_sub_total"` // ec_st
ECommerceTax string `json:"ecommerce_tax"` // ec_tx
ECommerceShipping string `json:"ecommerce_shipping"` // ec_sh
ECommerceDiscount string `json:"ecommerce_discount"` // ec_dt
ECommerceTime time.Time `json:"ecommerce_time"` // ec_ts
TokenAuth string `json:"token_auth"` //token_auth
CustomIP string `json:"custom_ip"` //cip
CustomDateTime string `json:"custom_date_time"` //cdt
CustomCountry string `json:"custom_country"` //country
CustomRegion string `json:"custom_region"` //region
CustomCity string `json:"custom_city"` //city
CustomLatitude string `json:"custom_latitude"` //lat
CustomLongitude string `json:"custom_longitude"` //long
MediaID string `json:"media_id"` // ma_id
MediaTitle string `json:"media_title"` // ma_ti
MediaResource string `json:"media_resource"` // ma_re
MediaType string `json:"media_type"` // ma_mt
MediaPlayerName string `json:"media_player_name"` // ma_pn
MediaSecondsPlayingTime string `json:"media_seconds_playing_time"` // ma_st
MediaLength string `json:"media_length"` // ma_le
MediaProgress string `json:"media_progress"` // ma_ps
MediaTimeUntilPlay string `json:"media_time_until_play"` // ma_ttp
MediaWidth string `json:"media_width"` // ma_w
MediaHeight string `json:"media_height"` // ma_h
MediaFullscreen bool `json:"media_fullscreen"` // ma_fs
SendImage string `json:"send_image"` //send_image
}
type Plugins struct {
Flash bool `json:"flash"` //fla
Java bool `json:"java"` //java
Director bool `json:"director"` //dir
Quicktime bool `json:"quicktime"` //qt
RealPlayer bool `json:"real_player"` //realp
PDF bool `json:"pdf"` //pdf
WindowsMedia bool `json:"windows_media"` //wma
Gears bool `json:"gears"` //gears
Silverlight bool `json:"silverlight"` //ag
}
func ParseV3(r *http.Request) *V3Visit {
/*
https://developer.piwik.org/api-reference/tracking-api
*/
v3 := new(V3Visit)
// (required) — The ID of the website we're tracking a visit/action for.
v3.SiteID = r.URL.Query().Get("idsite")
// (required) — Required for tracking, must be set to one, eg, &rec=1.
if r.URL.Query().Get("rec") == "1" {
v3.Record = true
} else {
v3.Record = false
}
// (required) — The full URL for the current action.
v3.URL = r.URL.Query().Get("url")
// (recommended) — The title of the action being tracked.
// It is possible to use slashes / to set one or several categories for this action.
// For example, Help / Feedback will create the Action Feedback in the category Help.
v3.ActionName = r.URL.Query().Get("action_name")
// (recommended) — The unique visitor ID, must be a 16 characters hexadecimal string.
// Every unique visitor must be assigned a different ID and this ID must not change after it is assigned.
// If this value is not set Piwik will still track visits, but the unique visitors metric might be less accurate.
v3.VisitorID = r.URL.Query().Get("_id")
/*
r.URL.Query().Get("rand") // (recommended) — Meant to hold a random value that is generated before each request. Using it helps avoid the tracking request being cached by the browser or a proxy.
r.URL.Query().Get("apiv") // (recommended) — The parameter &apiv=1 defines the api version to use (currently always set to 1)
*/
// The full HTTP Referrer URL.
// This value is used to determine how someone got to your website (ie, through a website, search engine or campaign).
v3.Referrer = r.URL.Query().Get("urlref")
// Visit scope custom variables.
// This is a JSON encoded string of the custom variable array (see below for an example value).
if r.URL.Query().Get("_cvar") != "" {
if e := json.Unmarshal([]byte(r.URL.Query().Get("_cvar")), v3.CustomVars); e != nil {
log.Println("Error unmarshalling _cvar", e)
}
}
// The current count of visits for this visitor.
// To set this value correctly, it would be required to store the value for each visitor in your application (using sessions or persisting in a database).
// Then you would manually increment the counts by one on each new visit or "session", depending on how you choose to define a visit.
// This value is used to populate the report Visitors > Engagement > Visits by visit number.
v3.VisitsCount = r.URL.Query().Get("_idvc")
// The UNIX timestamp of this visitor's previous visit. This parameter is used to populate the report Visitors > Engagement > Visits by days since last visit.
v3.ViewTime = stringToTime(r.URL.Query().Get("_viewts"))
// The UNIX timestamp of this visitor's first visit. This could be set to the date where the user first started using your software/app, or when he/she created an account.
// This parameter is used to populate the Goals > Days to Conversion report.
v3.VisitorFirstVisitTime = stringToTime(r.URL.Query().Get("_idts"))
// The Campaign name (see Tracking Campaigns).
// Used to populate the Referrers > Campaigns report.
// Note: this parameter will only be used for the first pageview of a visit.
v3.CampaignName = r.URL.Query().Get("_rcn")
// The Campaign Keyword (see Tracking Campaigns).
// Used to populate the Referrers > Campaigns report (clicking on a campaign loads all keywords for this campaign).
// Note: this parameter will only be used for the first pageview of a visit.
v3.CampaignKeyword = r.URL.Query().Get("_rck")
// The resolution of the device the visitor is using, eg 1280x1024.
v3.Resolution = r.URL.Query().Get("res")
// The current hour (local time).
v3.LocalHour = r.URL.Query().Get("h")
// The current minute (local time).
v3.LocalMinute = r.URL.Query().Get("m")
// The current second (local time).
v3.LocalSeconds = r.URL.Query().Get("s")
v3.LocalTimeUTC = time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), atoi(r.URL.Query().Get("h")), atoi(r.URL.Query().Get("m")), atoi(r.URL.Query().Get("s")), 0, time.UTC)
// plugins used by the visitor can be specified by setting the following parameters to 1
v3.Plugins = new(Plugins)
// Flash
if r.URL.Query().Get("fla") == "1" {
v3.Plugins.Flash = true
}
// Java
if r.URL.Query().Get("java") == "1" {
v3.Plugins.Java = true
}
// Director
if r.URL.Query().Get("dir") == "1" {
v3.Plugins.Director = true
}
// Quicktime
if r.URL.Query().Get("qt") == "1" {
v3.Plugins.Quicktime = true
}
// Real Player
if r.URL.Query().Get("realp") == "1" {
v3.Plugins.RealPlayer = true
}
// PDF
if r.URL.Query().Get("pdf") == "1" {
v3.Plugins.PDF = true
}
// Windows Media
if r.URL.Query().Get("wma") == "1" {
v3.Plugins.WindowsMedia = true
}
// Gears
if r.URL.Query().Get("gears") == "1" {
v3.Plugins.Gears = true
}
// Silverlight
if r.URL.Query().Get("ag") == "1" {
v3.Plugins.Silverlight = true
}
// when set to 1, the visitor's client is known to support cookies.
if r.URL.Query().Get("cookie") == "1" {
v3.SupportsCookies = true
}
// An override value for the User-Agent HTTP header field. The user agent is used to detect the operating system and browser used.
if r.URL.Query().Get("ua") != "" {
v3.UserAgent = r.URL.Query().Get("ua")
} else {
v3.UserAgent = r.UserAgent()
}
// An override value for the Accept-Language HTTP header field. This value is used to detect the visitor's country if GeoIP is not enabled.
if r.URL.Query().Get("lang") != "" {
v3.Language = r.URL.Query().Get("lang")
} else {
v3.Language = r.Header.Get("Accept-Language")
}
// defines the User ID for this request.
// User ID is any non empty unique string identifying the user (such as an email address or a username).
// To access this value, users must be logged-in in your system so you can fetch this user ID from your system, and pass it to Piwik.
// The User ID appears in the visitor log, the Visitor profile, and you can Segment reports for one or several User ID (userId segment).
// When specified, the User ID will be "enforced".
// This means that if there is no recent visit with this User ID, a new one will be created.
// If a visit is found in the last 30 minutes with your specified User ID, then the new action will be recorded to this existing visit.
v3.UserID = r.URL.Query().Get("uid")
// defines the visitor ID for this request.
// You must set this value to exactly a 16 character hexadecimal string (containing only characters 01234567890abcdefABCDEF).
// We recommended to set the User ID via uid rather than use this cid
v3.CustomUserID = r.URL.Query().Get("cid")
// If set to 1, will force a new visit to be created for this action. This feature is also available in JavaScript.
if r.URL.Query().Get("new_visit") == "1" {
v3.NewVisit = true
}
/*
// ? scopes?
dimension := make(map[string]string)
for k, v := range r.URL.Query() {
if strings.HasPrefix(k, "dimension") {
dimension[strings.TrimPrefix(k, "dimension")] = v
}
}
*/
/*
Optional Action info (measure Page view, Outlink, Download, Site search)
*/
// Page scope custom variables. This is a JSON encoded string of the custom variable array (see below for an example value).
json.Unmarshal([]byte(r.URL.Query().Get("cvar")), v3.PageCustomVars)
// An external URL the user has opened.
// Used for tracking outlink clicks.
// We recommend to also set the url parameter to this same value.
v3.Link = r.URL.Query().Get("link")
// URL of a file the user has downloaded.
// Used for tracking downloads.
// We recommend to also set the url parameter to this same value.
v3.Download = r.URL.Query().Get("download")
// The Site Search keyword.
// When specified, the request will not be tracked as a normal pageview but will instead be tracked as a Site Search request.
v3.Search = r.URL.Query().Get("search")
// when search is specified, you can optionally specify a search category with this parameter.
v3.SearchCategory = r.URL.Query().Get("search_cat")
// when search is specified, we also recommend to set the search_count to the number of search results displayed on the results page.
// When keywords are tracked with &search_count=0 they will appear in the "No Result Search Keyword" report.
v3.SearchCount = r.URL.Query().Get("search_count")
// Accepts a six character unique ID that identifies which actions were performed on a specific page view.
// When a page was viewed, all following tracking requests (such as events) during that page view should use the same pageview ID.
// Once another page was viewed a new unique ID should be generated.
// Use [0-9a-Z] as possible characters for the unique ID.
v3.PageViewID = r.URL.Query().Get("pv_id")
// If specified, the tracking request will trigger a conversion for the goal of the website being tracked with this ID.
v3.GoalID = r.URL.Query().Get("idgoal")
// A monetary value that was generated as revenue by this goal conversion.
// Only used if idgoal is specified in the request.
v3.Revenue = r.URL.Query().Get("revenue")
// The amount of time it took the server to generate this action, in milliseconds.
// This value is used to process the Page speed report Avg. generation time column in the Page URL and Page Title reports,
// as well as a site wide running average of the speed of your server.
// Note: when using the JavaScript tracker this value is set to the time for server to generate response + the time for client to download response.
v3.GenerationTime = r.URL.Query().Get("gt_ms")
// The charset of the page being tracked.
// Specify the charset if the data you send to Piwik is encoded in a different character set than the default utf-8.
v3.Characterset = r.URL.Query().Get("cs")
/*
Optional Event Tracking info
*/
// The event category. Must not be empty. (eg. Videos, Music, Games...)
v3.EventCategory = r.URL.Query().Get("e_c")
// The event action. Must not be empty. (eg. Play, Pause, Duration, Add Playlist, Downloaded, Clicked...)
v3.EventAction = r.URL.Query().Get("e_a")
// The event name. (eg. a Movie name, or Song name, or File name...)
v3.EventName = r.URL.Query().Get("e_n")
// The event value. Must be a float or integer value (numeric), not a string.
v3.EventValue = r.URL.Query().Get("e_v")
/*
Optional Content Tracking info
*/
// The name of the content. For instance 'Ad Foo Bar'
v3.ContentName = r.URL.Query().Get("c_n")
// The actual content piece. For instance the path to an image, video, audio, any text
v3.ContentPiece = r.URL.Query().Get("c_p")
// The target of the content. For instance the URL of a landing page
v3.ContentTarget = r.URL.Query().Get("c_t")
// The name of the interaction with the content. For instance a 'click'
v3.ContentInteraction = r.URL.Query().Get("c_i")
// Optional Ecommerce info
// you must set &idgoal=0 in the request to track an ecommerce interaction: cart update or an ecommerce order.
if r.URL.Query().Get("idgoal") == "0" {
// The unique string identifier for the ecommerce order (required when tracking an ecommerce order)
v3.ECommerceID = r.URL.Query().Get("ec_id")
// Items in the Ecommerce order.
// This is a JSON encoded array of items. Each item is an array with the following info in this order: item sku, item name, item category, item price, item quantity.
json.Unmarshal([]byte(r.URL.Query().Get("ec_items")), v3.ECommerceItems)
// The grand total for the ecommerce order (required when tracking an ecommerce order)
v3.Revenue = r.URL.Query().Get("revenue")
// The sub total of the order; excludes shipping.
v3.ECommerceSubTotal = r.URL.Query().Get("ec_st")
// Tax Amount of the order
v3.ECommerceTax = r.URL.Query().Get("ec_tx")
// Shipping cost of the Order
v3.ECommerceShipping = r.URL.Query().Get("ec_sh")
// Discount offered
v3.ECommerceDiscount = r.URL.Query().Get("ec_dt")
// The UNIX timestamp of this customer's last ecommerce order.
// This value is used to process the "Days since last order" report.
v3.ECommerceTime = stringToTime(r.URL.Query().Get("_ects"))
}
/*
Other parameters (require authentication via token_auth)
*/
// 32 character authorization key used to authenticate the API request.
v3.TokenAuth = r.URL.Query().Get("token_auth")
// Override value for the visitor IP (both IPv4 and IPv6 notations supported).
v3.CustomIP = r.URL.Query().Get("cip")
// Override for the datetime of the request (normally the current time is used).
// This can be used to record visits and page views in the past.
// The expected format is either a datetime such as: 2011-04-05 00:11:42 (remember to URL encode the value!), or a valid UNIX timestamp such as 1301919102.
// The datetime must be sent in UTC timezone.
// Note: if you record data in the past, you will need to force Piwik to re-process reports for the past dates.
// If you set cdt to a datetime older than 24 hours then token_auth must be set.
// If you set cdt with a datetime in the last 24 hours then you don't need to pass token_auth.
v3.CustomDateTime = r.URL.Query().Get("cdt")
// An override value for the country. Should be set to the two letter country code of the visitor (lowercase), eg fr, de, us.
v3.CustomCountry = r.URL.Query().Get("country")
// An override value for the region. Should be set to the two letter region code as defined by MaxMind's GeoIP databases. See here for a list of them for every country (the region codes are located in the second column, to the left of the region name and to the right of the country code).
v3.CustomRegion = r.URL.Query().Get("region")
// An override value for the city. The name of the city the visitor is located in, eg, Tokyo.
v3.CustomCity = r.URL.Query().Get("city")
// An override value for the visitor's latitude, eg 22.456.
v3.CustomLatitude = r.URL.Query().Get("lat")
// An override value for the visitor's longitude, eg 22.456.
v3.CustomLongitude = r.URL.Query().Get("long")
/*
Media Analytics parameters
https://developer.piwik.org/guides/media-analytics/custom-player#media-analytics-http-tracking-api-reference
*/
// A unique id that is always the same while playing a media. As soon as the played media changes (new video or audio started), this ID has to change.
v3.MediaID = r.URL.Query().Get("ma_id")
// The name / title of the media.
v3.MediaTitle = r.URL.Query().Get("ma_ti")
// The URL of the media resource.
v3.MediaResource = r.URL.Query().Get("ma_re")
// video or audio depending on the type of the media.
v3.MediaType = r.URL.Query().Get("ma_mt")
// The name of the media player, for example html5.
v3.MediaPlayerName = r.URL.Query().Get("ma_pn")
// The time in seconds for how long a user has been playing this media.
// This number should typically increase when you send a media tracking request.
// It should be 0 if the media was only visible/impressed but not played.
// Do not increase this number when a media is paused.
v3.MediaSecondsPlayingTime = r.URL.Query().Get("ma_st")
// The duration (the length) of the media in seconds. For example if a video is 90 seconds long, the value should be 90.
v3.MediaLength = r.URL.Query().Get("ma_le")
// The progress / current position within the media. Defines basically at which position within the total length the user is currently playing.
v3.MediaProgress = r.URL.Query().Get("ma_ps")
// Defines after how many seconds the user has started playing this media.
// For example a user might have seen the poster of the video for 30 seconds before a user actually pressed the play button.
v3.MediaTimeUntilPlay = r.URL.Query().Get("ma_ttp")
// The resolution width of the media in pixels. Only recommended to be set for videos.
v3.MediaWidth = r.URL.Query().Get("ma_w")
// The resolution height of the media in pixels. Only recommended to be set for videos.
v3.MediaHeight = r.URL.Query().Get("ma_h")
// Should be 0 or 1 and defines whether the media is currently viewed in full screen. Only recommended to be set for videos.
switch r.URL.Query().Get("ma_fs") {
case "0":
v3.MediaFullscreen = false
case "1":
v3.MediaFullscreen = true
default:
v3.MediaFullscreen = false
}
// If set to 0 (send_image=0) Piwik will respond with a HTTP 204 response code instead of a GIF image.
// This improves performance and can fix errors if images are not allowed to be obtained directly (eg Chrome Apps).
// Available since Piwik 2.10.0
v3.SendImage = r.URL.Query().Get("send_image")
return v3
/*
r.URL.Query().Get("") //
*/
}
func stringToTime(s string) time.Time {
i, err := strconv.ParseInt(s, 10, 64)
if err != nil {
return time.Time{}
}
return time.Unix(i, 0)
}
func atoi(a string) int {
i, err := strconv.Atoi(a)
if err != nil {
panic(err)
}
return i
}

98
piwik/storage.go Normal file
View File

@ -0,0 +1,98 @@
package piwik
import "time"
type SiteStorageInterface interface {
Add(site *Site) error
Remove(site *Site) error
Update(site *Site) error
List() ([]*Site, error)
FindId(id string) (*Site, error)
Find(query map[string]interface{}) (*Site, error)
FindAll(query map[string]interface{}) ([]*Site, error)
}
type VisitStorageInterface interface {
Add(visit *Visit) error
Find(query map[string]interface{}) (*Visit, error)
FindAll(query map[string]interface{}) ([]*Visit, error)
}
type Site struct {
ID string `bson:"_id" json:"id"`
Name string `bson:"name" json:"name"`
Domain string `bson:"domain" json:"domain"`
}
type Visit struct {
ID string `bson:"_id" json:"id"`
SiteID string `bson:"site_id" json:"site_id"` //idsite
URL string `bson:"url" json:"url"` //url
ActionName string `bson:"action_name,omitempty" json:"action_name,omitempty"` //action_anem
VisitorID string `bson:"visitor_id,omitempty" json:"visitor_id,omitempty"` //_id
Referrer string `bson:"referrer,omitempty" json:"referrer,omitempty"` //urlref
CustomVars map[string]interface{} `bson:"custom_vars,omitempty" json:"custom_vars,omitempty"` //_cvar
VisitsCount string `bson:"visits_count,omitempty" json:"visits_count,omitempty"` //_idvc
ViewTime time.Time `bson:"view_time,omitempty" json:"view_time,omitempty"` //_viewts
VisitorFirstVisitTime time.Time `bson:"visitor_first_visit_time,omitempty" json:"visitor_first_visit_time,omitempty"` //_idts
CampaignName string `bson:"campaign_name,omitempty" json:"campaign_name,omitempty"` //_rcn
CampaignKeyword string `bson:"campaign_keyword,omitempty" json:"campaign_keyword,omitempty"` //_rck
Resolution string `bson:"resolution,omitempty" json:"resolution,omitempty"` //res
LocalHour string `bson:"local_hour,omitempty" json:"local_hour,omitempty"` //h
LocalMinute string `bson:"local_minute,omitempty" json:"local_minute,omitempty"` //m
LocalSeconds string `bson:"local_seconds,omitempty" json:"local_seconds,omitempty"` //s
LocalTimeUTC time.Time `bson:"local_time_utc,omitempty" json:"local_time_utc,omitempty"`
Plugins *Plugins `bson:"plugins" json:"plugins"`
SupportsCookies bool `bson:"supports_cookies,omitempty" json:"supports_cookies,omitempty"` //cookie
UserAgent string `bson:"user_agent,omitempty" json:"user_agent,omitempty"` //ua
Language string `bson:"language,omitempty" json:"language,omitempty"` //lang
UserID string `bson:"user_id,omitempty" json:"user_id,omitempty"` //uid
CustomUserID string `bson:"custom_user_id,omitempty" json:"custom_user_id,omitempty"` //cid
NewVisit bool `bson:"new_visit,omitempty" json:"new_visit,omitempty"` //new_visit
PageCustomVars map[string]interface{} `bson:"page_custom_vars,omitempty" json:"page_custom_vars,omitempty"` //cvar
Link string `bson:"link,omitempty" json:"link,omitempty"` //link
Download string `bson:"download,omitempty" json:"download,omitempty"` //download
Search string `bson:"search,omitempty" json:"search,omitempty"` //search
SearchCategory string `bson:"search_category,omitempty" json:"search_category,omitempty"` //search_cat
SearchCount string `bson:"search_count,omitempty" json:"search_count,omitempty"` //search_count
PageViewID string `bson:"page_view_id,omitempty" json:"page_view_id,omitempty"` //pv_id
GoalID string `bson:"goal_id,omitempty" json:"goal_id,omitempty"` //idgoal
Revenue string `bson:"revenue,omitempty" json:"revenue,omitempty"` //revenue
GenerationTime string `bson:"generation_time,omitempty" json:"generation_time,omitempty"` //gt_ms
Characterset string `bson:"characterset,omitempty" json:"characterset,omitempty"` //cs
EventCategory string `bson:"event_category,omitempty" json:"event_category,omitempty"` //e_c
EventAction string `bson:"event_action,omitempty" json:"event_action,omitempty"` //e_a
EventName string `bson:"event_name,omitempty" json:"event_name,omitempty"` //e_n
EventValue string `bson:"event_value,omitempty" json:"event_value,omitempty"` //e_v
ContentName string `bson:"content_name,omitempty" json:"content_name,omitempty"` //c_n
ContentPiece string `bson:"content_piece,omitempty" json:"content_piece,omitempty"` //c_p
ContentTarget string `bson:"content_target,omitempty" json:"content_target,omitempty"` //c_t
ContentInteraction string `bson:"content_interaction,omitempty" json:"content_interaction,omitempty"` //c_i
ECommerceID string `bson:"ecommerce_id,omitempty" json:"ecommerce_id,omitempty"` // ec_id
ECommerceItems [][]string `bson:"ecommerce_items,omitempty" json:"ecommerce_items,omitempty"` // ec_items
ECommerceSubTotal string `bson:"ecommerce_sub_total,omitempty" json:"ecommerce_sub_total,omitempty"` // ec_st
ECommerceTax string `bson:"ecommerce_tax,omitempty" json:"ecommerce_tax,omitempty"` // ec_tx
ECommerceShipping string `bson:"ecommerce_shipping,omitempty" json:"ecommerce_shipping,omitempty"` // ec_sh
ECommerceDiscount string `bson:"ecommerce_discount,omitempty" json:"ecommerce_discount,omitempty"` // ec_dt
ECommerceTime time.Time `bson:"ecommerce_time,omitempty" json:"ecommerce_time,omitempty"` // ec_ts
TokenAuth string `bson:"token_auth,omitempty" json:"token_auth,omitempty"` //token_auth
CustomIP string `bson:"custom_ip,omitempty" json:"custom_ip,omitempty"` //cip
CustomDateTime string `bson:"custom_date_time,omitempty" json:"custom_date_time,omitempty"` //cdt
CustomCountry string `bson:"custom_country,omitempty" json:"custom_country,omitempty"` //country
CustomRegion string `bson:"custom_region,omitempty" json:"custom_region,omitempty"` //region
CustomCity string `bson:"custom_city,omitempty" json:"custom_city,omitempty"` //city
CustomLatitude string `bson:"custom_latitude,omitempty" json:"custom_latitude,omitempty"` //lat
CustomLongitude string `bson:"custom_longitude,omitempty" json:"custom_longitude,omitempty"` //long
MediaID string `bson:"media_id,omitempty" json:"media_id,omitempty"` // ma_id
MediaTitle string `bson:"media_title,omitempty" json:"media_title,omitempty"` // ma_ti
MediaResource string `bson:"media_resource,omitempty" json:"media_resource,omitempty"` // ma_re
MediaType string `bson:"media_type,omitempty" json:"media_type,omitempty"` // ma_mt
MediaPlayerName string `bson:"media_player_name,omitempty" json:"media_player_name,omitempty"` // ma_pn
MediaSecondsPlayingTime string `bson:"media_seconds_playing_time,omitempty" json:"media_seconds_playing_time,omitempty"` // ma_st
MediaLength string `bson:"media_length,omitempty" json:"media_length,omitempty"` // ma_le
MediaProgress string `bson:"media_progress,omitempty" json:"media_progress,omitempty"` // ma_ps
MediaTimeUntilPlay string `bson:"media_time_until_play,omitempty" json:"media_time_until_play,omitempty"` // ma_ttp
MediaWidth string `bson:"media_width,omitempty" json:"media_width,omitempty"` // ma_w
MediaHeight string `bson:"media_height,omitempty" json:"media_height,omitempty"` // ma_h
MediaFullscreen bool `bson:"media_fullscreen,omitempty" json:"media_fullscreen,omitempty"` // ma_fs
}