印度包网
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

573 lines
15 KiB

package call
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"mime/multipart"
"net/http"
"net/url"
"server/common"
"server/config"
"server/db"
"server/util"
"strconv"
"strings"
"time"
"github.com/liangdas/mqant/log"
)
// 初始化在线人数上报
func InitOnline(f func() []*common.ESNewOnline) {
now := time.Now()
zero := util.GetZeroTime(now)
h, m, _ := now.Clock()
m -= m % 5
lastRecord := zero.Add(time.Duration(h) * time.Hour).Add(time.Duration(m) * time.Minute)
next := lastRecord.Add(5 * time.Minute).Sub(now)
time.AfterFunc(next, func() {
WriteOnline(lastRecord.Add(5*time.Minute).Unix(), f)
InitOnline(f)
})
}
// WriteOnline 写入在线统计
func WriteOnline(time int64, f func() []*common.ESNewOnline) {
data := f()
if len(data) == 0 {
return
}
for i := range data {
if data[i].Total == 0 {
continue
}
data[i].Time = time
db.ES().InsertToESGO(common.ESIndexBackPlayerOnline, data[i])
}
}
type FBEvents struct {
EventName string `json:"event_name"`
EventTime int64 `json:"event_time"`
UserData struct {
Em string `json:"em,omitempty"`
Ph string `json:"ph,omitempty"`
FN string `json:"fn,omitempty"`
LN string `json:"ln,omitempty"`
Country string `json:"country,omitempty"`
IP string `json:"client_ip_address,omitempty"`
ClientUserAgent string `json:"client_user_agent"`
FBC string `json:"fbc,omitempty"`
FBP string `json:"fbp,omitempty"`
ExternalID string `json:"external_id,omitempty"`
CT string `json:"ct,omitempty"` // 城市,哈希处理
ST string `json:"st,omitempty"` // 州,哈希处理
ZP string `json:"zp,omitempty"` // 邮编,哈希处理
} `json:"user_data"`
CustomData struct {
Currency string `json:"currency"`
Value int64 `json:"value"`
} `json:"custom_data"`
ActionSource string `json:"action_source"`
// AppData struct {
// AdvertiserTrackingEnabled int `json:"advertiser_tracking_enabled"`
// ApplicationTrackingEnabled int `json:"application_tracking_enabled"`
// Extinfo []string `json:"extinfo"`
// } `json:"app_data"`
}
type FBEvent int
const (
FBEventRegist FBEvent = iota
FBEventPurchase
)
func (f FBEvent) GetName() string {
switch f {
case FBEventRegist:
return "CompleteRegistration"
case FBEventPurchase:
return "Purchase"
default:
return ""
}
}
// UploadFB 上报fb数据
func UploadFB(uid int, event FBEvent, amount int64) {
u := &common.PlayerDBInfo{Id: uid}
db.Mysql().Get(u)
channel := GetChannelByID(u.ChannelID)
if channel == nil || channel.FBPixelID == "" || channel.FBAccessToken == "" || config.GetBase().AD.FBAPIURL == "" {
return
}
if channel.ADUpload != common.ADFB {
return
}
pi := &common.PayInfo{UID: uid}
db.Mysql().Get(pi)
randPi := &common.PayInfo{}
db.Mysql().C().Raw("SELECT * FROM pay_info ORDER BY RAND() LIMIT 1").Scan(randPi)
pa := &common.PlayerADData{UID: uid}
db.Mysql().GetLast(pa)
if pa.FBC == "" {
pa = &common.PlayerADData{ChannelID: u.ChannelID}
db.Mysql().C().Raw("SELECT * FROM player_ad_data ORDER BY RAND() LIMIT 1").Scan(pa)
}
// 拿取玩家信息
em := pi.Email
if em == "" {
em = randPi.Email
}
ph := u.Mobile
if ph == "" {
ph = randPi.Mobile
}
fn, ln := util.FormatUserName(pi.Name)
if fn == "" {
fn, ln = util.FormatUserName(randPi.Name)
}
ua := "$CLIENT_USER_AGENT"
if pa.UserAgent != "" {
ua = pa.UserAgent
}
// 分析ip
var ct, st, zp string
ipinfo, err := SearchIP(u.IP)
if err != nil {
log.Error("err:%v", err)
} else {
ct = util.CalculateSHA256(strings.ToLower(ipinfo.City.Names["en"]))
if len(ipinfo.Subdivisions) > 0 {
st = util.CalculateSHA256(strings.ToLower(ipinfo.Subdivisions[0].Names["en"]))
}
zp = util.CalculateSHA256(strings.ToLower(ipinfo.Postal.Code))
}
util.IndexTry(func() error {
// 准备事件数据
var requestBody bytes.Buffer
multipartWriter := multipart.NewWriter(&requestBody)
// 添加文本字段
events := []FBEvents{{
EventName: event.GetName(),
EventTime: time.Now().Unix(),
}}
events[0].UserData.Em = util.CalculateSHA256(strings.ToLower(em))
events[0].UserData.Ph = util.CalculateSHA256("91" + ph)
events[0].UserData.FN = util.CalculateSHA256(strings.ToLower(fn))
events[0].UserData.LN = util.CalculateSHA256(strings.ToLower(ln))
events[0].UserData.IP = u.IP
events[0].UserData.ClientUserAgent = ua
events[0].UserData.FBC = pa.FBC
events[0].UserData.FBP = pa.FBP
events[0].UserData.Country = util.CalculateSHA256("in")
events[0].UserData.ExternalID = util.CalculateSHA256(fmt.Sprintf("%d", uid))
events[0].UserData.ST = st
events[0].UserData.CT = ct
events[0].UserData.ZP = zp
events[0].ActionSource = "website"
if event == FBEventPurchase {
events[0].CustomData.Currency = "brl"
events[0].CustomData.Value = amount
}
evJson, _ := json.Marshal(events)
err = multipartWriter.WriteField("data", string(evJson))
if err != nil {
log.Error("Error writing text field:%v", err)
return err
}
err = multipartWriter.WriteField("access_token", channel.FBAccessToken)
if err != nil {
log.Error("Error writing text field:%v", err)
return err
}
multipartWriter.Close()
req, err := http.NewRequest("POST", config.GetBase().AD.FBAPIURL+channel.FBPixelID+"/events?access_token="+channel.FBAccessToken, &requestBody)
if err != nil {
log.Error("err:%v", err)
return err
}
// 设置请求头,包括 Content-Type
req.Header.Set("Content-Type", multipartWriter.FormDataContentType())
// 发送请求
client := &http.Client{
Timeout: 10 * time.Second,
}
log.Debug("UploadFB:%+v", req)
resp, err := client.Do(req)
if err != nil {
log.Error("err:%v", err)
return err
}
defer resp.Body.Close()
ret, err := io.ReadAll(resp.Body)
if err != nil {
log.Error("http read body err:%v", err)
return err
}
log.Debug("fbres:%v", string(ret))
return nil
})
}
// UploadAdjust 上报adjust数据
func UploadAdjust(event common.AdjustEventType, u *common.PlayerDBInfo, param map[string]string) {
channel := GetChannelByID(u.ChannelID)
if channel == nil || (u.ADID == "" && u.GPSADID == "") || channel.AdjustAppToken == "" {
return
}
if channel.ADUpload != common.ADJust {
return
}
util.IndexTry(func() error {
var err error
// if u == nil {
// u, err = GetUserXInfo("channel_id", "adid", "gps_adid")
// if err != nil {
// log.Error("err:%v", err)
// return
// }
// }
reqURL, _ := url.Parse(config.GetConfig().Web.Adjust.URL)
params := url.Values{}
params.Add("app_token", channel.AdjustAppToken)
params.Add("event_token", channel.GetAdjustEventID(int(event)))
params.Add("s2s", "1")
params.Add("created_at", strconv.FormatInt(time.Now().Unix(), 10))
params.Add("adid", u.ADID)
params.Add("gps_adid", u.GPSADID)
params.Add("web_uuid", u.GPSADID)
for k, v := range param {
params.Add(k, v)
}
reqURL.RawQuery = params.Encode()
req, err := http.NewRequest("GET", reqURL.String(), nil)
if err != nil {
log.Error("err:%v", err)
return err
}
req.Header.Set("Authorization", "Bearer "+channel.AdjustAuth)
client := &http.Client{
Timeout: 5 * time.Second,
}
log.Debug("UploadAdjust:%+v", req)
res, err := client.Do(req)
if err != nil {
log.Error("http post call err:%v", err)
return err
}
ret, err := io.ReadAll(res.Body)
defer res.Body.Close()
if err != nil {
log.Error("http read body err:%v", err)
return err
}
log.Debug("adjustRes:%v", string(ret))
return nil
})
}
// IsOrganic 查询设备是否是自然量 count 重试次数
func IsOrganic(gpsID, appToken string, cid int, count int) (is bool) {
count--
// reqURL, _ := url.Parse(config.GetConfig().Web.Adjust.APIURL)
reqURL, _ := url.Parse("https://api.adjust.com/device_service/api/v1/inspect_device")
params := url.Values{}
params.Add("advertising_id", gpsID)
params.Add("app_token", appToken)
reqURL.RawQuery = params.Encode()
var lg string
defer func() {
if count > 0 && is {
return
}
db.ES().InsertToESByIDGO(common.ESIndexBackABLog, fmt.Sprintf("%d_%s", cid, gpsID), &common.ESABLog{
Channel: cid,
Time: time.Now().Unix(),
DeviceID: gpsID,
IsA: is,
Log: lg,
})
}()
req, err := http.NewRequest("GET", reqURL.String(), nil)
if err != nil {
log.Error("err:%v", err)
lg = err.Error()
return
}
// req.Header.Set("Authorization", "Bearer "+config.GetConfig().Web.Adjust.APIToken)
req.Header.Set("Authorization", "Bearer _2ZGYezfdZ7yD2he-zWx")
client := &http.Client{
Timeout: 5 * time.Second,
}
log.Debug("check organic:%+v", req)
res, err := client.Do(req)
if err != nil {
log.Error("http post call err:%v", err)
lg = err.Error()
return
}
if res.Status == http.StatusText(http.StatusNotFound) {
is = true
lg = "http not found"
return
}
ret, err := ioutil.ReadAll(res.Body)
defer res.Body.Close()
if err != nil {
log.Error("http read body err:%v", err)
lg = err.Error()
return
}
log.Debug("AdjustDeviceResp:%v", string(ret))
ad := new(AdjustDeviceResp)
if err := json.Unmarshal(ret, ad); err != nil {
log.Error("err:%v", err)
lg = string(ret)
}
is = ad.TrackerName == "Organic"
if is {
lg = "Organic"
}
if is && count > 0 {
time.Sleep(time.Second)
return IsOrganic(gpsID, appToken, cid, count)
}
return
}
// IsOrganic2 查询设备是否是自然量(更为严格,凡是找不到设备号的,一律禁止) count 重试次数
func IsOrganic2(gpsID, appToken string, cid int, count int) (is bool) {
count--
// reqURL, _ := url.Parse(config.GetConfig().Web.Adjust.APIURL)
reqURL, _ := url.Parse("https://api.adjust.com/device_service/api/v1/inspect_device")
params := url.Values{}
params.Add("advertising_id", gpsID)
params.Add("app_token", appToken)
reqURL.RawQuery = params.Encode()
var lg string
defer func() {
if count > 0 && is {
return
}
db.ES().InsertToESByIDGO(common.ESIndexBackABLog, fmt.Sprintf("%d_%s", cid, gpsID), &common.ESABLog{
Channel: cid,
Time: time.Now().Unix(),
DeviceID: gpsID,
IsA: is,
Log: lg,
})
}()
req, err := http.NewRequest("GET", reqURL.String(), nil)
if err != nil {
is = true
log.Error("err:%v", err)
lg = err.Error()
return
}
// req.Header.Set("Authorization", "Bearer "+config.GetConfig().Web.Adjust.APIToken)
req.Header.Set("Authorization", "Bearer _2ZGYezfdZ7yD2he-zWx")
client := &http.Client{
Timeout: 5 * time.Second,
}
log.Debug("check organic:%+v", req)
res, err := client.Do(req)
if err != nil {
is = true
log.Error("http post call err:%v", err)
lg = err.Error()
return
}
if res.Status == http.StatusText(http.StatusNotFound) {
is = true
lg = "http not found"
return
}
ret, err := ioutil.ReadAll(res.Body)
defer res.Body.Close()
if err != nil {
is = true
log.Error("http read body err:%v", err)
lg = err.Error()
return
}
log.Debug("AdjustDeviceResp2:%v", string(ret))
ad := new(AdjustDeviceResp)
if err := json.Unmarshal(ret, ad); err != nil {
log.Error("err:%v", err)
is = true
lg = string(ret)
}
if !is {
is = ad.TrackerName == "Organic"
if is {
lg = "Organic"
}
}
if is && count > 0 {
time.Sleep(time.Second)
return IsOrganic2(gpsID, appToken, cid, count)
}
return
}
type AdjustDeviceResp struct {
Adid string `json:"Adid"`
AdvertisingID string `json:"AdvertisingId"`
Tracker string `json:"Tracker"`
TrackerName string `json:"TrackerName"`
// ClickTime time.Time `json:"ClickTime"`
// InstallTime time.Time `json:"InstallTime"`
// LastAppVersion string `json:"LastAppVersion"`
// LastAppVersionShort string `json:"LastAppVersionShort"`
// LastSessionTime time.Time `json:"LastSessionTime"`
// LastEventTimes struct {
// Zcqz1Y time.Time `json:"zcqz1y"`
// } `json:"LastEventTimes"`
// PushToken string `json:"PushToken"`
// State string `json:"State"`
// InstallState string `json:"InstallState"`
// SignatureAcceptanceStatus string `json:"SignatureAcceptanceStatus"`
// SignatureVerificationResult string `json:"SignatureVerificationResult"`
}
// WriteRealOnline 写入各场次实时在线
func WriteRealOnline(field string, f func() map[int]*common.RedisRealOnline) {
now := time.Now()
zero := util.GetZeroTime(now)
h, m, _ := now.Clock()
m -= m % 5
lastRecord := zero.Add(time.Duration(h) * time.Hour).Add(time.Duration(m) * time.Minute)
next := lastRecord.Add(5 * time.Minute).Sub(now)
// _, _, s := now.Clock()
// next := now.Add(time.Duration(60-s) * time.Second).Sub(now)
log.Debug("next real:%v", next)
time.AfterFunc(next, func() {
real := f()
for i, v := range real {
keyField := fmt.Sprintf("%v", field)
if i > 0 {
keyField += fmt.Sprintf(":%v", i)
}
key := common.GetRedisKeyOnlineKey(keyField)
if !db.Redis().Exist(key) {
if err := db.Redis().HSet(key, v); err != nil {
log.Error("err:%v", err)
continue
}
if err := db.Redis().Expire(key, next-2*time.Second); err != nil {
log.Error("err:%v", err)
continue
}
} else {
if err := db.Redis().HIncrBy(key, "Total", int64(v.Total)); err != nil {
log.Error("err:%v", err)
continue
}
if err := db.Redis().HIncrBy(key, "New", int64(v.New)); err != nil {
log.Error("err:%v", err)
continue
}
}
}
WriteRealOnline(field, f)
})
}
type KwaiReq struct {
Clickid string `json:"clickid"`
EventName string `json:"event_name"`
PixelID string `json:"pixelId"`
AccessToken string `json:"access_token"`
TestFlag bool `json:"testFlag"`
TrackFlag bool `json:"trackFlag"`
IsAttributed int `json:"is_attributed"` // 归因标记,此处需写死为1
Mmpcode string `json:"mmpcode"` // 标记数据来源,此处需写死为 PL
PixelSdkVersion string `json:"pixelSdkVersion"` // 版本信息,此处需写死为,9.9.9
Properties string `json:"properties"`
}
type KwaiResp struct {
Result int `json:"result"` // 返回状态码
ErrorMsg string `json:"error_msg"` // 返回信息详情
}
type KwaiProperties struct {
Currency string `json:"currency"`
Price float64 `json:"price"`
}
const (
KwaiEventRegist = iota
KwaiEventPay
)
func GetKwaiEventName(event int) string {
switch event {
case KwaiEventRegist:
return "EVENT_COMPLETE_REGISTRATION"
case KwaiEventPay:
return "EVENT_PURCHASE"
}
return ""
}
func UploadKwai(uid, event int, amount int64) {
u := &common.PlayerDBInfo{Id: uid}
db.Mysql().Get(u)
channel := GetChannelByID(u.ChannelID)
if channel == nil || (u.ADID == "" && u.GPSADID == "") || channel.AdjustAppToken == "" {
return
}
if channel.ADUpload != common.ADKwai {
return
}
pa := &common.PlayerADData{UID: uid}
db.Mysql().GetLast(pa)
if pa.FBC == "" {
pa = &common.PlayerADData{ChannelID: u.ChannelID}
db.Mysql().C().Raw("SELECT * FROM player_ad_data ORDER BY RAND() LIMIT 1").Scan(pa)
}
send := &KwaiReq{
Clickid: pa.FBC,
EventName: GetKwaiEventName(event),
PixelID: channel.FBPixelID,
AccessToken: channel.FBAccessToken,
TestFlag: false,
TrackFlag: !config.GetBase().Release,
IsAttributed: 1,
Mmpcode: "PL",
PixelSdkVersion: "9.9.9",
}
if event == KwaiEventPay {
pro := &KwaiProperties{
Currency: "brl",
Price: float64(amount/1e6) / 100,
}
byt, _ := json.Marshal(pro)
send.Properties = string(byt)
}
util.IndexTry(func() error {
ret := &KwaiResp{}
return util.HttpPost(config.GetBase().AD.KwaiAPIURL, send, ret, nil)
})
}