mbuzz 0.2.0__py3-none-any.whl → 0.7.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mbuzz/__init__.py +2 -8
- mbuzz/client/conversion.py +26 -5
- mbuzz/client/track.py +16 -13
- mbuzz/context.py +3 -3
- mbuzz/cookies.py +1 -3
- mbuzz/middleware/flask.py +11 -55
- {mbuzz-0.2.0.dist-info → mbuzz-0.7.0.dist-info}/METADATA +1 -1
- mbuzz-0.7.0.dist-info/RECORD +16 -0
- mbuzz/client/session.py +0 -38
- mbuzz/utils/session_id.py +0 -39
- mbuzz-0.2.0.dist-info/RECORD +0 -18
- {mbuzz-0.2.0.dist-info → mbuzz-0.7.0.dist-info}/WHEEL +0 -0
mbuzz/__init__.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Mbuzz - Multi-touch attribution SDK for Python."""
|
|
2
|
+
# NOTE: Session ID removed in 0.7.0 - server handles session resolution
|
|
2
3
|
|
|
3
4
|
from typing import Any, Dict, Optional, Union
|
|
4
5
|
|
|
@@ -8,7 +9,7 @@ from .client.track import track, TrackResult
|
|
|
8
9
|
from .client.identify import identify
|
|
9
10
|
from .client.conversion import conversion, ConversionResult
|
|
10
11
|
|
|
11
|
-
__version__ = "0.
|
|
12
|
+
__version__ = "0.7.0"
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
def init(
|
|
@@ -61,12 +62,6 @@ def visitor_id() -> Optional[str]:
|
|
|
61
62
|
return ctx.visitor_id if ctx else None
|
|
62
63
|
|
|
63
64
|
|
|
64
|
-
def session_id() -> Optional[str]:
|
|
65
|
-
"""Get current session ID from context."""
|
|
66
|
-
ctx = get_context()
|
|
67
|
-
return ctx.session_id if ctx else None
|
|
68
|
-
|
|
69
|
-
|
|
70
65
|
def user_id() -> Optional[str]:
|
|
71
66
|
"""Get current user ID from context."""
|
|
72
67
|
ctx = get_context()
|
|
@@ -79,7 +74,6 @@ __all__ = [
|
|
|
79
74
|
"conversion",
|
|
80
75
|
"identify",
|
|
81
76
|
"visitor_id",
|
|
82
|
-
"session_id",
|
|
83
77
|
"user_id",
|
|
84
78
|
"TrackResult",
|
|
85
79
|
"ConversionResult",
|
mbuzz/client/conversion.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Conversion request for tracking conversions."""
|
|
2
|
+
# NOTE: Session ID removed in 0.7.0 - server handles session resolution
|
|
2
3
|
|
|
3
4
|
from dataclasses import dataclass
|
|
4
5
|
from typing import Any, Dict, Optional, Union
|
|
@@ -26,6 +27,9 @@ def conversion(
|
|
|
26
27
|
is_acquisition: bool = False,
|
|
27
28
|
inherit_acquisition: bool = False,
|
|
28
29
|
properties: Optional[Dict[str, Any]] = None,
|
|
30
|
+
ip: Optional[str] = None,
|
|
31
|
+
user_agent: Optional[str] = None,
|
|
32
|
+
identifier: Optional[Dict[str, str]] = None,
|
|
29
33
|
) -> ConversionResult:
|
|
30
34
|
"""Track a conversion.
|
|
31
35
|
|
|
@@ -39,6 +43,9 @@ def conversion(
|
|
|
39
43
|
is_acquisition: Whether this is a customer acquisition
|
|
40
44
|
inherit_acquisition: Whether to inherit acquisition from previous conversion
|
|
41
45
|
properties: Additional conversion properties
|
|
46
|
+
ip: Client IP address for server-side session resolution
|
|
47
|
+
user_agent: Client user agent for server-side session resolution
|
|
48
|
+
identifier: Cross-device identifier (email, user_id, etc.)
|
|
42
49
|
|
|
43
50
|
Returns:
|
|
44
51
|
ConversionResult with success status, conversion ID, and attribution data
|
|
@@ -47,22 +54,36 @@ def conversion(
|
|
|
47
54
|
|
|
48
55
|
visitor_id = visitor_id or (ctx.visitor_id if ctx else None)
|
|
49
56
|
user_id = user_id or (ctx.user_id if ctx else None)
|
|
57
|
+
ip = ip or (ctx.ip if ctx else None)
|
|
58
|
+
user_agent = user_agent or (ctx.user_agent if ctx else None)
|
|
50
59
|
|
|
51
60
|
if not visitor_id and not user_id:
|
|
52
61
|
return ConversionResult(success=False)
|
|
53
62
|
|
|
54
|
-
payload = {
|
|
63
|
+
payload: Dict[str, Any] = {
|
|
55
64
|
"conversion_type": conversion_type,
|
|
56
|
-
"visitor_id": visitor_id,
|
|
57
|
-
"user_id": str(user_id) if user_id else None,
|
|
58
|
-
"event_id": event_id,
|
|
59
|
-
"revenue": revenue,
|
|
60
65
|
"currency": currency,
|
|
61
66
|
"is_acquisition": is_acquisition,
|
|
62
67
|
"inherit_acquisition": inherit_acquisition,
|
|
63
68
|
"properties": properties or {},
|
|
64
69
|
}
|
|
65
70
|
|
|
71
|
+
# Only include non-None values
|
|
72
|
+
if visitor_id:
|
|
73
|
+
payload["visitor_id"] = visitor_id
|
|
74
|
+
if user_id:
|
|
75
|
+
payload["user_id"] = str(user_id)
|
|
76
|
+
if event_id:
|
|
77
|
+
payload["event_id"] = event_id
|
|
78
|
+
if revenue is not None:
|
|
79
|
+
payload["revenue"] = revenue
|
|
80
|
+
if ip:
|
|
81
|
+
payload["ip"] = ip
|
|
82
|
+
if user_agent:
|
|
83
|
+
payload["user_agent"] = user_agent
|
|
84
|
+
if identifier:
|
|
85
|
+
payload["identifier"] = identifier
|
|
86
|
+
|
|
66
87
|
response = post_with_response("/conversions", payload)
|
|
67
88
|
if not response:
|
|
68
89
|
return ConversionResult(success=False)
|
mbuzz/client/track.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Track request for event tracking."""
|
|
2
|
+
# NOTE: Session ID removed in 0.7.0 - server handles session resolution
|
|
2
3
|
|
|
3
4
|
from dataclasses import dataclass
|
|
4
5
|
from datetime import datetime, timezone
|
|
@@ -16,7 +17,6 @@ class TrackResult:
|
|
|
16
17
|
event_id: Optional[str] = None
|
|
17
18
|
event_type: Optional[str] = None
|
|
18
19
|
visitor_id: Optional[str] = None
|
|
19
|
-
session_id: Optional[str] = None
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
@dataclass
|
|
@@ -25,15 +25,15 @@ class TrackOptions:
|
|
|
25
25
|
|
|
26
26
|
event_type: str
|
|
27
27
|
visitor_id: Optional[str] = None
|
|
28
|
-
session_id: Optional[str] = None
|
|
29
28
|
user_id: Optional[str] = None
|
|
30
29
|
properties: Optional[Dict[str, Any]] = None
|
|
31
30
|
ip: Optional[str] = None
|
|
32
31
|
user_agent: Optional[str] = None
|
|
32
|
+
identifier: Optional[Dict[str, str]] = None
|
|
33
33
|
|
|
34
34
|
|
|
35
35
|
def _resolve_ids(options: TrackOptions) -> TrackOptions:
|
|
36
|
-
"""Resolve visitor/
|
|
36
|
+
"""Resolve visitor/user IDs and ip/user_agent from context if not provided."""
|
|
37
37
|
ctx = get_context()
|
|
38
38
|
if not ctx:
|
|
39
39
|
return options
|
|
@@ -41,11 +41,11 @@ def _resolve_ids(options: TrackOptions) -> TrackOptions:
|
|
|
41
41
|
return TrackOptions(
|
|
42
42
|
event_type=options.event_type,
|
|
43
43
|
visitor_id=options.visitor_id or ctx.visitor_id,
|
|
44
|
-
session_id=options.session_id or ctx.session_id,
|
|
45
44
|
user_id=options.user_id or ctx.user_id,
|
|
46
45
|
properties=options.properties,
|
|
47
46
|
ip=options.ip or ctx.ip,
|
|
48
47
|
user_agent=options.user_agent or ctx.user_agent,
|
|
48
|
+
identifier=options.identifier,
|
|
49
49
|
)
|
|
50
50
|
|
|
51
51
|
|
|
@@ -66,19 +66,23 @@ def _validate(options: TrackOptions) -> bool:
|
|
|
66
66
|
|
|
67
67
|
def _build_payload(options: TrackOptions, properties: Dict[str, Any]) -> Dict[str, Any]:
|
|
68
68
|
"""Build API payload from options."""
|
|
69
|
-
event = {
|
|
69
|
+
event: Dict[str, Any] = {
|
|
70
70
|
"event_type": options.event_type,
|
|
71
|
-
"visitor_id": options.visitor_id,
|
|
72
|
-
"session_id": options.session_id,
|
|
73
|
-
"user_id": options.user_id,
|
|
74
71
|
"properties": properties,
|
|
75
72
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
76
73
|
}
|
|
77
|
-
|
|
74
|
+
|
|
75
|
+
# Only include non-None values
|
|
76
|
+
if options.visitor_id:
|
|
77
|
+
event["visitor_id"] = options.visitor_id
|
|
78
|
+
if options.user_id:
|
|
79
|
+
event["user_id"] = options.user_id
|
|
78
80
|
if options.ip:
|
|
79
81
|
event["ip"] = options.ip
|
|
80
82
|
if options.user_agent:
|
|
81
83
|
event["user_agent"] = options.user_agent
|
|
84
|
+
if options.identifier:
|
|
85
|
+
event["identifier"] = options.identifier
|
|
82
86
|
|
|
83
87
|
return {"events": [event]}
|
|
84
88
|
|
|
@@ -94,29 +98,28 @@ def _parse_response(response: Optional[Dict[str, Any]], options: TrackOptions) -
|
|
|
94
98
|
event_id=event.get("id"),
|
|
95
99
|
event_type=options.event_type,
|
|
96
100
|
visitor_id=options.visitor_id,
|
|
97
|
-
session_id=options.session_id,
|
|
98
101
|
)
|
|
99
102
|
|
|
100
103
|
|
|
101
104
|
def track(
|
|
102
105
|
event_type: str,
|
|
103
106
|
visitor_id: Optional[str] = None,
|
|
104
|
-
session_id: Optional[str] = None,
|
|
105
107
|
user_id: Optional[str] = None,
|
|
106
108
|
properties: Optional[Dict[str, Any]] = None,
|
|
107
109
|
ip: Optional[str] = None,
|
|
108
110
|
user_agent: Optional[str] = None,
|
|
111
|
+
identifier: Optional[Dict[str, str]] = None,
|
|
109
112
|
) -> TrackResult:
|
|
110
113
|
"""Track an event.
|
|
111
114
|
|
|
112
115
|
Args:
|
|
113
116
|
event_type: Type of event (e.g., "page_view", "button_click")
|
|
114
117
|
visitor_id: Visitor ID (uses context if not provided)
|
|
115
|
-
session_id: Session ID (uses context if not provided)
|
|
116
118
|
user_id: User ID (uses context if not provided)
|
|
117
119
|
properties: Additional event properties
|
|
118
120
|
ip: Client IP address for server-side session resolution
|
|
119
121
|
user_agent: Client user agent for server-side session resolution
|
|
122
|
+
identifier: Cross-device identifier (email, user_id, etc.)
|
|
120
123
|
|
|
121
124
|
Returns:
|
|
122
125
|
TrackResult with success status and event details
|
|
@@ -124,11 +127,11 @@ def track(
|
|
|
124
127
|
options = TrackOptions(
|
|
125
128
|
event_type=event_type,
|
|
126
129
|
visitor_id=visitor_id,
|
|
127
|
-
session_id=session_id,
|
|
128
130
|
user_id=user_id,
|
|
129
131
|
properties=properties,
|
|
130
132
|
ip=ip,
|
|
131
133
|
user_agent=user_agent,
|
|
134
|
+
identifier=identifier,
|
|
132
135
|
)
|
|
133
136
|
|
|
134
137
|
options = _resolve_ids(options)
|
mbuzz/context.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Request context management using contextvars."""
|
|
2
|
+
# NOTE: Session ID removed in 0.7.0 - server handles session resolution
|
|
2
3
|
|
|
3
4
|
from contextvars import ContextVar
|
|
4
5
|
from dataclasses import dataclass
|
|
@@ -10,12 +11,11 @@ class RequestContext:
|
|
|
10
11
|
"""Holds request-scoped data for tracking."""
|
|
11
12
|
|
|
12
13
|
visitor_id: str
|
|
13
|
-
|
|
14
|
+
ip: str
|
|
15
|
+
user_agent: str
|
|
14
16
|
user_id: Optional[str] = None
|
|
15
17
|
url: Optional[str] = None
|
|
16
18
|
referrer: Optional[str] = None
|
|
17
|
-
ip: Optional[str] = None
|
|
18
|
-
user_agent: Optional[str] = None
|
|
19
19
|
|
|
20
20
|
def enrich_properties(self, properties: Dict[str, Any]) -> Dict[str, Any]:
|
|
21
21
|
"""Add url and referrer to properties if not already present."""
|
mbuzz/cookies.py
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
"""Cookie constants for mbuzz SDK."""
|
|
2
|
+
# NOTE: Session cookie removed in 0.7.0 - server handles session resolution
|
|
2
3
|
|
|
3
4
|
VISITOR_COOKIE = "_mbuzz_vid"
|
|
4
|
-
SESSION_COOKIE = "_mbuzz_sid"
|
|
5
|
-
|
|
6
5
|
VISITOR_MAX_AGE = 63072000 # 2 years in seconds
|
|
7
|
-
SESSION_MAX_AGE = 1800 # 30 minutes in seconds
|
mbuzz/middleware/flask.py
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
"""Flask middleware for mbuzz tracking."""
|
|
2
|
+
# NOTE: Session cookie removed in 0.7.0 - server handles session resolution
|
|
2
3
|
|
|
3
|
-
import threading
|
|
4
4
|
from flask import Flask, request, g, Response
|
|
5
|
-
from typing import Optional
|
|
6
5
|
|
|
7
6
|
from ..config import config
|
|
8
7
|
from ..context import RequestContext, set_context, clear_context
|
|
9
|
-
from ..cookies import VISITOR_COOKIE,
|
|
8
|
+
from ..cookies import VISITOR_COOKIE, VISITOR_MAX_AGE
|
|
10
9
|
from ..utils.identifier import generate_id
|
|
11
|
-
from ..utils.session_id import generate_deterministic, generate_from_fingerprint
|
|
12
|
-
from ..client.session import create_session
|
|
13
10
|
|
|
14
11
|
|
|
15
12
|
def init_app(app: Flask) -> None:
|
|
@@ -21,12 +18,11 @@ def init_app(app: Flask) -> None:
|
|
|
21
18
|
return
|
|
22
19
|
|
|
23
20
|
visitor_id = _get_or_create_visitor_id()
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
ip = _get_client_ip()
|
|
22
|
+
user_agent = _get_user_agent()
|
|
26
23
|
|
|
27
|
-
_set_request_context(visitor_id,
|
|
28
|
-
_store_in_g(visitor_id
|
|
29
|
-
_create_session_if_new(visitor_id, session_id, is_new_session)
|
|
24
|
+
_set_request_context(visitor_id, ip, user_agent)
|
|
25
|
+
_store_in_g(visitor_id)
|
|
30
26
|
|
|
31
27
|
@app.after_request
|
|
32
28
|
def after_request(response: Response) -> Response:
|
|
@@ -55,19 +51,6 @@ def _get_or_create_visitor_id() -> str:
|
|
|
55
51
|
return request.cookies.get(VISITOR_COOKIE) or generate_id()
|
|
56
52
|
|
|
57
53
|
|
|
58
|
-
def _get_or_create_session_id() -> str:
|
|
59
|
-
"""Get session ID from cookie or generate deterministic one."""
|
|
60
|
-
existing = request.cookies.get(SESSION_COOKIE)
|
|
61
|
-
if existing:
|
|
62
|
-
return existing
|
|
63
|
-
|
|
64
|
-
existing_visitor_id = request.cookies.get(VISITOR_COOKIE)
|
|
65
|
-
if existing_visitor_id:
|
|
66
|
-
return generate_deterministic(existing_visitor_id)
|
|
67
|
-
else:
|
|
68
|
-
return generate_from_fingerprint(_get_client_ip(), _get_user_agent())
|
|
69
|
-
|
|
70
|
-
|
|
71
54
|
def _get_client_ip() -> str:
|
|
72
55
|
"""Get client IP from request headers."""
|
|
73
56
|
forwarded = request.headers.get("X-Forwarded-For", "")
|
|
@@ -81,11 +64,12 @@ def _get_user_agent() -> str:
|
|
|
81
64
|
return request.headers.get("User-Agent", "unknown")
|
|
82
65
|
|
|
83
66
|
|
|
84
|
-
def _set_request_context(visitor_id: str,
|
|
67
|
+
def _set_request_context(visitor_id: str, ip: str, user_agent: str) -> None:
|
|
85
68
|
"""Set request context for tracking calls."""
|
|
86
69
|
ctx = RequestContext(
|
|
87
70
|
visitor_id=visitor_id,
|
|
88
|
-
|
|
71
|
+
ip=ip,
|
|
72
|
+
user_agent=user_agent,
|
|
89
73
|
user_id=None,
|
|
90
74
|
url=request.url,
|
|
91
75
|
referrer=request.referrer,
|
|
@@ -93,34 +77,14 @@ def _set_request_context(visitor_id: str, session_id: str) -> None:
|
|
|
93
77
|
set_context(ctx)
|
|
94
78
|
|
|
95
79
|
|
|
96
|
-
def _store_in_g(visitor_id: str
|
|
80
|
+
def _store_in_g(visitor_id: str) -> None:
|
|
97
81
|
"""Store tracking IDs in Flask g object for after_request."""
|
|
98
82
|
g.mbuzz_visitor_id = visitor_id
|
|
99
|
-
g.mbuzz_session_id = session_id
|
|
100
83
|
g.mbuzz_is_new_visitor = VISITOR_COOKIE not in request.cookies
|
|
101
|
-
g.mbuzz_is_new_session = is_new_session
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
def _create_session_if_new(visitor_id: str, session_id: str, is_new_session: bool) -> None:
|
|
105
|
-
"""Create session asynchronously if new session."""
|
|
106
|
-
if not is_new_session:
|
|
107
|
-
return
|
|
108
|
-
|
|
109
|
-
ctx = RequestContext(
|
|
110
|
-
visitor_id=visitor_id,
|
|
111
|
-
session_id=session_id,
|
|
112
|
-
url=request.url,
|
|
113
|
-
referrer=request.referrer,
|
|
114
|
-
)
|
|
115
|
-
threading.Thread(
|
|
116
|
-
target=create_session,
|
|
117
|
-
args=(visitor_id, session_id, ctx.url, ctx.referrer),
|
|
118
|
-
daemon=True
|
|
119
|
-
).start()
|
|
120
84
|
|
|
121
85
|
|
|
122
86
|
def _set_cookies(response: Response) -> None:
|
|
123
|
-
"""Set visitor
|
|
87
|
+
"""Set visitor cookie on response."""
|
|
124
88
|
secure = request.is_secure
|
|
125
89
|
|
|
126
90
|
response.set_cookie(
|
|
@@ -131,11 +95,3 @@ def _set_cookies(response: Response) -> None:
|
|
|
131
95
|
samesite="Lax",
|
|
132
96
|
secure=secure,
|
|
133
97
|
)
|
|
134
|
-
response.set_cookie(
|
|
135
|
-
SESSION_COOKIE,
|
|
136
|
-
g.mbuzz_session_id,
|
|
137
|
-
max_age=SESSION_MAX_AGE,
|
|
138
|
-
httponly=True,
|
|
139
|
-
samesite="Lax",
|
|
140
|
-
secure=secure,
|
|
141
|
-
)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
mbuzz/__init__.py,sha256=tIBoql6_TTtWXErayliL98YGa-EIFMq9rE-X2vgfVHk,2150
|
|
2
|
+
mbuzz/api.py,sha256=BITkW6TQEGmPA0OmYFFC611X6JWiY6C1dY-_wsKDT2U,2107
|
|
3
|
+
mbuzz/config.py,sha256=u6v1WnMTgiqyDXHszlXw5hyNENgYRy7-z-IWKuYp0-o,2399
|
|
4
|
+
mbuzz/context.py,sha256=tcvtKaAbHzH8dHGkKviNgXF31IKgintqo0lhb6uU4dU,1308
|
|
5
|
+
mbuzz/cookies.py,sha256=pDcvSq0ulzKrFLf5UK408s9Lv2pVzLANVaF_-P7iG3c,194
|
|
6
|
+
mbuzz/client/__init__.py,sha256=oNOTpPKdt-b_VH_f848OQTbEEDFhlpbEjZ-WjKvh3JI,36
|
|
7
|
+
mbuzz/client/conversion.py,sha256=mUkxU8FgbmvvXnliTskawnDzbwn1SxutZ5E4rFkNt5o,3155
|
|
8
|
+
mbuzz/client/identify.py,sha256=duyd-OWdBo4w7-7Nv5X-GeG2IpMI-1G_kYwnDXup0FE,859
|
|
9
|
+
mbuzz/client/track.py,sha256=NReHqXWx24cPhIlc7UBQjj9QVdnXfrJsFzS5UmOUMtA,4354
|
|
10
|
+
mbuzz/middleware/__init__.py,sha256=gIjwTArToaQNB2NC0iPE_RcmzuHjH7-7jjd7_Dyq4Pw,37
|
|
11
|
+
mbuzz/middleware/flask.py,sha256=ccotvyzS4PB5-cfaplpwGJJOQzR90hy5iz6sTrhwXXc,2687
|
|
12
|
+
mbuzz/utils/__init__.py,sha256=-ejtRN6CTkV3D8uujDtAPDoMT0DAG-ULpWOF-Ae55dY,103
|
|
13
|
+
mbuzz/utils/identifier.py,sha256=iAYmd4he2RTtTNlmszb6KQmD9McZbSMgZZdkMuoGOCE,174
|
|
14
|
+
mbuzz-0.7.0.dist-info/METADATA,sha256=phnoKfsZaw8nA4u3UTE9uF6n9TkXxzGSAOBGKi35J_U,1849
|
|
15
|
+
mbuzz-0.7.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
16
|
+
mbuzz-0.7.0.dist-info/RECORD,,
|
mbuzz/client/session.py
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
"""Session request for creating sessions."""
|
|
2
|
-
|
|
3
|
-
from datetime import datetime, timezone
|
|
4
|
-
from typing import Optional
|
|
5
|
-
|
|
6
|
-
from ..api import post
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def create_session(
|
|
10
|
-
visitor_id: str,
|
|
11
|
-
session_id: str,
|
|
12
|
-
url: str,
|
|
13
|
-
referrer: Optional[str] = None,
|
|
14
|
-
) -> bool:
|
|
15
|
-
"""Create a new session.
|
|
16
|
-
|
|
17
|
-
Called async from middleware on first request.
|
|
18
|
-
|
|
19
|
-
Args:
|
|
20
|
-
visitor_id: Visitor ID
|
|
21
|
-
session_id: Session ID
|
|
22
|
-
url: Current page URL
|
|
23
|
-
referrer: Referring URL
|
|
24
|
-
|
|
25
|
-
Returns:
|
|
26
|
-
True on success, False on failure
|
|
27
|
-
"""
|
|
28
|
-
payload = {
|
|
29
|
-
"session": {
|
|
30
|
-
"visitor_id": visitor_id,
|
|
31
|
-
"session_id": session_id,
|
|
32
|
-
"url": url,
|
|
33
|
-
"referrer": referrer,
|
|
34
|
-
"started_at": datetime.now(timezone.utc).isoformat(),
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return post("/sessions", payload)
|
mbuzz/utils/session_id.py
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
"""Deterministic session ID generation."""
|
|
2
|
-
|
|
3
|
-
import hashlib
|
|
4
|
-
import secrets
|
|
5
|
-
import time
|
|
6
|
-
|
|
7
|
-
SESSION_TIMEOUT_SECONDS = 1800
|
|
8
|
-
SESSION_ID_LENGTH = 64
|
|
9
|
-
FINGERPRINT_LENGTH = 32
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def generate_deterministic(visitor_id: str, timestamp: int | None = None) -> str:
|
|
13
|
-
"""Generate session ID for returning visitors."""
|
|
14
|
-
if timestamp is None:
|
|
15
|
-
timestamp = int(time.time())
|
|
16
|
-
time_bucket = timestamp // SESSION_TIMEOUT_SECONDS
|
|
17
|
-
raw = f"{visitor_id}_{time_bucket}"
|
|
18
|
-
return hashlib.sha256(raw.encode()).hexdigest()[:SESSION_ID_LENGTH]
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def generate_from_fingerprint(
|
|
22
|
-
client_ip: str,
|
|
23
|
-
user_agent: str,
|
|
24
|
-
timestamp: int | None = None
|
|
25
|
-
) -> str:
|
|
26
|
-
"""Generate session ID for new visitors using IP+UA fingerprint."""
|
|
27
|
-
if timestamp is None:
|
|
28
|
-
timestamp = int(time.time())
|
|
29
|
-
fingerprint = hashlib.sha256(
|
|
30
|
-
f"{client_ip}|{user_agent}".encode()
|
|
31
|
-
).hexdigest()[:FINGERPRINT_LENGTH]
|
|
32
|
-
time_bucket = timestamp // SESSION_TIMEOUT_SECONDS
|
|
33
|
-
raw = f"{fingerprint}_{time_bucket}"
|
|
34
|
-
return hashlib.sha256(raw.encode()).hexdigest()[:SESSION_ID_LENGTH]
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def generate_random() -> str:
|
|
38
|
-
"""Generate random session ID (fallback)."""
|
|
39
|
-
return secrets.token_hex(32)
|
mbuzz-0.2.0.dist-info/RECORD
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
mbuzz/__init__.py,sha256=ip9lSCsNe7T5k1qOfGs4pttoTNGx5Ws-Zf9fHoWXFPg,2247
|
|
2
|
-
mbuzz/api.py,sha256=BITkW6TQEGmPA0OmYFFC611X6JWiY6C1dY-_wsKDT2U,2107
|
|
3
|
-
mbuzz/config.py,sha256=u6v1WnMTgiqyDXHszlXw5hyNENgYRy7-z-IWKuYp0-o,2399
|
|
4
|
-
mbuzz/context.py,sha256=mM9Hi8GnqTkAVCqB_VKGCXl7a5AFME4WTM-2DCIji_w,1290
|
|
5
|
-
mbuzz/cookies.py,sha256=MCpMljQXaqJ2bhEtTv1aKR2fuqLfGBFofjPRayAQGpw,197
|
|
6
|
-
mbuzz/client/__init__.py,sha256=oNOTpPKdt-b_VH_f848OQTbEEDFhlpbEjZ-WjKvh3JI,36
|
|
7
|
-
mbuzz/client/conversion.py,sha256=Ua3ngFWmH6ekm3ZanNf5up_jV8t5nf788ipM1usvZoM,2346
|
|
8
|
-
mbuzz/client/identify.py,sha256=duyd-OWdBo4w7-7Nv5X-GeG2IpMI-1G_kYwnDXup0FE,859
|
|
9
|
-
mbuzz/client/session.py,sha256=mk8v892bvpeW-VNKFQYDsW442w881yfXBiM4sr9a0SU,835
|
|
10
|
-
mbuzz/client/track.py,sha256=HWRYWDWJ_WW_k5IHtkAn4s_MmVwSVFxsxR76KG2h_Ps,4288
|
|
11
|
-
mbuzz/middleware/__init__.py,sha256=gIjwTArToaQNB2NC0iPE_RcmzuHjH7-7jjd7_Dyq4Pw,37
|
|
12
|
-
mbuzz/middleware/flask.py,sha256=s3U77A2986L-KvWUH9MmqSwJwQWzQTYlZPZXH-wzE9o,4182
|
|
13
|
-
mbuzz/utils/__init__.py,sha256=-ejtRN6CTkV3D8uujDtAPDoMT0DAG-ULpWOF-Ae55dY,103
|
|
14
|
-
mbuzz/utils/identifier.py,sha256=iAYmd4he2RTtTNlmszb6KQmD9McZbSMgZZdkMuoGOCE,174
|
|
15
|
-
mbuzz/utils/session_id.py,sha256=3XJkzPg5iUIbRYzz5LThfS8WTAmlmtn60WZTA_TlVhk,1185
|
|
16
|
-
mbuzz-0.2.0.dist-info/METADATA,sha256=3Z8snpq6fv0PmZUvSuAjsH1KaiZIpE9pRLVHTaw8cPE,1849
|
|
17
|
-
mbuzz-0.2.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
18
|
-
mbuzz-0.2.0.dist-info/RECORD,,
|
|
File without changes
|