diff --git a/.gitignore b/.gitignore index bbf3282a..5bdcabc1 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ *.pyc *.swp *.swo +*.swn *.dylib .DS_Store .atom/ diff --git a/analysis_options.yaml b/analysis_options.yaml index c96f3b08..1489415f 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,12 +1,21 @@ -include: package:pedantic/analysis_options.yaml +include: package:lints/recommended.yaml linter: rules: - - camel_case_types - - avoid_print - - constant_identifier_names - - prefer_final_locals - - prefer_final_in_for_each + camel_case_types: true + avoid_print: true + constant_identifier_names: true + prefer_final_locals: true + prefer_final_in_for_each: true + sort_pub_dependencies: true + always_use_package_imports: true + always_declare_return_types: true + prefer_single_quotes: true + sort_child_properties_last: true + unawaited_futures: true + unsafe_html: true + avoid_function_literals_in_foreach_calls: false + non_constant_identifier_names: false # seems to wrongly diagnose static const variables analyzer: errors: diff --git a/lib/encryption/cross_signing.dart b/lib/encryption/cross_signing.dart index ac4afed7..e2116343 100644 --- a/lib/encryption/cross_signing.dart +++ b/lib/encryption/cross_signing.dart @@ -20,10 +20,10 @@ import 'dart:typed_data'; import 'package:olm/olm.dart' as olm; +import 'package:matrix/encryption/encryption.dart'; +import 'package:matrix/encryption/ssss.dart'; import 'package:matrix/encryption/utils/base64_unpadded.dart'; -import '../matrix.dart'; -import 'encryption.dart'; -import 'ssss.dart'; +import 'package:matrix/matrix.dart'; class CrossSigning { final Encryption encryption; @@ -131,14 +131,14 @@ class CrossSigning { throw Exception('[sign] keys are not in cache but sign was called'); } - final addSignature = - (SignableKey key, SignableKey signedWith, String signature) { + void addSignature( + SignableKey key, SignableKey signedWith, String signature) { final signedKey = key.cloneForSigning(); ((signedKey.signatures ??= >{})[signedWith.userId] ??= {})['ed25519:${signedWith.identifier}'] = signature; signedKeys.add(signedKey); - }; + } for (final key in keys) { if (key.userId == client.userID) { diff --git a/lib/encryption/encryption.dart b/lib/encryption/encryption.dart index 19c8f508..1c65540a 100644 --- a/lib/encryption/encryption.dart +++ b/lib/encryption/encryption.dart @@ -21,14 +21,14 @@ import 'dart:convert'; import 'package:olm/olm.dart' as olm; -import '../matrix.dart'; -import '../src/utils/run_in_root.dart'; -import 'cross_signing.dart'; -import 'key_manager.dart'; -import 'key_verification_manager.dart'; -import 'olm_manager.dart'; -import 'ssss.dart'; -import 'utils/bootstrap.dart'; +import 'package:matrix/encryption/cross_signing.dart'; +import 'package:matrix/encryption/key_manager.dart'; +import 'package:matrix/encryption/key_verification_manager.dart'; +import 'package:matrix/encryption/olm_manager.dart'; +import 'package:matrix/encryption/ssss.dart'; +import 'package:matrix/encryption/utils/bootstrap.dart'; +import 'package:matrix/matrix.dart'; +import 'package:matrix/src/utils/run_in_root.dart'; class Encryption { final Client client; @@ -206,10 +206,9 @@ class Encryption { canRequestSession = false; // we can't have the key be an int, else json-serializing will fail, thus we need it to be a string - final messageIndexKey = 'key-' + decryptResult.message_index.toString(); - final messageIndexValue = event.eventId + - '|' + - event.originServerTs.millisecondsSinceEpoch.toString(); + final messageIndexKey = 'key-${decryptResult.message_index}'; + final messageIndexValue = + '${event.eventId}|${event.originServerTs.millisecondsSinceEpoch}'; final haveIndex = inboundGroupSession.indexes.containsKey(messageIndexKey); if (haveIndex && diff --git a/lib/encryption/key_manager.dart b/lib/encryption/key_manager.dart index ea79f44d..b41ac249 100644 --- a/lib/encryption/key_manager.dart +++ b/lib/encryption/key_manager.dart @@ -22,13 +22,13 @@ import 'dart:convert'; import 'package:collection/collection.dart'; import 'package:olm/olm.dart' as olm; +import 'package:matrix/encryption/encryption.dart'; import 'package:matrix/encryption/utils/base64_unpadded.dart'; +import 'package:matrix/encryption/utils/outbound_group_session.dart'; +import 'package:matrix/encryption/utils/session_key.dart'; import 'package:matrix/encryption/utils/stored_inbound_group_session.dart'; -import '../matrix.dart'; -import '../src/utils/run_in_root.dart'; -import './encryption.dart'; -import './utils/outbound_group_session.dart'; -import './utils/session_key.dart'; +import 'package:matrix/matrix.dart'; +import 'package:matrix/src/utils/run_in_root.dart'; const megolmKey = EventTypes.MegolmBackup; @@ -445,13 +445,17 @@ class KeyManager { if (sess != null) { return sess; } - _pendingNewOutboundGroupSessions[roomId] = + final newSess = _pendingNewOutboundGroupSessions[roomId] = _createOutboundGroupSession(roomId); + try { - await _pendingNewOutboundGroupSessions[roomId]; + await newSess; } finally { - return _pendingNewOutboundGroupSessions.remove(roomId)!; + _pendingNewOutboundGroupSessions + .removeWhere((_, value) => value == newSess); } + + return newSess; } /// Prepares an outbound group session for a given room ID. That is, load it from @@ -794,7 +798,7 @@ class KeyManager { /// Handle an incoming to_device event that is related to key sharing Future handleToDeviceEvent(ToDeviceEvent event) async { if (event.type == EventTypes.RoomKeyRequest) { - if (!(event.content['request_id'] is String)) { + if (event.content['request_id'] is! String) { return; // invalid event } if (event.content['action'] == 'request') { @@ -899,7 +903,7 @@ class KeyManager { return; // someone we didn't send our request to replied....better ignore this } // we add the sender key to the forwarded key chain - if (!(event.content['forwarding_curve25519_key_chain'] is List)) { + if (event.content['forwarding_curve25519_key_chain'] is! List) { event.content['forwarding_curve25519_key_chain'] = []; } event.content['forwarding_curve25519_key_chain'] diff --git a/lib/encryption/key_verification_manager.dart b/lib/encryption/key_verification_manager.dart index 2b1dbfb2..f636dd77 100644 --- a/lib/encryption/key_verification_manager.dart +++ b/lib/encryption/key_verification_manager.dart @@ -16,9 +16,9 @@ * along with this program. If not, see . */ -import '../matrix.dart'; -import 'encryption.dart'; -import 'utils/key_verification.dart'; +import 'package:matrix/encryption/encryption.dart'; +import 'package:matrix/encryption/utils/key_verification.dart'; +import 'package:matrix/matrix.dart'; class KeyVerificationManager { final Encryption encryption; diff --git a/lib/encryption/olm_manager.dart b/lib/encryption/olm_manager.dart index 0a040733..80293246 100644 --- a/lib/encryption/olm_manager.dart +++ b/lib/encryption/olm_manager.dart @@ -23,11 +23,11 @@ import 'package:canonical_json/canonical_json.dart'; import 'package:collection/collection.dart'; import 'package:olm/olm.dart' as olm; +import 'package:matrix/encryption/encryption.dart'; +import 'package:matrix/encryption/utils/json_signature_check_extension.dart'; +import 'package:matrix/encryption/utils/olm_session.dart'; import 'package:matrix/matrix.dart'; -import '../encryption/utils/json_signature_check_extension.dart'; -import '../src/utils/run_in_root.dart'; -import 'encryption.dart'; -import 'utils/olm_session.dart'; +import 'package:matrix/src/utils/run_in_root.dart'; class OlmManager { final Encryption encryption; @@ -119,8 +119,8 @@ class OlmManager { bool updateDatabase = true, bool? unusedFallbackKey = false, }) async { - final _olmAccount = this._olmAccount; - if (_olmAccount == null) { + final olmAccount = _olmAccount; + if (olmAccount == null) { return true; } @@ -136,22 +136,22 @@ class OlmManager { // check if we have OTKs that still need uploading. If we do, we don't try to generate new ones, // instead we try to upload the old ones first final oldOTKsNeedingUpload = json - .decode(_olmAccount.one_time_keys())['curve25519'] + .decode(olmAccount.one_time_keys())['curve25519'] .entries .length as int; // generate one-time keys // we generate 2/3rds of max, so that other keys people may still have can // still be used final oneTimeKeysCount = - (_olmAccount.max_number_of_one_time_keys() * 2 / 3).floor() - + (olmAccount.max_number_of_one_time_keys() * 2 / 3).floor() - oldKeyCount - oldOTKsNeedingUpload; if (oneTimeKeysCount > 0) { - _olmAccount.generate_one_time_keys(oneTimeKeysCount); + olmAccount.generate_one_time_keys(oneTimeKeysCount); } uploadedOneTimeKeysCount = oneTimeKeysCount + oldOTKsNeedingUpload; final Map oneTimeKeys = - json.decode(_olmAccount.one_time_keys()); + json.decode(olmAccount.one_time_keys()); // now sign all the one-time keys for (final entry in oneTimeKeys['curve25519'].entries) { @@ -166,8 +166,8 @@ class OlmManager { final signedFallbackKeys = {}; if (encryption.isMinOlmVersion(3, 2, 0) && unusedFallbackKey == false) { // we don't have an unused fallback key uploaded....so let's change that! - _olmAccount.generate_fallback_key(); - final fallbackKey = json.decode(_olmAccount.fallback_key()); + olmAccount.generate_fallback_key(); + final fallbackKey = json.decode(olmAccount.fallback_key()); // now sign all the fallback keys for (final entry in fallbackKey['curve25519'].entries) { final key = entry.key; @@ -194,7 +194,7 @@ class OlmManager { }; if (uploadDeviceKeys) { final Map keys = - json.decode(_olmAccount.identity_keys()); + json.decode(olmAccount.identity_keys()); for (final entry in keys.entries) { final algorithm = entry.key; final value = entry.value; @@ -228,7 +228,7 @@ class OlmManager { } // mark the OTKs as published and save that to datbase - _olmAccount.mark_keys_as_published(); + olmAccount.mark_keys_as_published(); if (updateDatabase) { await client.database?.updateClientKeys(pickledOlmAccount!); } @@ -324,7 +324,8 @@ class OlmManager { final device = client.userDeviceKeys[event.sender]?.deviceKeys.values .firstWhereOrNull((d) => d.curve25519Key == senderKey); final existingSessions = olmSessions[senderKey]; - final updateSessionUsage = ([OlmSession? session]) => runInRoot(() async { + Future updateSessionUsage([OlmSession? session]) => + runInRoot(() async { if (session != null) { session.lastReceived = DateTime.now(); await storeOlmSession(session); @@ -480,10 +481,11 @@ class OlmManager { return event; } final senderKey = event.parsedRoomEncryptedContent.senderKey; - final loadFromDb = () async { + Future loadFromDb() async { final sessions = await getOlmSessions(senderKey); return sessions.isNotEmpty; - }; + } + if (!_olmSessions.containsKey(senderKey)) { await loadFromDb(); } diff --git a/lib/encryption/ssss.dart b/lib/encryption/ssss.dart index 6d001592..b8828d2c 100644 --- a/lib/encryption/ssss.dart +++ b/lib/encryption/ssss.dart @@ -25,12 +25,12 @@ import 'package:base58check/base58.dart'; import 'package:collection/collection.dart'; import 'package:crypto/crypto.dart'; +import 'package:matrix/encryption/encryption.dart'; import 'package:matrix/encryption/utils/base64_unpadded.dart'; -import '../matrix.dart'; -import '../src/utils/crypto/crypto.dart' as uc; -import '../src/utils/run_in_root.dart'; -import 'encryption.dart'; -import 'utils/ssss_cache.dart'; +import 'package:matrix/encryption/utils/ssss_cache.dart'; +import 'package:matrix/matrix.dart'; +import 'package:matrix/src/utils/crypto/crypto.dart' as uc; +import 'package:matrix/src/utils/run_in_root.dart'; const cacheTypes = { EventTypes.CrossSigningSelfSigning, @@ -286,7 +286,7 @@ class SSSS { if (keys == null) { return null; } - final isValid = (dbEntry) => + bool isValid(dbEntry) => keys.contains(dbEntry.keyId) && dbEntry.ciphertext != null && client.accountData[type]?.content['encrypted'][dbEntry.keyId] @@ -311,10 +311,10 @@ class SSSS { if (secretInfo == null) { throw Exception('Not found'); } - if (!(secretInfo.content['encrypted'] is Map)) { + if (secretInfo.content['encrypted'] is! Map) { throw Exception('Content is not encrypted'); } - if (!(secretInfo.content['encrypted'][keyId] is Map)) { + if (secretInfo.content['encrypted'][keyId] is! Map) { throw Exception('Wrong / unknown key'); } final enc = secretInfo.content['encrypted'][keyId]; @@ -338,7 +338,7 @@ class SSSS { Map? content; if (add && client.accountData[type] != null) { content = client.accountData[type]!.content.copy(); - if (!(content['encrypted'] is Map)) { + if (content['encrypted'] is! Map) { content['encrypted'] = {}; } } @@ -526,7 +526,7 @@ class SSSS { return; // someone replied whom we didn't send the share request to } final secret = event.content['secret']; - if (!(event.content['secret'] is String)) { + if (event.content['secret'] is! String) { Logs().i('[SSSS] Secret wasn\'t a string'); return; // the secret wasn't a string....wut? } diff --git a/lib/encryption/utils/bootstrap.dart b/lib/encryption/utils/bootstrap.dart index 62482dcc..66672ca9 100644 --- a/lib/encryption/utils/bootstrap.dart +++ b/lib/encryption/utils/bootstrap.dart @@ -22,11 +22,11 @@ import 'dart:typed_data'; import 'package:canonical_json/canonical_json.dart'; import 'package:olm/olm.dart' as olm; -import '../../matrix.dart'; -import '../encryption.dart'; -import '../key_manager.dart'; -import '../ssss.dart'; -import 'base64_unpadded.dart'; +import 'package:matrix/encryption/encryption.dart'; +import 'package:matrix/encryption/key_manager.dart'; +import 'package:matrix/encryption/ssss.dart'; +import 'package:matrix/encryption/utils/base64_unpadded.dart'; +import 'package:matrix/matrix.dart'; enum BootstrapState { /// Is loading. @@ -104,7 +104,7 @@ class Bootstrap { for (final entry in client.accountData.entries) { final type = entry.key; final event = entry.value; - if (!(event.content['encrypted'] is Map)) { + if (event.content['encrypted'] is! Map) { continue; } final validKeys = {}; @@ -112,13 +112,13 @@ class Bootstrap { for (final keyEntry in event.content['encrypted'].entries) { final key = keyEntry.key; final value = keyEntry.value; - if (!(value is Map)) { + if (value is! Map) { // we don't add the key to invalidKeys as this was not a proper secret anyways! continue; } - if (!(value['iv'] is String) || - !(value['ciphertext'] is String) || - !(value['mac'] is String)) { + if (value['iv'] is! String || + value['ciphertext'] is! String || + value['mac'] is! String) { invalidKeys.add(key); continue; } @@ -163,11 +163,12 @@ class Bootstrap { (k, v) => v.isEmpty); // we don't care about the failed secrets here final keys = {}; final defaultKeyId = encryption.ssss.defaultKeyId; - final removeKey = (String key) { + int removeKey(String key) { final sizeBefore = secrets.length; secrets.removeWhere((k, v) => v.contains(key)); return sizeBefore - secrets.length; - }; + } + // first we want to try the default key id if (defaultKeyId != null) { if (removeKey(defaultKeyId) > 0) { @@ -264,14 +265,15 @@ class Bootstrap { if (oldSsssKeys != null) { // alright, we have to re-encrypt old secrets with the new key final secrets = analyzeSecrets(); - final removeKey = (String key) { + Set removeKey(String key) { final s = secrets.entries .where((e) => e.value.contains(key)) .map((e) => e.key) .toSet(); secrets.removeWhere((k, v) => v.contains(key)); return s; - }; + } + secretMap = {}; for (final entry in oldSsssKeys!.entries) { final key = entry.value; @@ -400,7 +402,7 @@ class Bootstrap { master.free(); } } - final _sign = (Map object) { + String? sign(Map object) { final keyObj = olm.PkSigning(); try { keyObj.init_with_seed(masterSigningKey); @@ -409,7 +411,8 @@ class Bootstrap { } finally { keyObj.free(); } - }; + } + if (setupSelfSigningKey) { final selfSigning = olm.PkSigning(); try { @@ -422,7 +425,7 @@ class Bootstrap { 'ed25519:$selfSigningPub': selfSigningPub, }, }; - final signature = _sign(json); + final signature = sign(json); json['signatures'] = { userID: { 'ed25519:$masterPub': signature, @@ -447,7 +450,7 @@ class Bootstrap { 'ed25519:$userSigningPub': userSigningPub, }, }; - final signature = _sign(json); + final signature = sign(json); json['signatures'] = { userID: { 'ed25519:$masterPub': signature, diff --git a/lib/encryption/utils/json_signature_check_extension.dart b/lib/encryption/utils/json_signature_check_extension.dart index 63afe7b2..f53cdad9 100644 --- a/lib/encryption/utils/json_signature_check_extension.dart +++ b/lib/encryption/utils/json_signature_check_extension.dart @@ -19,14 +19,14 @@ import 'package:canonical_json/canonical_json.dart'; import 'package:olm/olm.dart' as olm; -import '../../matrix.dart'; +import 'package:matrix/matrix.dart'; extension JsonSignatureCheckExtension on Map { /// Checks the signature of a signed json object. bool checkJsonSignature(String key, String userId, String deviceId) { final signatures = this['signatures']; if (signatures == null || - !(signatures is Map) || + signatures is! Map || !signatures.containsKey(userId)) return false; remove('unsigned'); remove('signatures'); diff --git a/lib/encryption/utils/key_verification.dart b/lib/encryption/utils/key_verification.dart index 9f07cc39..c0cdcfc1 100644 --- a/lib/encryption/utils/key_verification.dart +++ b/lib/encryption/utils/key_verification.dart @@ -22,8 +22,8 @@ import 'dart:typed_data'; import 'package:canonical_json/canonical_json.dart'; import 'package:olm/olm.dart' as olm; -import '../../matrix.dart'; -import '../encryption.dart'; +import 'package:matrix/encryption/encryption.dart'; +import 'package:matrix/matrix.dart'; /* +-------------+ +-----------+ @@ -112,7 +112,7 @@ class KeyVerification { String? get deviceId => _deviceId; String? _deviceId; bool startedVerification = false; - _KeyVerificationMethod? method; + _KeyVerificationMethod? _method; List possibleMethods = []; Map? startPayload; String? _nextAction; @@ -140,7 +140,7 @@ class KeyVerification { void dispose() { Logs().i('[Key Verification] disposing object...'); - method?.dispose(); + _method?.dispose(); } static String? getTransactionId(Map payload) { @@ -194,7 +194,7 @@ class KeyVerification { await Future.delayed(Duration(milliseconds: 50)); } _handlePayloadLock = true; - Logs().i('[Key Verification] Received type $type: ' + payload.toString()); + Logs().i('[Key Verification] Received type $type: $payload'); try { var thisLastStep = lastStep; switch (type) { @@ -272,17 +272,17 @@ class KeyVerification { // as such, we better set it *before* we send our start lastStep = type; // TODO: Pick method? - final method = this.method = - _makeVerificationMethod(possibleMethods.first, this); + final method = + _method = _makeVerificationMethod(possibleMethods.first, this); await method.sendStart(); setState(KeyVerificationState.waitingAccept); break; case EventTypes.KeyVerificationStart: _deviceId ??= payload['from_device']; transactionId ??= eventId ?? payload['transaction_id']; - if (method != null) { + if (_method != null) { // the other side sent us a start, even though we already sent one - if (payload['method'] == method!.type) { + if (payload['method'] == _method!.type) { // same method. Determine priority final ourEntry = '${client.userID}|${client.deviceID}'; final entries = [ourEntry, '$userId|$deviceId']; @@ -295,7 +295,7 @@ class KeyVerification { startedVerification = false; // it is now as if they started thisLastStep = lastStep = EventTypes.KeyVerificationRequest; // we fake the last step - method!.dispose(); // in case anything got created already + _method!.dispose(); // in case anything got created already } } else { // methods don't match up, let's cancel this @@ -321,7 +321,7 @@ class KeyVerification { } } - method = _makeVerificationMethod(payload['method'], this); + _method = _makeVerificationMethod(payload['method'], this); if (lastStep == null) { // validate the start time if (room != null) { @@ -330,7 +330,7 @@ class KeyVerification { return; } // validate the specific payload - if (!method!.validateStart(payload)) { + if (!_method!.validateStart(payload)) { await cancel('m.unknown_method'); return; } @@ -338,7 +338,7 @@ class KeyVerification { setState(KeyVerificationState.askAccept); } else { Logs().i('handling start in method.....'); - await method!.handlePayload(type, payload); + await _method!.handlePayload(type, payload); } break; case EventTypes.KeyVerificationDone: @@ -351,7 +351,7 @@ class KeyVerification { setState(KeyVerificationState.error); break; default: - final method = this.method; + final method = _method; if (method != null) { await method.handlePayload(type, payload); } else { @@ -382,7 +382,7 @@ class KeyVerification { String? recoveryKey, String? keyOrPassphrase, bool skip = false}) async { - final next = () async { + Future next() async { if (_nextAction == 'request') { await sendStart(); } else if (_nextAction == 'done') { @@ -390,7 +390,8 @@ class KeyVerification { unawaited(encryption.crossSigning.sign(_verifiedDevices)); setState(KeyVerificationState.done); } - }; + } + if (skip) { await next(); return; @@ -420,7 +421,7 @@ class KeyVerification { }); } else { // we need to send an accept event - await method! + await _method! .handlePayload(EventTypes.KeyVerificationStart, startPayload!); } } @@ -440,20 +441,20 @@ class KeyVerification { } Future acceptSas() async { - if (method is _KeyVerificationMethodSas) { - await (method as _KeyVerificationMethodSas).acceptSas(); + if (_method is _KeyVerificationMethodSas) { + await (_method as _KeyVerificationMethodSas).acceptSas(); } } Future rejectSas() async { - if (method is _KeyVerificationMethodSas) { - await (method as _KeyVerificationMethodSas).rejectSas(); + if (_method is _KeyVerificationMethodSas) { + await (_method as _KeyVerificationMethodSas).rejectSas(); } } List get sasNumbers { - if (method is _KeyVerificationMethodSas) { - return _bytesToInt((method as _KeyVerificationMethodSas).makeSas(5), 13) + if (_method is _KeyVerificationMethodSas) { + return _bytesToInt((_method as _KeyVerificationMethodSas).makeSas(5), 13) .map((n) => n + 1000) .toList(); } @@ -461,16 +462,16 @@ class KeyVerification { } List get sasTypes { - if (method is _KeyVerificationMethodSas) { - return (method as _KeyVerificationMethodSas).authenticationTypes ?? []; + if (_method is _KeyVerificationMethodSas) { + return (_method as _KeyVerificationMethodSas).authenticationTypes ?? []; } return []; } List get sasEmojis { - if (method is _KeyVerificationMethodSas) { + if (_method is _KeyVerificationMethodSas) { final numbers = - _bytesToInt((method as _KeyVerificationMethodSas).makeSas(6), 6); + _bytesToInt((_method as _KeyVerificationMethodSas).makeSas(6), 6); return numbers.map((n) => KeyVerificationEmoji(n)).toList().sublist(0, 7); } return []; @@ -605,7 +606,7 @@ class KeyVerification { Future send(String type, Map payload) async { makePayload(payload); - Logs().i('[Key Verification] Sending type $type: ' + payload.toString()); + Logs().i('[Key Verification] Sending type $type: $payload'); if (room != null) { Logs().i('[Key Verification] Sending to $userId in room ${room!.id}...'); if ({EventTypes.KeyVerificationRequest}.contains(type)) { @@ -680,6 +681,7 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod { : super(request: request); @override + // ignore: overridden_fields final _type = 'm.sas.v1'; String? keyAgreementProtocol; @@ -897,19 +899,13 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod { '${client.userID}|${client.deviceID}|${sas!.get_pubkey()}|'; final theirInfo = '${request.userId}|${request.deviceId}|$theirPublicKey|'; - sasInfo = 'MATRIX_KEY_VERIFICATION_SAS|' + - (request.startedVerification - ? ourInfo + theirInfo - : theirInfo + ourInfo) + - request.transactionId!; + sasInfo = + 'MATRIX_KEY_VERIFICATION_SAS|${request.startedVerification ? ourInfo + theirInfo : theirInfo + ourInfo}${request.transactionId!}'; } else if (keyAgreementProtocol == 'curve25519') { final ourInfo = client.userID! + client.deviceID!; final theirInfo = request.userId + request.deviceId!; - sasInfo = 'MATRIX_KEY_VERIFICATION_SAS' + - (request.startedVerification - ? ourInfo + theirInfo - : theirInfo + ourInfo) + - request.transactionId!; + sasInfo = + 'MATRIX_KEY_VERIFICATION_SAS${request.startedVerification ? ourInfo + theirInfo : theirInfo + ourInfo}${request.transactionId!}'; } else { throw Exception('Unknown key agreement protocol'); } @@ -917,12 +913,8 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod { } Future _sendMac() async { - final baseInfo = 'MATRIX_KEY_VERIFICATION_MAC' + - client.userID! + - client.deviceID! + - request.userId + - request.deviceId! + - request.transactionId!; + final baseInfo = + 'MATRIX_KEY_VERIFICATION_MAC${client.userID!}${client.deviceID!}${request.userId}${request.deviceId!}${request.transactionId!}'; final mac = {}; final keyList = []; @@ -944,7 +936,7 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod { } keyList.sort(); - final keys = _calculateMac(keyList.join(','), baseInfo + 'KEY_IDS'); + final keys = _calculateMac(keyList.join(','), '${baseInfo}KEY_IDS'); await request.send('m.key.verification.mac', { 'mac': mac, 'keys': keys, @@ -953,17 +945,13 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod { Future _processMac() async { final payload = macPayload!; - final baseInfo = 'MATRIX_KEY_VERIFICATION_MAC' + - request.userId + - request.deviceId! + - client.userID! + - client.deviceID! + - request.transactionId!; + final baseInfo = + 'MATRIX_KEY_VERIFICATION_MAC${request.userId}${request.deviceId!}${client.userID!}${client.deviceID!}${request.transactionId!}'; final keyList = payload['mac'].keys.toList(); keyList.sort(); if (payload['keys'] != - _calculateMac(keyList.join(','), baseInfo + 'KEY_IDS')) { + _calculateMac(keyList.join(','), '${baseInfo}KEY_IDS')) { await request.cancel('m.key_mismatch'); return; } @@ -981,7 +969,7 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod { await request.verifyKeys(mac, (String mac, SignableKey key) async { return mac == _calculateMac( - key.ed25519Key!, baseInfo + 'ed25519:' + key.identifier!); + key.ed25519Key!, '${baseInfo}ed25519:${key.identifier!}'); }); } diff --git a/lib/encryption/utils/olm_session.dart b/lib/encryption/utils/olm_session.dart index 2d66b3ae..513699ae 100644 --- a/lib/encryption/utils/olm_session.dart +++ b/lib/encryption/utils/olm_session.dart @@ -18,7 +18,7 @@ import 'package:olm/olm.dart' as olm; -import '../../matrix.dart'; +import 'package:matrix/matrix.dart'; class OlmSession { String identityKey; @@ -38,9 +38,8 @@ class OlmSession { required this.lastReceived, }); - OlmSession.fromJson(Map dbEntry, String key) - : key = key, - identityKey = dbEntry['identity_key'] ?? '' { + OlmSession.fromJson(Map dbEntry, this.key) + : identityKey = dbEntry['identity_key'] ?? '' { session = olm.Session(); try { session!.unpickle(key, dbEntry['pickle']); diff --git a/lib/encryption/utils/outbound_group_session.dart b/lib/encryption/utils/outbound_group_session.dart index 45d9a090..ef6bb52d 100644 --- a/lib/encryption/utils/outbound_group_session.dart +++ b/lib/encryption/utils/outbound_group_session.dart @@ -20,7 +20,7 @@ import 'dart:convert'; import 'package:olm/olm.dart' as olm; -import '../../matrix.dart'; +import 'package:matrix/matrix.dart'; class OutboundGroupSession { /// The devices is a map from user id to device id to if the device is blocked. @@ -41,8 +41,7 @@ class OutboundGroupSession { required this.outboundGroupSession, required this.key}); - OutboundGroupSession.fromJson(Map dbEntry, String key) - : key = key { + OutboundGroupSession.fromJson(Map dbEntry, this.key) { try { for (final entry in json.decode(dbEntry['device_ids']).entries) { devices[entry.key] = Map.from(entry.value); @@ -50,8 +49,7 @@ class OutboundGroupSession { } catch (e) { // devices is bad (old data), so just not use this session Logs().i( - '[OutboundGroupSession] Session in database is old, not using it. ' + - e.toString()); + '[OutboundGroupSession] Session in database is old, not using it. $e'); return; } outboundGroupSession = olm.OutboundGroupSession(); diff --git a/lib/encryption/utils/session_key.dart b/lib/encryption/utils/session_key.dart index e219f658..4966788a 100644 --- a/lib/encryption/utils/session_key.dart +++ b/lib/encryption/utils/session_key.dart @@ -20,7 +20,7 @@ import 'package:matrix_api_lite/src/utils/filter_map_extension.dart'; import 'package:olm/olm.dart' as olm; import 'package:matrix/encryption/utils/stored_inbound_group_session.dart'; -import '../../matrix.dart'; +import 'package:matrix/matrix.dart'; class SessionKey { /// The raw json content of the key @@ -74,9 +74,8 @@ class SessionKey { : indexes = indexes ?? {}, allowedAtIndex = allowedAtIndex ?? >{}; - SessionKey.fromDb(StoredInboundGroupSession dbEntry, String key) - : key = key, - content = Event.getMapFromPayload(dbEntry.content), + SessionKey.fromDb(StoredInboundGroupSession dbEntry, this.key) + : content = Event.getMapFromPayload(dbEntry.content), indexes = Event.getMapFromPayload(dbEntry.indexes) .catchMap((k, v) => MapEntry(k, v)), allowedAtIndex = Event.getMapFromPayload(dbEntry.allowedAtIndex) diff --git a/lib/msc_extensions/msc_1236_widgets/msc_1236_widgets.dart b/lib/msc_extensions/msc_1236_widgets/msc_1236_widgets.dart index 85eb3773..b694e1aa 100644 --- a/lib/msc_extensions/msc_1236_widgets/msc_1236_widgets.dart +++ b/lib/msc_extensions/msc_1236_widgets/msc_1236_widgets.dart @@ -19,7 +19,7 @@ extension MatrixWidgets on Room { Future addWidget(MatrixWidget widget) { final user = client.userID; final widgetId = - widget.name!.toLowerCase().replaceAll(RegExp(r'\W'), '_') + '_' + user!; + '${widget.name!.toLowerCase().replaceAll(RegExp(r'\W'), '_')}_${user!}'; final json = widget.toJson(); json['creatorUserId'] = user; diff --git a/lib/src/client.dart b/lib/src/client.dart index 1b0a986c..4900ac2c 100644 --- a/lib/src/client.dart +++ b/lib/src/client.dart @@ -27,15 +27,15 @@ import 'package:mime/mime.dart'; import 'package:olm/olm.dart' as olm; import 'package:random_string/random_string.dart'; +import 'package:matrix/encryption.dart'; +import 'package:matrix/matrix.dart'; +import 'package:matrix/src/models/timeline_chunk.dart'; import 'package:matrix/src/utils/cached_stream_controller.dart'; +import 'package:matrix/src/utils/compute_callback.dart'; +import 'package:matrix/src/utils/multilock.dart'; +import 'package:matrix/src/utils/run_benchmarked.dart'; import 'package:matrix/src/utils/run_in_root.dart'; import 'package:matrix/src/utils/sync_update_item_count.dart'; -import '../encryption.dart'; -import '../matrix.dart'; -import 'models/timeline_chunk.dart'; -import 'utils/compute_callback.dart'; -import 'utils/multilock.dart'; -import 'utils/run_benchmarked.dart'; typedef RoomSorter = int Function(Room a, Room b); @@ -940,28 +940,30 @@ class Client extends MatrixApi { try { // stopping sync loop and subscriptions while keeping DB open await dispose(closeDatabase: false); - } finally { - _database ??= await databaseBuilder!.call(this); - - final success = await database!.importDump(export); - - if (success) { - // closing including DB - await dispose(); - - try { - bearerToken = null; - - await init( - waitForFirstSync: false, - waitUntilLoadCompletedLoaded: false, - ); - } catch (e) { - return false; - } - } - return success; + } catch (_) { + // Client was probably not initialized yet. } + + _database ??= await databaseBuilder!.call(this); + + final success = await database!.importDump(export); + + if (success) { + // closing including DB + await dispose(); + + try { + bearerToken = null; + + await init( + waitForFirstSync: false, + waitUntilLoadCompletedLoaded: false, + ); + } catch (e) { + return false; + } + } + return success; } /// Uploads a new user avatar for this user. Leave file null to remove the @@ -1336,13 +1338,13 @@ class Client extends MatrixApi { String? olmAccount; String? accessToken; - String? _userID; + String? userID; final account = await this.database?.getClient(clientName); if (account != null) { _id = account['client_id']; homeserver = Uri.parse(account['homeserver_url']); accessToken = this.accessToken = account['token']; - _userID = this._userID = account['user_id']; + userID = _userID = account['user_id']; _deviceID = account['device_id']; _deviceName = account['device_name']; syncFilterId = account['sync_filter_id']; @@ -1352,20 +1354,20 @@ class Client extends MatrixApi { if (newToken != null) { accessToken = this.accessToken = newToken; homeserver = newHomeserver; - _userID = this._userID = newUserID; + userID = _userID = newUserID; _deviceID = newDeviceID; _deviceName = newDeviceName; olmAccount = newOlmAccount; } else { accessToken = this.accessToken = newToken ?? accessToken; homeserver = newHomeserver ?? homeserver; - _userID = this._userID = newUserID ?? _userID; + userID = _userID = newUserID ?? userID; _deviceID = newDeviceID ?? _deviceID; _deviceName = newDeviceName ?? _deviceName; olmAccount = newOlmAccount ?? olmAccount; } - if (accessToken == null || homeserver == null || _userID == null) { + if (accessToken == null || homeserver == null || userID == null) { if (legacyDatabaseBuilder != null) { await _migrateFromLegacyDatabase(); if (isLogged()) return; @@ -1397,7 +1399,7 @@ class Client extends MatrixApi { await database.updateClient( homeserver.toString(), accessToken, - _userID, + userID, _deviceID, _deviceName, prevBatch, @@ -1408,7 +1410,7 @@ class Client extends MatrixApi { clientName, homeserver.toString(), accessToken, - _userID, + userID, _deviceID, _deviceName, prevBatch, @@ -1434,7 +1436,7 @@ class Client extends MatrixApi { _initLock = false; onLoginStateChanged.add(LoginState.loggedIn); Logs().i( - 'Successfully connected as ${userID?.localpart} with ${homeserver.toString()}', + 'Successfully connected as ${userID.localpart} with ${homeserver.toString()}', ); final syncFuture = _sync(); @@ -1508,13 +1510,13 @@ class Client extends MatrixApi { } Future _sync() { - final _currentSync = this._currentSync ??= _innerSync().whenComplete(() { - this._currentSync = null; + final currentSync = _currentSync ??= _innerSync().whenComplete(() { + _currentSync = null; if (_backgroundSync && isLogged() && !_disposed) { _sync(); } }); - return _currentSync; + return currentSync; } /// Presence that is set on sync. @@ -1533,13 +1535,13 @@ class Client extends MatrixApi { Future _innerSync() async { await _retryDelay; _retryDelay = Future.delayed(Duration(seconds: syncErrorTimeoutSec)); - if (!isLogged() || _disposed || _aborted) return null; + if (!isLogged() || _disposed || _aborted) return; try { if (_initLock) { Logs().d('Running sync while init isn\'t done yet, dropping request'); return; } - var syncError; + dynamic syncError; await _checkSyncFilter(); final syncRequest = sync( filter: syncFilterId, diff --git a/lib/src/database/database_api.dart b/lib/src/database/database_api.dart index 1d32f777..618b25da 100644 --- a/lib/src/database/database_api.dart +++ b/lib/src/database/database_api.dart @@ -22,8 +22,8 @@ import 'package:matrix/encryption/utils/olm_session.dart'; import 'package:matrix/encryption/utils/outbound_group_session.dart'; import 'package:matrix/encryption/utils/ssss_cache.dart'; import 'package:matrix/encryption/utils/stored_inbound_group_session.dart'; +import 'package:matrix/matrix.dart'; import 'package:matrix/src/utils/queued_to_device_event.dart'; -import '../../matrix.dart'; abstract class DatabaseApi { int get maxFileSize => 1 * 1024 * 1024; @@ -154,7 +154,7 @@ abstract class DatabaseApi { ); Future storeOlmSession( - String identitiyKey, + String identityKey, String sessionId, String pickle, int lastReceived, diff --git a/lib/src/database/fluffybox_database.dart b/lib/src/database/fluffybox_database.dart index c94c3069..226fa68b 100644 --- a/lib/src/database/fluffybox_database.dart +++ b/lib/src/database/fluffybox_database.dart @@ -448,9 +448,9 @@ class FluffyBoxDatabase extends DatabaseApi { @override Future> getOlmSessionsForDevices( - List identityKey, String userId) async { + List identityKeys, String userId) async { final sessions = await Future.wait( - identityKey.map((identityKey) => getOlmSessions(identityKey, userId))); + identityKeys.map((identityKey) => getOlmSessions(identityKey, userId))); return [for (final sublist in sessions) ...sublist]; } @@ -1433,10 +1433,9 @@ class FluffyBoxDatabase extends DatabaseApi { Future addSeenDeviceId( String userId, String deviceId, - String publicKeysHash, + String publicKeys, ) => - _seenDeviceIdsBox.put( - TupleKey(userId, deviceId).toString(), publicKeysHash); + _seenDeviceIdsBox.put(TupleKey(userId, deviceId).toString(), publicKeys); @override Future addSeenPublicKey( diff --git a/lib/src/database/hive_collections_database.dart b/lib/src/database/hive_collections_database.dart index 557b1152..55de1fa1 100644 --- a/lib/src/database/hive_collections_database.dart +++ b/lib/src/database/hive_collections_database.dart @@ -474,9 +474,9 @@ class HiveCollectionsDatabase extends DatabaseApi { @override Future> getOlmSessionsForDevices( - List identityKey, String userId) async { + List identityKeys, String userId) async { final sessions = await Future.wait( - identityKey.map((identityKey) => getOlmSessions(identityKey, userId))); + identityKeys.map((identityKey) => getOlmSessions(identityKey, userId))); return [for (final sublist in sessions) ...sublist]; } @@ -1459,10 +1459,9 @@ class HiveCollectionsDatabase extends DatabaseApi { Future addSeenDeviceId( String userId, String deviceId, - String publicKeysHash, + String publicKeys, ) => - _seenDeviceIdsBox.put( - TupleKey(userId, deviceId).toString(), publicKeysHash); + _seenDeviceIdsBox.put(TupleKey(userId, deviceId).toString(), publicKeys); @override Future addSeenPublicKey( @@ -1611,6 +1610,9 @@ class TupleKey { @override bool operator ==(other) => parts.toString() == other.toString(); + + @override + int get hashCode => Object.hashAll(parts); } dynamic _castValue(dynamic value) { diff --git a/lib/src/database/hive_database.dart b/lib/src/database/hive_database.dart index cb5f6386..2083101d 100644 --- a/lib/src/database/hive_database.dart +++ b/lib/src/database/hive_database.dart @@ -505,9 +505,9 @@ class FamedlySdkHiveDatabase extends DatabaseApi { @override Future> getOlmSessionsForDevices( - List identityKey, String userId) async { + List identityKeys, String userId) async { final sessions = await Future.wait( - identityKey.map((identityKey) => getOlmSessions(identityKey, userId))); + identityKeys.map((identityKey) => getOlmSessions(identityKey, userId))); return [for (final sublist in sessions) ...sublist]; } @@ -1380,10 +1380,9 @@ class FamedlySdkHiveDatabase extends DatabaseApi { Future addSeenDeviceId( String userId, String deviceId, - String publicKeysHash, + String publicKeys, ) => - _seenDeviceIdsBox.put( - MultiKey(userId, deviceId).toString(), publicKeysHash); + _seenDeviceIdsBox.put(MultiKey(userId, deviceId).toString(), publicKeys); @override Future addSeenPublicKey( @@ -1461,6 +1460,9 @@ class MultiKey { @override bool operator ==(other) => parts.toString() == other.toString(); + + @override + int get hashCode => Object.hashAll(parts); } extension HiveKeyExtension on String { diff --git a/lib/src/event.dart b/lib/src/event.dart index 8a2634e5..5e2ace74 100644 --- a/lib/src/event.dart +++ b/lib/src/event.dart @@ -23,11 +23,11 @@ import 'package:collection/collection.dart'; import 'package:html/parser.dart'; import 'package:http/http.dart' as http; +import 'package:matrix/matrix.dart'; +import 'package:matrix/src/utils/event_localizations.dart'; import 'package:matrix/src/utils/file_send_request_credentials.dart'; -import '../matrix.dart'; -import 'utils/event_localizations.dart'; -import 'utils/html_to_text.dart'; -import 'utils/markdown.dart'; +import 'package:matrix/src/utils/html_to_text.dart'; +import 'package:matrix/src/utils/markdown.dart'; abstract class RelationshipTypes { static const String reply = 'm.in_reply_to'; @@ -296,7 +296,7 @@ class Event extends MatrixEvent { if (redacted) return 'Redacted'; if (text != '') return text; if (formattedText != '') return formattedText; - return '$type'; + return type; } /// Use this to get a plain-text representation of the event, stripping things @@ -870,7 +870,7 @@ class Event extends MatrixEvent { bool get onlyEmotes { if (isRichMessage) { final formattedTextStripped = formattedText.replaceAll( - RegExp('.*<\/mx-reply>', + RegExp('.*', caseSensitive: false, multiLine: false, dotAll: true), ''); return _onlyEmojiEmoteRegex.hasMatch(formattedTextStripped); @@ -886,7 +886,7 @@ class Event extends MatrixEvent { int get numberEmotes { if (isRichMessage) { final formattedTextStripped = formattedText.replaceAll( - RegExp('.*<\/mx-reply>', + RegExp('.*', caseSensitive: false, multiLine: false, dotAll: true), ''); return _countEmojiEmoteRegex.allMatches(formattedTextStripped).length; diff --git a/lib/src/models/timeline_chunk.dart b/lib/src/models/timeline_chunk.dart index f4296f86..a1e7002d 100644 --- a/lib/src/models/timeline_chunk.dart +++ b/lib/src/models/timeline_chunk.dart @@ -1,4 +1,4 @@ -import '../../matrix.dart'; +import 'package:matrix/matrix.dart'; class TimelineChunk { String prevBatch; // pos of the first event of the database timeline chunk diff --git a/lib/src/presence.dart b/lib/src/presence.dart index c9ea1901..b0a7086c 100644 --- a/lib/src/presence.dart +++ b/lib/src/presence.dart @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import '../matrix.dart'; +import 'package:matrix/matrix.dart'; class CachedPresence { PresenceType presence; @@ -45,9 +45,7 @@ class CachedPresence { : this(event.presence, event.lastActiveAgo, event.statusMsg, event.currentlyActive, userid); - CachedPresence.neverSeen(String userid) - : presence = PresenceType.offline, - userid = userid; + CachedPresence.neverSeen(this.userid) : presence = PresenceType.offline; Presence toPresence() { final content = { diff --git a/lib/src/room.dart b/lib/src/room.dart index f31c00bd..f2227c19 100644 --- a/lib/src/room.dart +++ b/lib/src/room.dart @@ -23,14 +23,14 @@ import 'dart:typed_data'; import 'package:collection/collection.dart'; import 'package:html_unescape/html_unescape.dart'; +import 'package:matrix/matrix.dart'; import 'package:matrix/src/models/timeline_chunk.dart'; import 'package:matrix/src/utils/cached_stream_controller.dart'; import 'package:matrix/src/utils/crypto/crypto.dart'; import 'package:matrix/src/utils/file_send_request_credentials.dart'; +import 'package:matrix/src/utils/markdown.dart'; +import 'package:matrix/src/utils/marked_unread.dart'; import 'package:matrix/src/utils/space_child.dart'; -import '../matrix.dart'; -import 'utils/markdown.dart'; -import 'utils/marked_unread.dart'; enum PushRuleState { notify, mentionsOnly, dontNotify } @@ -907,7 +907,7 @@ class Room { } inPrefix = false; - temp += temp.isEmpty ? l : ('\n' + l); + temp += temp.isEmpty ? l : ('\n$l'); } return temp; @@ -936,7 +936,7 @@ class Room { if (inReplyTo != null) { var replyText = - '<${inReplyTo.senderId}> ' + _stripBodyFallback(inReplyTo.body); + '<${inReplyTo.senderId}> ${_stripBodyFallback(inReplyTo.body)}'; replyText = replyText.split('\n').map((line) => '> $line').join('\n'); content['format'] = 'org.matrix.custom.html'; // be sure that we strip any previous reply fallbacks @@ -971,10 +971,10 @@ class Room { 'rel_type': RelationshipTypes.edit, }; if (content['body'] is String) { - content['body'] = '* ' + content['body']; + content['body'] = '* ${content['body']}'; } if (content['formatted_body'] is String) { - content['formatted_body'] = '* ' + content['formatted_body']; + content['formatted_body'] = '* ${content['formatted_body']}'; } } final sentDate = DateTime.now(); @@ -1113,7 +1113,7 @@ class Room { /// power level event, there might something broken and this returns null. Future setPower(String userID, int power) async { var powerMap = getState(EventTypes.RoomPowerLevels)?.content; - if (!(powerMap is Map)) { + if (powerMap is! Map) { powerMap = {}; } (powerMap['users'] ??= {})[userID] = power; @@ -1155,7 +1155,7 @@ class Room { if (onHistoryReceived != null) onHistoryReceived(); this.prev_batch = resp.end; - final loadFn = () async { + Future loadFn() async { if (!((resp.chunk.isNotEmpty) && resp.end != null)) return; await client.handleSync( @@ -1196,7 +1196,7 @@ class Room { : null), ), direction: Direction.b); - }; + } if (client.database != null) { await client.database?.transaction(() async { @@ -1331,7 +1331,7 @@ class Room { String? eventContextId}) async { await postLoad(); - var events; + List events; if (!isArchived) { events = await client.database?.getEventList( @@ -1762,7 +1762,7 @@ class Room { PushRuleState get pushRuleState { final globalPushRules = client.accountData['m.push_rules']?.content['global']; - if (!(globalPushRules is Map)) { + if (globalPushRules is! Map) { return PushRuleState.notify; } @@ -1794,7 +1794,7 @@ class Room { /// Sends a request to the homeserver to set the [PushRuleState] for this room. /// Returns ErrorResponse if something goes wrong. Future setPushRuleState(PushRuleState newState) async { - if (newState == pushRuleState) return null; + if (newState == pushRuleState) return; dynamic resp; switch (newState) { // All push notifications should be sent to the user @@ -2076,6 +2076,9 @@ class Room { @override bool operator ==(dynamic other) => (other is Room && other.id == id); + + @override + int get hashCode => Object.hashAll([id]); } enum EncryptionHealthState { diff --git a/lib/src/timeline.dart b/lib/src/timeline.dart index 581bfdde..c230b8d4 100644 --- a/lib/src/timeline.dart +++ b/lib/src/timeline.dart @@ -19,10 +19,10 @@ import 'dart:async'; import 'dart:convert'; -import 'package:collection/src/iterable_extensions.dart'; +import 'package:collection/collection.dart'; -import '../matrix.dart'; -import 'models/timeline_chunk.dart'; +import 'package:matrix/matrix.dart'; +import 'package:matrix/src/models/timeline_chunk.dart'; /// Represents the timeline of a room. The callback [onUpdate] will be triggered /// automatically. The initial @@ -305,7 +305,7 @@ class Timeline { void _sessionKeyReceived(String sessionId) async { var decryptAtLeastOneEvent = false; - final decryptFn = () async { + Future decryptFn() async { final encryption = room.client.encryption; if (!room.client.encryptionEnabled || encryption == null) { return; @@ -322,7 +322,8 @@ class Timeline { } } } - }; + } + if (room.client.database != null) { await room.client.database?.transaction(decryptFn); } else { diff --git a/lib/src/user.dart b/lib/src/user.dart index dcff2dcd..f7321baa 100644 --- a/lib/src/user.dart +++ b/lib/src/user.dart @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import '../matrix.dart'; +import 'package:matrix/matrix.dart'; /// Represents a Matrix User which may be a participant in a Matrix Room. class User extends Event { @@ -80,7 +80,7 @@ class User extends Event { /// ban Membership get membership => Membership.values.firstWhere((e) { if (content['membership'] != null) { - return e.toString() == 'Membership.' + content['membership']; + return e.toString() == 'Membership.${content['membership']}'; } return false; }, orElse: () => Membership.join); @@ -197,6 +197,9 @@ class User extends Event { other.room == room && other.membership == membership); + @override + int get hashCode => Object.hash(id, room, membership); + /// Get the mention text to use in a plain text body to mention this specific user /// in this specific room String get mention { @@ -210,11 +213,8 @@ class User extends Event { return id; } - final identifier = '@' + - // if we have non-word characters we need to surround with [] - (RegExp(r'^\w+$').hasMatch(displayName) - ? displayName - : '[$displayName]'); + final identifier = + '@${RegExp(r'^\w+$').hasMatch(displayName) ? displayName : '[$displayName]'}'; // get all the users with the same display name final allUsersWithSameDisplayname = room.getParticipants(); @@ -243,11 +243,8 @@ class User extends Event { {'[', ']', ':'}.any(displayName.contains)) { return {}; } - final identifier = '@' + - // if we have non-word characters we need to surround with [] - (RegExp(r'^\w+$').hasMatch(displayName) - ? displayName - : '[$displayName]'); + final identifier = + '@${RegExp(r'^\w+$').hasMatch(displayName) ? displayName : '[$displayName]'}'; final hash = _hash(id); return {identifier, '$identifier#$hash'}; diff --git a/lib/src/utils/commands_extension.dart b/lib/src/utils/commands_extension.dart index 038ab4e4..6a4ca5e7 100644 --- a/lib/src/utils/commands_extension.dart +++ b/lib/src/utils/commands_extension.dart @@ -18,7 +18,7 @@ import 'dart:async'; -import '../../matrix.dart'; +import 'package:matrix/matrix.dart'; extension CommandsClientExtension on Client { /// Add a command to the command handler. `command` is its name, and `callback` is the diff --git a/lib/src/utils/crypto/encrypted_file.dart b/lib/src/utils/crypto/encrypted_file.dart index 163a420b..c5b35488 100644 --- a/lib/src/utils/crypto/encrypted_file.dart +++ b/lib/src/utils/crypto/encrypted_file.dart @@ -20,7 +20,7 @@ import 'dart:convert'; import 'dart:typed_data'; import 'package:matrix/encryption/utils/base64_unpadded.dart'; -import 'crypto.dart'; +import 'package:matrix/src/utils/crypto/crypto.dart'; class EncryptedFile { EncryptedFile({ diff --git a/lib/src/utils/crypto/js.dart b/lib/src/utils/crypto/js.dart index 7b6ad5f2..48429ca0 100644 --- a/lib/src/utils/crypto/js.dart +++ b/lib/src/utils/crypto/js.dart @@ -3,8 +3,8 @@ import 'dart:typed_data'; -import 'subtle.dart' as subtle; -import 'subtle.dart'; +import 'package:matrix/src/utils/crypto/subtle.dart' as subtle; +import 'package:matrix/src/utils/crypto/subtle.dart'; abstract class Hash { Hash._(this.name); diff --git a/lib/src/utils/crypto/native.dart b/lib/src/utils/crypto/native.dart index e66c0b7a..8ef84ef2 100644 --- a/lib/src/utils/crypto/native.dart +++ b/lib/src/utils/crypto/native.dart @@ -4,7 +4,7 @@ import 'dart:typed_data'; import 'package:ffi/ffi.dart'; -import 'ffi.dart'; +import 'package:matrix/src/utils/crypto/ffi.dart'; abstract class Hash { Hash._(this.ptr); diff --git a/lib/src/utils/device_keys_list.dart b/lib/src/utils/device_keys_list.dart index 7265dec4..a18c90be 100644 --- a/lib/src/utils/device_keys_list.dart +++ b/lib/src/utils/device_keys_list.dart @@ -22,8 +22,8 @@ import 'package:canonical_json/canonical_json.dart'; import 'package:collection/collection.dart' show IterableExtension; import 'package:olm/olm.dart' as olm; +import 'package:matrix/encryption.dart'; import 'package:matrix/matrix.dart'; -import '../../encryption.dart'; enum UserVerifiedStatus { verified, unknown, unknownDevice } @@ -349,6 +349,9 @@ abstract class SignableKey extends MatrixSignableKey { bool operator ==(dynamic other) => (other is SignableKey && other.userId == userId && other.identifier == identifier); + + @override + int get hashCode => Object.hash(userId, identifier); } class CrossSigningKey extends SignableKey { diff --git a/lib/src/utils/event_localizations.dart b/lib/src/utils/event_localizations.dart index c3ac0e0b..135d0875 100644 --- a/lib/src/utils/event_localizations.dart +++ b/lib/src/utils/event_localizations.dart @@ -18,8 +18,8 @@ import 'package:collection/collection.dart'; -import '../../encryption.dart'; -import '../../matrix.dart'; +import 'package:matrix/encryption.dart'; +import 'package:matrix/matrix.dart'; abstract class EventLocalizations { // As we need to create the localized body off of a different set of parameters, we @@ -54,16 +54,16 @@ abstract class EventLocalizations { String errorText; switch (event.body) { case DecryptException.channelCorrupted: - errorText = i18n.channelCorruptedDecryptError + '.'; + errorText = '${i18n.channelCorruptedDecryptError}.'; break; case DecryptException.notEnabled: - errorText = i18n.encryptionNotEnabled + '.'; + errorText = '${i18n.encryptionNotEnabled}.'; break; case DecryptException.unknownAlgorithm: - errorText = i18n.unknownEncryptionAlgorithm + '.'; + errorText = '${i18n.unknownEncryptionAlgorithm}.'; break; case DecryptException.unknownSession: - errorText = i18n.noPermission + '.'; + errorText = '${i18n.noPermission}.'; break; default: errorText = body; @@ -240,7 +240,7 @@ abstract class EventLocalizations { var localizedBody = i18n.activatedEndToEndEncryption( event.senderFromMemoryOrFallback.calcDisplayname()); if (event.room.client.encryptionEnabled == false) { - localizedBody += '. ' + i18n.needPantalaimonWarning; + localizedBody += '. ${i18n.needPantalaimonWarning}'; } return localizedBody; }, diff --git a/lib/src/utils/event_update.dart b/lib/src/utils/event_update.dart index 9571367e..2f36687e 100644 --- a/lib/src/utils/event_update.dart +++ b/lib/src/utils/event_update.dart @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import '../../matrix.dart'; +import 'package:matrix/matrix.dart'; enum EventUpdateType { timeline, diff --git a/lib/src/utils/html_to_text.dart b/lib/src/utils/html_to_text.dart index ba216ccc..d5ac20c3 100644 --- a/lib/src/utils/html_to_text.dart +++ b/lib/src/utils/html_to_text.dart @@ -32,7 +32,7 @@ class HtmlToText { // miss-matching tags, and this way we actually correctly identify what we want to strip and, well, // strip it. final renderHtml = html.replaceAll( - RegExp('.*<\/mx-reply>', + RegExp('.*', caseSensitive: false, multiLine: false, dotAll: true), ''); @@ -84,7 +84,7 @@ class HtmlToText { static String _parseBlockquoteContent(_ConvertOpts opts, Element node) { final msg = _walkChildNodes(opts, node); - return msg.split('\n').map((s) => '> $s').join('\n') + '\n'; + return '${msg.split('\n').map((s) => '> $s').join('\n')}\n'; } static String _parseSpanContent(_ConvertOpts opts, Element node) { @@ -109,10 +109,7 @@ class HtmlToText { return entries .map((s) => - (' ' * opts.listDepth) + - bulletPoint + - ' ' + - s.replaceAll('\n', '\n' + (' ' * opts.listDepth) + ' ')) + '${' ' * opts.listDepth}$bulletPoint ${s.replaceAll('\n', '\n${' ' * opts.listDepth} ')}') .join('\n'); } @@ -128,9 +125,7 @@ class HtmlToText { return entries .mapIndexed((index, s) => - (' ' * opts.listDepth) + - '${start + index}. ' + - s.replaceAll('\n', '\n' + (' ' * opts.listDepth) + ' ')) + '${' ' * opts.listDepth}${start + index}. ${s.replaceAll('\n', '\n${' ' * opts.listDepth} ')}') .join('\n'); } diff --git a/lib/src/utils/http_timeout.dart b/lib/src/utils/http_timeout.dart index 04ca5f51..943c33cd 100644 --- a/lib/src/utils/http_timeout.dart +++ b/lib/src/utils/http_timeout.dart @@ -20,7 +20,7 @@ import 'dart:async'; import 'package:http/http.dart' as http; -import '../../matrix.dart'; +import 'package:matrix/matrix.dart'; /// Stream.timeout fails if no progress is made in timeLimit. /// In contrast, streamTotalTimeout fails if the stream isn't completed @@ -68,10 +68,6 @@ class FixedTimeoutHttpClient extends TimeoutHttpClient { FixedTimeoutHttpClient(http.Client inner, this.timeout) : super(inner); @override Duration timeout; - - @override - Future send(http.BaseRequest request) => - super.send(request); } class VariableTimeoutHttpClient extends TimeoutHttpClient { diff --git a/lib/src/utils/image_pack_extension.dart b/lib/src/utils/image_pack_extension.dart index d5b9e3ff..222067c3 100644 --- a/lib/src/utils/image_pack_extension.dart +++ b/lib/src/utils/image_pack_extension.dart @@ -19,14 +19,15 @@ import 'package:matrix_api_lite/matrix_api_lite.dart'; import 'package:slugify/slugify.dart'; -import '../room.dart'; +import 'package:matrix/src/room.dart'; extension ImagePackRoomExtension on Room { /// Get all the active image packs for the specified [usage], mapped by their slug Map getImagePacks([ImagePackUsage? usage]) { final allMxcs = {}; // used for easy deduplication final packs = {}; - final addImagePack = (BasicEvent? event, {Room? room, String? slug}) { + + void addImagePack(BasicEvent? event, {Room? room, String? slug}) { if (event == null) return; final imagePack = event.parsedImagePackContent; final finalSlug = slugify(slug ?? 'pack'); @@ -53,7 +54,8 @@ extension ImagePackRoomExtension on Room { .images[entry.key] = image; allMxcs.add(image.url); } - }; + } + // first we add the user image pack addImagePack(client.accountData['im.ponies.user_emotes'], slug: 'user'); // next we add all the external image packs diff --git a/lib/src/utils/marked_unread.dart b/lib/src/utils/marked_unread.dart index 0ab98099..553eb071 100644 --- a/lib/src/utils/marked_unread.dart +++ b/lib/src/utils/marked_unread.dart @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import 'package:matrix_api_lite/src/utils/try_get_map_extension.dart'; +import 'package:matrix_api_lite/matrix_api_lite.dart'; mixin EventType { static const String markedUnread = 'com.famedly.marked_unread'; diff --git a/lib/src/utils/matrix_file.dart b/lib/src/utils/matrix_file.dart index 99d2c9ec..ceceea4f 100644 --- a/lib/src/utils/matrix_file.dart +++ b/lib/src/utils/matrix_file.dart @@ -25,8 +25,8 @@ import 'package:blurhash_dart/blurhash_dart.dart'; import 'package:image/image.dart'; import 'package:mime/mime.dart'; -import '../../matrix.dart'; -import 'compute_callback.dart'; +import 'package:matrix/matrix.dart'; +import 'package:matrix/src/utils/compute_callback.dart'; class MatrixFile { final Uint8List bytes; diff --git a/lib/src/utils/matrix_id_string_extension.dart b/lib/src/utils/matrix_id_string_extension.dart index 5ac0582e..530ebfe7 100644 --- a/lib/src/utils/matrix_id_string_extension.dart +++ b/lib/src/utils/matrix_id_string_extension.dart @@ -81,14 +81,8 @@ extension MatrixIdExtension on String { } return uri.replace(pathSegments: identifiers); } else if (toLowerCase().startsWith(matrixToPrefix)) { - return Uri.tryParse('//' + - substring(matrixToPrefix.length - 1) - .replaceAllMapped( - RegExp(r'(?<=/)[#!@+][^:]*:|(\?.*$)'), - (m) => m[0]!.replaceAllMapped( - RegExp(m.group(1) != null ? '' : '[/?]'), - (m) => Uri.encodeComponent(m.group(0)!))) - .replaceAll('#', '%23')); + return Uri.tryParse( + '//${substring(matrixToPrefix.length - 1).replaceAllMapped(RegExp(r'(?<=/)[#!@+][^:]*:|(\?.*$)'), (m) => m[0]!.replaceAllMapped(RegExp(m.group(1) != null ? '' : '[/?]'), (m) => Uri.encodeComponent(m.group(0)!))).replaceAll('#', '%23')}'); } else { return Uri( pathSegments: RegExp(r'/((?:[#!@+][^:]*:)?[^/?]*)(?:\?.*$)?') diff --git a/lib/src/utils/matrix_localizations.dart b/lib/src/utils/matrix_localizations.dart index 60103ed0..d952c38a 100644 --- a/lib/src/utils/matrix_localizations.dart +++ b/lib/src/utils/matrix_localizations.dart @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import '../room.dart'; +import 'package:matrix/src/room.dart'; abstract class MatrixLocalizations { const MatrixLocalizations(); diff --git a/lib/src/utils/native_implementations.dart b/lib/src/utils/native_implementations.dart index 59be73c7..79099278 100644 --- a/lib/src/utils/native_implementations.dart +++ b/lib/src/utils/native_implementations.dart @@ -3,7 +3,7 @@ import 'dart:typed_data'; import 'package:matrix/encryption.dart'; import 'package:matrix/matrix.dart'; -import 'compute_callback.dart'; +import 'package:matrix/src/utils/compute_callback.dart'; /// provides native implementations for demanding arithmetic operations /// in order to prevent the UI from blocking diff --git a/lib/src/utils/receipt.dart b/lib/src/utils/receipt.dart index 60bc4711..60b7508a 100644 --- a/lib/src/utils/receipt.dart +++ b/lib/src/utils/receipt.dart @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import '../user.dart'; +import 'package:matrix/src/user.dart'; /// Represents a receipt. /// This [user] has read an event at the given [time]. @@ -30,4 +30,7 @@ class Receipt { bool operator ==(dynamic other) => (other is Receipt && other.user == user && other.time.microsecondsSinceEpoch == time.microsecondsSinceEpoch); + + @override + int get hashCode => Object.hash(user, time); } diff --git a/lib/src/utils/run_in_root.dart b/lib/src/utils/run_in_root.dart index 6fa6e949..614a0db0 100644 --- a/lib/src/utils/run_in_root.dart +++ b/lib/src/utils/run_in_root.dart @@ -18,7 +18,7 @@ import 'dart:async'; -import '../../matrix.dart'; +import 'package:matrix/matrix.dart'; Future runInRoot(FutureOr Function() fn) async { return await Zone.root.run(() async { diff --git a/lib/src/utils/space_child.dart b/lib/src/utils/space_child.dart index bb409809..38c32270 100644 --- a/lib/src/utils/space_child.dart +++ b/lib/src/utils/space_child.dart @@ -18,7 +18,7 @@ import 'package:matrix_api_lite/matrix_api_lite.dart'; -import '../event.dart'; +import 'package:matrix/src/event.dart'; class SpaceChild { final String? roomId; diff --git a/lib/src/utils/sync_update_extension.dart b/lib/src/utils/sync_update_extension.dart index 82dfb428..c330697a 100644 --- a/lib/src/utils/sync_update_extension.dart +++ b/lib/src/utils/sync_update_extension.dart @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import '../../matrix.dart'; +import 'package:matrix/matrix.dart'; /// This extension adds easy-to-use filters for the sync update, meant to be used on the `client.onSync` stream, e.g. /// `client.onSync.stream.where((s) => s.hasRoomUpdate)`. Multiple filters can easily be diff --git a/lib/src/utils/to_device_event.dart b/lib/src/utils/to_device_event.dart index 137906a5..09a8c550 100644 --- a/lib/src/utils/to_device_event.dart +++ b/lib/src/utils/to_device_event.dart @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import '../../matrix.dart'; +import 'package:matrix/matrix.dart'; class ToDeviceEvent extends BasicEventWithSender { Map? encryptedContent; diff --git a/lib/src/utils/uia_request.dart b/lib/src/utils/uia_request.dart index 174cc106..e3a3b81f 100644 --- a/lib/src/utils/uia_request.dart +++ b/lib/src/utils/uia_request.dart @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import '../../matrix.dart'; +import 'package:matrix/matrix.dart'; enum UiaRequestState { /// The request is done diff --git a/lib/src/utils/uri_extension.dart b/lib/src/utils/uri_extension.dart index 3028d9d9..1b56d3f6 100644 --- a/lib/src/utils/uri_extension.dart +++ b/lib/src/utils/uri_extension.dart @@ -18,7 +18,7 @@ import 'dart:core'; -import '../client.dart'; +import 'package:matrix/src/client.dart'; extension MxcUriExtension on Uri { /// Returns a download Link to this content. diff --git a/lib/src/utils/web_worker/web_worker.dart b/lib/src/utils/web_worker/web_worker.dart index f4fc5ee6..e4ab902d 100644 --- a/lib/src/utils/web_worker/web_worker.dart +++ b/lib/src/utils/web_worker/web_worker.dart @@ -11,7 +11,7 @@ import 'package:js/js_util.dart'; import 'package:matrix/encryption.dart'; import 'package:matrix/matrix.dart' hide Event; -import 'native_implementations_web_worker.dart'; +import 'package:matrix/src/utils/web_worker/native_implementations_web_worker.dart'; /// /// diff --git a/lib/src/voip/call.dart b/lib/src/voip/call.dart index 4943897f..9de4a161 100644 --- a/lib/src/voip/call.dart +++ b/lib/src/voip/call.dart @@ -21,8 +21,8 @@ import 'dart:core'; import 'package:webrtc_interface/webrtc_interface.dart'; +import 'package:matrix/matrix.dart'; import 'package:matrix/src/utils/cached_stream_controller.dart'; -import '../../matrix.dart'; /// https://github.com/matrix-org/matrix-doc/pull/2746 /// version 1 @@ -997,7 +997,7 @@ class CallSession { } Future onRejectReceived(String? reason) async { - Logs().v('[VOIP] Reject received for call ID ' + callId); + Logs().v('[VOIP] Reject received for call ID $callId'); // No need to check party_id for reject because if we'd received either // an answer or reject, we wouldn't be in state InviteSent final shouldTerminate = (state == CallState.kFledgling && diff --git a/lib/src/voip/utils.dart b/lib/src/voip/utils.dart index 4eb6f223..c331d104 100644 --- a/lib/src/voip/utils.dart +++ b/lib/src/voip/utils.dart @@ -28,5 +28,5 @@ String roomAliasFromRoomName(String roomName) { } String genCallID() { - return '${DateTime.now().millisecondsSinceEpoch}' + randomAlphaNumeric(16); + return '${DateTime.now().millisecondsSinceEpoch}${randomAlphaNumeric(16)}'; } diff --git a/lib/src/voip/voip.dart b/lib/src/voip/voip.dart index 45f031ea..d9e69a08 100644 --- a/lib/src/voip/voip.dart +++ b/lib/src/voip/voip.dart @@ -4,8 +4,8 @@ import 'dart:core'; import 'package:sdp_transform/sdp_transform.dart' as sdp_transform; import 'package:webrtc_interface/webrtc_interface.dart'; +import 'package:matrix/matrix.dart'; import 'package:matrix/src/utils/cached_stream_controller.dart'; -import '../../matrix.dart'; /// Delegate WebRTC basic functionality. abstract class WebRTCDelegate { @@ -317,7 +317,7 @@ class VoIP { return; } final String callId = content['call_id']; - Logs().d('Reject received for call ID ' + callId); + Logs().d('Reject received for call ID $callId'); final call = calls[callId]; if (call != null) { @@ -339,7 +339,7 @@ class VoIP { return; } final String callId = content['call_id']; - Logs().d('onCallReplaces received for call ID ' + callId); + Logs().d('onCallReplaces received for call ID $callId'); final call = calls[callId]; if (call != null) { if (call.room.id != roomId) { @@ -358,7 +358,7 @@ class VoIP { return; } final String callId = content['call_id']; - Logs().d('SelectAnswer received for call ID ' + callId); + Logs().d('SelectAnswer received for call ID $callId'); final call = calls[callId]; final String selectedPartyId = content['selected_party_id']; @@ -379,7 +379,7 @@ class VoIP { return; } final String callId = content['call_id']; - Logs().d('SDP Stream metadata received for call ID ' + callId); + Logs().d('SDP Stream metadata received for call ID $callId'); final call = calls[callId]; if (call != null) { if (call.room.id != roomId) { @@ -404,7 +404,7 @@ class VoIP { return; } final String callId = content['call_id']; - Logs().d('Asserted identity received for call ID ' + callId); + Logs().d('Asserted identity received for call ID $callId'); final call = calls[callId]; if (call != null) { if (call.room.id != roomId) { @@ -429,7 +429,7 @@ class VoIP { return; } final String callId = content['call_id']; - Logs().d('Negotiate received for call ID ' + callId); + Logs().d('Negotiate received for call ID $callId'); final call = calls[callId]; if (call != null) { if (call.room.id != roomId) { diff --git a/pubspec.yaml b/pubspec.yaml index cc23a56b..7d5205fb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -9,33 +9,33 @@ environment: dependencies: async: ^2.8.0 - blurhash_dart: ^1.1.0 - http: ^0.13.0 - mime: ^1.0.0 - canonical_json: ^1.1.0 - markdown: ^4.0.0 - html_unescape: ^2.0.0 - random_string: ^2.3.1 - crypto: ^3.0.0 base58check: ^2.0.0 - olm: ^2.0.0 - matrix_api_lite: ^1.1.0 - hive: ^2.2.1 - image: ^3.1.1 - ffi: ^1.0.0 - js: ^0.6.3 - slugify: ^2.0.0 - html: ^0.15.0 + blurhash_dart: ^1.1.0 + canonical_json: ^1.1.0 collection: ^1.15.0 - webrtc_interface: ^1.0.1 - sdp_transform: ^0.3.2 + crypto: ^3.0.0 + ffi: ^1.0.0 fluffybox: ^0.4.3 + hive: ^2.2.1 + html: ^0.15.0 + html_unescape: ^2.0.0 + http: ^0.13.0 + image: ^3.1.1 + js: ^0.6.3 + markdown: ^4.0.0 + matrix_api_lite: ^1.1.0 + mime: ^1.0.0 + olm: ^2.0.0 + random_string: ^2.3.1 + sdp_transform: ^0.3.2 + slugify: ^2.0.0 + webrtc_interface: ^1.0.1 dev_dependencies: - import_sorter: ^4.6.0 - dart_code_metrics: ^4.10.1 - pedantic: ^1.11.0 - test: ^1.15.7 coverage: ">=0.15.0 <2.0.0" + dart_code_metrics: ^4.10.1 file: ^6.1.1 + import_sorter: ^4.6.0 + lints: ^2.0.0 + test: ^1.15.7 #flutter_test: {sdk: flutter} diff --git a/test/client_test.dart b/test/client_test.dart index fd39c64d..fc8233d3 100644 --- a/test/client_test.dart +++ b/test/client_test.dart @@ -535,7 +535,7 @@ void main() { client.rooms.clear(); await client.database?.clearCache(); await client.handleSync(SyncUpdate.fromJson(jsonDecode( - '{\"next_batch\":\"s82_571_2_6_39_1_2_34_1\",\"account_data\":{\"events\":[{\"type\":\"m.push_rules\",\"content\":{\"global\":{\"underride\":[{\"conditions\":[{\"kind\":\"event_match\",\"key\":\"type\",\"pattern\":\"m.call.invite\"}],\"actions\":[\"notify\",{\"set_tweak\":\"sound\",\"value\":\"ring\"},{\"set_tweak\":\"highlight\",\"value\":false}],\"rule_id\":\".m.rule.call\",\"default\":true,\"enabled\":true},{\"conditions\":[{\"kind\":\"room_member_count\",\"is\":\"2\"},{\"kind\":\"event_match\",\"key\":\"type\",\"pattern\":\"m.room.message\"}],\"actions\":[\"notify\",{\"set_tweak\":\"sound\",\"value\":\"default\"},{\"set_tweak\":\"highlight\",\"value\":false}],\"rule_id\":\".m.rule.room_one_to_one\",\"default\":true,\"enabled\":true},{\"conditions\":[{\"kind\":\"room_member_count\",\"is\":\"2\"},{\"kind\":\"event_match\",\"key\":\"type\",\"pattern\":\"m.room.encrypted\"}],\"actions\":[\"notify\",{\"set_tweak\":\"sound\",\"value\":\"default\"},{\"set_tweak\":\"highlight\",\"value\":false}],\"rule_id\":\".m.rule.encrypted_room_one_to_one\",\"default\":true,\"enabled\":true},{\"conditions\":[{\"kind\":\"event_match\",\"key\":\"type\",\"pattern\":\"m.room.message\"}],\"actions\":[\"notify\",{\"set_tweak\":\"highlight\",\"value\":false}],\"rule_id\":\".m.rule.message\",\"default\":true,\"enabled\":true},{\"conditions\":[{\"kind\":\"event_match\",\"key\":\"type\",\"pattern\":\"m.room.encrypted\"}],\"actions\":[\"notify\",{\"set_tweak\":\"highlight\",\"value\":false}],\"rule_id\":\".m.rule.encrypted\",\"default\":true,\"enabled\":true},{\"conditions\":[{\"kind\":\"event_match\",\"key\":\"type\",\"pattern\":\"im.vector.modular.widgets\"},{\"kind\":\"event_match\",\"key\":\"content.type\",\"pattern\":\"jitsi\"},{\"kind\":\"event_match\",\"key\":\"state_key\",\"pattern\":\"*\"}],\"actions\":[\"notify\",{\"set_tweak\":\"highlight\",\"value\":false}],\"rule_id\":\".im.vector.jitsi\",\"default\":true,\"enabled\":true}],\"sender\":[],\"room\":[],\"content\":[{\"actions\":[\"notify\",{\"set_tweak\":\"sound\",\"value\":\"default\"},{\"set_tweak\":\"highlight\"}],\"pattern\":\"056d6976-fb61-47cf-86f0-147387461565\",\"rule_id\":\".m.rule.contains_user_name\",\"default\":true,\"enabled\":true}],\"override\":[{\"conditions\":[],\"actions\":[\"dont_notify\"],\"rule_id\":\".m.rule.master\",\"default\":true,\"enabled\":false},{\"conditions\":[{\"kind\":\"event_match\",\"key\":\"content.msgtype\",\"pattern\":\"m.notice\"}],\"actions\":[\"dont_notify\"],\"rule_id\":\".m.rule.suppress_notices\",\"default\":true,\"enabled\":true},{\"conditions\":[{\"kind\":\"event_match\",\"key\":\"type\",\"pattern\":\"m.room.member\"},{\"kind\":\"event_match\",\"key\":\"content.membership\",\"pattern\":\"invite\"},{\"kind\":\"event_match\",\"key\":\"state_key\",\"pattern\":\"@056d6976-fb61-47cf-86f0-147387461565:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de\"}],\"actions\":[\"notify\",{\"set_tweak\":\"sound\",\"value\":\"default\"},{\"set_tweak\":\"highlight\",\"value\":false}],\"rule_id\":\".m.rule.invite_for_me\",\"default\":true,\"enabled\":true},{\"conditions\":[{\"kind\":\"event_match\",\"key\":\"type\",\"pattern\":\"m.room.member\"}],\"actions\":[\"dont_notify\"],\"rule_id\":\".m.rule.member_event\",\"default\":true,\"enabled\":true},{\"conditions\":[{\"kind\":\"contains_display_name\"}],\"actions\":[\"notify\",{\"set_tweak\":\"sound\",\"value\":\"default\"},{\"set_tweak\":\"highlight\"}],\"rule_id\":\".m.rule.contains_display_name\",\"default\":true,\"enabled\":true},{\"conditions\":[{\"kind\":\"event_match\",\"key\":\"content.body\",\"pattern\":\"@room\"},{\"kind\":\"sender_notification_permission\",\"key\":\"room\"}],\"actions\":[\"notify\",{\"set_tweak\":\"highlight\",\"value\":true}],\"rule_id\":\".m.rule.roomnotif\",\"default\":true,\"enabled\":true},{\"conditions\":[{\"kind\":\"event_match\",\"key\":\"type\",\"pattern\":\"m.room.tombstone\"},{\"kind\":\"event_match\",\"key\":\"state_key\",\"pattern\":\"\"}],\"actions\":[\"notify\",{\"set_tweak\":\"highlight\",\"value\":true}],\"rule_id\":\".m.rule.tombstone\",\"default\":true,\"enabled\":true},{\"conditions\":[{\"kind\":\"event_match\",\"key\":\"type\",\"pattern\":\"m.reaction\"}],\"actions\":[\"dont_notify\"],\"rule_id\":\".m.rule.reaction\",\"default\":true,\"enabled\":true}]},\"device\":{}}}]},\"presence\":{\"events\":[{\"type\":\"m.presence\",\"sender\":\"@056d6976-fb61-47cf-86f0-147387461565:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de\",\"content\":{\"presence\":\"online\",\"last_active_ago\":43,\"currently_active\":true}}]},\"device_one_time_keys_count\":{\"signed_curve25519\":66},\"org.matrix.msc2732.device_unused_fallback_key_types\":[\"signed_curve25519\"],\"device_unused_fallback_key_types\":[\"signed_curve25519\"],\"rooms\":{\"join\":{\"!MEgZosbiZqjSjbHFqI:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de\":{\"timeline\":{\"events\":[{\"type\":\"m.room.member\",\"sender\":\"@8640f1e6-a824-4f9c-9924-2d8fc40bc030:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de\",\"content\":{\"membership\":\"join\",\"displayname\":\"Lars Kaiser\"},\"state_key\":\"@8640f1e6-a824-4f9c-9924-2d8fc40bc030:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de\",\"origin_server_ts\":1647296944593,\"unsigned\":{\"age\":545455},\"event_id\":\"\$mk9kFUEAKBZJgarWApLyYqOZQQocLIVV8tWp_gJEZFU\"},{\"type\":\"m.room.power_levels\",\"sender\":\"@8640f1e6-a824-4f9c-9924-2d8fc40bc030:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de\",\"content\":{\"users\":{\"@8640f1e6-a824-4f9c-9924-2d8fc40bc030:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de\":100},\"users_default\":0,\"events\":{\"m.room.name\":50,\"m.room.power_levels\":100,\"m.room.history_visibility\":100,\"m.room.canonical_alias\":50,\"m.room.avatar\":50,\"m.room.tombstone\":100,\"m.room.server_acl\":100,\"m.room.encryption\":100},\"events_default\":0,\"state_default\":50,\"ban\":50,\"kick\":50,\"redact\":50,\"invite\":50,\"historical\":100},\"state_key\":\"\",\"origin_server_ts\":1647296944690,\"unsigned\":{\"age\":545358},\"event_id\":\"\$3wL2YgVNQzgfl8y_ksi3BPMqRs94jb_m0WRonL1HNpY\"},{\"type\":\"m.room.canonical_alias\",\"sender\":\"@8640f1e6-a824-4f9c-9924-2d8fc40bc030:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de\",\"content\":{\"alias\":\"#user-discovery:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de\"},\"state_key\":\"\",\"origin_server_ts\":1647296944806,\"unsigned\":{\"age\":545242},\"event_id\":\"\$yXaVETL9F4jSN9rpRNyT_kUoctzD07n5Z4AIHziP7DQ\"},{\"type\":\"m.room.join_rules\",\"sender\":\"@8640f1e6-a824-4f9c-9924-2d8fc40bc030:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de\",\"content\":{\"join_rule\":\"public\"},\"state_key\":\"\",\"origin_server_ts\":1647296944894,\"unsigned\":{\"age\":545154},\"event_id\":\"\$jBDHhgpNqr125eWUsGVw4r7ZG2hgr0BTzzR77S-ubvY\"},{\"type\":\"m.room.history_visibility\",\"sender\":\"@8640f1e6-a824-4f9c-9924-2d8fc40bc030:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de\",\"content\":{\"history_visibility\":\"shared\"},\"state_key\":\"\",\"origin_server_ts\":1647296944965,\"unsigned\":{\"age\":545083},\"event_id\":\"\$kMessP7gAphUKW7mzOLlJT6NT8IsVGPmGir3_1uBNCE\"},{\"type\":\"m.room.name\",\"sender\":\"@8640f1e6-a824-4f9c-9924-2d8fc40bc030:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de\",\"content\":{\"name\":\"User Discovery\"},\"state_key\":\"\",\"origin_server_ts\":1647296945062,\"unsigned\":{\"age\":544986},\"event_id\":\"\$Bo9Ut_0vcr3FuxCRye4IHEMxUxIIcSwc-ePnMzx-hYU\"},{\"type\":\"m.room.member\",\"sender\":\"@test:fakeServer.notExisting\",\"content\":{\"membership\":\"join\",\"displayname\":\"1c2e5c2b-f958-45a5-9fcb-eef3969c31df\"},\"state_key\":\"@test:fakeServer.notExisting\",\"origin_server_ts\":1647296989893,\"unsigned\":{\"age\":500155},\"event_id\":\"\$fYCf2qtlHwzcdLgwjHb2EOdStv3isAlIUy2Esh5qfVE\"},{\"type\":\"m.room.member\",\"sender\":\"@test:fakeServer.notExisting\",\"content\":{\"membership\":\"join\",\"displayname\":\"Some First Name Some Last Name\"},\"state_key\":\"@test:fakeServer.notExisting\",\"origin_server_ts\":1647296990076,\"unsigned\":{\"replaces_state\":\"\$fYCf2qtlHwzcdLgwjHb2EOdStv3isAlIUy2Esh5qfVE\",\"prev_content\":{\"membership\":\"join\",\"displayname\":\"1c2e5c2b-f958-45a5-9fcb-eef3969c31df\"},\"prev_sender\":\"@test:fakeServer.notExisting\",\"age\":499972},\"event_id\":\"\$3Ut97nFBgOtsrnRPW-pqr28z7ETNMttj7GcjkIv4zWw\"},{\"type\":\"m.room.member\",\"sender\":\"@056d6976-fb61-47cf-86f0-147387461565:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de\",\"content\":{\"membership\":\"join\",\"displayname\":\"056d6976-fb61-47cf-86f0-147387461565\"},\"state_key\":\"@056d6976-fb61-47cf-86f0-147387461565:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de\",\"origin_server_ts\":1647297489154,\"unsigned\":{\"age\":894},\"event_id\":\"\$6EsjHSLQDVDW9WDH1c5Eu57VaPGZmOPtNRjCjtWPLV0\"},{\"type\":\"m.room.member\",\"sender\":\"@056d6976-fb61-47cf-86f0-147387461565:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de\",\"content\":{\"membership\":\"join\",\"displayname\":\"Another User\"},\"state_key\":\"@056d6976-fb61-47cf-86f0-147387461565:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de\",\"origin_server_ts\":1647297489290,\"unsigned\":{\"replaces_state\":\"\$6EsjHSLQDVDW9WDH1c5Eu57VaPGZmOPtNRjCjtWPLV0\",\"prev_content\":{\"membership\":\"join\",\"displayname\":\"056d6976-fb61-47cf-86f0-147387461565\"},\"prev_sender\":\"@056d6976-fb61-47cf-86f0-147387461565:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de\",\"age\":758},\"event_id\":\"\$dtQblqCbjr3TGc3WmrQ4YTkHaXJ2PcO0TAYDr9K7iQc\"}],\"prev_batch\":\"t2-62_571_2_6_39_1_2_34_1\",\"limited\":true},\"state\":{\"events\":[{\"type\":\"m.room.create\",\"sender\":\"@8640f1e6-a824-4f9c-9924-2d8fc40bc030:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de\",\"content\":{\"m.federate\":false,\"room_version\":\"9\",\"creator\":\"@8640f1e6-a824-4f9c-9924-2d8fc40bc030:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de\"},\"state_key\":\"\",\"origin_server_ts\":1647296944511,\"unsigned\":{\"age\":545537},\"event_id\":\"\$PAWKKULBVOLnqfrAAtXZz8tHEPXXjgRVbJJLifwQWbE\"}]},\"account_data\":{\"events\":[]},\"ephemeral\":{\"events\":[]},\"unread_notifications\":{\"notification_count\":0,\"highlight_count\":0},\"summary\":{\"m.joined_member_count\":3,\"m.invited_member_count\":0},\"org.matrix.msc2654.unread_count\":0}}}}'))); + '{"next_batch":"s82_571_2_6_39_1_2_34_1","account_data":{"events":[{"type":"m.push_rules","content":{"global":{"underride":[{"conditions":[{"kind":"event_match","key":"type","pattern":"m.call.invite"}],"actions":["notify",{"set_tweak":"sound","value":"ring"},{"set_tweak":"highlight","value":false}],"rule_id":".m.rule.call","default":true,"enabled":true},{"conditions":[{"kind":"room_member_count","is":"2"},{"kind":"event_match","key":"type","pattern":"m.room.message"}],"actions":["notify",{"set_tweak":"sound","value":"default"},{"set_tweak":"highlight","value":false}],"rule_id":".m.rule.room_one_to_one","default":true,"enabled":true},{"conditions":[{"kind":"room_member_count","is":"2"},{"kind":"event_match","key":"type","pattern":"m.room.encrypted"}],"actions":["notify",{"set_tweak":"sound","value":"default"},{"set_tweak":"highlight","value":false}],"rule_id":".m.rule.encrypted_room_one_to_one","default":true,"enabled":true},{"conditions":[{"kind":"event_match","key":"type","pattern":"m.room.message"}],"actions":["notify",{"set_tweak":"highlight","value":false}],"rule_id":".m.rule.message","default":true,"enabled":true},{"conditions":[{"kind":"event_match","key":"type","pattern":"m.room.encrypted"}],"actions":["notify",{"set_tweak":"highlight","value":false}],"rule_id":".m.rule.encrypted","default":true,"enabled":true},{"conditions":[{"kind":"event_match","key":"type","pattern":"im.vector.modular.widgets"},{"kind":"event_match","key":"content.type","pattern":"jitsi"},{"kind":"event_match","key":"state_key","pattern":"*"}],"actions":["notify",{"set_tweak":"highlight","value":false}],"rule_id":".im.vector.jitsi","default":true,"enabled":true}],"sender":[],"room":[],"content":[{"actions":["notify",{"set_tweak":"sound","value":"default"},{"set_tweak":"highlight"}],"pattern":"056d6976-fb61-47cf-86f0-147387461565","rule_id":".m.rule.contains_user_name","default":true,"enabled":true}],"override":[{"conditions":[],"actions":["dont_notify"],"rule_id":".m.rule.master","default":true,"enabled":false},{"conditions":[{"kind":"event_match","key":"content.msgtype","pattern":"m.notice"}],"actions":["dont_notify"],"rule_id":".m.rule.suppress_notices","default":true,"enabled":true},{"conditions":[{"kind":"event_match","key":"type","pattern":"m.room.member"},{"kind":"event_match","key":"content.membership","pattern":"invite"},{"kind":"event_match","key":"state_key","pattern":"@056d6976-fb61-47cf-86f0-147387461565:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de"}],"actions":["notify",{"set_tweak":"sound","value":"default"},{"set_tweak":"highlight","value":false}],"rule_id":".m.rule.invite_for_me","default":true,"enabled":true},{"conditions":[{"kind":"event_match","key":"type","pattern":"m.room.member"}],"actions":["dont_notify"],"rule_id":".m.rule.member_event","default":true,"enabled":true},{"conditions":[{"kind":"contains_display_name"}],"actions":["notify",{"set_tweak":"sound","value":"default"},{"set_tweak":"highlight"}],"rule_id":".m.rule.contains_display_name","default":true,"enabled":true},{"conditions":[{"kind":"event_match","key":"content.body","pattern":"@room"},{"kind":"sender_notification_permission","key":"room"}],"actions":["notify",{"set_tweak":"highlight","value":true}],"rule_id":".m.rule.roomnotif","default":true,"enabled":true},{"conditions":[{"kind":"event_match","key":"type","pattern":"m.room.tombstone"},{"kind":"event_match","key":"state_key","pattern":""}],"actions":["notify",{"set_tweak":"highlight","value":true}],"rule_id":".m.rule.tombstone","default":true,"enabled":true},{"conditions":[{"kind":"event_match","key":"type","pattern":"m.reaction"}],"actions":["dont_notify"],"rule_id":".m.rule.reaction","default":true,"enabled":true}]},"device":{}}}]},"presence":{"events":[{"type":"m.presence","sender":"@056d6976-fb61-47cf-86f0-147387461565:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","content":{"presence":"online","last_active_ago":43,"currently_active":true}}]},"device_one_time_keys_count":{"signed_curve25519":66},"org.matrix.msc2732.device_unused_fallback_key_types":["signed_curve25519"],"device_unused_fallback_key_types":["signed_curve25519"],"rooms":{"join":{"!MEgZosbiZqjSjbHFqI:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de":{"timeline":{"events":[{"type":"m.room.member","sender":"@8640f1e6-a824-4f9c-9924-2d8fc40bc030:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","content":{"membership":"join","displayname":"Lars Kaiser"},"state_key":"@8640f1e6-a824-4f9c-9924-2d8fc40bc030:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","origin_server_ts":1647296944593,"unsigned":{"age":545455},"event_id":"\$mk9kFUEAKBZJgarWApLyYqOZQQocLIVV8tWp_gJEZFU"},{"type":"m.room.power_levels","sender":"@8640f1e6-a824-4f9c-9924-2d8fc40bc030:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","content":{"users":{"@8640f1e6-a824-4f9c-9924-2d8fc40bc030:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de":100},"users_default":0,"events":{"m.room.name":50,"m.room.power_levels":100,"m.room.history_visibility":100,"m.room.canonical_alias":50,"m.room.avatar":50,"m.room.tombstone":100,"m.room.server_acl":100,"m.room.encryption":100},"events_default":0,"state_default":50,"ban":50,"kick":50,"redact":50,"invite":50,"historical":100},"state_key":"","origin_server_ts":1647296944690,"unsigned":{"age":545358},"event_id":"\$3wL2YgVNQzgfl8y_ksi3BPMqRs94jb_m0WRonL1HNpY"},{"type":"m.room.canonical_alias","sender":"@8640f1e6-a824-4f9c-9924-2d8fc40bc030:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","content":{"alias":"#user-discovery:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de"},"state_key":"","origin_server_ts":1647296944806,"unsigned":{"age":545242},"event_id":"\$yXaVETL9F4jSN9rpRNyT_kUoctzD07n5Z4AIHziP7DQ"},{"type":"m.room.join_rules","sender":"@8640f1e6-a824-4f9c-9924-2d8fc40bc030:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","content":{"join_rule":"public"},"state_key":"","origin_server_ts":1647296944894,"unsigned":{"age":545154},"event_id":"\$jBDHhgpNqr125eWUsGVw4r7ZG2hgr0BTzzR77S-ubvY"},{"type":"m.room.history_visibility","sender":"@8640f1e6-a824-4f9c-9924-2d8fc40bc030:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","content":{"history_visibility":"shared"},"state_key":"","origin_server_ts":1647296944965,"unsigned":{"age":545083},"event_id":"\$kMessP7gAphUKW7mzOLlJT6NT8IsVGPmGir3_1uBNCE"},{"type":"m.room.name","sender":"@8640f1e6-a824-4f9c-9924-2d8fc40bc030:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","content":{"name":"User Discovery"},"state_key":"","origin_server_ts":1647296945062,"unsigned":{"age":544986},"event_id":"\$Bo9Ut_0vcr3FuxCRye4IHEMxUxIIcSwc-ePnMzx-hYU"},{"type":"m.room.member","sender":"@test:fakeServer.notExisting","content":{"membership":"join","displayname":"1c2e5c2b-f958-45a5-9fcb-eef3969c31df"},"state_key":"@test:fakeServer.notExisting","origin_server_ts":1647296989893,"unsigned":{"age":500155},"event_id":"\$fYCf2qtlHwzcdLgwjHb2EOdStv3isAlIUy2Esh5qfVE"},{"type":"m.room.member","sender":"@test:fakeServer.notExisting","content":{"membership":"join","displayname":"Some First Name Some Last Name"},"state_key":"@test:fakeServer.notExisting","origin_server_ts":1647296990076,"unsigned":{"replaces_state":"\$fYCf2qtlHwzcdLgwjHb2EOdStv3isAlIUy2Esh5qfVE","prev_content":{"membership":"join","displayname":"1c2e5c2b-f958-45a5-9fcb-eef3969c31df"},"prev_sender":"@test:fakeServer.notExisting","age":499972},"event_id":"\$3Ut97nFBgOtsrnRPW-pqr28z7ETNMttj7GcjkIv4zWw"},{"type":"m.room.member","sender":"@056d6976-fb61-47cf-86f0-147387461565:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","content":{"membership":"join","displayname":"056d6976-fb61-47cf-86f0-147387461565"},"state_key":"@056d6976-fb61-47cf-86f0-147387461565:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","origin_server_ts":1647297489154,"unsigned":{"age":894},"event_id":"\$6EsjHSLQDVDW9WDH1c5Eu57VaPGZmOPtNRjCjtWPLV0"},{"type":"m.room.member","sender":"@056d6976-fb61-47cf-86f0-147387461565:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","content":{"membership":"join","displayname":"Another User"},"state_key":"@056d6976-fb61-47cf-86f0-147387461565:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","origin_server_ts":1647297489290,"unsigned":{"replaces_state":"\$6EsjHSLQDVDW9WDH1c5Eu57VaPGZmOPtNRjCjtWPLV0","prev_content":{"membership":"join","displayname":"056d6976-fb61-47cf-86f0-147387461565"},"prev_sender":"@056d6976-fb61-47cf-86f0-147387461565:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","age":758},"event_id":"\$dtQblqCbjr3TGc3WmrQ4YTkHaXJ2PcO0TAYDr9K7iQc"}],"prev_batch":"t2-62_571_2_6_39_1_2_34_1","limited":true},"state":{"events":[{"type":"m.room.create","sender":"@8640f1e6-a824-4f9c-9924-2d8fc40bc030:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","content":{"m.federate":false,"room_version":"9","creator":"@8640f1e6-a824-4f9c-9924-2d8fc40bc030:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de"},"state_key":"","origin_server_ts":1647296944511,"unsigned":{"age":545537},"event_id":"\$PAWKKULBVOLnqfrAAtXZz8tHEPXXjgRVbJJLifwQWbE"}]},"account_data":{"events":[]},"ephemeral":{"events":[]},"unread_notifications":{"notification_count":0,"highlight_count":0},"summary":{"m.joined_member_count":3,"m.invited_member_count":0},"org.matrix.msc2654.unread_count":0}}}}'))); final profile = await client.fetchOwnProfile(); expect(profile.displayName, 'Some First Name Some Last Name'); await client.dispose(closeDatabase: true); diff --git a/test/commands_test.dart b/test/commands_test.dart index 053afc5d..aaeac122 100644 --- a/test/commands_test.dart +++ b/test/commands_test.dart @@ -31,15 +31,15 @@ void main() { late Room room; var olmEnabled = true; - final getLastMessagePayload = - ([String type = 'm.room.message', String? stateKey]) { + Map getLastMessagePayload( + [String type = 'm.room.message', String? stateKey]) { final state = stateKey != null; return json.decode(FakeMatrixApi.calledEndpoints.entries .firstWhere((e) => e.key.startsWith( - '/client/v3/rooms/${Uri.encodeComponent(room.id)}/${state ? 'state' : 'send'}/${Uri.encodeComponent(type)}${state && stateKey?.isNotEmpty == true ? '/' + Uri.encodeComponent(stateKey!) : ''}')) + '/client/v3/rooms/${Uri.encodeComponent(room.id)}/${state ? 'state' : 'send'}/${Uri.encodeComponent(type)}${state && stateKey?.isNotEmpty == true ? '/${Uri.encodeComponent(stateKey!)}' : ''}')) .value .first); - }; + } test('setupClient', () async { try { @@ -57,7 +57,7 @@ void main() { stateKey: '', eventId: '\$fakeeventid', originServerTs: DateTime.now(), - senderId: '\@fakeuser:fakeServer.notExisting', + senderId: '@fakeuser:fakeServer.notExisting', )); room.setState(Event( type: 'm.room.member', @@ -66,7 +66,7 @@ void main() { stateKey: client.userID, eventId: '\$fakeeventid', originServerTs: DateTime.now(), - senderId: '\@fakeuser:fakeServer.notExisting', + senderId: '@fakeuser:fakeServer.notExisting', )); }); diff --git a/test/encryption/ssss_test.dart b/test/encryption/ssss_test.dart index d7a9e7f4..1217840d 100644 --- a/test/encryption/ssss_test.dart +++ b/test/encryption/ssss_test.dart @@ -128,7 +128,7 @@ void main() { var decoded = SSSS.decodeRecoveryKey(encoded); expect(key, decoded); - decoded = SSSS.decodeRecoveryKey(encoded + ' \n\t'); + decoded = SSSS.decodeRecoveryKey('$encoded \n\t'); expect(key, decoded); final handle = client.encryption!.ssss.open(); diff --git a/test/event_test.dart b/test/event_test.dart index 8cc4edd2..e3599555 100644 --- a/test/event_test.dart +++ b/test/event_test.dart @@ -1043,7 +1043,7 @@ void main() { '# Title\nsome text and [link](https://example.com)\nokay and this is **important**', 'format': 'org.matrix.custom.html', 'formatted_body': - '

