add twitter api
Serhii committed
Jan 24, 2023 at 20:24 UTC
83c1907e31460d7fc5f4679249e52f6dfe1ed1e4
6 files changed
+110
-7
lib/core/address_validator.dart
+18
@@ -198,4 +198,22 @@ class AddressValidator extends TextValidator {
198
return [];
199
}
200
}
201
+
202
+ static String? getAddressFromStringPattern(CryptoCurrency type) {
203
+ switch (type) {
204
+ case CryptoCurrency.xmr:
205
+ return '([^0-9a-zA-Z]|^)4[0-9a-zA-Z]{94}([^0-9a-zA-Z]|\$)'
206
+ '|([^0-9a-zA-Z]|^)8[0-9a-zA-Z]{94}([^0-9a-zA-Z]|\$)'
207
+ '|([^0-9a-zA-Z]|^)[0-9a-zA-Z]{106}([^0-9a-zA-Z]|\$)';
208
+ case CryptoCurrency.btc:
209
+ return '([^0-9a-zA-Z]|^)1[0-9a-zA-Z]{32}([^0-9a-zA-Z]|\$)'
210
+ '|([^0-9a-zA-Z]|^)1[0-9a-zA-Z]{33}([^0-9a-zA-Z]|\$)'
211
+ '|([^0-9a-zA-Z]|^)3[0-9a-zA-Z]{32}([^0-9a-zA-Z]|\$)'
212
+ '|([^0-9a-zA-Z]|^)3[0-9a-zA-Z]{33}([^0-9a-zA-Z]|\$)'
213
+ '|([^0-9a-zA-Z]|^)bc1[0-9a-zA-Z]{39}([^0-9a-zA-Z]|\$)'
214
+ '|([^0-9a-zA-Z]|^)bc1[0-9a-zA-Z]{59}([^0-9a-zA-Z]|\$)';
215
+ default:
216
+ return null;
217
+ }
218
+ }
219
}
lib/entities/parse_address_from_domain.dart
+25
-5
@@ -1,18 +1,19 @@
1
+import 'package:cake_wallet/core/address_validator.dart';
2
import 'package:cake_wallet/core/yat_service.dart';
3
import 'package:cake_wallet/entities/openalias_record.dart';
4
import 'package:cake_wallet/entities/parsed_address.dart';
5
import 'package:cake_wallet/entities/unstoppable_domain_address.dart';
6
import 'package:cake_wallet/entities/emoji_string_extension.dart';
7
+import 'package:cake_wallet/twitter/twitter_api.dart';
8
+import 'package:cw_core/crypto_currency.dart';
9
import 'package:cw_core/wallet_type.dart';
10
import 'package:cake_wallet/entities/fio_address_provider.dart';
11
12
class AddressResolver {
10
-
13
AddressResolver({required this.yatService, required this.walletType});
12
-
14
final YatService yatService;
15
final WalletType walletType;
15
-
16
+
17
static const unstoppableDomains = [
18
'crypto',
19
'zil',
@@ -26,9 +27,28 @@ class AddressResolver {
27
'blockchain'
28
];
29
30
+ static String? extractAddressByType({required String raw, required CryptoCurrency type}) {
31
+ final addressPattern = AddressValidator.getAddressFromStringPattern(type);
32
+
33
+ if (addressPattern == null) {
34
+ throw 'Unexpected token: $type for getAddressFromStringPattern';
35
+ }
36
+
37
+ final match = RegExp(addressPattern).firstMatch(raw);
38
+ return match?.group(0)?.replaceAll(RegExp('[^0-9a-zA-Z]'), '');
39
+ }
40
+
41
Future<ParsedAddress> resolve(String text, String ticker) async {
42
try {
31
- if (text.contains('@') && !text.contains('.')) {
43
+ if (text.startsWith('@') && !text.substring(1).contains('@')) {
44
+ final formattedName = text.substring(1);
45
+ final twitterUser = await TwitterApi.lookupUserByName(userName: formattedName);
46
+ final address = extractAddressByType(raw: twitterUser.description ?? '', type: CryptoCurrency.fromString(ticker));
47
+ if (address != null) {
48
+ return ParsedAddress.fetchTwitterAddress(address: address, name: text);
49
+ }
50
+ }
51
+ if (!text.startsWith('@') && text.contains('@') && !text.contains('.')) {
52
final bool isFioRegistered = await FioAddressProvider.checkAvail(text);
53
if (isFioRegistered) {
54
final address = await FioAddressProvider.getPubAddress(text, ticker);
@@ -58,7 +78,7 @@ class AddressResolver {
78
final record = await OpenaliasRecord.fetchAddressAndName(
79
formattedName: formattedName, ticker: ticker);
80
return ParsedAddress.fetchOpenAliasAddress(record: record, name: text);
61
-
81
+
82
} catch (e) {
83
print(e.toString());
84
}
lib/entities/parsed_address.dart
+9
-2
@@ -1,8 +1,7 @@
1
import 'package:cake_wallet/entities/openalias_record.dart';
2
import 'package:cake_wallet/entities/yat_record.dart';
3
-import 'package:flutter/material.dart';
3
5
-enum ParseFrom { unstoppableDomains, openAlias, yatRecord, fio, notParsed }
4
+enum ParseFrom { unstoppableDomains, openAlias, yatRecord, fio, notParsed, twitter }
5
6
class ParsedAddress {
7
ParsedAddress({
@@ -62,6 +61,14 @@ class ParsedAddress {
61
);
62
}
63
64
+ factory ParsedAddress.fetchTwitterAddress({required String address, required String name}){
65
+ return ParsedAddress(
66
+ addresses: [address],
67
+ name: name,
68
+ parseFrom: ParseFrom.twitter,
69
+ );
70
+ }
71
+
72
final List<String> addresses;
73
final String name;
74
final String description;
lib/src/screens/send/widgets/extract_address_from_parsed.dart
+5
@@ -28,6 +28,11 @@ Future<String> extractAddressFromParsed(
28
content = S.of(context).openalias_alert_content(parsedAddress.name);
29
address = parsedAddress.addresses.first;
30
break;
31
+ case ParseFrom.twitter:
32
+ title = S.of(context).address_detected;
33
+ content = S.of(context).openalias_alert_content(parsedAddress.name);
34
+ address = parsedAddress.addresses.first;
35
+ break;
36
case ParseFrom.yatRecord:
37
if (parsedAddress.name.isEmpty) {
38
title = S.of(context).yat_error;
lib/twitter/twitter_api.dart
new
+37
@@ -0,0 +1,37 @@
1
+import 'dart:convert';
2
+import 'package:cake_wallet/twitter/twitter_user.dart';
3
+import 'package:http/http.dart' as http;
4
+import 'package:cake_wallet/.secrets.g.dart' as secrets;
5
+
6
+class TwitterApi {
7
+ static const twitterBearerToken = secrets.twitterBearerToken;
8
+ static const httpsScheme = 'https';
9
+ static const apiHost = 'api.twitter.com';
10
+ static const userPath = '/2/users/by/username/';
11
+
12
+ static Future<TwitterUser> lookupUserByName({required String userName}) async {
13
+ final queryParams = {'user.fields': 'description'};
14
+
15
+ final headers = {'authorization': 'Bearer ${secrets.twitterBearerToken}'};
16
+
17
+ final uri = Uri(
18
+ scheme: httpsScheme,
19
+ host: apiHost,
20
+ path: userPath + userName,
21
+ queryParameters: queryParams,
22
+ );
23
+
24
+ var response = await http.get(uri, headers: headers);
25
+
26
+ if (response.statusCode != 200) {
27
+ throw Exception('Unexpected http status: ${response.statusCode}');
28
+ }
29
+ final responseJSON = json.decode(response.body) as Map<String, dynamic>;
30
+
31
+ if (responseJSON['errors'] != null) {
32
+ throw Exception(responseJSON['errors'][0]['detail']);
33
+ }
34
+
35
+ return TwitterUser.fromJson(responseJSON['data'] as Map<String, dynamic>);
36
+ }
37
+}
lib/twitter/twitter_user.dart
new
+16
@@ -0,0 +1,16 @@
1
+class TwitterUser {
2
+ TwitterUser({required this.id, required this.username, required this.name, this.description});
3
+
4
+ final String id;
5
+ final String username;
6
+ final String name;
7
+ final String? description;
8
+
9
+ factory TwitterUser.fromJson(Map<String, dynamic> json) {
10
+ return TwitterUser(
11
+ id: json['id'] as String,
12
+ username: json['username'] as String,
13
+ name: json['name'] as String,
14
+ description: json['description'] as String?);
15
+ }
16
+}