418 lines
16 KiB
Objective-C
418 lines
16 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);
|
|
}
|
|
}];
|
|
}
|
|
|
|
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);
|
|
}
|