add twitter api

This commit is contained in:
Serhii 2023-01-24 20:24:46 +02:00
parent 2294480da5
commit 83c1907e31
6 changed files with 110 additions and 7 deletions

View file

@ -198,4 +198,22 @@ class AddressValidator extends TextValidator {
return [];
}
}
static String? getAddressFromStringPattern(CryptoCurrency type) {
switch (type) {
case CryptoCurrency.xmr:
return '([^0-9a-zA-Z]|^)4[0-9a-zA-Z]{94}([^0-9a-zA-Z]|\$)'
'|([^0-9a-zA-Z]|^)8[0-9a-zA-Z]{94}([^0-9a-zA-Z]|\$)'
'|([^0-9a-zA-Z]|^)[0-9a-zA-Z]{106}([^0-9a-zA-Z]|\$)';
case CryptoCurrency.btc:
return '([^0-9a-zA-Z]|^)1[0-9a-zA-Z]{32}([^0-9a-zA-Z]|\$)'
'|([^0-9a-zA-Z]|^)1[0-9a-zA-Z]{33}([^0-9a-zA-Z]|\$)'
'|([^0-9a-zA-Z]|^)3[0-9a-zA-Z]{32}([^0-9a-zA-Z]|\$)'
'|([^0-9a-zA-Z]|^)3[0-9a-zA-Z]{33}([^0-9a-zA-Z]|\$)'
'|([^0-9a-zA-Z]|^)bc1[0-9a-zA-Z]{39}([^0-9a-zA-Z]|\$)'
'|([^0-9a-zA-Z]|^)bc1[0-9a-zA-Z]{59}([^0-9a-zA-Z]|\$)';
default:
return null;
}
}
}

View file

@ -1,18 +1,19 @@
import 'package:cake_wallet/core/address_validator.dart';
import 'package:cake_wallet/core/yat_service.dart';
import 'package:cake_wallet/entities/openalias_record.dart';
import 'package:cake_wallet/entities/parsed_address.dart';
import 'package:cake_wallet/entities/unstoppable_domain_address.dart';
import 'package:cake_wallet/entities/emoji_string_extension.dart';
import 'package:cake_wallet/twitter/twitter_api.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cake_wallet/entities/fio_address_provider.dart';
class AddressResolver {
AddressResolver({required this.yatService, required this.walletType});
final YatService yatService;
final WalletType walletType;
static const unstoppableDomains = [
'crypto',
'zil',
@ -26,9 +27,28 @@ class AddressResolver {
'blockchain'
];
static String? extractAddressByType({required String raw, required CryptoCurrency type}) {
final addressPattern = AddressValidator.getAddressFromStringPattern(type);
if (addressPattern == null) {
throw 'Unexpected token: $type for getAddressFromStringPattern';
}
final match = RegExp(addressPattern).firstMatch(raw);
return match?.group(0)?.replaceAll(RegExp('[^0-9a-zA-Z]'), '');
}
Future<ParsedAddress> resolve(String text, String ticker) async {
try {
if (text.contains('@') && !text.contains('.')) {
if (text.startsWith('@') && !text.substring(1).contains('@')) {
final formattedName = text.substring(1);
final twitterUser = await TwitterApi.lookupUserByName(userName: formattedName);
final address = extractAddressByType(raw: twitterUser.description ?? '', type: CryptoCurrency.fromString(ticker));
if (address != null) {
return ParsedAddress.fetchTwitterAddress(address: address, name: text);
}
}
if (!text.startsWith('@') && text.contains('@') && !text.contains('.')) {
final bool isFioRegistered = await FioAddressProvider.checkAvail(text);
if (isFioRegistered) {
final address = await FioAddressProvider.getPubAddress(text, ticker);
@ -58,7 +78,7 @@ class AddressResolver {
final record = await OpenaliasRecord.fetchAddressAndName(
formattedName: formattedName, ticker: ticker);
return ParsedAddress.fetchOpenAliasAddress(record: record, name: text);
} catch (e) {
print(e.toString());
}

View file

@ -1,8 +1,7 @@
import 'package:cake_wallet/entities/openalias_record.dart';
import 'package:cake_wallet/entities/yat_record.dart';
import 'package:flutter/material.dart';
enum ParseFrom { unstoppableDomains, openAlias, yatRecord, fio, notParsed }
enum ParseFrom { unstoppableDomains, openAlias, yatRecord, fio, notParsed, twitter }
class ParsedAddress {
ParsedAddress({
@ -62,6 +61,14 @@ class ParsedAddress {
);
}
factory ParsedAddress.fetchTwitterAddress({required String address, required String name}){
return ParsedAddress(
addresses: [address],
name: name,
parseFrom: ParseFrom.twitter,
);
}
final List<String> addresses;
final String name;
final String description;

View file

@ -28,6 +28,11 @@ Future<String> extractAddressFromParsed(
content = S.of(context).openalias_alert_content(parsedAddress.name);
address = parsedAddress.addresses.first;
break;
case ParseFrom.twitter:
title = S.of(context).address_detected;
content = S.of(context).openalias_alert_content(parsedAddress.name);
address = parsedAddress.addresses.first;
break;
case ParseFrom.yatRecord:
if (parsedAddress.name.isEmpty) {
title = S.of(context).yat_error;

View file

@ -0,0 +1,37 @@
import 'dart:convert';
import 'package:cake_wallet/twitter/twitter_user.dart';
import 'package:http/http.dart' as http;
import 'package:cake_wallet/.secrets.g.dart' as secrets;
class TwitterApi {
static const twitterBearerToken = secrets.twitterBearerToken;
static const httpsScheme = 'https';
static const apiHost = 'api.twitter.com';
static const userPath = '/2/users/by/username/';
static Future<TwitterUser> lookupUserByName({required String userName}) async {
final queryParams = {'user.fields': 'description'};
final headers = {'authorization': 'Bearer ${secrets.twitterBearerToken}'};
final uri = Uri(
scheme: httpsScheme,
host: apiHost,
path: userPath + userName,
queryParameters: queryParams,
);
var response = await http.get(uri, headers: headers);
if (response.statusCode != 200) {
throw Exception('Unexpected http status: ${response.statusCode}');
}
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
if (responseJSON['errors'] != null) {
throw Exception(responseJSON['errors'][0]['detail']);
}
return TwitterUser.fromJson(responseJSON['data'] as Map<String, dynamic>);
}
}

View file

@ -0,0 +1,16 @@
class TwitterUser {
TwitterUser({required this.id, required this.username, required this.name, this.description});
final String id;
final String username;
final String name;
final String? description;
factory TwitterUser.fromJson(Map<String, dynamic> json) {
return TwitterUser(
id: json['id'] as String,
username: json['username'] as String,
name: json['name'] as String,
description: json['description'] as String?);
}
}