選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

BaconReader.xm 7.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. #import "BaconReader.h"
  2. #import "assets/MMMarkdown.h"
  3. static BOOL isBaconReaderEnabled;
  4. static BOOL isBaconReaderDeletedOnly;
  5. static CGFloat pushshiftRequestTimeoutValue;
  6. %group BaconReader
  7. BOOL shouldHaveBRUndeleteAction = NO;
  8. NSString *tfPostSelftext;
  9. NSString *tfPostAuthor;
  10. id tfCommentCellView;
  11. id tfStoryController;
  12. %hook CommentCell
  13. %end
  14. %hook CommentCellView
  15. %end
  16. %hook StoryDetailView
  17. %end
  18. %hook BRComment
  19. -(BOOL) contains_htmlValue{
  20. return YES;
  21. }
  22. -(BOOL) primitiveContains_htmlValue{
  23. return YES;
  24. }
  25. -(BOOL) is_deletedValue{
  26. return NO;
  27. }
  28. -(BOOL) primitiveIs_deletedValue{
  29. return NO;
  30. }
  31. %end
  32. %hook BRStory
  33. +(id) storyWithDictionary:(id) arg1 inContext:(id) arg2 {
  34. id orig = %orig;
  35. if (tfPostSelftext){
  36. [orig setSelftext_html:tfPostSelftext];
  37. [orig setAuthor:tfPostAuthor];
  38. tfPostSelftext = nil;
  39. tfPostAuthor = nil;
  40. }
  41. return orig;
  42. }
  43. %end
  44. %hook UIViewController
  45. -(void) presentViewController:(id) arg1 animated:(BOOL) arg2 completion:(id) arg3 {
  46. if ([arg1 isKindOfClass:[UIAlertController class]] && shouldHaveBRUndeleteAction){
  47. UIAlertAction *undeleteAction;
  48. if (tfCommentCellView){
  49. undeleteAction = [UIAlertAction actionWithTitle:@"TF Did That Say?" style:nil handler:^(UIAlertAction* action){[tfStoryController handleUndeleteCommentAction];}];
  50. } else {
  51. undeleteAction = [UIAlertAction actionWithTitle:@"TF Did That Say?" style:nil handler:^(UIAlertAction* action){[tfStoryController handleUndeletePostAction];}];
  52. }
  53. [arg1 addAction:undeleteAction];
  54. }
  55. %orig;
  56. }
  57. %end
  58. %hook StoryDetailViewController
  59. -(void) showMoreCommentActions:(id) arg1 showAll:(BOOL) arg2 {
  60. NSString *commentBody = [[arg1 comment] body];
  61. if ((isBaconReaderDeletedOnly && ([commentBody isEqualToString:@"[deleted]"] || [commentBody isEqualToString:@"[removed]"])) || !isBaconReaderDeletedOnly) {
  62. shouldHaveBRUndeleteAction = YES;
  63. tfCommentCellView = arg1;
  64. tfStoryController = self;
  65. tfPostSelftext = nil;
  66. }
  67. %orig;
  68. shouldHaveBRUndeleteAction = NO;
  69. }
  70. -(void) menuTouchedWithSender:(id) arg1 {
  71. if ([[self story] is_selfValue]){
  72. NSString *postBody = [[self story] selftext];
  73. if ((isBaconReaderDeletedOnly && ([postBody isEqualToString:@"[deleted]"] || [postBody isEqualToString:@"[removed]"])) || !isBaconReaderDeletedOnly) {
  74. shouldHaveBRUndeleteAction = YES;
  75. tfCommentCellView = nil;
  76. tfStoryController = self;
  77. tfPostSelftext = nil;
  78. }
  79. }
  80. %orig;
  81. shouldHaveBRUndeleteAction = NO;
  82. }
  83. %new
  84. -(void) handleUndeleteCommentAction{
  85. id comment = [tfCommentCellView comment];
  86. NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
  87. NSOperationQueue *queue = [[NSOperationQueue alloc] init];
  88. [request setURL:[NSURL URLWithString:[NSString stringWithFormat:@"https://api.pushshift.io/reddit/search/comment/?ids=%@&fields=author,body", [comment serverID]]]];
  89. [request setHTTPMethod:@"GET"];
  90. [request setTimeoutInterval:pushshiftRequestTimeoutValue];
  91. [NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
  92. NSString *author = @"[author]";
  93. NSString *body = @"[body]";
  94. if (data != nil && error == nil){
  95. id jsonData = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
  96. if ([[jsonData objectForKey:@"data"] count] != 0){
  97. author = [[jsonData objectForKey:@"data"][0] objectForKey:@"author"];
  98. body = [[jsonData objectForKey:@"data"][0] objectForKey:@"body"];
  99. if ([body isEqualToString:@"[deleted]"] || [body isEqualToString:@"[removed]"]){
  100. body = @"[pushshift was unable to archive this]";
  101. }
  102. } else {
  103. body = @"[pushshift has not archived this yet]";
  104. }
  105. } else if (error != nil || data == nil){
  106. body = [NSString stringWithFormat:@"[an error occured while attempting to contact pushshift api (%@)]", [error localizedDescription]];
  107. }
  108. [comment setAuthor:author];
  109. [comment setBody:body];
  110. [comment setBody_html:[%c(MMMarkdown) HTMLStringWithMarkdown:body extensions:MMMarkdownExtensionsGitHubFlavored error:nil]];
  111. [comment setAttributedDescriptionString:nil];
  112. NSAttributedString *commentAttrString = [%c(BRUtils) attributedDescriptionForComment:comment];
  113. [comment setAttributedDescriptionString:commentAttrString];
  114. [[[self detailPage] tableView] performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO];
  115. }];
  116. }
  117. %new
  118. -(void) handleUndeletePostAction{
  119. id post = [self story];
  120. NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
  121. NSOperationQueue *queue = [[NSOperationQueue alloc] init];
  122. [request setURL:[NSURL URLWithString:[NSString stringWithFormat:@"https://api.pushshift.io/reddit/search/submission/?ids=%@&fields=author,selftext", [post serverID]]]];
  123. [request setHTTPMethod:@"GET"];
  124. [request setTimeoutInterval:pushshiftRequestTimeoutValue];
  125. [NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
  126. NSString *author = @"[author]";
  127. NSString *body = @"[body]";
  128. if (data != nil && error == nil){
  129. id jsonData = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
  130. if ([[jsonData objectForKey:@"data"] count] != 0){
  131. author = [[jsonData objectForKey:@"data"][0] objectForKey:@"author"];
  132. body = [[jsonData objectForKey:@"data"][0] objectForKey:@"selftext"];
  133. if ([body isEqualToString:@"[deleted]"] || [body isEqualToString:@"[removed]"]){
  134. body = @"[pushshift was unable to archive this]";
  135. }
  136. } else {
  137. body = @"[pushshift has not archived this yet]";
  138. }
  139. } else if (error != nil || data == nil){
  140. body = [NSString stringWithFormat:@"[an error occured while attempting to contact pushshift api (%@)]", [error localizedDescription]];
  141. }
  142. tfPostAuthor = author;
  143. tfPostSelftext = [%c(MMMarkdown) HTMLStringWithMarkdown:body extensions:MMMarkdownExtensionsGitHubFlavored error:nil];
  144. [[self detailPage] performSelectorOnMainThread:@selector(refreshTouched) withObject:nil waitUntilDone:NO];
  145. }];
  146. }
  147. %end
  148. %end
  149. static void loadPrefs(){
  150. NSMutableDictionary *prefs = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/User/Library/Preferences/com.lint.undelete.prefs.plist"];
  151. if (prefs){
  152. if ([prefs objectForKey:@"isBaconReaderEnabled"] != nil){
  153. isBaconReaderEnabled = [[prefs objectForKey:@"isBaconReaderEnabled"] boolValue];
  154. } else {
  155. isBaconReaderEnabled = YES;
  156. }
  157. if ([prefs objectForKey:@"requestTimeoutValue"] != nil){
  158. pushshiftRequestTimeoutValue = [[prefs objectForKey:@"requestTimeoutValue"] doubleValue];
  159. } else {
  160. pushshiftRequestTimeoutValue = 10;
  161. }
  162. if ([prefs objectForKey:@"isBaconReaderDeletedOnly"] != nil) {
  163. isBaconReaderDeletedOnly = [[prefs objectForKey:@"isBaconReaderDeletedOnly"] boolValue];
  164. } else {
  165. isBaconReaderDeletedOnly = YES;
  166. }
  167. } else {
  168. isBaconReaderEnabled = YES;
  169. isBaconReaderDeletedOnly = YES;
  170. pushshiftRequestTimeoutValue = 10;
  171. }
  172. }
  173. static void prefsChanged(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) {
  174. loadPrefs();
  175. }
  176. %ctor {
  177. loadPrefs();
  178. NSString* processName = [[NSProcessInfo processInfo] processName];
  179. if ([processName isEqualToString:@"BaconReader"]){
  180. if (isBaconReaderEnabled){
  181. CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), NULL, prefsChanged, CFSTR("com.lint.undelete.prefs.changed"), NULL, CFNotificationSuspensionBehaviorDeliverImmediately);
  182. %init(BaconReader, StoryDetailView = objc_getClass("BaconReader.StoryDetailView"), StoryDetailViewController = objc_getClass("BaconReader.StoryDetailViewController"), CommentCellView = objc_getClass("BaconReader.CommentCellView"), CommentCell = objc_getClass("BaconReader.CommentCell"));
  183. }
  184. }
  185. }