You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

287 lines
10KB

  1. #import "Apollo.h"
  2. static BOOL isApolloDeletedCommentsOnly;
  3. static BOOL isApolloEnabled;
  4. static CGFloat pushshiftRequestTimeoutValue;
  5. %group Apollo
  6. NSDictionary* apolloBodyAttributes = nil;
  7. %hook ApolloApolloButtonNode
  8. %end
  9. %hook RKComment
  10. -(BOOL) isDeleted{
  11. return NO;
  12. }
  13. -(BOOL) isModeratorRemoved{
  14. return NO;
  15. }
  16. %end
  17. %hook MarkdownRenderer
  18. +(id) attributedStringFromHTML:(id)arg1 attributes:(id) arg2 compact:(BOOL) arg3{
  19. apolloBodyAttributes = [arg2 copy];
  20. return %orig;
  21. }
  22. %end
  23. %hook ApolloCommentCellNode
  24. %property(strong,nonatomic) id undeleteButton;
  25. %new
  26. -(void) didTapUndeleteButton:(id) sender{
  27. [sender setEnabled:NO];
  28. id bodyNode = MSHookIvar<id>(self, "bodyNode");
  29. id authorNode = MSHookIvar<id>(self, "authorNode");
  30. id authorTextNode = [authorNode subnodes][0];
  31. id comment = MSHookIvar<id>(self, "comment");
  32. NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
  33. NSOperationQueue *queue = [[NSOperationQueue alloc] init];
  34. [request setURL:[NSURL URLWithString:[NSString stringWithFormat:@"https://api.pushshift.io/reddit/search/comment/?ids=%@&fields=author,body",[[comment fullName] componentsSeparatedByString:@"_"][1]]]];
  35. [request setHTTPMethod:@"GET"];
  36. [request setTimeoutInterval:pushshiftRequestTimeoutValue];
  37. [NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
  38. NSString *author = @"[author]";
  39. NSString *body = @"[body]";
  40. if (data != nil && error == nil){
  41. id jsonData = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
  42. if ([[jsonData objectForKey:@"data"] count] != 0){
  43. author = [[jsonData objectForKey:@"data"][0] objectForKey:@"author"];
  44. body = [[jsonData objectForKey:@"data"][0] objectForKey:@"body"];
  45. if ([body isEqualToString:@"[deleted]"] || [body isEqualToString:@"[removed]"]){
  46. body = @"[pushshift was unable to archive this]";
  47. }
  48. } else {
  49. body = @"[pushshift has not archived this yet]";
  50. }
  51. } else if (error != nil || data == nil){
  52. body = [NSString stringWithFormat:@"[an error occured while attempting to contact pushshift api (%@)]", [error localizedDescription]];
  53. }
  54. id prevAuthorAttributedString = [authorTextNode attributedString];
  55. NSDictionary *authorStringAttributes = [prevAuthorAttributedString attributesAtIndex:0 longestEffectiveRange:nil inRange:NSMakeRange(0, [prevAuthorAttributedString length])];
  56. NSAttributedString* newAuthorAttributedString = [[NSAttributedString alloc] initWithString:author attributes:authorStringAttributes];
  57. [authorTextNode setAttributedText:newAuthorAttributedString];
  58. [authorTextNode setAttributedString:newAuthorAttributedString];
  59. [comment setAuthor:author];
  60. [bodyNode setAttributedString:[%c(MarkdownRenderer) attributedStringFromMarkdown:body withAttributes:apolloBodyAttributes]];
  61. [sender setEnabled:YES];
  62. }];
  63. }
  64. -(void) didLoad {
  65. %orig;
  66. id commentBody = [MSHookIvar<id>(self, "comment") body];
  67. if ((isApolloDeletedCommentsOnly && ([commentBody isEqualToString:@"[deleted]"] || [commentBody isEqualToString:@"[removed]"])) || !isApolloDeletedCommentsOnly) {
  68. CGFloat imageSize = 20.0f;
  69. UIButton *undeleteButton = [UIButton buttonWithType:UIButtonTypeCustom];
  70. [undeleteButton addTarget:self action:@selector(didTapUndeleteButton:) forControlEvents:UIControlEventTouchUpInside];
  71. undeleteButton.frame = CGRectMake(0, 0, imageSize, imageSize);
  72. UIImage* undeleteImage = [UIImage imageWithContentsOfFile:@"/var/mobile/Library/Application Support/TFDidThatSay/eye160dark.png"];
  73. [undeleteButton setImage:undeleteImage forState:UIControlStateNormal];
  74. [[self view] addSubview:undeleteButton];
  75. [self setUndeleteButton:undeleteButton];
  76. }
  77. }
  78. -(void) _layoutSublayouts{
  79. %orig;
  80. if ([self undeleteButton]){
  81. CGFloat imageSize = 20.0f;
  82. id moreNode = MSHookIvar<id>(self, "moreOptionsNode");
  83. id ageNode = MSHookIvar<id>(self, "ageNode");
  84. CGRect nodeFrame = [moreNode frame];
  85. CGFloat centerHeight = (nodeFrame.size.height + nodeFrame.origin.y * 2) / 2.0f;
  86. CGFloat nodeSpacing =[ageNode frame].origin.x - nodeFrame.origin.x - nodeFrame.size.width;
  87. [[self undeleteButton] setFrame:CGRectMake(nodeFrame.origin.x - imageSize - nodeSpacing, centerHeight - (imageSize / 2), imageSize, imageSize)];
  88. }
  89. }
  90. %end
  91. %hook ApolloCommentsHeaderCellNode
  92. %property(strong, nonatomic) id undeleteButton;
  93. %new
  94. -(void) didTapUndeleteButton:(id) sender{
  95. [sender setEnabled:NO];
  96. id bodyNode = MSHookIvar<id>(self, "bodyNode");
  97. id postInfoNode = MSHookIvar<id>(self, "postInfoNode");
  98. id authorNode = MSHookIvar<id>(postInfoNode, "authorButtonNode");
  99. id authorTextNode = [authorNode subnodes][0];
  100. id post = MSHookIvar<id>(self, "link");
  101. NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
  102. NSOperationQueue *queue = [[NSOperationQueue alloc] init];
  103. [request setURL:[NSURL URLWithString:[NSString stringWithFormat:@"https://api.pushshift.io/reddit/search/submission/?ids=%@&fields=author,selftext",[[post fullName] componentsSeparatedByString:@"_"][1]]]];
  104. [request setHTTPMethod:@"GET"];
  105. [request setTimeoutInterval:pushshiftRequestTimeoutValue];
  106. [NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
  107. NSString *author = @"[author]";
  108. NSString *body = @"[body]";
  109. if (data != nil && error == nil){
  110. id jsonData = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
  111. if ([[jsonData objectForKey:@"data"] count] != 0){
  112. author = [[jsonData objectForKey:@"data"][0] objectForKey:@"author"];
  113. body = [[jsonData objectForKey:@"data"][0] objectForKey:@"selftext"];
  114. if ([body isEqualToString:@"[deleted]"] || [body isEqualToString:@"[removed]"]){
  115. body = @"[pushshift was unable to archive this]";
  116. }
  117. } else {
  118. body = @"[pushshift has not archived this yet]";
  119. }
  120. } else if (error != nil || data == nil){
  121. body = [NSString stringWithFormat:@"[an error occured while attempting to contact pushshift api (%@)]", [error localizedDescription]];
  122. }
  123. //MSHookIvar<NSString*>(post, "_author") = author; //Crashes when clicking on author name. You will have to search the author name to go find the profile.
  124. author = [NSString stringWithFormat:@"by %@", author];
  125. id prevAuthorAttributedString = [authorTextNode attributedString];
  126. NSDictionary *authorStringAttributes = [prevAuthorAttributedString attributesAtIndex:0 longestEffectiveRange:nil inRange:NSMakeRange(0, [prevAuthorAttributedString length])];
  127. NSAttributedString* newAuthorAttributedString = [[NSAttributedString alloc] initWithString:author attributes:authorStringAttributes];
  128. [authorTextNode setAttributedText:newAuthorAttributedString];
  129. [authorTextNode setAttributedString:newAuthorAttributedString];
  130. [bodyNode setAttributedString:[%c(MarkdownRenderer) attributedStringFromMarkdown:body withAttributes:apolloBodyAttributes]];
  131. [sender setEnabled:YES];
  132. }];
  133. }
  134. -(void) didLoad{
  135. %orig;
  136. if ([MSHookIvar<id>(self, "link") isSelfPost]){
  137. CGFloat imageSize = 20.0f;
  138. UIButton *undeleteButton = [UIButton buttonWithType:UIButtonTypeCustom];
  139. [undeleteButton addTarget:self action:@selector(didTapUndeleteButton:) forControlEvents:UIControlEventTouchUpInside];
  140. UIImage* undeleteImage = [UIImage imageWithContentsOfFile:@"/var/mobile/Library/Application Support/TFDidThatSay/eye160dark.png"];
  141. [undeleteButton setImage:undeleteImage forState:UIControlStateNormal];
  142. undeleteButton.frame = CGRectMake(0, 0, imageSize, imageSize);
  143. [[self view] addSubview:undeleteButton];
  144. [self setUndeleteButton:undeleteButton];
  145. }
  146. }
  147. -(void) _layoutSublayouts{
  148. %orig;
  149. if ([self undeleteButton]){
  150. CGFloat imageSize = 20.0f;
  151. id postInfoNode = MSHookIvar<id>(self, "postInfoNode");
  152. id ageNode = MSHookIvar<id>(postInfoNode, "ageButtonNode");
  153. CGFloat centerHeight = [postInfoNode frame].origin.y + ([ageNode frame].size.height + [ageNode frame].origin.y * 2) / 2.0f;
  154. CGFloat buttonXPos = [postInfoNode frame].origin.x + [postInfoNode frame].size.width - imageSize;
  155. [[self undeleteButton] setFrame:CGRectMake(buttonXPos, centerHeight - (imageSize / 2), imageSize, imageSize)];
  156. }
  157. }
  158. %end
  159. %end
  160. static void loadPrefs(){
  161. NSMutableDictionary *prefs = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/User/Library/Preferences/com.lint.undelete.prefs.plist"];
  162. if (prefs){
  163. if ([prefs objectForKey:@"isApolloEnabled"] != nil) {
  164. isApolloEnabled = [[prefs objectForKey:@"isApolloEnabled"] boolValue];
  165. } else {
  166. isApolloEnabled = YES;
  167. }
  168. if ([prefs objectForKey:@"requestTimeoutValue"] != nil){
  169. pushshiftRequestTimeoutValue = [[prefs objectForKey:@"requestTimeoutValue"] doubleValue];
  170. } else {
  171. pushshiftRequestTimeoutValue = 10;
  172. }
  173. if ([prefs objectForKey:@"isApolloDeletedCommentsOnly"] != nil) {
  174. isApolloDeletedCommentsOnly = [[prefs objectForKey:@"isApolloDeletedCommentsOnly"] boolValue];
  175. } else {
  176. isApolloDeletedCommentsOnly = YES;
  177. }
  178. } else {
  179. isApolloEnabled = YES;
  180. pushshiftRequestTimeoutValue = 10;
  181. isApolloDeletedCommentsOnly = YES;
  182. }
  183. }
  184. static void prefsChanged(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) {
  185. loadPrefs();
  186. }
  187. %ctor {
  188. loadPrefs();
  189. NSString* processName = [[NSProcessInfo processInfo] processName];
  190. if ([processName isEqualToString:@"Apollo"]){
  191. if (isApolloEnabled){
  192. CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), NULL, prefsChanged, CFSTR("com.lint.undelete.prefs.changed"), NULL, CFNotificationSuspensionBehaviorDeliverImmediately);
  193. %init(Apollo, ApolloCommentsHeaderCellNode = objc_getClass("Apollo.CommentsHeaderCellNode"), ApolloCommentCellNode = objc_getClass("Apollo.CommentCellNode"), ApolloApolloButtonNode = objc_getClass("Apollo.ApolloButtonNode"));
  194. }
  195. }
  196. }