PageRenderTime 56ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 1ms

/MGTwitterEngine/MGTwitterEngine.m

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