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 }