Implement the Retweet API. This means checking home_timeline and sending proper retweet messages. Fixes #12556.
On an annoying note, home_timeline (despite saying "Returns the 20 most recent statuses, including retweets, posted by the authenticating user and that user's friends") does not include outgoing retweets. This will either be fixed by Twitter quickly, or not. I'm tired of Twitter's inconsistent and buggy API. So, as such, there's currently no way to remove a retweet done by yourself.
1.1 --- a/Plugins/Twitter Plugin/AITwitterAccount.h Wed Nov 11 22:27:35 2009 -0500
1.2 +++ b/Plugins/Twitter Plugin/AITwitterAccount.h Thu Nov 19 21:12:23 2009 -0500
1.3 @@ -182,6 +182,7 @@
1.4 location:(NSString *)location
1.5 description:(NSString *)description;
1.6
1.7 +- (void)retweetTweet:(NSString *)tweetID;
1.8 - (void)toggleFavoriteTweet:(NSString *)tweetID;
1.9 - (void)destroyTweet:(NSString *)tweetID;
1.10 - (void)destroyDirectMessage:(NSString *)messageID
2.1 --- a/Plugins/Twitter Plugin/AITwitterAccount.m Wed Nov 11 22:27:35 2009 -0500
2.2 +++ b/Plugins/Twitter Plugin/AITwitterAccount.m Thu Nov 19 21:12:23 2009 -0500
2.3 @@ -1195,6 +1195,25 @@
2.4 }
2.5
2.6 /*!
2.7 + * @brief Retweet the selected tweet.
2.8 + *
2.9 + * Attempts to retweet a tweet.
2.10 + * Prints a status message in the chat on success/failure, behaves identical to sending a new tweet.
2.11 + */
2.12 +- (void)retweetTweet:(NSString *)tweetID
2.13 +{
2.14 + NSString *requestID = [twitterEngine retweetUpdate:tweetID];
2.15 +
2.16 + if (requestID) {
2.17 + [self setRequestType:AITwitterSendUpdate
2.18 + forRequestID:requestID
2.19 + withDictionary:[NSDictionary dictionaryWithObjectsAndKeys:tweetID, @"tweetID", nil]];
2.20 + } else {
2.21 + [self.timelineChat receivedError:[NSNumber numberWithInt:AIChatMessageSendingConnectionError]];
2.22 + }
2.23 +}
2.24 +
2.25 +/*!
2.26 * @brief Toggle the favorite status for a tweet.
2.27 *
2.28 * Attempts to favorite a tweet. If that fails, it removes favorite status.
2.29 @@ -1708,7 +1727,7 @@
2.30 AIChat *chat = [[self dictionaryForRequestID:identifier] objectForKey:@"Chat"];
2.31
2.32 if (chat) {
2.33 - [chat receivedError:[NSNumber numberWithInt:AIChatUnknownError]];
2.34 + [chat receivedError:[NSNumber numberWithInt:AIChatMessageSendingConnectionError]];
2.35
2.36 AILogWithSignature(@"%@ Chat send error on %@", self, chat);
2.37 }
2.38 @@ -1896,7 +1915,7 @@
2.39 * @brief Status updates received
2.40 */
2.41 - (void)statusesReceived:(NSArray *)statuses forRequest:(NSString *)identifier
2.42 -{
2.43 +{
2.44 if([self requestTypeForRequestID:identifier] == AITwitterUpdateFollowedTimeline ||
2.45 [self requestTypeForRequestID:identifier] == AITwitterUpdateReplies) {
2.46 NSString *lastID;
3.1 --- a/Plugins/Twitter Plugin/AITwitterURLHandler.m Wed Nov 11 22:27:35 2009 -0500
3.2 +++ b/Plugins/Twitter Plugin/AITwitterURLHandler.m Thu Nov 19 21:12:23 2009 -0500
3.3 @@ -98,8 +98,10 @@
3.4 // No exact match. Fail.
3.5 return;
3.6 }
3.7 -
3.8 - if ([inAction isEqualToString:@"reply"] || [inAction isEqualToString:@"retweet"]) {
3.9 +
3.10 + if ([inAction isEqualToString:@"retweet"]) {
3.11 + [account retweetTweet:inTweet];
3.12 + } else if ([inAction isEqualToString:@"reply"]) {
3.13 AIChat *timelineChat = [adium.chatController existingChatWithName:account.timelineChatName
3.14 onAccount:account];
3.15
3.16 @@ -120,13 +122,7 @@
3.17 AIMessageEntryTextView *textView = ((AIMessageTabViewItem *)[timelineChat valueForProperty:@"MessageTabViewItem"]).messageViewController.textEntryView;
3.18
3.19 // Insert the @reply text
3.20 - NSString *prefix = nil;
3.21 -
3.22 - if (inMessage) {
3.23 - prefix = [NSString stringWithFormat:@"RT @%@ %@", inUser, [inMessage stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
3.24 - } else {
3.25 - prefix = [NSString stringWithFormat:@"@%@ ", inUser];
3.26 - }
3.27 + NSString *prefix = [NSString stringWithFormat:@"@%@ ", inUser];
3.28
3.29 if (![textView.string hasPrefix:prefix]) {
3.30 NSMutableAttributedString *newString;
4.1 --- a/Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterEngine.h Wed Nov 11 22:27:35 2009 -0500
4.2 +++ b/Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterEngine.h Thu Nov 19 21:12:23 2009 -0500
4.3 @@ -128,6 +128,7 @@
4.4 - (NSString *)sendUpdate:(NSString *)status inReplyTo:(NSString *)updateID;
4.5 - (NSString *)deleteUpdate:(NSString *)updateID; // this user must be the AUTHOR
4.6 - (NSString *)markUpdate:(NSString *)updateID asFavorite:(BOOL)flag;
4.7 +- (NSString *)retweetUpdate:(NSString *)updateID;
4.8
4.9 // Sending and editing direct messages
4.10 - (NSString *)sendDirectMessage:(NSString *)message to:(NSString *)username;
5.1 --- a/Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterEngine.m Wed Nov 11 22:27:35 2009 -0500
5.2 +++ b/Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterEngine.m Thu Nov 19 21:12:23 2009 -0500
5.3 @@ -934,7 +934,7 @@
5.4
5.5 - (NSString *)getFollowedTimelineFor:(NSString *)username since:(NSDate *)date startingAtPage:(int)pageNum count:(int)count
5.6 {
5.7 - NSString *path = @"statuses/friends_timeline.xml";
5.8 + NSString *path = @"statuses/home_timeline.xml";
5.9
5.10 NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
5.11 if (date) {
5.12 @@ -944,7 +944,7 @@
5.13 [params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
5.14 }
5.15 if (username) {
5.16 - path = [NSString stringWithFormat:@"statuses/friends_timeline/%@.xml", username];
5.17 + path = [NSString stringWithFormat:@"statuses/home_timeline/%@.xml", username];
5.18 }
5.19 int tweetCount = DEFAULT_TWEET_COUNT;
5.20 if (count > 0) {
5.21 @@ -960,7 +960,7 @@
5.22
5.23 - (NSString *)getFollowedTimelineFor:(NSString *)username sinceID:(NSString *)updateID startingAtPage:(int)pageNum count:(int)count
5.24 {
5.25 - NSString *path = @"statuses/friends_timeline.xml";
5.26 + NSString *path = @"statuses/home_timeline.xml";
5.27
5.28 NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
5.29 if (updateID > 0) {
5.30 @@ -970,14 +970,14 @@
5.31 [params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
5.32 }
5.33 if (username) {
5.34 - path = [NSString stringWithFormat:@"statuses/friends_timeline/%@.xml", username];
5.35 + path = [NSString stringWithFormat:@"statuses/home_timeline/%@.xml", username];
5.36 }
5.37 int tweetCount = DEFAULT_TWEET_COUNT;
5.38 if (count > 0) {
5.39 tweetCount = count;
5.40 }
5.41 [params setObject:[NSString stringWithFormat:@"%d", tweetCount] forKey:@"count"];
5.42 -
5.43 +
5.44 return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
5.45 requestType:MGTwitterStatusesRequest
5.46 responseType:MGTwitterStatuses];
5.47 @@ -1318,6 +1318,14 @@
5.48 responseType:MGTwitterStatus];
5.49 }
5.50
5.51 +- (NSString *)retweetUpdate:(NSString *)updateID
5.52 +{
5.53 + NSString *path = [NSString stringWithFormat:@"statuses/retweet/%@.xml", updateID];
5.54 +
5.55 + return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path queryParameters:nil body:nil
5.56 + requestType:MGTwitterAccountRequest
5.57 + responseType:MGTwitterStatus];
5.58 +}
5.59
5.60 #pragma mark Sending and editing direct messages
5.61
6.1 --- a/Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterStatusesParser.m Wed Nov 11 22:27:35 2009 -0500
6.2 +++ b/Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterStatusesParser.m Thu Nov 19 21:12:23 2009 -0500
6.3 @@ -26,12 +26,17 @@
6.4 // Make new entry in parsedObjects.
6.5 NSMutableDictionary *newNode = [NSMutableDictionary dictionaryWithCapacity:0];
6.6 [parsedObjects addObject:newNode];
6.7 - currentNode = newNode;
6.8 + currentNode = newNode;
6.9 } else if ([elementName isEqualToString:@"user"]) {
6.10 // Add a 'user' dictionary to current node.
6.11 NSMutableDictionary *newNode = [NSMutableDictionary dictionaryWithCapacity:0];
6.12 [currentNode setObject:newNode forKey:elementName];
6.13 currentNode = newNode;
6.14 + } else if ([elementName isEqualToString:@"retweeted_status"]) {
6.15 + // Add a 'retweeted_status' dictionary to current node.
6.16 + NSMutableDictionary *newNode = [NSMutableDictionary dictionaryWithCapacity:0];
6.17 + [currentNode setObject:newNode forKey:elementName];
6.18 + currentNode = newNode;
6.19 } else if (currentNode) {
6.20 // Create relevant name-value pair.
6.21 [currentNode setObject:[NSMutableString string] forKey:elementName];
6.22 @@ -54,7 +59,7 @@
6.23 {
6.24 [super parser:theParser didEndElement:elementName namespaceURI:namespaceURI qualifiedName:qName];
6.25
6.26 - if ([elementName isEqualToString:@"user"]) {
6.27 + if ([elementName isEqualToString:@"user"] || [elementName isEqualToString:@"retweeted_status"]) {
6.28 currentNode = [parsedObjects lastObject];
6.29 } else if ([elementName isEqualToString:@"status"]) {
6.30 [self addSource];