Skip to content

Commit 07aba02

Browse files
committed
Allow enhanced Twitter/X requests for searches as an option in 'Settings/General/X API' subsection.
1 parent 760575e commit 07aba02

File tree

13 files changed

+115
-18
lines changed

13 files changed

+115
-18
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
* Feature: Allow enhanced Twitter/X requests for searches as an option in 'Settings/General/X API' subsection,
2+
although those requests have lower rate limits (50 per 15 min vs 190 per 15 min).
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
* Feature: Allow enhanced Twitter/X requests for searches as an option in 'Settings/General/X API' subsection,
2+
although those requests have lower rate limits (50 per 15 min vs 190 per 15 min).

lib/client.dart

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -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();

lib/generated/intl/messages_en.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,10 @@ class MessageLookup extends MessageLookupByLibrary {
168168
"Enhanced requests for feeds (but with lower rate limits)"),
169169
"enhanced_feeds_label":
170170
MessageLookupByLibrary.simpleMessage("Enhanced feeds"),
171+
"enhanced_searches_description": MessageLookupByLibrary.simpleMessage(
172+
"Enhanced requests for searches (but with lower rate limits)"),
173+
"enhanced_searches_label":
174+
MessageLookupByLibrary.simpleMessage("Enhanced searches"),
171175
"enter_your_twitter_username": MessageLookupByLibrary.simpleMessage(
172176
"Enter your Twitter/X username"),
173177
"export": MessageLookupByLibrary.simpleMessage("Export"),

lib/generated/intl/messages_fr.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,10 @@ class MessageLookup extends MessageLookupByLibrary {
175175
"Requêtes améliorés pour les flux (mais avec des limites plus basses de fréquence)"),
176176
"enhanced_feeds_label":
177177
MessageLookupByLibrary.simpleMessage("Flux améliorés"),
178+
"enhanced_searches_description": MessageLookupByLibrary.simpleMessage(
179+
"Requêtes améliorés pour les recherches (mais avec des limites plus basses de fréquence)"),
180+
"enhanced_searches_label":
181+
MessageLookupByLibrary.simpleMessage("Recherches améliorés"),
178182
"enter_your_twitter_username": MessageLookupByLibrary.simpleMessage(
179183
"Entrer votre nom d\'utilisateur Twitter/X"),
180184
"export": MessageLookupByLibrary.simpleMessage("Exporter"),

lib/generated/l10n.dart

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/group/_feed.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ class SubscriptionGroupFeedState extends State<SubscriptionGroupFeed> with Widge
218218
}
219219

220220
_errorResponse = null;
221-
RateFetchContext fetchContext = RateFetchContext(prefs.get(optionEnhancedFeeds) ? Twitter.searchTweetsGraphqlUriPath : Twitter.searchTweetsUriPath, widget.chunks.length);
221+
RateFetchContext fetchContext = RateFetchContext(prefs.get(optionEnhancedFeeds) ? Twitter.graphqlSearchTimelineUriPath : Twitter.searchTweetsUriPath, widget.chunks.length);
222222
await fetchContext.init();
223223
for (var chunk in widget.chunks) {
224224
var hash = chunk.hash;

lib/l10n/intl_en.arb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -436,5 +436,7 @@
436436
"which_tab_is_shown_when_the_subscription_opens": "Which tab is shown when the subscription opens",
437437
"x_api": "X API",
438438
"enhanced_feeds_label": "Enhanced feeds",
439-
"enhanced_feeds_description": "Enhanced requests for feeds (but with lower rate limits)"
439+
"enhanced_feeds_description": "Enhanced requests for feeds (but with lower rate limits)",
440+
"enhanced_searches_label": "Enhanced searches",
441+
"enhanced_searches_description": "Enhanced requests for searches (but with lower rate limits)"
440442
}

lib/l10n/intl_fr.arb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -508,5 +508,7 @@
508508
"which_tab_is_shown_when_the_subscription_opens": "Quel onglet s’affiche à l’ouverture de l’abonnement",
509509
"x_api": "API X",
510510
"enhanced_feeds_label": "Flux améliorés",
511-
"enhanced_feeds_description": "Requêtes améliorés pour les flux (mais avec des limites plus basses de fréquence)"
511+
"enhanced_feeds_description": "Requêtes améliorés pour les flux (mais avec des limites plus basses de fréquence)",
512+
"enhanced_searches_label": "Recherches améliorés",
513+
"enhanced_searches_description": "Requêtes améliorés pour les recherches (mais avec des limites plus basses de fréquence)"
512514
}

lib/search/search.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,12 +156,12 @@ class _SearchScreenState extends State<_SearchScreen> with SingleTickerProviderS
156156
TweetSearchResultList<SearchUsersModel, UserWithExtra>(
157157
queryController: _queryController,
158158
store: context.read<SearchUsersModel>(),
159-
searchFunction: (q) => context.read<SearchUsersModel>().searchUsers(q),
159+
searchFunction: (q) => context.read<SearchUsersModel>().searchUsers(q, PrefService.of(context).get(optionEnhancedSearches)),
160160
itemBuilder: (context, user) => UserTile(user: UserSubscription.fromUser(user))),
161161
TweetSearchResultList<SearchTweetsModel, TweetWithCard>(
162162
queryController: _queryController,
163163
store: context.read<SearchTweetsModel>(),
164-
searchFunction: (q) => context.read<SearchTweetsModel>().searchTweets(q),
164+
searchFunction: (q) => context.read<SearchTweetsModel>().searchTweets(q, PrefService.of(context).get(optionEnhancedSearches)),
165165
itemBuilder: (context, item) {
166166
return TweetTile(tweet: item, clickable: true);
167167
})

0 commit comments

Comments
 (0)