Files
moboos-ad-report/update.sh
T

110 lines
4.3 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# Moboost 报告 - 每日自动更新脚本
# 服务器每天执行一次(schedule.conf=24)。
# 逻辑:拉取"前一天"的 Mintegral 数据,增量合并进 mtg_data.json(去重,不覆盖历史)。
# 工作目录 = 仓库根目录(同目录下有 mtg_data.json / campaigns.json)。
set -e
cd "$(dirname "$0")"
python3 - <<'PYEOF'
import hashlib, time, json, os, urllib.request, urllib.parse, urllib.error
from datetime import date, timedelta
# ---- Mintegral 认证(access-key=平台"Success key",token secret=平台"API key")----
API_KEY = "22793ef96b076fbdefa7c1266d4ebccb"
SKEY = "c47267b67f9e4231325efe4283044721"
BASE = "https://ss-api.mintegral.com/api/v2/reports/data"
def headers():
ts = str(int(time.time()))
token = hashlib.md5((SKEY + hashlib.md5(ts.encode()).hexdigest()).encode()).hexdigest()
return {"access-key": API_KEY, "token": token, "timestamp": ts}
def req(params):
url = BASE + "?" + urllib.parse.urlencode(params)
for attempt in range(6):
try:
r = urllib.request.Request(url, headers=headers())
with urllib.request.urlopen(r, timeout=90) as resp:
return resp.status, resp.headers.get("Content-Type",""), resp.read()
except urllib.error.HTTPError as e:
return e.code, e.headers.get("Content-Type",""), e.read()
except Exception as e:
time.sleep(5*(attempt+1))
raise RuntimeError("网络请求失败")
# ---- 目标 campid(Mintegral 渠道),优先从 campaigns.json 读,读不到则用内置列表 ----
FALLBACK_IDS = ["91530535","91530534","91530536","91530547","91530548",
"91530549","91530550","91530537","91530538","91551606",
"91534913","91533906"]
try:
camps = json.load(open("campaigns.json"))
mtg = {c["campid"]: c for c in camps if c.get("channel")=="Mintegral"}
except Exception:
mtg = {}
if not mtg:
mtg = {cid: {"campid":cid,"product":"","pkg":""} for cid in FALLBACK_IDS}
mtg_ids = list(mtg.keys())
# ---- 目标日期:前一天(UTC+8)----
target = (date.today() - timedelta(days=1)).isoformat()
print(f"[update] 拉取 {target} 的 Mintegral 数据 ...")
base = {"timezone":"+8","start_time":target,"end_time":target,
"dimension_option":"Offer,Location","time_granularity":"daily"}
# type=1 生成
for attempt in range(20):
p = dict(base); p["type"]="1"
s,ct,b = req(p)
try: code = json.loads(b.decode("utf-8","replace")).get("code")
except: code = None
if code == 200: break
time.sleep(6)
else:
print("[update] 数据生成超时,退出"); raise SystemExit(0)
# type=2 下载
p = dict(base); p["type"]="2"
s,ct,b = req(p)
if "octet-stream" not in ct:
print("[update] 下载失败:", b.decode("utf-8","replace")[:200]); raise SystemExit(0)
lines = b.decode("utf-8","replace").splitlines()
header = lines[0].split("\t"); col = {n:i for i,n in enumerate(header)}
new_rows = []
for line in lines[1:]:
f = line.split("\t")
if len(f) < len(header): continue
offer = f[col["Offer Name"]]
matched = next((cid for cid in mtg_ids if cid in offer), None)
if not matched: continue
rec = mtg[matched]
d = f[col["Date"]]
new_rows.append({
"date": f"{d[:4]}-{d[4:6]}-{d[6:]}",
"channel":"Mintegral","campid":matched,"offer_name":offer,
"product":rec.get("product",""),"pkg":rec.get("pkg",""),
"location":f[col["Location"]],"bid_type":"",
"impression":int(f[col["Impression"]]),"clicks":int(f[col["Click"]]),
"conversions":int(f[col["Conversion"]]),
"ctr":float(f[col["Ctr"]]),"cvr":float(f[col["Cvr"]]),"spend":float(f[col["Spend"]]),
})
print(f"[update] 新增匹配 {len(new_rows)} 条")
# ---- 增量合并(按 date+campid+location 去重;新数据覆盖同键,历史保留)----
existing = []
if os.path.exists("mtg_data.json"):
try: existing = json.load(open("mtg_data.json"))
except: existing = []
merged = {(r["date"],r["campid"],r["location"]): r for r in existing}
for r in new_rows:
merged[(r["date"],r["campid"],r["location"])] = r
out = sorted(merged.values(), key=lambda x:(x["date"], -x["impression"]))
json.dump(out, open("mtg_data.json","w"), ensure_ascii=False, indent=2)
print(f"[update] 合并后共 {len(out)} 条,已写入 mtg_data.json")
PYEOF
echo "[update] $(date '+%Y-%m-%d %H:%M:%S') 完成"