mirror of
https://gitlab.com/mysocialportal/fediverse-archiving-tools.git
synced 2024-10-18 08:53:31 +00:00
Implement Diaspora Contact Importing
This commit is contained in:
parent
521ed49947
commit
e051a3ea52
8 changed files with 238 additions and 26 deletions
|
@ -0,0 +1,41 @@
|
||||||
|
import 'package:friendica_archive_browser/src/models/friendica_contact.dart';
|
||||||
|
|
||||||
|
Contact friendicaContactFromDiasporaJson(Map<String, dynamic> json) {
|
||||||
|
const network = "Diaspora";
|
||||||
|
final accountId = json['account_id'] ?? '';
|
||||||
|
final profileUrl = _profileUrlFromAccountId(accountId);
|
||||||
|
final name = json['person_name'] ?? '';
|
||||||
|
final id = json['person_guid'] ?? '';
|
||||||
|
final following = json['following'] ?? false;
|
||||||
|
final followed = json['followed'] ?? false;
|
||||||
|
var status = ConnectionStatus.none;
|
||||||
|
if (following && followed) {
|
||||||
|
status = ConnectionStatus.mutual;
|
||||||
|
} else if (following) {
|
||||||
|
status = ConnectionStatus.youFollowThem;
|
||||||
|
} else if (followed) {
|
||||||
|
status = ConnectionStatus.theyFollowYou;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Contact(
|
||||||
|
status: status,
|
||||||
|
name: name,
|
||||||
|
id: id,
|
||||||
|
profileUrl: profileUrl,
|
||||||
|
network: network);
|
||||||
|
}
|
||||||
|
|
||||||
|
Uri _profileUrlFromAccountId(String accountId) {
|
||||||
|
if (accountId.isEmpty) {
|
||||||
|
return Uri();
|
||||||
|
}
|
||||||
|
final accountIdPieces = accountId.split('@');
|
||||||
|
if (accountIdPieces.length != 2) {
|
||||||
|
return Uri();
|
||||||
|
}
|
||||||
|
|
||||||
|
final userName = accountIdPieces[0];
|
||||||
|
final server = accountIdPieces[1];
|
||||||
|
|
||||||
|
return Uri.parse('https://$server/u/$userName');
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
import 'package:friendica_archive_browser/src/friendica/models/friendica_contact.dart';
|
|
||||||
import 'package:friendica_archive_browser/src/friendica/services/path_mapping_service.dart';
|
import 'package:friendica_archive_browser/src/friendica/services/path_mapping_service.dart';
|
||||||
|
import 'package:friendica_archive_browser/src/models/friendica_contact.dart';
|
||||||
import 'package:friendica_archive_browser/src/services/friendica_connections.dart';
|
import 'package:friendica_archive_browser/src/services/friendica_connections.dart';
|
||||||
import 'package:friendica_archive_browser/src/utils/offsetdatetime_utils.dart';
|
import 'package:friendica_archive_browser/src/utils/offsetdatetime_utils.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
@ -44,9 +44,9 @@ class FriendicaTimelineEntry {
|
||||||
|
|
||||||
final List<Uri> links;
|
final List<Uri> links;
|
||||||
|
|
||||||
final List<FriendicaContact> likes;
|
final List<Contact> likes;
|
||||||
|
|
||||||
final List<FriendicaContact> dislikes;
|
final List<Contact> dislikes;
|
||||||
|
|
||||||
FriendicaTimelineEntry(
|
FriendicaTimelineEntry(
|
||||||
{this.id = '',
|
{this.id = '',
|
||||||
|
@ -63,8 +63,8 @@ class FriendicaTimelineEntry {
|
||||||
this.parentAuthorId = '',
|
this.parentAuthorId = '',
|
||||||
this.externalLink = '',
|
this.externalLink = '',
|
||||||
this.locationData = const LocationData(),
|
this.locationData = const LocationData(),
|
||||||
this.likes = const <FriendicaContact>[],
|
this.likes = const <Contact>[],
|
||||||
this.dislikes = const <FriendicaContact>[],
|
this.dislikes = const <Contact>[],
|
||||||
List<FriendicaMediaAttachment>? mediaAttachments,
|
List<FriendicaMediaAttachment>? mediaAttachments,
|
||||||
List<Uri>? links})
|
List<Uri>? links})
|
||||||
: mediaAttachments = mediaAttachments ?? <FriendicaMediaAttachment>[],
|
: mediaAttachments = mediaAttachments ?? <FriendicaMediaAttachment>[],
|
||||||
|
@ -85,8 +85,8 @@ class FriendicaTimelineEntry {
|
||||||
parentAuthor = 'Random parent author ${randomId()}',
|
parentAuthor = 'Random parent author ${randomId()}',
|
||||||
parentAuthorId = 'Random parent author id ${randomId()}',
|
parentAuthorId = 'Random parent author id ${randomId()}',
|
||||||
locationData = LocationData.randomBuilt(),
|
locationData = LocationData.randomBuilt(),
|
||||||
likes = const <FriendicaContact>[],
|
likes = const <Contact>[],
|
||||||
dislikes = const <FriendicaContact>[],
|
dislikes = const <Contact>[],
|
||||||
links = [
|
links = [
|
||||||
Uri.parse('http://localhost/${randomId()}'),
|
Uri.parse('http://localhost/${randomId()}'),
|
||||||
Uri.parse('http://localhost/${randomId()}')
|
Uri.parse('http://localhost/${randomId()}')
|
||||||
|
@ -112,8 +112,8 @@ class FriendicaTimelineEntry {
|
||||||
String? parentAuthorId,
|
String? parentAuthorId,
|
||||||
LocationData? locationData,
|
LocationData? locationData,
|
||||||
List<FriendicaMediaAttachment>? mediaAttachments,
|
List<FriendicaMediaAttachment>? mediaAttachments,
|
||||||
List<FriendicaContact>? likes,
|
List<Contact>? likes,
|
||||||
List<FriendicaContact>? dislikes,
|
List<Contact>? dislikes,
|
||||||
List<Uri>? links}) {
|
List<Uri>? links}) {
|
||||||
return FriendicaTimelineEntry(
|
return FriendicaTimelineEntry(
|
||||||
creationTimestamp: creationTimestamp ?? this.creationTimestamp,
|
creationTimestamp: creationTimestamp ?? this.creationTimestamp,
|
||||||
|
@ -207,15 +207,15 @@ class FriendicaTimelineEntry {
|
||||||
.toList();
|
.toList();
|
||||||
final likes =
|
final likes =
|
||||||
(json['friendica_activities']?['like'] as List<dynamic>? ?? [])
|
(json['friendica_activities']?['like'] as List<dynamic>? ?? [])
|
||||||
.map((json) => FriendicaContact.fromJson(json))
|
.map((json) => Contact.fromJson(json))
|
||||||
.toList();
|
.toList();
|
||||||
final dislikes =
|
final dislikes =
|
||||||
(json['friendica_activities']?['dislike'] as List<dynamic>? ?? [])
|
(json['friendica_activities']?['dislike'] as List<dynamic>? ?? [])
|
||||||
.map((json) => FriendicaContact.fromJson(json))
|
.map((json) => Contact.fromJson(json))
|
||||||
.toList();
|
.toList();
|
||||||
final announce =
|
final announce =
|
||||||
(json['friendica_activities']?['announce'] as List<dynamic>? ?? [])
|
(json['friendica_activities']?['announce'] as List<dynamic>? ?? [])
|
||||||
.map((json) => FriendicaContact.fromJson(json))
|
.map((json) => Contact.fromJson(json))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
for (final contact in [...likes, ...dislikes, ...announce]) {
|
for (final contact in [...likes, ...dislikes, ...announce]) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
class FriendicaContact {
|
class Contact {
|
||||||
final ConnectionStatus status;
|
final ConnectionStatus status;
|
||||||
|
|
||||||
final String name;
|
final String name;
|
||||||
|
@ -9,14 +9,14 @@ class FriendicaContact {
|
||||||
|
|
||||||
final String network;
|
final String network;
|
||||||
|
|
||||||
FriendicaContact(
|
Contact(
|
||||||
{required this.status,
|
{required this.status,
|
||||||
required this.name,
|
required this.name,
|
||||||
required this.id,
|
required this.id,
|
||||||
required this.profileUrl,
|
required this.profileUrl,
|
||||||
required this.network});
|
required this.network});
|
||||||
|
|
||||||
static FriendicaContact fromJson(Map<String, dynamic> json) {
|
static Contact fromJson(Map<String, dynamic> json) {
|
||||||
final status = (json['following'] ?? '') == 'true'
|
final status = (json['following'] ?? '') == 'true'
|
||||||
? ConnectionStatus.youFollowThem
|
? ConnectionStatus.youFollowThem
|
||||||
: ConnectionStatus.none;
|
: ConnectionStatus.none;
|
||||||
|
@ -25,13 +25,18 @@ class FriendicaContact {
|
||||||
final profileUrl = Uri.parse(json['url'] ?? '');
|
final profileUrl = Uri.parse(json['url'] ?? '');
|
||||||
final network = json['network'] ?? 'unkn';
|
final network = json['network'] ?? 'unkn';
|
||||||
|
|
||||||
return FriendicaContact(
|
return Contact(
|
||||||
status: status,
|
status: status,
|
||||||
name: name,
|
name: name,
|
||||||
id: id,
|
id: id,
|
||||||
profileUrl: profileUrl,
|
profileUrl: profileUrl,
|
||||||
network: network);
|
network: network);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'FriendicaContact{status: $status, name: $name, id: $id, profileUrl: $profileUrl, network: $network}';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ConnectionStatus {
|
enum ConnectionStatus {
|
|
@ -0,0 +1,127 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:path/path.dart' as p;
|
||||||
|
import 'package:result_monad/result_monad.dart';
|
||||||
|
|
||||||
|
import '../friendica/models/friendica_entry_tree_item.dart';
|
||||||
|
import '../friendica/models/friendica_timeline_entry.dart';
|
||||||
|
import '../friendica/services/path_mapping_service.dart';
|
||||||
|
import '../models/local_image_archive_entry.dart';
|
||||||
|
import '../utils/exec_error.dart';
|
||||||
|
import 'friendica_connections.dart';
|
||||||
|
|
||||||
|
class DiasporaArchiveService {
|
||||||
|
final PathMappingService pathMappingService;
|
||||||
|
final Map<String, ImageEntry> _imagesByRequestUrl = {};
|
||||||
|
final List<FriendicaEntryTreeItem> _postEntries = [];
|
||||||
|
final List<FriendicaEntryTreeItem> _orphanedCommentEntries = [];
|
||||||
|
final List<FriendicaEntryTreeItem> _allComments = [];
|
||||||
|
final FriendicaConnections connections = FriendicaConnections();
|
||||||
|
String _ownersName = '';
|
||||||
|
|
||||||
|
DiasporaArchiveService({required this.pathMappingService});
|
||||||
|
|
||||||
|
String get ownersName => _ownersName;
|
||||||
|
|
||||||
|
void clearCaches() {
|
||||||
|
connections.clearCaches();
|
||||||
|
_imagesByRequestUrl.clear();
|
||||||
|
_orphanedCommentEntries.clear();
|
||||||
|
_allComments.clear();
|
||||||
|
_postEntries.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
FutureResult<List<FriendicaEntryTreeItem>, ExecError> getPosts() async {
|
||||||
|
if (_postEntries.isEmpty && _allComments.isEmpty) {
|
||||||
|
_loadEntries();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.ok(_postEntries);
|
||||||
|
}
|
||||||
|
|
||||||
|
FutureResult<List<FriendicaEntryTreeItem>, ExecError> getAllComments() async {
|
||||||
|
if (_postEntries.isEmpty && _allComments.isEmpty) {
|
||||||
|
_loadEntries();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.ok(_allComments);
|
||||||
|
}
|
||||||
|
|
||||||
|
FutureResult<List<FriendicaEntryTreeItem>, ExecError>
|
||||||
|
getOrphanedComments() async {
|
||||||
|
if (_postEntries.isEmpty && _allComments.isEmpty) {
|
||||||
|
_loadEntries();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.ok(_orphanedCommentEntries);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<ImageEntry, ExecError> getImageByUrl(String url) {
|
||||||
|
if (_imagesByRequestUrl.isEmpty) {
|
||||||
|
_loadImages();
|
||||||
|
}
|
||||||
|
|
||||||
|
final result = _imagesByRequestUrl[url];
|
||||||
|
return result == null
|
||||||
|
? Result.error(ExecError(errorMessage: '$url not found'))
|
||||||
|
: Result.ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
String get _baseArchiveFolder => pathMappingService.rootFolder;
|
||||||
|
|
||||||
|
void _loadEntries() {
|
||||||
|
final entriesJsonPath = p.join(_baseArchiveFolder, 'postsAndComments.json');
|
||||||
|
final jsonFile = File(entriesJsonPath);
|
||||||
|
if (jsonFile.existsSync()) {
|
||||||
|
final json = jsonDecode(jsonFile.readAsStringSync()) as List<dynamic>;
|
||||||
|
final entries =
|
||||||
|
json.map((j) => FriendicaTimelineEntry.fromJson(j, connections));
|
||||||
|
final topLevelEntries =
|
||||||
|
entries.where((element) => element.parentId.isEmpty);
|
||||||
|
final commentEntries =
|
||||||
|
entries.where((element) => element.parentId.isNotEmpty).toList();
|
||||||
|
final entryTrees = <String, FriendicaEntryTreeItem>{};
|
||||||
|
|
||||||
|
final postTreeEntries = <FriendicaEntryTreeItem>[];
|
||||||
|
for (final entry in topLevelEntries) {
|
||||||
|
final treeEntry = FriendicaEntryTreeItem(entry, false);
|
||||||
|
entryTrees[entry.id] = treeEntry;
|
||||||
|
postTreeEntries.add(treeEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
final commentTreeEntries = <FriendicaEntryTreeItem>[];
|
||||||
|
commentEntries.sort(
|
||||||
|
(c1, c2) => c1.creationTimestamp.compareTo(c2.creationTimestamp));
|
||||||
|
for (final entry in commentEntries) {
|
||||||
|
final parent = entryTrees[entry.parentId];
|
||||||
|
final treeEntry = FriendicaEntryTreeItem(entry, parent == null);
|
||||||
|
parent?.addChild(treeEntry);
|
||||||
|
entryTrees[entry.id] = treeEntry;
|
||||||
|
commentTreeEntries.add(treeEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
_postEntries.clear();
|
||||||
|
_postEntries.addAll(postTreeEntries);
|
||||||
|
|
||||||
|
_allComments.clear();
|
||||||
|
_allComments.addAll(commentTreeEntries);
|
||||||
|
|
||||||
|
_orphanedCommentEntries.clear();
|
||||||
|
_orphanedCommentEntries
|
||||||
|
.addAll(entryTrees.values.where((element) => element.isOrphaned));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _loadImages() {
|
||||||
|
final imageJsonPath = p.join(_baseArchiveFolder, 'images.json');
|
||||||
|
final jsonFile = File(imageJsonPath);
|
||||||
|
if (jsonFile.existsSync()) {
|
||||||
|
final json = jsonDecode(jsonFile.readAsStringSync()) as List<dynamic>;
|
||||||
|
final imageEntries = json.map((j) => ImageEntry.fromJson(j));
|
||||||
|
for (final entry in imageEntries) {
|
||||||
|
_imagesByRequestUrl[entry.url] = entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:friendica_archive_browser/src/diaspora/serializers/diaspora_contact_serializer.dart';
|
||||||
|
import 'package:friendica_archive_browser/src/models/friendica_contact.dart';
|
||||||
|
|
||||||
|
class DiasporaProfileJsonReader {
|
||||||
|
final String jsonFilePath;
|
||||||
|
|
||||||
|
DiasporaProfileJsonReader(this.jsonFilePath);
|
||||||
|
|
||||||
|
List<Contact> readContacts() {
|
||||||
|
final jsonFile = File(jsonFilePath);
|
||||||
|
if (jsonFile.existsSync()) {
|
||||||
|
final json =
|
||||||
|
jsonDecode(jsonFile.readAsStringSync()) as Map<String, dynamic>;
|
||||||
|
final contactsJson = json['user']?['contacts'] as List<dynamic>;
|
||||||
|
final contacts =
|
||||||
|
contactsJson.map((j) => friendicaContactFromDiasporaJson(j)).toList();
|
||||||
|
return contacts;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,16 +1,16 @@
|
||||||
import 'package:friendica_archive_browser/src/friendica/models/friendica_contact.dart';
|
import 'package:friendica_archive_browser/src/models/friendica_contact.dart';
|
||||||
import 'package:result_monad/result_monad.dart';
|
import 'package:result_monad/result_monad.dart';
|
||||||
|
|
||||||
class FriendicaConnections {
|
class FriendicaConnections {
|
||||||
final _connectionsById = <String, FriendicaContact>{};
|
final _connectionsById = <String, Contact>{};
|
||||||
final _connectionsByName = <String, FriendicaContact>{};
|
final _connectionsByName = <String, Contact>{};
|
||||||
|
|
||||||
void clearCaches() {
|
void clearCaches() {
|
||||||
_connectionsById.clear();
|
_connectionsById.clear();
|
||||||
_connectionsByName.clear();
|
_connectionsByName.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool addConnection(FriendicaContact contact) {
|
bool addConnection(Contact contact) {
|
||||||
if (_connectionsById.containsKey(contact.id)) {
|
if (_connectionsById.containsKey(contact.id)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -20,13 +20,13 @@ class FriendicaConnections {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<FriendicaContact, String> getById(String id) {
|
Result<Contact, String> getById(String id) {
|
||||||
final result = _connectionsById[id];
|
final result = _connectionsById[id];
|
||||||
|
|
||||||
return result != null ? Result.ok(result) : Result.error('$id not found');
|
return result != null ? Result.ok(result) : Result.error('$id not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<FriendicaContact, String> getByName(String name) {
|
Result<Contact, String> getByName(String name) {
|
||||||
final result = _connectionsByName[name];
|
final result = _connectionsByName[name];
|
||||||
|
|
||||||
return result != null ? Result.ok(result) : Result.error('$name not found');
|
return result != null ? Result.ok(result) : Result.error('$name not found');
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import 'package:friendica_archive_browser/src/friendica/models/friendica_contact.dart';
|
|
||||||
import 'package:friendica_archive_browser/src/friendica/models/friendica_timeline_entry.dart';
|
import 'package:friendica_archive_browser/src/friendica/models/friendica_timeline_entry.dart';
|
||||||
|
import 'package:friendica_archive_browser/src/models/friendica_contact.dart';
|
||||||
import 'package:friendica_archive_browser/src/services/friendica_connections.dart';
|
import 'package:friendica_archive_browser/src/services/friendica_connections.dart';
|
||||||
|
|
||||||
class TopInteractorsGenerator {
|
class TopInteractorsGenerator {
|
||||||
|
@ -66,7 +66,7 @@ class TopInteractorsGenerator {
|
||||||
|
|
||||||
final contact = contacts.getById(id).fold(
|
final contact = contacts.getById(id).fold(
|
||||||
onSuccess: (contact) => contact,
|
onSuccess: (contact) => contact,
|
||||||
onError: (error) => FriendicaContact(
|
onError: (error) => Contact(
|
||||||
status: ConnectionStatus.none,
|
status: ConnectionStatus.none,
|
||||||
name: '',
|
name: '',
|
||||||
id: id,
|
id: id,
|
||||||
|
@ -77,7 +77,7 @@ class TopInteractorsGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
class InteractorItem {
|
class InteractorItem {
|
||||||
final FriendicaContact contact;
|
final Contact contact;
|
||||||
final int resharedOrCommentedOn;
|
final int resharedOrCommentedOn;
|
||||||
final int likeCount;
|
final int likeCount;
|
||||||
final int dislikeCount;
|
final int dislikeCount;
|
||||||
|
@ -94,7 +94,7 @@ class InteractorItem {
|
||||||
}
|
}
|
||||||
|
|
||||||
InteractorItem copy(
|
InteractorItem copy(
|
||||||
{FriendicaContact? contact,
|
{Contact? contact,
|
||||||
int? resharedOrCommentedOn,
|
int? resharedOrCommentedOn,
|
||||||
int? likeCount,
|
int? likeCount,
|
||||||
int? dislikeCount}) {
|
int? dislikeCount}) {
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
// ignore_for_file: avoid_print
|
||||||
|
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:friendica_archive_browser/src/services/diaspora_profile_json_reader.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('Diaspora Connections Test', () {
|
||||||
|
final reader = DiasporaProfileJsonReader(
|
||||||
|
'/Users/hankdev/Desktop/diaspora_pretty.json');
|
||||||
|
final contacts = reader.readContacts();
|
||||||
|
print(contacts.length);
|
||||||
|
print(contacts.first);
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in a new issue