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/models/friendica_contact.dart';
|
||||
import 'package:friendica_archive_browser/src/services/friendica_connections.dart';
|
||||
import 'package:friendica_archive_browser/src/utils/offsetdatetime_utils.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
@ -44,9 +44,9 @@ class FriendicaTimelineEntry {
|
|||
|
||||
final List<Uri> links;
|
||||
|
||||
final List<FriendicaContact> likes;
|
||||
final List<Contact> likes;
|
||||
|
||||
final List<FriendicaContact> dislikes;
|
||||
final List<Contact> dislikes;
|
||||
|
||||
FriendicaTimelineEntry(
|
||||
{this.id = '',
|
||||
|
@ -63,8 +63,8 @@ class FriendicaTimelineEntry {
|
|||
this.parentAuthorId = '',
|
||||
this.externalLink = '',
|
||||
this.locationData = const LocationData(),
|
||||
this.likes = const <FriendicaContact>[],
|
||||
this.dislikes = const <FriendicaContact>[],
|
||||
this.likes = const <Contact>[],
|
||||
this.dislikes = const <Contact>[],
|
||||
List<FriendicaMediaAttachment>? mediaAttachments,
|
||||
List<Uri>? links})
|
||||
: mediaAttachments = mediaAttachments ?? <FriendicaMediaAttachment>[],
|
||||
|
@ -85,8 +85,8 @@ class FriendicaTimelineEntry {
|
|||
parentAuthor = 'Random parent author ${randomId()}',
|
||||
parentAuthorId = 'Random parent author id ${randomId()}',
|
||||
locationData = LocationData.randomBuilt(),
|
||||
likes = const <FriendicaContact>[],
|
||||
dislikes = const <FriendicaContact>[],
|
||||
likes = const <Contact>[],
|
||||
dislikes = const <Contact>[],
|
||||
links = [
|
||||
Uri.parse('http://localhost/${randomId()}'),
|
||||
Uri.parse('http://localhost/${randomId()}')
|
||||
|
@ -112,8 +112,8 @@ class FriendicaTimelineEntry {
|
|||
String? parentAuthorId,
|
||||
LocationData? locationData,
|
||||
List<FriendicaMediaAttachment>? mediaAttachments,
|
||||
List<FriendicaContact>? likes,
|
||||
List<FriendicaContact>? dislikes,
|
||||
List<Contact>? likes,
|
||||
List<Contact>? dislikes,
|
||||
List<Uri>? links}) {
|
||||
return FriendicaTimelineEntry(
|
||||
creationTimestamp: creationTimestamp ?? this.creationTimestamp,
|
||||
|
@ -207,15 +207,15 @@ class FriendicaTimelineEntry {
|
|||
.toList();
|
||||
final likes =
|
||||
(json['friendica_activities']?['like'] as List<dynamic>? ?? [])
|
||||
.map((json) => FriendicaContact.fromJson(json))
|
||||
.map((json) => Contact.fromJson(json))
|
||||
.toList();
|
||||
final dislikes =
|
||||
(json['friendica_activities']?['dislike'] as List<dynamic>? ?? [])
|
||||
.map((json) => FriendicaContact.fromJson(json))
|
||||
.map((json) => Contact.fromJson(json))
|
||||
.toList();
|
||||
final announce =
|
||||
(json['friendica_activities']?['announce'] as List<dynamic>? ?? [])
|
||||
.map((json) => FriendicaContact.fromJson(json))
|
||||
.map((json) => Contact.fromJson(json))
|
||||
.toList();
|
||||
|
||||
for (final contact in [...likes, ...dislikes, ...announce]) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
class FriendicaContact {
|
||||
class Contact {
|
||||
final ConnectionStatus status;
|
||||
|
||||
final String name;
|
||||
|
@ -9,14 +9,14 @@ class FriendicaContact {
|
|||
|
||||
final String network;
|
||||
|
||||
FriendicaContact(
|
||||
Contact(
|
||||
{required this.status,
|
||||
required this.name,
|
||||
required this.id,
|
||||
required this.profileUrl,
|
||||
required this.network});
|
||||
|
||||
static FriendicaContact fromJson(Map<String, dynamic> json) {
|
||||
static Contact fromJson(Map<String, dynamic> json) {
|
||||
final status = (json['following'] ?? '') == 'true'
|
||||
? ConnectionStatus.youFollowThem
|
||||
: ConnectionStatus.none;
|
||||
|
@ -25,13 +25,18 @@ class FriendicaContact {
|
|||
final profileUrl = Uri.parse(json['url'] ?? '');
|
||||
final network = json['network'] ?? 'unkn';
|
||||
|
||||
return FriendicaContact(
|
||||
return Contact(
|
||||
status: status,
|
||||
name: name,
|
||||
id: id,
|
||||
profileUrl: profileUrl,
|
||||
network: network);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'FriendicaContact{status: $status, name: $name, id: $id, profileUrl: $profileUrl, network: $network}';
|
||||
}
|
||||
}
|
||||
|
||||
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';
|
||||
|
||||
class FriendicaConnections {
|
||||
final _connectionsById = <String, FriendicaContact>{};
|
||||
final _connectionsByName = <String, FriendicaContact>{};
|
||||
final _connectionsById = <String, Contact>{};
|
||||
final _connectionsByName = <String, Contact>{};
|
||||
|
||||
void clearCaches() {
|
||||
_connectionsById.clear();
|
||||
_connectionsByName.clear();
|
||||
}
|
||||
|
||||
bool addConnection(FriendicaContact contact) {
|
||||
bool addConnection(Contact contact) {
|
||||
if (_connectionsById.containsKey(contact.id)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -20,13 +20,13 @@ class FriendicaConnections {
|
|||
return true;
|
||||
}
|
||||
|
||||
Result<FriendicaContact, String> getById(String id) {
|
||||
Result<Contact, String> getById(String id) {
|
||||
final result = _connectionsById[id];
|
||||
|
||||
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];
|
||||
|
||||
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/models/friendica_contact.dart';
|
||||
import 'package:friendica_archive_browser/src/services/friendica_connections.dart';
|
||||
|
||||
class TopInteractorsGenerator {
|
||||
|
@ -66,7 +66,7 @@ class TopInteractorsGenerator {
|
|||
|
||||
final contact = contacts.getById(id).fold(
|
||||
onSuccess: (contact) => contact,
|
||||
onError: (error) => FriendicaContact(
|
||||
onError: (error) => Contact(
|
||||
status: ConnectionStatus.none,
|
||||
name: '',
|
||||
id: id,
|
||||
|
@ -77,7 +77,7 @@ class TopInteractorsGenerator {
|
|||
}
|
||||
|
||||
class InteractorItem {
|
||||
final FriendicaContact contact;
|
||||
final Contact contact;
|
||||
final int resharedOrCommentedOn;
|
||||
final int likeCount;
|
||||
final int dislikeCount;
|
||||
|
@ -94,7 +94,7 @@ class InteractorItem {
|
|||
}
|
||||
|
||||
InteractorItem copy(
|
||||
{FriendicaContact? contact,
|
||||
{Contact? contact,
|
||||
int? resharedOrCommentedOn,
|
||||
int? likeCount,
|
||||
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