using DidabuCloud.Unity.Core.NetWorking; using Mogafa.App.Attributions; using Mogafa.App.LogEvents; using Mogafa.App.ThirdParties; using Mogafa.Common; using Mogafa.Common.HttpClients; using Mogafa.Common.Ioc; using Mogafa.Common.Logging; using Mogafa.Common.Storages; using Mogafa.Unity.Common.Contexts; using Mogafa.Unity.Common.Ioc.Zenject; using Mogafa.Unity.Common.Logging; using Mogafa.Unity.Common.Storages; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using UnityEngine; namespace DidabuCloud.Unity.Core { public delegate void ApplicationPauseHandler(); public delegate void AttributionChangedHandler(AttributionData oldAttributionData, AttributionData newAttributionData); public class DidabuConstants { internal const int AppLoginRetryTimes = 30; } public partial class Didabu : MogafaBase { private event AttributionChangedHandler attributionChanged; public event AttributionChangedHandler OnAttributionChanged { add { attributionChanged += value; } remove { attributionChanged -= value; } } private readonly string didabuDataKey = "didabuData"; private readonly string url = "https://api.didabu.com/"; private readonly string sandboxUrl = "https://ll4tscl8ad.execute-api.cn-northwest-1.amazonaws.com.cn/Prod/"; private string appDevKey; public readonly string Version = "0.6.10"; private int initRetryTimes = 0; private readonly int maxRetryTimes = 30; private static Didabu application; private long deltaTimestamp; private bool didabuSignatureHttpRequestHandlerAdded = false; #region online time private long gameStartTime; private long lastReportSuccessTime; #endregion public static Didabu Application { get { if (application == null) { application = new Didabu(); DidabuGlobalMonoBehaviour.Global.Didabu = application; application.gameStartTime = (DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000; application.lastReportSuccessTime = application.gameStartTime; } return application; } } public long RemoteUtcTimestamp { get { return LocalTimestamp + deltaTimestamp; } } public long LocalTimestamp { get { return (DateTime.Now.Ticks - 621355968000000000) / 10000; } } public long LocalUtcTimestamp { get { return (DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000; } } public UserInfo User { get; private set; } public AttributionData Attribution { get; private set; } public List ThirdPartyUsers { get; private set; } public bool IsFirstOpen { get; private set; } public bool FirstLoginSuccessed { get; private set; } internal Didabu() { ThirdPartyUsers = new List(); Attribution = new AttributionData(); User = new UserInfo(); abGroups = new List(); var utcTimestamp = (DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000; var timestamp = LocalTimestamp; deltaTimestamp = utcTimestamp - timestamp; IocContainer.SetContainer(new ZenjectContainer()); IocContainer.Current.Build(); IocContainer.Register(null, LifeStyle.Transient); IocContainer.RegisterInstance(new UnityLocalStorage()); cumulativePurchase = GetPurchaseData(); GetDidabuData().Wait(); Config = GetConfigFromFile(); InitLogEventReporter(); if (IsFirstOpen) { LogEvent("first_open").Wait(); } } internal Didabu(string appId, string appKey) : this() { this.appDevKey = appKey; AppId = appId; } internal Didabu(string appId, string appKey, bool isSandbox) : this(appId, appKey) { IsSandbox = isSandbox; } public T AddComponent() where T : Component { return DidabuGlobalMonoBehaviour.Global.AddComponent(); } public event Action OnInitialSuccessed; public string AppId { get; private set; } public bool IsSandbox { get; set; } private LogLevel logLevel = LogLevel.Debug; public LogLevel LogLevel { get { return logLevel; } set { logLevel = value; } } private string deviceId; public string DeviceId { get { if (deviceId == null) { deviceId = MogafaApplication.GetDeviceId().Result; SaveDidabuData(); } return deviceId; } private set { deviceId = value; } } protected string BaseUrl { get { if (IsSandbox) { return sandboxUrl; } return url; } } protected string ThirdPartyBindUrl { get { return BaseUrl + "thirdPartyUser/bind"; } } protected string ReportUserInfoUrl { get { return BaseUrl + "application/userInfo"; } } protected string AppLoginUrl { get { return BaseUrl + "account/appLogin"; } } protected string AppRegisterUrl { get { return BaseUrl + "account/register"; } } protected string FillInvitedCodeUrl { get { return $"{BaseUrl}application/invitedCode"; } } private async void SaveDidabuData() { var didabuData = new DidabuData() { IsFirstOpen = false, FirstLoginSuccessed = FirstLoginSuccessed, AbGroups = abGroups, Attribution = Attribution, ControlData = controlData, DeviceId = DeviceId, User = User, ThirdPartyUsers = ThirdPartyUsers }; await LocalStorage.Set(didabuDataKey, didabuData); await LocalStorage.Save(); } public Guid AddTask(int delay, int interval, int maxTimes, Func func) { return TimesTaskManager.AddTask(delay, interval, maxTimes, func); } public Guid AddTask(int delay, int interval, int maxTimes, Func func, T1 arg) { return TimesTaskManager.AddTask(delay, interval, maxTimes, func, arg); } public Guid AddTask(int delay, int interval, int maxTimes, Func func, T1 arg1, T2 arg2) { return TimesTaskManager.AddTask(delay, interval, maxTimes, func, arg1, arg2); } public Guid AddTask(int delay, int interval, int maxTimes, Func func, T1 arg1, T2 arg2, T3 arg3) { return TimesTaskManager.AddTask(delay, interval, maxTimes, func, arg1, arg2, arg3); } public Guid AddTask(int delay, int interval, int maxTimes, Func func, T1 arg1, T2 arg2, T3 arg3, T4 arg4) { return TimesTaskManager.AddTask(delay, interval, maxTimes, func, arg1, arg2, arg3, arg4); } private async Task GetDidabuData() { var didabuData = await LocalStorage.Get(didabuDataKey); if (didabuData == null) { didabuData = new DidabuData(); } IsFirstOpen = didabuData.IsFirstOpen; FirstLoginSuccessed = didabuData.FirstLoginSuccessed; User = didabuData.User; ThirdPartyUsers = didabuData.ThirdPartyUsers; Attribution = didabuData.Attribution; if (Attribution != null && Attribution.LastModifiedTime != 0L) { AddCommonEventParameter("isOrganic", Attribution.IsOrganic ? "true" : "false"); AddCommonEventParameter("mediaSource", string.IsNullOrEmpty(Attribution.MediaSource) ? "organic" : Attribution.MediaSource); AddCommonEventParameter("adCampaign", Attribution.Campaign); AddCommonEventParameter("adSet", Attribution.Creative); AddCommonEventParameter("adCampaignId", Attribution.CampaignId); AddCommonEventParameter("adSiteId", Attribution.SiteId); AddCommonEventParameter("adCreative", Attribution.Creative); AddCommonEventParameter("adCpi", string.IsNullOrEmpty(Attribution.Cpi) ? "0" : Attribution.Cpi); } DeviceId = didabuData.DeviceId; controlData = didabuData.ControlData; abGroups = didabuData.AbGroups; } public Task Init(string appId, string appDevKey, string configJson, CommonEventParameter commonEventParameters = null) { DidabuCoreConfig defaultConfig = null; try { defaultConfig = JsonConvert.DeserializeObject(configJson); } catch (Exception ex) { Logger.LogError($"Read config from local file error:{ex.Message}"); } return Init(appId, appDevKey, defaultConfig, commonEventParameters); } private bool beginInitConfiguration = false; public async Task Init(string appId, string appDevKey, DidabuCoreConfig defaultConfig, CommonEventParameter commonEventParameters = null) { Logger.EnableLogLevel = LogLevel; AppId = appId; this.appDevKey = appDevKey; AddCommonEventParameter("deviceId", DeviceId); if (!didabuSignatureHttpRequestHandlerAdded) { HttpUtility.AddHttpRequestHandler(new DidabuSignatureHttpRequestHandler(appId, this.appDevKey, Version)); didabuSignatureHttpRequestHandlerAdded = true; } if (commonEventParameters != null) { foreach (var kv in commonEventParameters) { LogEventReporter.AddCommonEventParameter(kv.Key, kv.Value); } } if (!beginInitConfiguration) { AddTask(100, 100, 1, InitConfiguration, defaultConfig); beginInitConfiguration = true; } if (!string.IsNullOrEmpty(User.DidabuId)) { SaveDidabuData(); AddTask(0, 15000, 0, ReportUserInfo); Logger.LogInformation($"Didabu init finished (cached)."); OnInitialSuccessed?.Invoke(User.DidabuId); return; } var registerSuccessed = await AppRegister(); if (!registerSuccessed) { if (initRetryTimes <= maxRetryTimes) { initRetryTimes++; Logger.LogWarning($"Init method retry, times:${initRetryTimes}"); AddTask(initRetryTimes * 500, initRetryTimes * 500, 1, Init, appId, appDevKey, defaultConfig, commonEventParameters); } return; } AddTask(0, 15000, 0, ReportUserInfo); SaveDidabuData(); Logger.LogInformation($"Didabu init finished."); OnInitialSuccessed?.Invoke(User.DidabuId); } public Task> ThirdPartyBind(string type, string appId, string userId) { return ThirdPartyBind(type, appId, userId, null, null, null, null, null, null, null); } public Task> ThirdPartyBind(string type, string appId, string userId, string originalMessage) { return ThirdPartyBind(type, appId, userId, null, null, null, null, null, null, originalMessage); } public async Task> ThirdPartyBind(string type, string appId, string userId, string userId2, string token, string refreshToken, string userName, string userHeader, string userEmail, string originalMessage) { var bindInfo = new ThirdPartyBindRequestInfo { AccountId = User.DidabuId, AppId = appId, BoundDevice = MogafaApplication.PlatfromName, DeviceId = DeviceId, OriginalMessage = originalMessage, RefreshToken = refreshToken, Token = token, Type = type, UserHeader = userHeader, UserId = userId, UserId2 = userId2, UserName = userName, UserEmail = userEmail }; DidabuHttpResponse response; if (type != "Device" && string.IsNullOrEmpty(User.DidabuId)) { var deviceBindInfo = new ThirdPartyBindRequestInfo { AppId = MogafaApplication.PlatfromName, BoundDevice = MogafaApplication.PlatfromName, DeviceId = DeviceId, Type = "Device", UserId = DeviceId }; response = await HttpUtility.Post(ThirdPartyBindUrl, JsonConvert.SerializeObject(deviceBindInfo)); if (response.Code == HttpStatusCodes.OK && response.IsSuccessful) { User.DidabuId = response.Data.AccountId; bindInfo.AccountId = User.DidabuId; await AppLogin(); } else { Logger.LogError($"Third party bind {deviceBindInfo.Type} error, response code:{response.Code},error message:{response.Message}"); return response; } } response = await HttpUtility.Post(ThirdPartyBindUrl, JsonConvert.SerializeObject(bindInfo)); if (response.Code == HttpStatusCodes.OK && response.IsSuccessful) { deltaTimestamp = response.DeltaTimestamp; if (type == "Device") { User.DidabuId = response.Data.AccountId; await AppLogin(); } else { var thirdPartyUser = ThirdPartyUsers.FirstOrDefault(tpu => tpu.Type == type); if (thirdPartyUser == null) { ThirdPartyUsers.Add(new ThirdPartyUser() { AppId = appId, Type = type, UserId = userId, UserId2 = userId2 }); if (string.IsNullOrEmpty(User.UserName) && !string.IsNullOrEmpty(userName)) { User.UserName = userName; } if (string.IsNullOrEmpty(User.HeadImageUrl) && !string.IsNullOrEmpty(userHeader)) { User.HeadImageUrl = userHeader; } SaveDidabuData(); } } } else { Logger.LogError($"Third party bind {bindInfo.Type} error, response code:{response.Code},error message:{response.Message}"); } return response; } private int appLoginRetryTimes = 0; private async Task AppLogin() { if (FirstLoginSuccessed) { return; } FirstLoginSuccessed = true; var appLoginInfo = new AppLoginInfoDto { AccountId = User.DidabuId, AppId = AppId, AppVersion = UnityEngine.Application.version, IsOrganic = Attribution.IsOrganic, MediaSource = Attribution.MediaSource }; var response = await HttpUtility.Post(AppLoginUrl, JsonConvert.SerializeObject(appLoginInfo)); if (response.Code == HttpStatusCodes.OK && response.IsSuccessful) { await LogEvent("first_login"); SaveDidabuData(); } else { if (appLoginRetryTimes <= DidabuConstants.AppLoginRetryTimes) { appLoginRetryTimes++; Logger.LogInformation($"App login failed, retry times:{appLoginRetryTimes}"); FirstLoginSuccessed = false; AddTask(appLoginRetryTimes * 5000, appLoginRetryTimes * 5000, 1, AppLogin); } } } private async Task AppRegister() { var appLoginInfo = new AppRegisterInfoDto { AppId = AppId, AppVersion = UnityEngine.Application.version, DevicePlatform = MogafaApplication.PlatfromName, DeviceId = DeviceId }; try { var response = await HttpUtility.Post(AppRegisterUrl, JsonConvert.SerializeObject(appLoginInfo)); if (response.Code == HttpStatusCodes.OK && response.IsSuccessful) { deltaTimestamp = response.DeltaTimestamp; User.DidabuId = response.Data.AccountId; User.LoginDays = response.Data.LoginDays; Attribution.InvitationCode = response.Data.InvitationCode; SaveDidabuData(); return true; } } catch (Exception ex) { Logger.LogError($"Register failed, error:{ex.Message}"); } return false; } public async Task ReportUserInfo() { var utcNow = LocalUtcTimestamp; Logger.LogDebug($"Start report user info."); var userInfo = new UserInfoDto { AppId = AppId, AccountId = User.DidabuId, IsOrganic = Attribution.IsOrganic, MediaSource = Attribution.MediaSource, AppVersion = UnityEngine.Application.version, InvitationCode = Attribution.InvitationCode, OnlineTimeIncreased = (int)(utcNow - lastReportSuccessTime), AbGroups = abGroups, Level = User.Level }; var userAsset = await GetUserAsset(); foreach (var asset in userAsset.Assets) { userInfo.Assets.Add(new AssetDto { Code = asset.Code, Value = asset.Value }); } foreach (var periodicAsset in userAsset.PeriodicAssets) { userInfo.PeriodicAssets.Add(new UserPeriodicAssetDto { AssetCode = periodicAsset.AssetCode, AssetDeltaValue = periodicAsset.AssetDeltaValue, EndTime = periodicAsset.EndTime, IsPeriodEnded = periodicAsset.IsPeriodEnded, PeriodCode = periodicAsset.PeriodCode, StartTime = periodicAsset.StartTime, Income = periodicAsset.Income, Consumption = periodicAsset.Consumption }); } var userBehaviour = await GetUserBehaviour(); foreach (var asset in userBehaviour.Behaviours) { userInfo.Behaviours.Add(new BehaviourDto { Code = asset.Code, Value = asset.Value }); } var response = await HttpUtility.Post(ReportUserInfoUrl, JsonConvert.SerializeObject(userInfo)); if (!response.IsSuccessful) { Logger.LogError($"Report user info error:{response.Message}"); } else { deltaTimestamp = response.DeltaTimestamp; lastReportSuccessTime = utcNow; User.IsNewLogin = response.Data.IsNewLogin; User.LoginDays = response.Data.LoginDays; User.TotalOnlineTime = response.Data.TotalOnlineTime; User.UserName = response.Data.Name; User.HeadImageUrl = response.Data.HeadImage; SaveDidabuData(); userAsset.PeriodicAssets.RemoveAll(p => p.IsPeriodEnded); await SetUserAsset(); } } internal string IsBoundUrl { get { return BaseUrl + "thirdPartyUser/isBound"; } } public async Task IsBound(string thirdPartyType) { var url = $"{IsBoundUrl}?accountId={User.DidabuId}&thirdParty={thirdPartyType}"; var response = await HttpUtility.Get(url); if (response.Code == HttpStatusCodes.OK && response.IsSuccessful) { User.UserName = response.Data.Name; User.HeadImageUrl = response.Data.HeadImageUrl; return response.Data.IsBound; } return false; } public void SetLevel(int level) { User.Level = level; SaveDidabuData(); } public string GetThirdPartyUserId(string thirdPartyType) { var thirdPartyUser = ThirdPartyUsers.FirstOrDefault(tpu => tpu.Type == thirdPartyType); if (thirdPartyUser == null) { return ""; } return thirdPartyUser.UserId; } public void BeginGetAttribution() { AddTask(30000, 30000, 1, GetAttributionTimeout); } private Task GetAttributionTimeout() { Logger.LogWarning($"Get attribution timeout."); if (Attribution.LastModifiedTime == 0L) { Logger.LogInformation("Get attribution timeout."); AddCommonEventParameter("isOrganic", "true"); AddCommonEventParameter("mediaSource", "organic"); AddCommonEventParameter("adCampaign", ""); AddCommonEventParameter("adCampaignId", ""); AddCommonEventParameter("adSiteId", ""); AddCommonEventParameter("adSet", ""); AddCommonEventParameter("adCreative", ""); AddCommonEventParameter("adCpi", "0"); } return Task.CompletedTask; } public void SetAttribution(bool organic, string mediaSource, string campaign, string creative, string siteId, string campaignId, string cpi) { var oldAttribution = new AttributionData { Campaign = Attribution.Campaign, CampaignId = Attribution.CampaignId, Cpi= Attribution.Cpi, Creative = Attribution.Creative, InvitationCode = Attribution.InvitationCode, IsOrganic = Attribution.IsOrganic, SiteId = Attribution.SiteId, MediaSource = Attribution.MediaSource, LastModifiedTime = Attribution.LastModifiedTime, UpperInvitationCode = Attribution.UpperInvitationCode }; Logger.LogDebug($"organic:{organic}, last time:{Attribution.LastModifiedTime}"); if (Attribution.LastModifiedTime == 0L) { Attribution.LastModifiedTime = RemoteUtcTimestamp; Attribution.IsOrganic = organic; } Attribution.Campaign = campaign; if (string.IsNullOrEmpty(campaign)) { Attribution.Campaign = ""; } Attribution.Creative = creative; if (string.IsNullOrEmpty(creative)) { Attribution.Creative = ""; } Attribution.MediaSource = mediaSource; if (string.IsNullOrEmpty(mediaSource)) { Attribution.MediaSource = "organic"; } Attribution.SiteId = siteId; if (string.IsNullOrEmpty(siteId)) { Attribution.SiteId = ""; } Attribution.CampaignId = campaignId; if (string.IsNullOrEmpty(campaignId)) { Attribution.CampaignId = ""; } Attribution.Cpi = cpi; if (string.IsNullOrEmpty(cpi)) { Attribution.Cpi = "0"; } AddCommonEventParameter("isOrganic", Attribution.IsOrganic ? "true" : "false"); AddCommonEventParameter("mediaSource", Attribution.MediaSource); AddCommonEventParameter("adCampaign", Attribution.Campaign); AddCommonEventParameter("adCampaignId", Attribution.CampaignId); AddCommonEventParameter("adSiteId", Attribution.SiteId); AddCommonEventParameter("adSet", Attribution.Creative); AddCommonEventParameter("adCreative", Attribution.Creative); AddCommonEventParameter("adCpi", Attribution.Cpi); SaveDidabuData(); attributionChanged?.Invoke(oldAttribution, Attribution); } internal void SetInvitationCode(string invitationCode) { Attribution.InvitationCode = invitationCode; SaveDidabuData(); } internal void SetUpperInvitationCode(string upperInvitationCode) { Attribution.UpperInvitationCode = upperInvitationCode; SaveDidabuData(); } internal ApplicationPauseHandler ApplicationPauseHandler; public event ApplicationPauseHandler OnApplicationToBackground { add { ApplicationPauseHandler += value; } remove { ApplicationPauseHandler -= value; } } } internal class DidabuData { public DidabuData() { IsFirstOpen = true; FirstLoginSuccessed = false; AbGroups = new List(); User = new UserInfo(); Attribution = new AttributionData() { IsOrganic = true, MediaSource = "organic" }; ThirdPartyUsers = new List(); } public bool IsFirstOpen; public bool FirstLoginSuccessed; public string DeviceId; public List AbGroups; public UserInfo User; public string ControlData; public AttributionData Attribution; public List ThirdPartyUsers; } }