PageRenderTime 49ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/MGTwitterEngine.m

https://github.com/lachie/cheepcheep
Objective C | 1049 lines | 741 code | 250 blank | 58 comment | 76 complexity | 97d4405dc5ceca2c0c01d11b29dcc840 MD5 | raw file
  1. //
  2. // MGTwitterEngine.m
  3. // MGTwitterEngine
  4. //
  5. // Created by Matt Gemmell on 10/02/2008.
  6. // Copyright 2008 Magic Aubergine.
  7. //
  8. #import "MGTwitterEngine.h"
  9. #import "MGTwitterStatusesParser.h"
  10. #import "MGTwitterUsersParser.h"
  11. #import "MGTwitterMessagesParser.h"
  12. #import "MGTwitterHTTPURLConnection.h"
  13. #define TWITTER_DOMAIN @"twitter.com"
  14. #define HTTP_POST_METHOD @"POST"
  15. #define MAX_MESSAGE_LENGTH 140 // Twitter recommends tweets of max 140 chars
  16. #define DEFAULT_CLIENT_NAME @"MGTwitterEngine"
  17. #define DEFAULT_CLIENT_VERSION @"1.0"
  18. #define DEFAULT_CLIENT_URL @"http://mattgemmell.com/"
  19. #define URL_REQUEST_TIMEOUT 25.0 // Twitter usually fails quickly if it's going to fail at all.
  20. @interface MGTwitterEngine (PrivateMethods)
  21. // Utility methods
  22. - (NSDateFormatter *)_HTTPDateFormatter;
  23. - (NSString *)_queryStringWithBase:(NSString *)base parameters:(NSDictionary *)params prefixed:(BOOL)prefixed;
  24. - (NSDate *)_HTTPToDate:(NSString *)httpDate;
  25. - (NSString *)_dateToHTTP:(NSDate *)date;
  26. - (NSString *)_encodeString:(NSString *)string;
  27. // Connection/Request methods
  28. - (NSString *)_sendRequestWithMethod:(NSString *)method
  29. path:(NSString *)path
  30. queryParameters:(NSDictionary *)params
  31. body:(NSString *)body
  32. requestType:(MGTwitterRequestType)requestType
  33. responseType:(MGTwitterResponseType)responseType;
  34. // Parsing methods
  35. - (void)_parseXMLForConnection:(MGTwitterHTTPURLConnection *)connection;
  36. @end
  37. @implementation MGTwitterEngine
  38. #pragma mark Constructors
  39. + (MGTwitterEngine *)twitterEngineWithDelegate:(NSObject *)theDelegate
  40. {
  41. return [[[MGTwitterEngine alloc] initWithDelegate:theDelegate] autorelease];
  42. }
  43. - (MGTwitterEngine *)initWithDelegate:(NSObject *)newDelegate
  44. {
  45. if (self = [super init]) {
  46. _delegate = newDelegate; // deliberately weak reference
  47. _connections = [[NSMutableDictionary alloc] initWithCapacity:0];
  48. _clientName = [DEFAULT_CLIENT_NAME retain];
  49. _clientVersion = [DEFAULT_CLIENT_VERSION retain];
  50. _clientURL = [DEFAULT_CLIENT_URL retain];
  51. _secureConnection = YES;
  52. _clearsCookies = NO;
  53. }
  54. return self;
  55. }
  56. - (void)dealloc
  57. {
  58. _delegate = nil;
  59. [[_connections allValues] makeObjectsPerformSelector:@selector(cancel)];
  60. [_connections release];
  61. [_username release];
  62. [_password release];
  63. [_clientName release];
  64. [_clientVersion release];
  65. [_clientURL release];
  66. [_clientSourceToken release];
  67. [super dealloc];
  68. }
  69. #pragma mark Configuration and Accessors
  70. + (NSString *)version
  71. {
  72. // 1.0.0 = 22 Feb 2008
  73. // 1.0.1 = 26 Feb 2008
  74. // 1.0.2 = 04 Mar 2008
  75. // 1.0.3 = 04 Mar 2008
  76. // 1.0.4 = 11 Apr 2008
  77. return @"1.0.4";
  78. }
  79. - (NSString *)username
  80. {
  81. return [[_username retain] autorelease];
  82. }
  83. - (NSString *)password
  84. {
  85. return [[_password retain] autorelease];
  86. }
  87. - (void)setUsername:(NSString *)newUsername password:(NSString *)newPassword
  88. {
  89. // Set new credentials.
  90. [_username release];
  91. _username = [newUsername retain];
  92. [_password release];
  93. _password = [newPassword retain];
  94. if ([self clearsCookies]) {
  95. // Remove all cookies for twitter, to ensure next connection uses new credentials.
  96. NSString *urlString = [NSString stringWithFormat:@"%@://%@",
  97. (_secureConnection) ? @"https" : @"http",
  98. TWITTER_DOMAIN];
  99. NSURL *url = [NSURL URLWithString:urlString];
  100. NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
  101. NSEnumerator *enumerator = [[cookieStorage cookiesForURL:url] objectEnumerator];
  102. NSHTTPCookie *cookie = nil;
  103. while (cookie = [enumerator nextObject]) {
  104. [cookieStorage deleteCookie:cookie];
  105. }
  106. }
  107. }
  108. - (NSString *)clientName
  109. {
  110. return [[_clientName retain] autorelease];
  111. }
  112. - (NSString *)clientVersion
  113. {
  114. return [[_clientVersion retain] autorelease];
  115. }
  116. - (NSString *)clientURL
  117. {
  118. return [[_clientURL retain] autorelease];
  119. }
  120. - (NSString *)clientSourceToken
  121. {
  122. return [[_clientSourceToken retain] autorelease];
  123. }
  124. - (void)setClientName:(NSString *)name version:(NSString *)version URL:(NSString *)url token:(NSString *)token;
  125. {
  126. [_clientName release];
  127. _clientName = [name retain];
  128. [_clientVersion release];
  129. _clientVersion = [version retain];
  130. [_clientURL release];
  131. _clientURL = [url retain];
  132. [_clientSourceToken release];
  133. _clientSourceToken = [token retain];
  134. }
  135. - (BOOL)usesSecureConnection
  136. {
  137. return _secureConnection;
  138. }
  139. - (void)setUsesSecureConnection:(BOOL)flag
  140. {
  141. _secureConnection = flag;
  142. }
  143. - (BOOL)clearsCookies
  144. {
  145. return _clearsCookies;
  146. }
  147. - (void)setClearsCookies:(BOOL)flag
  148. {
  149. _clearsCookies = flag;
  150. }
  151. #pragma mark Connection methods
  152. - (int)numberOfConnections
  153. {
  154. return [_connections count];
  155. }
  156. - (NSArray *)connectionIdentifiers
  157. {
  158. return [_connections allKeys];
  159. }
  160. - (void)closeConnection:(NSString *)identifier
  161. {
  162. MGTwitterHTTPURLConnection *connection = [_connections objectForKey:identifier];
  163. if (connection) {
  164. [connection cancel];
  165. [_connections removeObjectForKey:identifier];
  166. }
  167. }
  168. - (void)closeAllConnections
  169. {
  170. [[_connections allValues] makeObjectsPerformSelector:@selector(cancel)];
  171. [_connections removeAllObjects];
  172. }
  173. #pragma mark Utility methods
  174. - (NSDateFormatter *)_HTTPDateFormatter
  175. {
  176. // Returns a formatter for dates in HTTP format (i.e. RFC 822, updated by RFC 1123).
  177. // e.g. "Sun, 06 Nov 1994 08:49:37 GMT"
  178. return [[[NSDateFormatter alloc]
  179. initWithDateFormat:@"%a, %d %b %Y %H:%M:%S GMT"
  180. allowNaturalLanguage:NO] autorelease];
  181. }
  182. - (NSString *)_queryStringWithBase:(NSString *)base parameters:(NSDictionary *)params prefixed:(BOOL)prefixed
  183. {
  184. // Append base if specified.
  185. NSMutableString *str = [NSMutableString stringWithCapacity:0];
  186. if (base) {
  187. [str appendString:base];
  188. }
  189. // Append each name-value pair.
  190. if (params) {
  191. int i;
  192. NSArray *names = [params allKeys];
  193. for (i = 0; i < [names count]; i++) {
  194. if (i == 0 && prefixed) {
  195. [str appendString:@"?"];
  196. } else if (i > 0) {
  197. [str appendString:@"&"];
  198. }
  199. NSString *name = [names objectAtIndex:i];
  200. [str appendString:[NSString stringWithFormat:@"%@=%@",
  201. name, [self _encodeString:[params objectForKey:name]]]];
  202. }
  203. }
  204. return str;
  205. }
  206. - (NSDate *)_HTTPToDate:(NSString *)httpDate
  207. {
  208. NSDateFormatter *dateFormatter = [self _HTTPDateFormatter];
  209. return [dateFormatter dateFromString:httpDate];
  210. }
  211. - (NSString *)_dateToHTTP:(NSDate *)date
  212. {
  213. NSDateFormatter *dateFormatter = [self _HTTPDateFormatter];
  214. return [dateFormatter stringFromDate:date];
  215. }
  216. - (NSString *)_encodeString:(NSString *)string
  217. {
  218. NSMutableString *tempStr = [NSMutableString stringWithString:string];
  219. [tempStr replaceOccurrencesOfString:@" "
  220. withString:@"+"
  221. options:NSLiteralSearch
  222. range:NSMakeRange(0, [string length])];
  223. CFStringRef strRef = CFURLCreateStringByAddingPercentEscapes(NULL,
  224. (CFStringRef)tempStr,
  225. NULL,
  226. (CFStringRef)@";/?:@&=$,",
  227. kCFStringEncodingUTF8);
  228. NSString *replaced = [NSString stringWithString:(NSString *)strRef];
  229. CFRelease(strRef);
  230. return replaced;
  231. }
  232. - (NSString *)getImageAtURL:(NSString *)urlString
  233. {
  234. // This is a method implemented for the convenience of the client,
  235. // allowing asynchronous downloading of users' Twitter profile images.
  236. NSURL *url = [NSURL URLWithString:urlString];
  237. if (!url) {
  238. return nil;
  239. }
  240. // Construct an NSMutableURLRequest for the URL and set appropriate request method.
  241. NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:url
  242. cachePolicy:NSURLRequestReloadIgnoringCacheData
  243. timeoutInterval:URL_REQUEST_TIMEOUT];
  244. // Create a connection using this request, with the default timeout and caching policy,
  245. // and appropriate Twitter request and response types for parsing and error reporting.
  246. MGTwitterHTTPURLConnection *connection;
  247. connection = [[MGTwitterHTTPURLConnection alloc] initWithRequest:theRequest
  248. delegate:self
  249. requestType:MGTwitterImageRequest
  250. responseType:MGTwitterImage];
  251. if (!connection) {
  252. return nil;
  253. } else {
  254. [_connections setObject:connection forKey:[connection identifier]];
  255. [connection release];
  256. }
  257. return [connection identifier];
  258. }
  259. #pragma mark Request sending methods
  260. - (NSString *)_sendRequestWithMethod:(NSString *)method
  261. path:(NSString *)path
  262. queryParameters:(NSDictionary *)params
  263. body:(NSString *)body
  264. requestType:(MGTwitterRequestType)requestType
  265. responseType:(MGTwitterResponseType)responseType
  266. {
  267. // Construct appropriate URL string.
  268. NSString *fullPath = path;
  269. if (params) {
  270. fullPath = [self _queryStringWithBase:fullPath parameters:params prefixed:YES];
  271. }
  272. NSString *urlString = [NSString stringWithFormat:@"%@://%@:%@@%@/%@",
  273. (_secureConnection) ? @"https" : @"http",
  274. [self _encodeString:_username], [self _encodeString:_password],
  275. TWITTER_DOMAIN, fullPath];
  276. NSLog(@"url: %@",urlString);
  277. // TODO remove!
  278. // urlString = @"http://0.0.0.0:8000/eg.xml";
  279. NSURL *finalURL = [NSURL URLWithString:urlString];
  280. if (!finalURL) {
  281. return nil;
  282. }
  283. // Construct an NSMutableURLRequest for the URL and set appropriate request method.
  284. NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:finalURL
  285. cachePolicy:NSURLRequestReloadIgnoringCacheData
  286. timeoutInterval:URL_REQUEST_TIMEOUT];
  287. if (method) {
  288. [theRequest setHTTPMethod:method];
  289. }
  290. [theRequest setHTTPShouldHandleCookies:NO];
  291. // Set headers for client information, for tracking purposes at Twitter.
  292. [theRequest setValue:_clientName forHTTPHeaderField:@"X-Twitter-Client"];
  293. [theRequest setValue:_clientVersion forHTTPHeaderField:@"X-Twitter-Client-Version"];
  294. [theRequest setValue:_clientURL forHTTPHeaderField:@"X-Twitter-Client-URL"];
  295. // Set the request body if this is a POST request.
  296. BOOL isPOST = (method && [method isEqualToString:HTTP_POST_METHOD]);
  297. if (isPOST) {
  298. // Set request body, if specified (hopefully so), with 'source' parameter if appropriate.
  299. NSString *finalBody = [@"" stringByAppendingString:body];
  300. if (_clientSourceToken) {
  301. finalBody = [finalBody stringByAppendingString:[NSString stringWithFormat:@"%@source=%@",
  302. (body) ? @"&" : @"?" ,
  303. _clientSourceToken]];
  304. }
  305. if (finalBody) {
  306. [theRequest setHTTPBody:[finalBody dataUsingEncoding:NSUTF8StringEncoding]];
  307. }
  308. }
  309. // Create a connection using this request, with the default timeout and caching policy,
  310. // and appropriate Twitter request and response types for parsing and error reporting.
  311. MGTwitterHTTPURLConnection *connection;
  312. connection = [[MGTwitterHTTPURLConnection alloc] initWithRequest:theRequest
  313. delegate:self
  314. requestType:requestType
  315. responseType:responseType];
  316. if (!connection) {
  317. return nil;
  318. } else {
  319. [_connections setObject:connection forKey:[connection identifier]];
  320. [connection release];
  321. }
  322. return [connection identifier];
  323. }
  324. #pragma mark Parsing methods
  325. - (void)_parseXMLForConnection:(MGTwitterHTTPURLConnection *)connection
  326. {
  327. NSString *identifier = [[[connection identifier] copy] autorelease];
  328. NSData *xmlData = [[[connection data] copy] autorelease];
  329. MGTwitterRequestType requestType = [connection requestType];
  330. MGTwitterResponseType responseType = [connection responseType];
  331. // Determine which type of parser to use.
  332. switch (responseType) {
  333. case MGTwitterStatuses:
  334. case MGTwitterStatus:
  335. [MGTwitterStatusesParser parserWithXML:xmlData delegate:self
  336. connectionIdentifier:identifier requestType:requestType
  337. responseType:responseType];
  338. break;
  339. case MGTwitterUsers:
  340. case MGTwitterUser:
  341. [MGTwitterUsersParser parserWithXML:xmlData delegate:self
  342. connectionIdentifier:identifier requestType:requestType
  343. responseType:responseType];
  344. break;
  345. case MGTwitterDirectMessages:
  346. case MGTwitterDirectMessage:
  347. [MGTwitterMessagesParser parserWithXML:xmlData delegate:self
  348. connectionIdentifier:identifier requestType:requestType
  349. responseType:responseType];
  350. break;
  351. default:
  352. break;
  353. }
  354. }
  355. #pragma mark MGTwitterParserDelegate methods
  356. - (void)parsingSucceededForRequest:(NSString *)identifier
  357. ofResponseType:(MGTwitterResponseType)responseType
  358. withParsedObjects:(NSArray *)parsedObjects
  359. {
  360. // Forward appropriate message to _delegate, depending on responseType.
  361. switch (responseType) {
  362. case MGTwitterStatuses:
  363. case MGTwitterStatus:
  364. [_delegate statusesReceived:parsedObjects forRequest:identifier];
  365. break;
  366. case MGTwitterUsers:
  367. case MGTwitterUser:
  368. [_delegate userInfoReceived:parsedObjects forRequest:identifier];
  369. break;
  370. case MGTwitterDirectMessages:
  371. case MGTwitterDirectMessage:
  372. [_delegate directMessagesReceived:parsedObjects forRequest:identifier];
  373. break;
  374. default:
  375. break;
  376. }
  377. }
  378. - (void)parsingFailedForRequest:(NSString *)requestIdentifier
  379. ofResponseType:(MGTwitterResponseType)responseType
  380. withError:(NSError *)error
  381. {
  382. [_delegate requestFailed:requestIdentifier withError:error];
  383. }
  384. #pragma mark NSURLConnection delegate methods
  385. - (void)connection:(MGTwitterHTTPURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
  386. {
  387. // This method is called when the server has determined that it has enough information to create the NSURLResponse.
  388. // it can be called multiple times, for example in the case of a redirect, so each time we reset the data.
  389. [connection resetDataLength];
  390. // Get response code.
  391. NSHTTPURLResponse *resp = (NSHTTPURLResponse *)response;
  392. int statusCode = [resp statusCode];
  393. if (statusCode >= 400) {
  394. // Assume failure, and report to delegate.
  395. NSError *error = [NSError errorWithDomain:@"HTTP" code:statusCode userInfo:nil];
  396. [_delegate requestFailed:[connection identifier] withError:error];
  397. // Destroy the connection.
  398. [connection cancel];
  399. [_connections removeObjectForKey:[connection identifier]];
  400. } else if (statusCode == 304 || [connection responseType] == MGTwitterGeneric) {
  401. // Not modified, or generic success.
  402. [_delegate requestSucceeded:[connection identifier]];
  403. if (statusCode == 304) {
  404. [self parsingSucceededForRequest:[connection identifier]
  405. ofResponseType:[connection responseType]
  406. withParsedObjects:[NSArray array]];
  407. }
  408. // Destroy the connection.
  409. [connection cancel];
  410. [_connections removeObjectForKey:[connection identifier]];
  411. }
  412. if (NO) {
  413. // Display headers for debugging.
  414. NSHTTPURLResponse *resp = (NSHTTPURLResponse *)response;
  415. NSLog(@"(%d) [%@]:\r%@",
  416. [resp statusCode],
  417. [NSHTTPURLResponse localizedStringForStatusCode:[resp statusCode]],
  418. [resp allHeaderFields]);
  419. }
  420. }
  421. - (void)connection:(MGTwitterHTTPURLConnection *)connection didReceiveData:(NSData *)data
  422. {
  423. // Append the new data to the receivedData.
  424. [connection appendData:data];
  425. }
  426. - (void)connection:(MGTwitterHTTPURLConnection *)connection didFailWithError:(NSError *)error
  427. {
  428. // Inform delegate.
  429. [_delegate requestFailed:[connection identifier] withError:error];
  430. // Release the connection.
  431. [_connections removeObjectForKey:[connection identifier]];
  432. }
  433. - (void)connectionDidFinishLoading:(MGTwitterHTTPURLConnection *)connection
  434. {
  435. // Inform delegate.
  436. [_delegate requestSucceeded:[connection identifier]];
  437. NSData *receivedData = [connection data];
  438. if (receivedData) {
  439. if (NO) {
  440. // Dump data as string for debugging.
  441. NSString *dataString = [NSString stringWithUTF8String:[receivedData bytes]];
  442. NSLog(@"Succeeded! Received %d bytes of data:\r\r%@", [receivedData length], dataString);
  443. }
  444. if (NO) {
  445. // Dump XML to file for debugging.
  446. NSString *dataString = [NSString stringWithUTF8String:[receivedData bytes]];
  447. [dataString writeToFile:[@"~/Desktop/twitter_messages.xml" stringByExpandingTildeInPath]
  448. atomically:NO encoding:NSUnicodeStringEncoding error:NULL];
  449. }
  450. if ([connection responseType] == MGTwitterImage) {
  451. #if TARGET_OS_ASPEN
  452. // Create UIImage from data.
  453. UIImage *image = [[[UIImage alloc] initWithData:[connection data]] autorelease];
  454. #else
  455. // Create NSImage from data.
  456. NSImage *image = [[[NSImage alloc] initWithData:[connection data]] autorelease];
  457. #endif
  458. // Inform delegate.
  459. [_delegate imageReceived:image forRequest:[connection identifier]];
  460. } else {
  461. // Parse XML appropriately.
  462. [self _parseXMLForConnection:connection];
  463. }
  464. }
  465. // Release the connection.
  466. [_connections removeObjectForKey:[connection identifier]];
  467. }
  468. #pragma mark -
  469. #pragma mark Twitter API methods
  470. #pragma mark -
  471. #pragma mark Account methods
  472. - (NSString *)checkUserCredentials
  473. {
  474. NSString *path = @"account/verify_credentials.xml";
  475. return [self _sendRequestWithMethod:nil path:path queryParameters:nil body:nil
  476. requestType:MGTwitterAccountRequest
  477. responseType:MGTwitterGeneric];
  478. }
  479. - (NSString *)endUserSession
  480. {
  481. NSString *path = @"account/end_session"; // deliberately no format specified
  482. return [self _sendRequestWithMethod:nil path:path queryParameters:nil body:nil
  483. requestType:MGTwitterAccountRequest
  484. responseType:MGTwitterGeneric];
  485. }
  486. - (NSString *)enableUpdatesFor:(NSString *)username
  487. {
  488. // i.e. follow
  489. if (!username) {
  490. return nil;
  491. }
  492. NSString *path = [NSString stringWithFormat:@"friendships/create/%@.xml", username];
  493. return [self _sendRequestWithMethod:nil path:path queryParameters:nil body:nil
  494. requestType:MGTwitterAccountRequest
  495. responseType:MGTwitterUser];
  496. }
  497. - (NSString *)disableUpdatesFor:(NSString *)username
  498. {
  499. // i.e. no longer follow
  500. if (!username) {
  501. return nil;
  502. }
  503. NSString *path = [NSString stringWithFormat:@"friendships/destroy/%@.xml", username];
  504. return [self _sendRequestWithMethod:nil path:path queryParameters:nil body:nil
  505. requestType:MGTwitterAccountRequest
  506. responseType:MGTwitterUser];
  507. }
  508. - (NSString *)enableNotificationsFor:(NSString *)username
  509. {
  510. if (!username) {
  511. return nil;
  512. }
  513. NSString *path = [NSString stringWithFormat:@"notifications/follow/%@.xml", username];
  514. return [self _sendRequestWithMethod:nil path:path queryParameters:nil body:nil
  515. requestType:MGTwitterAccountRequest
  516. responseType:MGTwitterUser];
  517. }
  518. - (NSString *)disableNotificationsFor:(NSString *)username
  519. {
  520. if (!username) {
  521. return nil;
  522. }
  523. NSString *path = [NSString stringWithFormat:@"notifications/leave/%@.xml", username];
  524. return [self _sendRequestWithMethod:nil path:path queryParameters:nil body:nil
  525. requestType:MGTwitterAccountRequest
  526. responseType:MGTwitterUser];
  527. }
  528. #pragma mark Retrieving updates
  529. - (NSString *)getFollowedTimelineFor:(NSString *)username since:(NSDate *)date startingAtPage:(int)pageNum
  530. {
  531. NSString *path = @"statuses/friends_timeline.xml";
  532. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
  533. if (date) {
  534. [params setObject:[self _dateToHTTP:date] forKey:@"since"];
  535. }
  536. if (pageNum > 0) {
  537. [params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
  538. }
  539. if (username) {
  540. path = [NSString stringWithFormat:@"statuses/friends_timeline/%@.xml", username];
  541. }
  542. return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
  543. requestType:MGTwitterStatusesRequest
  544. responseType:MGTwitterStatuses];
  545. }
  546. - (NSString *)getUserTimelineFor:(NSString *)username since:(NSDate *)date count:(int)numUpdates
  547. {
  548. NSString *path = @"statuses/user_timeline.xml";
  549. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
  550. if (date) {
  551. [params setObject:[self _dateToHTTP:date] forKey:@"since"];
  552. }
  553. if (numUpdates > 0) {
  554. [params setObject:[NSString stringWithFormat:@"%d", numUpdates] forKey:@"count"];
  555. }
  556. if (username) {
  557. path = [NSString stringWithFormat:@"statuses/user_timeline/%@.xml", username];
  558. }
  559. return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
  560. requestType:MGTwitterStatusesRequest
  561. responseType:MGTwitterStatuses];
  562. }
  563. - (NSString *)getUserUpdatesArchiveStartingAtPage:(int)pageNum
  564. {
  565. NSString *path = @"account/archive.xml";
  566. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
  567. if (pageNum > 0) {
  568. [params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
  569. }
  570. return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
  571. requestType:MGTwitterStatusesRequest
  572. responseType:MGTwitterStatuses];
  573. }
  574. - (NSString *)getPublicTimelineSinceID:(int)updateID
  575. {
  576. NSString *path = @"statuses/public_timeline.xml";
  577. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
  578. if (updateID > 0) {
  579. [params setObject:[NSString stringWithFormat:@"%d", updateID] forKey:@"since_id"];
  580. }
  581. return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
  582. requestType:MGTwitterStatusesRequest
  583. responseType:MGTwitterStatuses];
  584. }
  585. - (NSString *)getRepliesStartingAtPage:(int)pageNum
  586. {
  587. NSString *path = @"statuses/replies.xml";
  588. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
  589. if (pageNum > 0) {
  590. [params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
  591. }
  592. return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
  593. requestType:MGTwitterRepliesRequest
  594. responseType:MGTwitterStatuses];
  595. }
  596. - (NSString *)getFavoriteUpdatesFor:(NSString *)username startingAtPage:(int)pageNum
  597. {
  598. NSString *path = @"favorites.xml";
  599. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
  600. if (pageNum > 0) {
  601. [params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
  602. }
  603. if (username) {
  604. path = [NSString stringWithFormat:@"favorites/%@.xml", username];
  605. }
  606. return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
  607. requestType:MGTwitterStatusesRequest
  608. responseType:MGTwitterStatuses];
  609. }
  610. - (NSString *)getUpdate:(int)updateID
  611. {
  612. NSString *path = [NSString stringWithFormat:@"statuses/show/%d.xml", updateID];
  613. return [self _sendRequestWithMethod:nil path:path queryParameters:nil body:nil
  614. requestType:MGTwitterStatusesRequest
  615. responseType:MGTwitterStatus];
  616. }
  617. #pragma mark Retrieving direct messages
  618. - (NSString *)getDirectMessagesSince:(NSDate *)date startingAtPage:(int)pageNum
  619. {
  620. NSString *path = @"direct_messages.xml";
  621. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
  622. if (date) {
  623. [params setObject:[self _dateToHTTP:date] forKey:@"since"];
  624. }
  625. if (pageNum > 0) {
  626. [params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
  627. }
  628. return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
  629. requestType:MGTwitterDirectMessagesRequest
  630. responseType:MGTwitterDirectMessages];
  631. }
  632. - (NSString *)getDirectMessagesSinceID:(int)updateID startingAtPage:(int)pageNum
  633. {
  634. NSString *path = @"direct_messages.xml";
  635. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
  636. if (updateID > 0) {
  637. [params setObject:[NSString stringWithFormat:@"%d", updateID] forKey:@"since_id"];
  638. }
  639. if (pageNum > 0) {
  640. [params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
  641. }
  642. return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
  643. requestType:MGTwitterDirectMessagesRequest
  644. responseType:MGTwitterDirectMessages];
  645. }
  646. - (NSString *)getSentDirectMessagesSince:(NSDate *)date startingAtPage:(int)pageNum
  647. {
  648. NSString *path = @"direct_messages/sent.xml";
  649. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
  650. if (date) {
  651. [params setObject:[self _dateToHTTP:date] forKey:@"since"];
  652. }
  653. if (pageNum > 0) {
  654. [params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
  655. }
  656. return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
  657. requestType:MGTwitterDirectMessagesRequest
  658. responseType:MGTwitterDirectMessages];
  659. }
  660. - (NSString *)getSentDirectMessagesSinceID:(int)updateID startingAtPage:(int)pageNum
  661. {
  662. NSString *path = @"direct_messages/sent.xml";
  663. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
  664. if (updateID > 0) {
  665. [params setObject:[NSString stringWithFormat:@"%d", updateID] forKey:@"since_id"];
  666. }
  667. if (pageNum > 0) {
  668. [params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
  669. }
  670. return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
  671. requestType:MGTwitterDirectMessagesRequest
  672. responseType:MGTwitterDirectMessages];
  673. }
  674. #pragma mark Retrieving user information
  675. - (NSString *)getUserInformationFor:(NSString *)username
  676. {
  677. if (!username) {
  678. return nil;
  679. }
  680. NSString *path = [NSString stringWithFormat:@"users/show/%@.xml", username];
  681. return [self _sendRequestWithMethod:nil path:path queryParameters:nil body:nil
  682. requestType:MGTwitterUserInfoRequest
  683. responseType:MGTwitterUser];
  684. }
  685. - (NSString *)getUserInformationForEmail:(NSString *)email
  686. {
  687. NSString *path = @"users/show.xml";
  688. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
  689. if (email) {
  690. [params setObject:email forKey:@"email"];
  691. } else {
  692. return nil;
  693. }
  694. return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
  695. requestType:MGTwitterUserInfoRequest
  696. responseType:MGTwitterUser];
  697. }
  698. - (NSString *)getRecentlyUpdatedFriendsFor:(NSString *)username startingAtPage:(int)pageNum
  699. {
  700. NSString *path = @"statuses/friends.xml";
  701. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
  702. if (username) {
  703. path = [NSString stringWithFormat:@"statuses/friends/%@.xml", username];
  704. }
  705. if (pageNum > 0) {
  706. [params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
  707. }
  708. return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
  709. requestType:MGTwitterUserInfoRequest
  710. responseType:MGTwitterUsers];
  711. }
  712. - (NSString *)getFollowersIncludingCurrentStatus:(BOOL)flag
  713. {
  714. NSString *path = @"statuses/followers.xml";
  715. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
  716. if (!flag) {
  717. [params setObject:@"true" forKey:@"lite"]; // slightly bizarre, but correct.
  718. }
  719. return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
  720. requestType:MGTwitterUserInfoRequest
  721. responseType:MGTwitterUsers];
  722. }
  723. - (NSString *)getFeaturedUsers
  724. {
  725. NSString *path = @"statuses/featured.xml";
  726. return [self _sendRequestWithMethod:nil path:path queryParameters:nil body:nil
  727. requestType:MGTwitterUserInfoRequest
  728. responseType:MGTwitterUsers];
  729. }
  730. #pragma mark Sending and editing updates
  731. - (NSString *)sendUpdate:(NSString *)status
  732. {
  733. return [self sendUpdate:status inReplyTo:nil];
  734. }
  735. - (NSString *)sendUpdate:(NSString *)status inReplyTo:(NSString *)updateID
  736. {
  737. if (!status) {
  738. return nil;
  739. }
  740. NSString *path = @"statuses/update.xml";
  741. NSString *trimmedText = status;
  742. if ([trimmedText length] > MAX_MESSAGE_LENGTH) {
  743. trimmedText = [trimmedText substringToIndex:MAX_MESSAGE_LENGTH];
  744. }
  745. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
  746. [params setObject:trimmedText forKey:@"status"];
  747. if (updateID) {
  748. [params setObject:updateID forKey:@"reference_id"];
  749. }
  750. NSString *body = [self _queryStringWithBase:nil parameters:params prefixed:NO];
  751. return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path
  752. queryParameters:params body:body
  753. requestType:MGTwitterStatusSend
  754. responseType:MGTwitterStatus];
  755. }
  756. - (NSString *)deleteUpdate:(int)updateID
  757. {
  758. NSString *path = [NSString stringWithFormat:@"statuses/destroy/%d.xml", updateID];
  759. return [self _sendRequestWithMethod:nil path:path queryParameters:nil body:nil
  760. requestType:MGTwitterAccountRequest
  761. responseType:MGTwitterGeneric];
  762. }
  763. - (NSString *)markUpdate:(int)updateID asFavorite:(BOOL)flag
  764. {
  765. NSString *path = [NSString stringWithFormat:@"favorites/%@/%d.xml",
  766. (flag) ? @"create" : @"destroy" ,
  767. updateID];
  768. return [self _sendRequestWithMethod:nil path:path queryParameters:nil body:nil
  769. requestType:MGTwitterAccountRequest
  770. responseType:MGTwitterStatus];
  771. }
  772. #pragma mark Sending and editing direct messages
  773. - (NSString *)sendDirectMessage:(NSString *)message to:(NSString *)username
  774. {
  775. if (!message || !username) {
  776. return nil;
  777. }
  778. NSString *path = @"direct_messages/new.xml";
  779. NSString *trimmedText = message;
  780. if ([trimmedText length] > MAX_MESSAGE_LENGTH) {
  781. trimmedText = [trimmedText substringToIndex:MAX_MESSAGE_LENGTH];
  782. }
  783. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
  784. [params setObject:trimmedText forKey:@"text"];
  785. [params setObject:username forKey:@"user"];
  786. NSString *body = [self _queryStringWithBase:nil parameters:params prefixed:NO];
  787. return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path
  788. queryParameters:params body:body
  789. requestType:MGTwitterDirectMessageSend
  790. responseType:MGTwitterDirectMessage];
  791. }
  792. - (NSString *)deleteDirectMessage:(int)updateID
  793. {
  794. NSString *path = [NSString stringWithFormat:@"direct_messages/destroy/%d.xml", updateID];
  795. return [self _sendRequestWithMethod:nil path:path queryParameters:nil body:nil
  796. requestType:MGTwitterAccountRequest
  797. responseType:MGTwitterGeneric];
  798. }
  799. @end