Files
iOS-DiscordPresence/Tweak/Tweak.x
2022-05-09 07:11:28 -07:00

207 lines
7.4 KiB
Plaintext

#import <Tweak.h>
// Variable to store value from preferences
Boolean isEnabled = false;
NSString* discordToken = nil;
SBApplication* focusedApplication = nil;
NSString* lastKnownBundleIdentifier = nil;
Boolean isDeviceLocked = true;
%hook SpringBoard
-(void)frontDisplayDidChange:(id)arg1 {
%orig;
// Switched to SpringBoard
if(arg1 == nil) {
// If switched from SpringBoard to SpringBoard, ignore
if(focusedApplication == arg1) return;
// User is not in any application, clear the presence
focusedApplication = nil;
[self updateDiscordPresence:focusedApplication withState:@"STOP"];
return;
}
// Switched to Application
else if ([arg1 isKindOfClass:[%c(SBApplication) class]]) {
SBApplication *app = arg1;
Boolean isSystemApplication = [app isSystemApplication];
Boolean isGameApplication = [self isSBApplicationAGame:app];
// Switched to itself, ignore
if (focusedApplication == app) return;
// Ignore if the application is system application or is not a game
if(isSystemApplication || !isGameApplication) {
// Remove any focused application, since the user switched to non game or system application
if(focusedApplication != nil) {
// Remove current focused application and stop the presence
focusedApplication = nil;
[self updateDiscordPresence:focusedApplication withState:@"STOP"];
}
return;
}
// Didn't switched from one application to another, start new presence
if(focusedApplication == nil) {
focusedApplication = app;
[self updateDiscordPresence:focusedApplication withState:@"START"];
}
// Switched from one application to another, update the presence
else {
focusedApplication = app;
[self updateDiscordPresence:focusedApplication withState:@"UPDATE"];
}
return;
}
}
%new
- (bool)isSBApplicationAGame:(SBApplication *)app {
SBApplicationInfo *appInfo = [app info];
NSArray *category = [appInfo iTunesCategoriesOrderedByRelevancy];
//NSString *categoryStr = [category componentsJoinedByString:@", "];
// Does application contains "Games" category?
return [category containsObject:@"Games"];
}
%new
-(void)updateDiscordPresence:(id)arg1 withState:(NSString *)state {
if(![arg1 isKindOfClass:[%c(SBApplication) class]] && arg1 != nil) return;
if(!isEnabled) return;
NSString *accessToken = @"";
if(discordToken != nil)
accessToken = discordToken;
// If SBApplication is passed
if(arg1 != nil) {
SBApplication *app = (SBApplication *)arg1;
[self sendRequestToDiscord:accessToken withBundleIdentifier:[app bundleIdentifier] withState:state];
}
// No SBApplication passed, only STOP should be sent
else {
if(![state isEqualToString: @"STOP"]) return;
[self sendRequestToDiscord:accessToken withBundleIdentifier:nil withState:state];
}
}
%new
-(void)sendRequestToDiscord:(NSString *)accessToken withBundleIdentifier:(NSString *)bundleIdentifier withState:(NSString *)state {
// Discord API Presences endpoint
NSMutableURLRequest *urlRequest = [[NSMutableURLRequest alloc]
initWithURL:[NSURL URLWithString:@"https://discord.com/api/v6/presences"]
];
// Our body payload
NSDictionary *jsonBodyDict;
if(bundleIdentifier != nil) {
jsonBodyDict = @{ @"package_name":bundleIdentifier, @"update": state };
NSLog(@"Sending: Package_name: %@ State: %@", bundleIdentifier, state);
}
else {
if(lastKnownBundleIdentifier == nil) return;
jsonBodyDict = @{ @"package_name":lastKnownBundleIdentifier, @"update": state };
NSLog(@"Sending: Package_name: %@ State: %@", lastKnownBundleIdentifier, state);
}
lastKnownBundleIdentifier = bundleIdentifier;
// Serializate to body JSON
NSData *jsonBodyData = [NSJSONSerialization dataWithJSONObject:jsonBodyDict options:kNilOptions error:nil];
if(jsonBodyData == nil) return;
//Apply the data to the body
[urlRequest setHTTPBody:jsonBodyData];
// Set the request method to POST
[urlRequest setHTTPMethod:@"POST"];
// Set headers
// Clean user token
NSString *token = [accessToken stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
[urlRequest setValue:token forHTTPHeaderField:@"Authorization"];
[urlRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[urlRequest setValue:@"max-age=121" forHTTPHeaderField:@"Cache-Control"];
// Use Android useragent. Well, so it's not sus
[urlRequest setValue:@"Mozilla/5.0 (Linux; Android 11) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.127 Mobile OceanHero/6 Safari/537.36" forHTTPHeaderField:@"User-Agent"];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:urlRequest completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if(httpResponse.statusCode == 204)
{
NSLog(@"Request sent to Discord successfully");
}
else
{
NSLog(@"Error from discord, Status Code: %ld", (long)httpResponse.statusCode);
}
}];
[dataTask resume];
}
%end
%hook SBLockScreenManager
-(void)_authenticationStateChanged:(id)arg1 {
%orig;
// Get the state changed notification, and find the SBFUserAuthenticationStateWasAuthenticatedKey value
NSConcreteNotification *notification = arg1;
int state = [[[notification userInfo] objectForKey: @"SBFUserAuthenticationStateWasAuthenticatedKey"] integerValue];
isDeviceLocked = state != 0;
// We don't need to take care anything else here. The frontDisplayDidChange will take care of it
}
-(void)_sendUILockStateChangedNotification {
%orig;
// The phone is locked or focused application is nil, ignore
if(isDeviceLocked || focusedApplication == nil) return;
// If the phone is unlocked, then the user is peeking at the notification or didn't swipe up to unlock yet.
Boolean isOnLockScreen = [self isLockScreenVisible];
// Switched to the lockscreen UI
if(isOnLockScreen) {
// Remove current focused application and stop the presence
focusedApplication = nil;
[[%c(SpringBoard) sharedApplication] updateDiscordPresence:focusedApplication withState:@"STOP"];
}
}
%end
static void loadPreferences()
{
NSString *preferencesPath = @"/var/mobile/Library/Preferences/pink.kirameki.yuzu.iosdiscordpresencepreferences.plist";
NSMutableDictionary *preferences = [[NSMutableDictionary alloc] initWithContentsOfFile: preferencesPath];
if(preferences)
{
NSLog(@"Updating preferences");
isEnabled = ( [preferences objectForKey:@"isEnabled"] ? [[preferences objectForKey:@"isEnabled"] boolValue] : isEnabled );
discordToken = ( [preferences objectForKey:@"discordToken"] ? [preferences objectForKey:@"discordToken"] : discordToken );
}
}
%ctor
{
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), NULL, (CFNotificationCallback)loadPreferences, CFSTR("pink.kirameki.yuzu.iosdiscordpresencepreferences/PreferencesChanged"), NULL, CFNotificationSuspensionBehaviorCoalesce);
loadPreferences();
}