mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-17 05:41:29 +02:00
feat: parsing addresses+unstoppable domains from twitter profiles (#2663)
* feat: parsing addresses+unstoppable domains from twitter profiles * fix: address resolution
This commit is contained in:
@@ -193,6 +193,17 @@ class AddressResolver {
|
||||
});
|
||||
}
|
||||
|
||||
static String extractUnstoppableDomain(String raw) {
|
||||
// sort by length to avoid matching shorter tld instead of longer
|
||||
final domains = List<String>.from(unstoppableDomains)..sort((a, b) => b.length.compareTo(a.length));
|
||||
for (final tld in domains) {
|
||||
final pattern = RegExp(r'([A-Za-z0-9-]+)\.' + RegExp.escape(tld), caseSensitive: false);
|
||||
final match = pattern.firstMatch(raw);
|
||||
if (match != null) return match.group(0)!;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
bool isEmailFormat(String address) {
|
||||
final RegExp emailRegex = RegExp(
|
||||
r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$',
|
||||
@@ -228,19 +239,78 @@ class AddressResolver {
|
||||
name: text,
|
||||
profileImageUrl: twitterUser.profileImageUrl,
|
||||
profileName: twitterUser.name);
|
||||
} else {
|
||||
final domain = extractUnstoppableDomain(twitterUser.description);
|
||||
if (domain.isNotEmpty) {
|
||||
final parsedAddressFromDomain = await resolve(context, domain, currency);
|
||||
if (parsedAddressFromDomain.addresses.isNotEmpty) {
|
||||
return ParsedAddress(
|
||||
addresses: parsedAddressFromDomain.addresses,
|
||||
name: text,
|
||||
profileImageUrl: twitterUser.profileImageUrl,
|
||||
profileName: twitterUser.name,
|
||||
parseFrom: ParseFrom.twitter,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
final addressFromLocation = extractAddressByType(
|
||||
raw: twitterUser.location,
|
||||
type: CryptoCurrency.fromString(ticker,
|
||||
walletCurrency: wallet.currency), requireSurroundingWhitespaces: false);
|
||||
if (addressFromLocation != null && addressFromLocation.isNotEmpty) {
|
||||
return ParsedAddress.fetchTwitterAddress(
|
||||
address: addressFromLocation,
|
||||
name: text,
|
||||
profileImageUrl: twitterUser.profileImageUrl,
|
||||
profileName: twitterUser.name);
|
||||
} else {
|
||||
final domain = extractUnstoppableDomain(twitterUser.location);
|
||||
if (domain.isNotEmpty) {
|
||||
final parsedAddressFromDomain = await resolve(context, domain, currency);
|
||||
if (parsedAddressFromDomain.addresses.isNotEmpty) {
|
||||
return ParsedAddress(
|
||||
addresses: parsedAddressFromDomain.addresses,
|
||||
name: text,
|
||||
profileImageUrl: twitterUser.profileImageUrl,
|
||||
profileName: twitterUser.name,
|
||||
parseFrom: ParseFrom.twitter,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
final pinnedTweet = twitterUser.pinnedTweet?.text;
|
||||
if (pinnedTweet != null) {
|
||||
final addressFromPinnedTweet = extractAddressByType(
|
||||
raw: pinnedTweet,
|
||||
type: CryptoCurrency.fromString(ticker, walletCurrency: wallet.currency));
|
||||
type: CryptoCurrency.fromString(ticker, walletCurrency: wallet.currency),
|
||||
requireSurroundingWhitespaces: false);
|
||||
if (addressFromPinnedTweet != null) {
|
||||
return ParsedAddress.fetchTwitterAddress(
|
||||
address: addressFromPinnedTweet,
|
||||
name: text,
|
||||
profileImageUrl: twitterUser.profileImageUrl,
|
||||
profileName: twitterUser.name);
|
||||
} else {
|
||||
final domain = extractUnstoppableDomain(pinnedTweet);
|
||||
if (domain.isNotEmpty) {
|
||||
final parsedAddressFromDomain = await resolve(context, domain, currency);
|
||||
if (parsedAddressFromDomain.addresses.isNotEmpty) {
|
||||
return ParsedAddress(
|
||||
addresses: parsedAddressFromDomain.addresses,
|
||||
name: text,
|
||||
profileImageUrl: twitterUser.profileImageUrl,
|
||||
profileName: twitterUser.name,
|
||||
parseFrom: ParseFrom.twitter,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -258,7 +328,7 @@ class AddressResolver {
|
||||
await MastodonAPI.lookupUserByUserName(userName: userName, apiHost: hostName);
|
||||
|
||||
if (mastodonUser != null) {
|
||||
String? addressFromBio = extractAddressByType(raw: mastodonUser.note, type: currency);
|
||||
String? addressFromBio = extractAddressByType(raw: mastodonUser.note, type: currency, requireSurroundingWhitespaces: false);
|
||||
|
||||
if (addressFromBio != null && addressFromBio.isNotEmpty) {
|
||||
return ParsedAddress.fetchMastodonAddress(
|
||||
@@ -273,7 +343,8 @@ class AddressResolver {
|
||||
if (pinnedPosts.isNotEmpty) {
|
||||
final userPinnedPostsText = pinnedPosts.map((item) => item.content).join('\n');
|
||||
String? addressFromPinnedPost =
|
||||
extractAddressByType(raw: userPinnedPostsText, type: currency);
|
||||
extractAddressByType(raw: userPinnedPostsText, type: currency,
|
||||
requireSurroundingWhitespaces: false);
|
||||
|
||||
if (addressFromPinnedPost != null && addressFromPinnedPost.isNotEmpty) {
|
||||
return ParsedAddress.fetchMastodonAddress(
|
||||
|
||||
@@ -12,7 +12,7 @@ class TwitterApi {
|
||||
|
||||
static Future<TwitterUser> lookupUserByName({required String userName}) async {
|
||||
final queryParams = {
|
||||
'user.fields': 'description,profile_image_url',
|
||||
'user.fields': 'description,profile_image_url,location',
|
||||
'expansions': 'pinned_tweet_id',
|
||||
'tweet.fields': 'note_tweet'
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@ class TwitterUser {
|
||||
required this.username,
|
||||
required this.name,
|
||||
required this.description,
|
||||
required this.location,
|
||||
required this.profileImageUrl,
|
||||
this.pinnedTweet});
|
||||
|
||||
@@ -11,6 +12,7 @@ class TwitterUser {
|
||||
final String username;
|
||||
final String name;
|
||||
final String description;
|
||||
final String location;
|
||||
final String profileImageUrl;
|
||||
final Tweet? pinnedTweet;
|
||||
|
||||
@@ -22,6 +24,7 @@ class TwitterUser {
|
||||
username: json['data']['username'] as String? ?? '',
|
||||
name: json['data']['name'] as String,
|
||||
description: json['data']['description'] as String? ?? '',
|
||||
location: json['data']['location'] as String? ?? '',
|
||||
profileImageUrl: scaledProfileImageUrl,
|
||||
pinnedTweet: pinnedTweet,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user