@@ -77,7 +77,7 @@ class Twitter {
7777
7878 static final FFCache _cache = FFCache ();
7979
80- static const searchTweetsGraphqlUriPath = '/graphql/nK1dw4oV3k4w5TdtcAdSww/SearchTimeline' ;
80+ static const graphqlSearchTimelineUriPath = '/graphql/nK1dw4oV3k4w5TdtcAdSww/SearchTimeline' ;
8181 static const searchTweetsUriPath = '/1.1/search/tweets.json' ;
8282
8383 static final Map <String , String > defaultParams = {
@@ -365,7 +365,7 @@ class Twitter {
365365 variables['cursor' ] = cursor;
366366 }
367367
368- var uri = Uri .https ('api.twitter.com' , searchTweetsGraphqlUriPath , {
368+ var uri = Uri .https ('api.twitter.com' , graphqlSearchTimelineUriPath , {
369369 'variables' : jsonEncode (variables),
370370 'features' : jsonEncode (defaultFeatures)
371371 });
@@ -511,14 +511,57 @@ class Twitter {
511511 }
512512
513513 List result = json.decode (response.body);
514-
515514 if (result.isEmpty) {
516515 return [];
517516 }
518517
519518 return result.map ((e) => UserWithExtra .fromJson (e)).toList ();
520519 }
521520
521+ static Future <List <UserWithExtra >> searchUsersGraphql (String query, {int limit = 25 , String ? cursor}) async {
522+ var variables = {
523+ "rawQuery" : query,
524+ "count" : limit.toString (),
525+ "product" : 'People' ,
526+ "withDownvotePerspective" : false ,
527+ "withReactionsMetadata" : false ,
528+ "withReactionsPerspective" : false
529+ };
530+
531+ if (cursor != null ) {
532+ variables['cursor' ] = cursor;
533+ }
534+
535+ var uri = Uri .https ('api.twitter.com' , graphqlSearchTimelineUriPath, {
536+ 'variables' : jsonEncode (variables),
537+ 'features' : jsonEncode (defaultFeatures)
538+ });
539+
540+ var response = await _twitterApi.client.get (uri);
541+ if (response.body.isEmpty) {
542+ return [];
543+ }
544+
545+ var result = json.decode (response.body);
546+ if (result.isEmpty) {
547+ return [];
548+ }
549+
550+ List instructions = List .from (result? ['data' ]? ['search_by_raw_query' ]? ['search_timeline' ]? ['timeline' ]? ['instructions' ] ?? []);
551+ if (instructions.isEmpty) {
552+ return [];
553+ }
554+ List addEntries = List .from (instructions.firstWhere ((e) => e['type' ] == 'TimelineAddEntries' , orElse: () => null )? ['entries' ] ?? []);
555+ if (addEntries.isEmpty) {
556+ return [];
557+ }
558+
559+ return addEntries.where ((entry) => entry['entryId' ]? .startsWith ('user-' )).where ((entry) => entry['content' ]? ['itemContent' ]? ['user_results' ]? ['result' ]? ['legacy' ] != null ).map ((entry) {
560+ var res = entry['content' ]['itemContent' ]['user_results' ]['result' ];
561+ return UserWithExtra .fromJson ({...res['legacy' ], 'id_str' : res['rest_id' ], 'ext_is_blue_verified' : res['is_blue_verified' ]});
562+ }).toList ();
563+ }
564+
522565 static Future <List <TrendLocation >> getTrendLocations () async {
523566 var result = await _cache.getOrCreateAsJSON ('trends.locations' , const Duration (days: 2 ), () async {
524567 var locations = await _twitterApi.trendsService.available ();
0 commit comments