Title

\n

some text and link
okay and this is important

\n', + '

Title

\n

some text and link
okay and this is important

\n', 'msgtype': 'm.text' }, 'event_id': '\$143273582443PhrSn:example.org', @@ -1253,12 +1253,13 @@ void main() { test('attachments', () async { final FILE_BUFF = Uint8List.fromList([0]); final THUMBNAIL_BUFF = Uint8List.fromList([2]); - final downloadCallback = (Uri uri) async { + Future downloadCallback(Uri uri) async { return { '/_matrix/media/v3/download/example.org/file': FILE_BUFF, '/_matrix/media/v3/download/example.org/thumb': THUMBNAIL_BUFF, }[uri.path]!; - }; + } + await client.checkHomeserver(Uri.parse('https://fakeserver.notexisting'), checkWellKnown: false); final room = Room(id: '!localpart:server.abc', client: client); @@ -1344,12 +1345,13 @@ void main() { Uint8List.fromList([0x55, 0xD7, 0xEB, 0x72, 0x05, 0x13]); final THUMB_BUFF_DEC = Uint8List.fromList([0x74, 0x68, 0x75, 0x6D, 0x62, 0x0A]); - final downloadCallback = (Uri uri) async { + Future downloadCallback(Uri uri) async { return { '/_matrix/media/v3/download/example.com/file': FILE_BUFF_ENC, '/_matrix/media/v3/download/example.com/thumb': THUMB_BUFF_ENC, }[uri.path]!; - }; + } + final room = Room(id: '!localpart:server.abc', client: await getClient()); var event = Event.fromJson({ 'type': EventTypes.Message, @@ -1440,12 +1442,13 @@ void main() { test('downloadAndDecryptAttachment store', () async { final FILE_BUFF = Uint8List.fromList([0]); var serverHits = 0; - final downloadCallback = (Uri uri) async { + Future downloadCallback(Uri uri) async { serverHits++; return { '/_matrix/media/v3/download/example.org/newfile': FILE_BUFF, }[uri.path]!; - }; + } + await client.checkHomeserver(Uri.parse('https://fakeserver.notexisting'), checkWellKnown: false); final room = Room(id: '!localpart:server.abc', client: await getClient()); @@ -1606,7 +1609,7 @@ void main() { ❤❤❤''', 'format': 'org.matrix.custom.html', 'formatted_body': - '\
In reply to @alice:example.org
😒😒
❤❤❤' + '
In reply to @alice:example.org
😒😒
❤❤❤' }, 'event_id': '\$edit2', 'sender': '@alice:example.org', @@ -1622,7 +1625,7 @@ void main() { ❤❤''', 'format': 'org.matrix.custom.html', 'formatted_body': - '\
In reply to @alice:example.org
A 😒
❤❤' + '
In reply to @alice:example.org
A 😒
❤❤' }, 'event_id': '\$edit2', 'sender': '@alice:example.org', @@ -1638,7 +1641,7 @@ void main() { ❤A❤''', 'format': 'org.matrix.custom.html', 'formatted_body': - '\
In reply to @alice:example.org
😒😒😒
❤A❤' + '
In reply to @alice:example.org
😒😒😒
❤A❤' }, 'event_id': '\$edit2', 'sender': '@alice:example.org', @@ -1654,7 +1657,7 @@ void main() { ❤A❤''', 'format': 'org.matrix.custom.html', 'formatted_body': - '\
In reply to @alice:example.org
A😒
❤A❤' + '
In reply to @alice:example.org
A😒
❤A❤' }, 'event_id': '\$edit2', 'sender': '@alice:example.org', diff --git a/test/fake_matrix_api.dart b/test/fake_matrix_api.dart index 5eea00d8..f36b1162 100644 --- a/test/fake_matrix_api.dart +++ b/test/fake_matrix_api.dart @@ -92,7 +92,7 @@ class FakeMatrixApi extends BaseClient { var action = request.url.path; if (request.url.path.contains('/_matrix')) { action = - request.url.path.split('/_matrix').last + '?' + request.url.query; + '${request.url.path.split('/_matrix').last}?${request.url.query}'; } // ignore: avoid_print diff --git a/test/image_pack_test.dart b/test/image_pack_test.dart index c801d2c3..bcb7e87e 100644 --- a/test/image_pack_test.dart +++ b/test/image_pack_test.dart @@ -45,7 +45,7 @@ void main() { content: {'membership': 'join'}, room: room, stateKey: client.userID, - senderId: '\@fakeuser:fakeServer.notExisting', + senderId: '@fakeuser:fakeServer.notExisting', eventId: '\$fakeid2:fakeServer.notExisting', originServerTs: DateTime.now(), )); @@ -63,7 +63,7 @@ void main() { content: {'membership': 'join'}, room: room, stateKey: client.userID, - senderId: '\@fakeuser:fakeServer.notExisting', + senderId: '@fakeuser:fakeServer.notExisting', eventId: '\$fakeid4:fakeServer.notExisting', originServerTs: DateTime.now(), )); @@ -81,7 +81,7 @@ void main() { }, room: room, stateKey: '', - senderId: '\@fakeuser:fakeServer.notExisting', + senderId: '@fakeuser:fakeServer.notExisting', eventId: '\$fakeid5:fakeServer.notExisting', originServerTs: DateTime.now(), )); @@ -110,7 +110,7 @@ void main() { }, room: room, stateKey: '', - senderId: '\@fakeuser:fakeServer.notExisting', + senderId: '@fakeuser:fakeServer.notExisting', eventId: '\$fakeid6:fakeServer.notExisting', originServerTs: DateTime.now(), )); @@ -135,7 +135,7 @@ void main() { }, room: room, stateKey: '', - senderId: '\@fakeuser:fakeServer.notExisting', + senderId: '@fakeuser:fakeServer.notExisting', eventId: '\$fakeid7:fakeServer.notExisting', originServerTs: DateTime.now(), )); @@ -158,7 +158,7 @@ void main() { }, room: room, stateKey: 'fox', - senderId: '\@fakeuser:fakeServer.notExisting', + senderId: '@fakeuser:fakeServer.notExisting', eventId: '\$fakeid8:fakeServer.notExisting', originServerTs: DateTime.now(), )); @@ -201,7 +201,7 @@ void main() { }, room: room2, stateKey: '', - senderId: '\@fakeuser:fakeServer.notExisting', + senderId: '@fakeuser:fakeServer.notExisting', eventId: '\$fakeid9:fakeServer.notExisting', originServerTs: DateTime.now(), )); @@ -234,7 +234,7 @@ void main() { }, room: room2, stateKey: 'fox', - senderId: '\@fakeuser:fakeServer.notExisting', + senderId: '@fakeuser:fakeServer.notExisting', eventId: '\$fakeid10:fakeServer.notExisting', originServerTs: DateTime.now(), )); diff --git a/test/markdown_test.dart b/test/markdown_test.dart index dac27912..1d634401 100644 --- a/test/markdown_test.dart +++ b/test/markdown_test.dart @@ -40,7 +40,7 @@ void main() { '@[Fast Fox]#123': '@fastfox:example.org', '@[">]': '@blah:example.org', }; - final getMention = (mention) => mentionMap[mention]; + String? getMention(mention) => mentionMap[mention]; test('simple markdown', () { expect(markdown('hey *there* how are **you** doing?'), 'hey there how are you doing?'); diff --git a/test/sync_filter_test.dart b/test/sync_filter_test.dart index 60d1ed56..72ab33d2 100644 --- a/test/sync_filter_test.dart +++ b/test/sync_filter_test.dart @@ -139,7 +139,7 @@ void main() { group('Sync Filters', () { Logs().level = Level.error; test('room update', () { - final testFn = (SyncUpdate s) => s.hasRoomUpdate; + bool testFn(SyncUpdate s) => s.hasRoomUpdate; final expected = { 'empty': false, 'presence': false, @@ -153,7 +153,7 @@ void main() { }); test('presence update', () { - final testFn = (SyncUpdate s) => s.hasPresenceUpdate; + bool testFn(SyncUpdate s) => s.hasPresenceUpdate; final expected = { 'empty': false, 'presence': true,