-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpixel_tracker.py
More file actions
133 lines (108 loc) · 3.9 KB
/
Copy pathpixel_tracker.py
File metadata and controls
133 lines (108 loc) · 3.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
"""
pixel_tracker.py
Serves a 1x1 tracking pixel and sends a Telegram push notification
when an email is opened.
Setup (one-time):
1. Message @BotFather on Telegram → /newbot → copy BOT_TOKEN
2. Message your new bot once, then run:
python pixel_tracker.py --get-chat-id
to print your CHAT_ID
3. Set env vars: TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID
4. Deploy to Railway (railway.app) or run locally with ngrok
Local test:
pip install flask requests
TELEGRAM_BOT_TOKEN=xxx TELEGRAM_CHAT_ID=yyy python pixel_tracker.py
Then set PIXEL_BASE_URL=https://your-app.up.railway.app in your env
before running enrich_and_draft.py.
Note: Gmail proxies images through Google's servers, so the notification
fires when the email is first rendered in the inbox (still a useful signal).
"""
import os
import json
import sys
import requests
from datetime import datetime
from pathlib import Path
from flask import Flask, Response, request
app = Flask(__name__)
BOT_TOKEN = os.environ.get("TELEGRAM_BOT_TOKEN", "")
CHAT_ID = os.environ.get("TELEGRAM_CHAT_ID", "")
LOG_FILE = Path(__file__).parent / "pixel_opens.json"
# Minimal 1x1 transparent GIF
TRANSPARENT_GIF = (
b"\x47\x49\x46\x38\x39\x61\x01\x00\x01\x00\x80\x00\x00"
b"\xff\xff\xff\x00\x00\x00\x21\xf9\x04\x01\x00\x00\x00"
b"\x00\x2c\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02"
b"\x44\x01\x00\x3b"
)
def send_telegram(message: str):
if not BOT_TOKEN or not CHAT_ID:
print(f"[Telegram not configured] {message}")
return
try:
requests.post(
f"https://api.telegram.org/bot{BOT_TOKEN}/sendMessage",
json={"chat_id": CHAT_ID, "text": message, "parse_mode": "HTML"},
timeout=5,
)
except Exception as e:
print(f"Telegram error: {e}")
def log_open(firm_id: str, ip: str):
log = {}
if LOG_FILE.exists():
try:
log = json.loads(LOG_FILE.read_text())
except Exception:
log = {}
ts = datetime.now().strftime("%Y-%m-%d %H:%M")
if firm_id not in log:
log[firm_id] = []
log[firm_id].append({"time": ts, "ip": ip})
LOG_FILE.write_text(json.dumps(log, indent=2))
return len(log[firm_id])
@app.route("/pixel/<firm_id>.gif")
def pixel(firm_id):
ip = request.headers.get("X-Forwarded-For", request.remote_addr or "").split(",")[0].strip()
open_count = log_open(firm_id, ip)
firm_display = firm_id.replace("_", " ").title()
ts = datetime.now().strftime("%d %b %H:%M")
if open_count == 1:
msg = f"👀 <b>Email opened</b>\n{firm_display}\n{ts}"
else:
msg = f"👀 <b>Re-opened (#{open_count})</b>\n{firm_display}\n{ts}"
send_telegram(msg)
return Response(
TRANSPARENT_GIF,
mimetype="image/gif",
headers={
"Cache-Control": "no-store, no-cache, must-revalidate, max-age=0",
"Pragma": "no-cache",
},
)
@app.route("/health")
def health():
return "ok"
@app.route("/opens")
def opens():
if not LOG_FILE.exists():
return json.dumps({})
return Response(LOG_FILE.read_text(), mimetype="application/json")
def get_chat_id():
if not BOT_TOKEN:
print("Set TELEGRAM_BOT_TOKEN first")
return
resp = requests.get(f"https://api.telegram.org/bot{BOT_TOKEN}/getUpdates")
updates = resp.json().get("result", [])
if not updates:
print("No messages yet. Send your bot a message on Telegram first, then re-run.")
return
chat_id = updates[-1]["message"]["chat"]["id"]
print(f"Your CHAT_ID is: {chat_id}")
if __name__ == "__main__":
if len(sys.argv) > 1 and sys.argv[1] == "--get-chat-id":
get_chat_id()
else:
port = int(os.environ.get("PORT", 5000))
print(f"Pixel tracker running on port {port}")
print(f"Telegram configured: {'yes' if BOT_TOKEN and CHAT_ID else 'NO — set env vars'}")
app.run(host="0.0.0.0", port=port, debug=False)