// // MyAdServer.m // nochange // // Created by mac on 2024/7/26. // #import #import #import #import "XSHelper.h" #import "MyAdServer.h" #import "XSPhoneConfig.h" #import "XSPhoneInfo.h" void pushAdTaskLog(AdTaskLogData *data) { NSDictionary *dic = @{ @"taskId": data->taskId, @"title": data->title, @"message": data->message, @"idfa": data->idfa, @"appid": data->appid, @"adid": data->adid, @"level": @(data->level), @"ecpm": data->ecpm ? : @0.0, @"iphoneId": data->iphoneId, @"ipAddr": data->ipAddr }; saveAdTaskLog(dic); } void saveAdTaskLog(NSDictionary *dic) { XSPhoneConfig *config = [XSPhoneConfig sharedInstance]; NSString *url = [config GetFullServerURL:@"ios/top_selection/push_ad_task_log"]; NSString *json = [XSHelper dic2Json:dic]; XSHttpHelper *http = [[XSHttpHelper alloc] init]; [http doPOST:url json:json withCallback:^(NSData *res) { NSLog(@"-ad log- push:\n %@", [XSHelper data2str:res]); } withError:^(NSError *err) { NSLog(@"XS- %@", err); }]; } void getLowEcpm(LowEcpmCallback callback) { XSPhoneConfig *config = [XSPhoneConfig sharedInstance]; NSString *url = [NSString stringWithFormat:@"ios/top_selection/low_ecpm?iphoneId=%@", config.IPhoneName]; NSLog(@"XS- getLowEcpm url %@", url); NSString *fullurl = [config GetFullServerURL:url]; XSHttpHelper *http = [[XSHttpHelper alloc] init]; [http doGET:fullurl withCallback:^(NSData *data) { if(data && ![data isEqual:[NSNull null]]) { NSDictionary *dic = [XSHelper jsonData2Dictionary:data]; NSLog(@"XS- getLowEcpm data %@", dic); NSNumber *ecpm = dic[@"data"]; if (ecpm) { callback(ecpm); } else { callback(ecpm); } } else { callback(@(0.0001)); } } withError:^(NSError *err) { callback(@(0.0001)); }]; } BOOL needAdContinue(NSString *appid, NSString *idfa, NSNumber *maxEcpm) { XSPhoneConfig *config = [XSPhoneConfig sharedInstance]; NSString *fullurl = [config GetFullServerURL: @"ios/top_selection/get_need_continue"]; NSDictionary *postData = @{ @"id": config.IPhoneName, @"appid": appid, @"idfa": idfa, @"maxEcpm": maxEcpm }; XSHttpHelper *http = [[XSHttpHelper alloc] init]; NSData *data = [http doPOST:fullurl json:[XSHelper dic2Json:postData]]; if(data == nil || [data isEqual:[NSNull null]]) { return NO; } NSDictionary *dic = [XSHelper jsonData2Dictionary:data]; NSLog(@"XS- needAdContinue data %@", dic); return [dic[@"data"] isEqual:@(true)]; } void getChangeInfoWithRetry(NSString *idfa, ChangeDataSaveCallback callback, error_callback errorCallback, int retryCount) { if (retryCount <= 0) { NSLog(@"getChangeInfo failed after maximum retries"); if (errorCallback) { errorCallback([NSError errorWithDomain:@"getChangeInfo" code:-1 userInfo:@{NSLocalizedDescriptionKey: @"Maximum retry attempts reached"}]); } return; } XSPhoneConfig *config = [XSPhoneConfig sharedInstance]; NSString *url = [config GetFullServerURL:@"ios/top_selection/get_change_data_json"]; XSHttpHelper *http = [[XSHttpHelper alloc] init]; NSDictionary *dic = @{ @"packageName": [config PackageName], @"washParam": @([config WashParam]), }; NSString *json = [XSHelper dic2Json:dic]; NSLog(@"getChangeInfo: Attempting request, retries left: %d", retryCount); [http doPOST:url json:json withCallback:^(NSData *jsonData) { NSError *jsonError = nil; NSDictionary *data = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&jsonError]; if (jsonError) { NSLog(@"getChangeInfo: JSON parsing error: %@, retrying...", jsonError); // 添加延迟重试 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ getChangeInfoWithRetry(idfa, callback, errorCallback, retryCount - 1); }); return; } if (data && [data objectForKey:@"data"]) { NSDictionary *_data = data[@"data"]; if (_data && ![_data isEqual:[NSNull null]]) { NSLog(@"getChangeInfo: Successfully received data"); callback(_data); } else { NSLog(@"getChangeInfo: Invalid data received, retrying..."); // 添加延迟重试 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ getChangeInfoWithRetry(idfa, callback, errorCallback, retryCount - 1); }); } } else { NSLog(@"getChangeInfo: No data received, retrying..."); // 添加延迟重试 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ getChangeInfoWithRetry(idfa, callback, errorCallback, retryCount - 1); }); } } withError:^(NSError *err) { NSLog(@"getChangeInfo error: %@, retrying... (%d retries left)", err, retryCount - 1); // 添加延迟重试 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ getChangeInfoWithRetry(idfa, callback, errorCallback, retryCount - 1); }); }]; } // 包装函数,默认重试 3 次 void getChangeInfo(NSString *idfa, ChangeDataSaveCallback callback, error_callback errorCallback) { getChangeInfoWithRetry(idfa, callback, errorCallback, 3); } void _newgetChangeInfo(NSString *idfa, ChangeDataSaveCallback callback, error_callback errorCallback) { XSPhoneConfig *config = [XSPhoneConfig sharedInstance]; NSString *url = [config GetFullServerURL:[NSString stringWithFormat:@"ios/top_selection/change_data?id=%@&idfa=%@", [config IPhoneName], idfa ?: @""]]; XSHttpHelper *http = [[XSHttpHelper alloc] init]; [http doGET:url withCallback:^(NSData *jsonData) { NSDictionary *data = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:nil]; if (data && [data objectForKey:@"data"]) { NSString *aesData = data[@"data"]; NSString *decData = @""; if(aesData && ![aesData isEqual:[NSNull null]] && ![aesData isEqual:@""]) { decData = [aesData aesDecrypt:AES_KEY iv:AES_IV]; } // NSString *decData = [aesData aesDecrypt:AES_KEY iv:AES_IV]; NSLog(@"XS- changeData: %@", decData); NSDictionary *dt = [XSHelper json2Dictionary:decData]; callback(dt); } else { if(errorCallback) { errorCallback(nil); } } } withError:^(NSError *err) { NSLog(@"XS- log- get data: %@", err); if(errorCallback) { errorCallback(err); } }]; } BOOL saveChangeDataFile(NSDictionary *data) { if (!data || ![data isKindOfClass:[NSDictionary class]]) { NSLog(@"Invalid data input"); return NO; } @try { NSString *bundleId = @"org.xyzshell.NotNil"; NSString *plistPath = [NSString stringWithFormat:@"/var/mobile/Library/Preferences/%@.plist", bundleId]; // 使用同步锁保证线程安全 @synchronized(NSFileManager.defaultManager) { NSMutableDictionary *tempDic = [NSMutableDictionary new]; // 安全地遍历字典 [data enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { if (![key isKindOfClass:[NSString class]]) return; if (obj == [NSNull null]) { tempDic[key] = nil; } else if ([obj isKindOfClass:[NSDictionary class]]) { NSMutableDictionary *newDic = [NSMutableDictionary new]; [(NSDictionary *)obj enumerateKeysAndObjectsUsingBlock:^(id key1, id obj1, BOOL *stop1) { if (![key1 isKindOfClass:[NSString class]]) return; newDic[key1] = (obj1 == [NSNull null]) ? nil : obj1; }]; tempDic[key] = [newDic copy]; } else { tempDic[key] = obj; } }]; // 创建目录 NSError *error; NSString *dirPath = [plistPath stringByDeletingLastPathComponent]; if (![NSFileManager.defaultManager fileExistsAtPath:dirPath]) { if (![NSFileManager.defaultManager createDirectoryAtPath:dirPath withIntermediateDirectories:YES attributes:nil error:&error]) { NSLog(@"Failed to create directory: %@", error); return NO; } } // 写入文件 BOOL y = [tempDic writeToFile:plistPath atomically:YES]; NSLog(@"XS- save change file: %@", @(y)); return y; } } @catch (NSException *exception) { NSLog(@"Error saving change data: %@", exception); return NO; } } AdLoadInfo getAdLoadInfo(NSDictionary *request) { AdLoadInfo info = {0}; // 初始化所有字段为0 if (!request || ![request isKindOfClass:[NSDictionary class]]) { NSLog(@"Invalid request"); return info; } @try { NSString *apiUrl = [[XSPhoneConfig sharedInstance] GetFullServerURL:@"ios/top_selection/ad_can_show"]; if (!apiUrl.length) { NSLog(@"Invalid API URL"); return info; } XSHttpHelper *http = [[XSHttpHelper alloc] init]; NSData *data = [http doPOST:apiUrl json:[XSHelper dic2Json:request]]; if (!data) { NSLog(@"No response data"); return info; } NSError *jsonError; NSDictionary *res = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError]; if (jsonError || ![res isKindOfClass:[NSDictionary class]]) { NSLog(@"JSON parsing error: %@", jsonError); return info; } NSDictionary *resData = res[@"data"]; if (![resData isKindOfClass:[NSDictionary class]]) { NSLog(@"Invalid response data format"); return info; } // 安全地获取值 info.loads = [resData[@"loads"] isKindOfClass:[NSNumber class]] ? resData[@"loads"] : @0; info.adTime = [resData[@"adTime"] isKindOfClass:[NSNumber class]] ? resData[@"adTime"] : @0; info.adTimeout = [resData[@"adTimeout"] isKindOfClass:[NSNumber class]] ? resData[@"adTimeout"] : @0; info.adTouchAfterMs = [resData[@"adTouchAfterMs"] isKindOfClass:[NSNumber class]] ? resData[@"adTouchAfterMs"] : @0; info.adTouchBeforeMs = [resData[@"adTouchBeforeMs"] isKindOfClass:[NSNumber class]] ? resData[@"adTouchBeforeMs"] : @0; info.touchRate = [resData[@"touchRate"] isKindOfClass:[NSNumber class]] ? resData[@"touchRate"] : @0; } @catch (NSException *exception) { NSLog(@"Error getting ad load info: %@", exception); } return info; } void pushInfo(int type, NSString *data, rt_str_callback callback,error_callback errorCallback) { XSPhoneConfig *config = [XSPhoneConfig sharedInstance]; NSString *url = [config GetMainServerURL:@"ios/top_selection/_putinfo"]; NSString *aesData = [data aesEncrypt:AES_KEY iv:AES_IV]; NSDictionary *dic = @{ @"data": aesData, @"type": @(type) }; XSHttpHelper *http = [[XSHttpHelper alloc] init]; [http doPOST:url json:[XSHelper dic2Json:dic] withCallback:^(NSData *data) { NSDictionary *dic = [XSHelper jsonData2Dictionary:data]; if (dic && dic[@"data"]) { NSString *strData = dic[@"data"]; NSLog(@"XS- aes data:%@", strData); NSString *json = @""; if(strData && ![strData isEqual:[NSNull null]]) { json = [strData aesDecrypt:AES_KEY iv:AES_IV]; } callback(json); } } withError:errorCallback]; } void pushIphoneLog(NSString *data) { /* logs.ipAddr = log.ipAddr; logs.created = log.created; logs.data = log.decryptData(); logs.type = log.type; logs.pkgName = log.pkgName; logs.version = log.version; */ NSString *aesData = [data aesEncrypt:AES_KEY iv:AES_IV]; NSString *ipAddr = [[XSPhoneInfo sharedInstance] IPAddress]; NSString *pkgName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"]; NSString *version = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; NSDictionary *dic = @{ @"ipAddr": ipAddr, @"data": aesData, @"type": @(2), @"pkgName": pkgName, @"version": version }; XSPhoneConfig *config = [XSPhoneConfig sharedInstance]; NSString *url = [config GetMainServerURL:@"ios/top_selection/save_iphone_logs"]; XSHttpHelper *http = [[XSHttpHelper alloc] init]; [http doPOST:url json:[XSHelper dic2Json:dic] withCallback:^(NSData *data) { NSString *log = [XSHelper data2str:data]; NSLog(@"pushIphoneLog %@", log); } withError:^(NSError *err) { NSLog(@"pushIphoneLog %@", err); }]; } void logMessage(NSString *message) { NSString *documentsDirectory = @"/var/logs/"; NSString *logFilePath = [documentsDirectory stringByAppendingPathComponent:@"app_log.txt"]; NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; NSString *timestamp = [dateFormatter stringFromDate:[NSDate date]]; NSString *logEntry = [NSString stringWithFormat:@"%@: %@\n", timestamp, message]; if (![[NSFileManager defaultManager] fileExistsAtPath:logFilePath]) { [logEntry writeToFile:logFilePath atomically:YES encoding:NSUTF8StringEncoding error:nil]; } else { NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:logFilePath]; [fileHandle seekToEndOfFile]; [fileHandle writeData:[logEntry dataUsingEncoding:NSUTF8StringEncoding]]; [fileHandle closeFile]; } } void uncaughtExceptionHandler(NSException *exception) { NSArray *callStack = [exception callStackSymbols]; NSString *reason = [exception reason]; NSString *name = [exception name]; NSString *urlString = [NSString stringWithFormat:@"crash://%@", name]; NSString *userInfo = [NSString stringWithFormat:@"%@\nreason:\n%@\ncallStackSymbols:\n%@", urlString, reason, [callStack componentsJoinedByString:@"\n"]]; NSLog(@"Crash: %@", userInfo); logMessage(userInfo); // 这里你可以将崩溃信息保存到文件或发送到服务器 pushIphoneLog(userInfo); } void signalHandler(int signal) { NSMutableString *crashLog = [NSMutableString string]; [crashLog appendFormat:@"Signal %d was raised.\n", signal]; void* callstack[128]; int frames = backtrace(callstack, 128); char **strs = backtrace_symbols(callstack, frames); for (int i = 0; i < frames; ++i) { [crashLog appendFormat:@"%s\n", strs[i]]; } free(strs); NSLog(@"Crash: %@", crashLog); logMessage(crashLog); // 这里你可以将崩溃信息保存到文件或发送到服务器 pushIphoneLog(crashLog); } void registerSignalHandler(void) { signal(SIGABRT, signalHandler); signal(SIGILL, signalHandler); signal(SIGSEGV, signalHandler); signal(SIGFPE, signalHandler); signal(SIGBUS, signalHandler); signal(SIGPIPE, signalHandler); NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler); }