472 lines
18 KiB
Objective-C
472 lines
18 KiB
Objective-C
//
|
|
// MyAdServer.m
|
|
// nochange
|
|
//
|
|
// Created by mac on 2024/7/26.
|
|
//
|
|
|
|
#import <Foundation/Foundation.h>
|
|
#import <signal.h>
|
|
#import <execinfo.h>
|
|
|
|
#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);
|
|
}
|