mirror of
https://github.com/faroukbmiled/RyukGram.git
synced 2026-06-09 08:53:52 +02:00
feat: Replace Domain in Instagram urls
feat: Remove user param from copied links feat: Open links in External Browser feat: Strip Instagram tracking from browser links fix: Confirm reel refresh confirmation appears on app launch #2
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
// Rewrite Instagram share links — replace domain + optionally strip tracking params.
|
||||
// Waits for IG's async clipboard write via changeCount, then rewrites once.
|
||||
|
||||
#import "../../Utils.h"
|
||||
#import <objc/runtime.h>
|
||||
#import <objc/message.h>
|
||||
#import <substrate.h>
|
||||
|
||||
static NSString *sciRewriteIGURL(NSString *url) {
|
||||
if (!url.length) return url;
|
||||
|
||||
// Domain replacement
|
||||
if ([SCIUtils getBoolPref:@"embed_links"]) {
|
||||
NSString *domain = [SCIUtils getStringPref:@"embed_link_domain"];
|
||||
if (!domain.length) domain = @"kkinstagram.com";
|
||||
if (![url containsString:domain]) {
|
||||
NSArray *igDomains = @[@"www.instagram.com", @"instagram.com", @"www.instagr.am", @"instagr.am"];
|
||||
for (NSString *d in igDomains) {
|
||||
NSRange r = [url rangeOfString:d];
|
||||
if (r.location != NSNotFound) {
|
||||
NSString *target = [d hasPrefix:@"www."]
|
||||
? [NSString stringWithFormat:@"www.%@", domain] : domain;
|
||||
url = [url stringByReplacingCharactersInRange:r withString:target];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Strip tracking params
|
||||
if ([SCIUtils getBoolPref:@"strip_tracking_params"]) {
|
||||
NSURLComponents *comps = [NSURLComponents componentsWithString:url];
|
||||
if (comps.queryItems.count) {
|
||||
NSArray *strip = @[@"igsh", @"ig_rid", @"utm_source", @"utm_medium", @"utm_campaign"];
|
||||
NSMutableArray *clean = [NSMutableArray array];
|
||||
for (NSURLQueryItem *q in comps.queryItems) {
|
||||
if (![strip containsObject:q.name]) [clean addObject:q];
|
||||
}
|
||||
comps.queryItems = clean.count ? clean : nil;
|
||||
NSString *result = comps.string;
|
||||
if (result) url = result;
|
||||
}
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
static BOOL sciShouldRewrite(void) {
|
||||
return [SCIUtils getBoolPref:@"embed_links"] || [SCIUtils getBoolPref:@"strip_tracking_params"];
|
||||
}
|
||||
|
||||
// Rewrite clipboard once after IG writes
|
||||
static void sciPollAndRewrite(NSInteger countBefore, int polls, double interval) {
|
||||
__block BOOL done = NO;
|
||||
for (int i = 0; i < polls; i++) {
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)((interval + i * interval) * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
if (done) return;
|
||||
if ([UIPasteboard generalPasteboard].changeCount == countBefore) return;
|
||||
NSString *clip = [UIPasteboard generalPasteboard].string;
|
||||
if (!clip || ![clip containsString:@"instagram"]) return;
|
||||
NSString *rewritten = sciRewriteIGURL(clip);
|
||||
if (![rewritten isEqualToString:clip]) {
|
||||
[UIPasteboard generalPasteboard].string = rewritten;
|
||||
done = YES;
|
||||
} else {
|
||||
done = YES;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ============ Hooks ============
|
||||
|
||||
static void (*orig_copyLink)(id, SEL, id);
|
||||
static void new_copyLink(id self, SEL _cmd, id vc) {
|
||||
if (!sciShouldRewrite()) { orig_copyLink(self, _cmd, vc); return; }
|
||||
NSInteger countBefore = [UIPasteboard generalPasteboard].changeCount;
|
||||
orig_copyLink(self, _cmd, vc);
|
||||
sciPollAndRewrite(countBefore, 30, 0.05);
|
||||
}
|
||||
|
||||
static void (*orig_shareMore)(id, SEL, id);
|
||||
static void new_shareMore(id self, SEL _cmd, id vc) {
|
||||
if (!sciShouldRewrite()) { orig_shareMore(self, _cmd, vc); return; }
|
||||
NSInteger countBefore = [UIPasteboard generalPasteboard].changeCount;
|
||||
orig_shareMore(self, _cmd, vc);
|
||||
sciPollAndRewrite(countBefore, 120, 0.1);
|
||||
}
|
||||
|
||||
__attribute__((constructor)) static void _embedLinksInit(void) {
|
||||
Class cls = NSClassFromString(@"IGExternalShareOptionsViewController");
|
||||
if (!cls) return;
|
||||
SEL copy = NSSelectorFromString(@"_shareToClipboardFromVC:");
|
||||
if (class_getInstanceMethod(cls, copy))
|
||||
MSHookMessageEx(cls, copy, (IMP)new_copyLink, (IMP *)&orig_copyLink);
|
||||
SEL more = NSSelectorFromString(@"_shareToMoreFromVC:");
|
||||
if (class_getInstanceMethod(cls, more))
|
||||
MSHookMessageEx(cls, more, (IMP)new_shareMore, (IMP *)&orig_shareMore);
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// Open links in external browser + strip IG tracking from URLs
|
||||
#import "../../Utils.h"
|
||||
#import <objc/runtime.h>
|
||||
#import <objc/message.h>
|
||||
|
||||
// Extract the real URL from l.instagram.com redirects and strip tracking params
|
||||
static NSURL *sciCleanBrowserURL(NSURL *url) {
|
||||
if (![SCIUtils getBoolPref:@"strip_browser_tracking"]) return url;
|
||||
if (!url) return url;
|
||||
|
||||
NSString *urlStr = url.absoluteString;
|
||||
|
||||
// Unwrap l.instagram.com/?u=ENCODED_URL&e=TRACKING redirects
|
||||
if ([url.host isEqualToString:@"l.instagram.com"]) {
|
||||
NSURLComponents *comps = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO];
|
||||
for (NSURLQueryItem *q in comps.queryItems) {
|
||||
if ([q.name isEqualToString:@"u"] && q.value.length) {
|
||||
NSString *decoded = [q.value stringByRemovingPercentEncoding];
|
||||
if (decoded) urlStr = decoded;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Strip common tracking params from the destination URL
|
||||
NSURLComponents *comps = [NSURLComponents componentsWithString:urlStr];
|
||||
if (comps.queryItems.count) {
|
||||
NSSet *trackingParams = [NSSet setWithArray:@[
|
||||
@"utm_source", @"utm_medium", @"utm_campaign", @"utm_content",
|
||||
@"utm_term", @"utm_id", @"fbclid", @"igshid", @"igsh",
|
||||
@"ig_rid", @"campaign_id", @"ad_id", @"aem"
|
||||
]];
|
||||
NSMutableArray *clean = [NSMutableArray array];
|
||||
for (NSURLQueryItem *q in comps.queryItems) {
|
||||
if (![trackingParams containsObject:q.name]) [clean addObject:q];
|
||||
}
|
||||
comps.queryItems = clean.count ? clean : nil;
|
||||
}
|
||||
|
||||
NSURL *result = comps.URL;
|
||||
return result ?: url;
|
||||
}
|
||||
|
||||
%hook IGBrowserNavigationController
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
id session = ((id(*)(id,SEL))objc_msgSend)(self, @selector(browserSession));
|
||||
Ivar urlIvar = session ? class_getInstanceVariable([session class], "_urlRequest") : nil;
|
||||
NSURLRequest *req = urlIvar ? object_getIvar(session, urlIvar) : nil;
|
||||
NSURL *url = req.URL;
|
||||
|
||||
if (url && [SCIUtils getBoolPref:@"open_links_external"]) {
|
||||
NSURL *cleaned = sciCleanBrowserURL(url);
|
||||
[[UIApplication sharedApplication] openURL:cleaned options:@{} completionHandler:nil];
|
||||
[(UIViewController *)self dismissViewControllerAnimated:NO completion:nil];
|
||||
return;
|
||||
}
|
||||
|
||||
// For in-app browser: replace the URL request with the cleaned version
|
||||
if (url && [SCIUtils getBoolPref:@"strip_browser_tracking"]) {
|
||||
NSURL *cleaned = sciCleanBrowserURL(url);
|
||||
if (![cleaned isEqual:url]) {
|
||||
NSURLRequest *cleanReq = [NSURLRequest requestWithURL:cleaned];
|
||||
object_setIvar(session, urlIvar, cleanReq);
|
||||
}
|
||||
}
|
||||
|
||||
%orig;
|
||||
}
|
||||
%end
|
||||
@@ -28,27 +28,46 @@
|
||||
}
|
||||
%end
|
||||
|
||||
static BOOL sciReelRefreshInFlight = NO;
|
||||
|
||||
%hook IGSundialFeedViewController
|
||||
- (void)_refreshReelsWithParamsForNetworkRequest:(NSInteger)arg1 userDidPullToRefresh:(BOOL)arg2 {
|
||||
if ([SCIUtils getBoolPref:@"prevent_doom_scrolling"]) {
|
||||
IGRefreshControl *_refreshControl = MSHookIvar<IGRefreshControl *>(self, "_refreshControl");
|
||||
[self refreshControlDidEndFinishLoadingAnimation:_refreshControl];
|
||||
|
||||
IGRefreshControl *rc = MSHookIvar<IGRefreshControl *>(self, "_refreshControl");
|
||||
[self refreshControlDidEndFinishLoadingAnimation:rc];
|
||||
return;
|
||||
}
|
||||
|
||||
if ([SCIUtils getBoolPref:@"refresh_reel_confirm"]) {
|
||||
NSLog(@"[SCInsta] Reel refresh triggered");
|
||||
|
||||
[SCIUtils showConfirmation:^(void) { %orig(arg1, arg2); }
|
||||
cancelHandler:^(void) {
|
||||
IGRefreshControl *_refreshControl = MSHookIvar<IGRefreshControl *>(self, "_refreshControl");
|
||||
[self refreshControlDidEndFinishLoadingAnimation:_refreshControl];
|
||||
}
|
||||
title:@"Refresh Reels"];
|
||||
} else {
|
||||
return %orig(arg1, arg2);
|
||||
if (![(UIViewController *)self isViewLoaded] || sciReelRefreshInFlight || ![SCIUtils getBoolPref:@"refresh_reel_confirm"]) {
|
||||
%orig(arg1, arg2);
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset the refresh control state so pull-to-refresh can trigger again
|
||||
IGRefreshControl *rc = MSHookIvar<IGRefreshControl *>(self, "_refreshControl");
|
||||
Ivar stateIvar = class_getInstanceVariable([rc class], "_refreshState");
|
||||
if (stateIvar) {
|
||||
ptrdiff_t off = ivar_getOffset(stateIvar);
|
||||
*(NSInteger *)((char *)(__bridge void *)rc + off) = 0;
|
||||
}
|
||||
if ([rc respondsToSelector:@selector(endRefreshing)])
|
||||
((void(*)(id,SEL))objc_msgSend)(rc, @selector(endRefreshing));
|
||||
[self refreshControlDidEndFinishLoadingAnimation:rc];
|
||||
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Refresh Reels?"
|
||||
message:nil
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
__weak id weakSelf = self;
|
||||
[alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:@"Refresh" style:UIAlertActionStyleDefault handler:^(UIAlertAction *_) {
|
||||
sciReelRefreshInFlight = YES;
|
||||
SEL rSel = @selector(_refreshReelsWithParamsForNetworkRequest:userDidPullToRefresh:);
|
||||
((void(*)(id,SEL,NSInteger,BOOL))objc_msgSend)(weakSelf, rSel, arg1, arg2);
|
||||
sciReelRefreshInFlight = NO;
|
||||
}]];
|
||||
|
||||
UIViewController *presenter = (UIViewController *)self;
|
||||
[presenter presentViewController:alert animated:YES completion:nil];
|
||||
}
|
||||
%end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user