// // 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); } }]; } static dispatch_queue_t XSFileIOQueue() { static dispatch_queue_t q; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ q = dispatch_queue_create("com.xs.fileio.queue", DISPATCH_QUEUE_SERIAL); }); return q; } static id XSFilterJSONValue(id obj) { if (obj == [NSNull null]) return nil; if ([obj isKindOfClass:[NSDictionary class]]) { NSMutableDictionary *md = [NSMutableDictionary dictionary]; [(NSDictionary *)obj enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) { if (![key isKindOfClass:[NSString class]]) return; id v = XSFilterJSONValue(value); if (v) md[key] = v; }]; return md; } if ([obj isKindOfClass:[NSArray class]]) { NSMutableArray *ma = [NSMutableArray array]; for (id v in (NSArray *)obj) { id nv = XSFilterJSONValue(v); [ma addObject:nv ?: [NSNull null]]; } return ma; } // 允许的基础类型 if ([obj isKindOfClass:[NSString class]] || [obj isKindOfClass:[NSNumber class]]) { return obj; } // 其他类型转字符串以避免序列化失败 return [obj description]; } BOOL saveChangeDataFile(NSDictionary *data) { if (!data || ![data isKindOfClass:[NSDictionary class]]) { NSLog(@"[saveChangeDataFile] Invalid data input: %@", data); return NO; } __block BOOL result = NO; __block NSError *writeError = nil; dispatch_sync(XSFileIOQueue(), ^{ @try { NSString *jsonPath = @"/var/mobile/Documents/changeapp.json"; NSString *dirPath = [jsonPath stringByDeletingLastPathComponent]; NSError *dirErr = nil; if (![[NSFileManager defaultManager] fileExistsAtPath:dirPath]) { [[NSFileManager defaultManager] createDirectoryAtPath:dirPath withIntermediateDirectories:YES attributes:@{NSFileProtectionKey: NSFileProtectionNone} error:&dirErr]; if (dirErr) { NSLog(@"[saveChangeDataFile] Failed to create directory %@, error: %@", dirPath, dirErr); return; } } NSDictionary *filtered = XSFilterJSONValue(data); if (!filtered || ![NSJSONSerialization isValidJSONObject:filtered]) { NSLog(@"[saveChangeDataFile] JSON object invalid after filtering: %@", filtered); return; } NSError *jsonError = nil; NSData *jsonData = [NSJSONSerialization dataWithJSONObject:filtered options:0 error:&jsonError]; if (jsonError || !jsonData) { NSLog(@"[saveChangeDataFile] Error serializing to JSON: %@", jsonError); return; } NSURL *fileURL = [NSURL fileURLWithPath:jsonPath]; result = [jsonData writeToURL:fileURL options:NSDataWritingAtomic error:&writeError]; NSString *idfa = data[@"idfa"]; NSLog(@"XS- save change file: %@; idfa:%@; path:%@", @(result), idfa, jsonPath); /* if (!result) { struct rlimit limit; getrlimit(RLIMIT_NOFILE, &limit); NSLog(@"soft limit: %llu, hard limit: %llu", limit.rlim_cur, limit.rlim_max); } */ } @catch (NSException *exception) { NSLog(@"[saveChangeDataFile] Exception: %@", exception); result = NO; } }); if (writeError) { NSLog(@"[saveChangeDataFile] Write error: %@", writeError); } return result; } 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(@"SpringBoard crash:%@", crashLog); */ const char *signalName = strsignal(signal); write(STDERR_FILENO, "Signal received: ", 17); write(STDERR_FILENO, signalName, strlen(signalName)); write(STDERR_FILENO, "\n", 1); // 设置标志位,通知主线程处理 // 避免在信号处理函数中调用复杂逻辑 _exit(signal); // 这里你可以将崩溃信息保存到文件或发送到服务器 // 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); }