feat(xep): Implement ignoring the message reflection

This commit is contained in:
PapaTutuWawa 2023-09-22 20:42:45 +02:00
parent d7c13abde6
commit fbe3b90200
12 changed files with 139 additions and 45 deletions

View File

@ -38,6 +38,7 @@ void main(List<String> args) async {
DiscoManager([]), DiscoManager([]),
PubSubManager(), PubSubManager(),
MessageManager(), MessageManager(),
StableIdManager(),
MUCManager(), MUCManager(),
]); ]);
await connection.registerFeatureNegotiators([ await connection.registerFeatureNegotiators([
@ -57,12 +58,28 @@ void main(List<String> args) async {
} }
Logger.root.info('Connected.'); Logger.root.info('Connected.');
// Print received messages.
connection
.asBroadcastStream()
.where((event) => event is MessageEvent)
.listen((event) {
event as MessageEvent;
// Ignore messages with no <body />
final body = event.get<MessageBodyData>()?.body;
if (body == null) {
return;
}
print('=====> [${event.from}] $body');
});
// Join room // Join room
await connection.getManagerById<MUCManager>(mucManager)!.joinRoom( await connection.getManagerById<MUCManager>(mucManager)!.joinRoom(
muc, muc,
nick, nick,
maxHistoryStanzas: 0, maxHistoryStanzas: 0,
); );
final repl = Repl(prompt: '> '); final repl = Repl(prompt: '> ');
await for (final line in repl.runAsync()) { await for (final line in repl.runAsync()) {
@ -72,6 +89,11 @@ void main(List<String> args) async {
muc, muc,
TypedMap<StanzaHandlerExtension>.fromList([ TypedMap<StanzaHandlerExtension>.fromList([
MessageBodyData(line), MessageBodyData(line),
StableIdData(
// NOTE: Don't do this. Use a UUID.
DateTime.now().millisecondsSinceEpoch.toString(),
null,
),
]), ]),
type: 'groupchat'); type: 'groupchat');
} }

View File

@ -3,7 +3,7 @@ description: A collection of samples for moxxmpp.
version: 1.0.0 version: 1.0.0
environment: environment:
sdk: '>=2.18.0 <3.0.0' sdk: '>=3.0.0 <4.0.0'
dependencies: dependencies:
args: 2.4.1 args: 2.4.1

View File

@ -7,11 +7,11 @@
"nixpkgs": "nixpkgs" "nixpkgs": "nixpkgs"
}, },
"locked": { "locked": {
"lastModified": 1689798050, "lastModified": 1694377165,
"narHash": "sha256-ZyFPra7N0MF803o55dYQQyX9b/BmXr6QTCyN7slRThY=", "narHash": "sha256-NeIlZIElbkbKaNK5SZv6ULcFT/UGIICb3q7GPpkf9jk=",
"owner": "tadfisher", "owner": "tadfisher",
"repo": "android-nixpkgs", "repo": "android-nixpkgs",
"rev": "9aa0e2990da86de8ca203af313668851dcb9ea6e", "rev": "b020dc733ee69393841a50cf94d45735d5a5a57a",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -29,11 +29,11 @@
"systems": "systems" "systems": "systems"
}, },
"locked": { "locked": {
"lastModified": 1688380630, "lastModified": 1693833206,
"narHash": "sha256-8ilApWVb1mAi4439zS3iFeIT0ODlbrifm/fegWwgHjA=", "narHash": "sha256-wHOY0nnD6gWj8u9uI85/YlsganYyWRK1hLFZulZwfmY=",
"owner": "numtide", "owner": "numtide",
"repo": "devshell", "repo": "devshell",
"rev": "f9238ec3d75cefbb2b42a44948c4e8fb1ae9a205", "rev": "65114ea495a8d3cc1352368bf170d67ef005aa5a",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -47,11 +47,11 @@
"systems": "systems_2" "systems": "systems_2"
}, },
"locked": { "locked": {
"lastModified": 1689068808, "lastModified": 1692799911,
"narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=", "narHash": "sha256-3eihraek4qL744EvQXsK1Ha6C3CR7nnT8X2qWap4RNk=",
"owner": "numtide", "owner": "numtide",
"repo": "flake-utils", "repo": "flake-utils",
"rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4", "rev": "f9e7cf818399d17d347f847525c5a5a8032e4e44",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -61,12 +61,15 @@
} }
}, },
"flake-utils_2": { "flake-utils_2": {
"inputs": {
"systems": "systems_3"
},
"locked": { "locked": {
"lastModified": 1667395993, "lastModified": 1692799911,
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", "narHash": "sha256-3eihraek4qL744EvQXsK1Ha6C3CR7nnT8X2qWap4RNk=",
"owner": "numtide", "owner": "numtide",
"repo": "flake-utils", "repo": "flake-utils",
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", "rev": "f9e7cf818399d17d347f847525c5a5a8032e4e44",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -77,11 +80,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1689679375, "lastModified": 1694183432,
"narHash": "sha256-LHUC52WvyVDi9PwyL1QCpaxYWBqp4ir4iL6zgOkmcb8=", "narHash": "sha256-YyPGNapgZNNj51ylQMw9lAgvxtM2ai1HZVUu3GS8Fng=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "684c17c429c42515bafb3ad775d2a710947f3d67", "rev": "db9208ab987cdeeedf78ad9b4cf3c55f5ebd269b",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -93,11 +96,11 @@
}, },
"nixpkgs_2": { "nixpkgs_2": {
"locked": { "locked": {
"lastModified": 1689752456, "lastModified": 1694343207,
"narHash": "sha256-VOChdECcEI8ixz8QY+YC4JaNEFwQd1V8bA0G4B28Ki0=", "narHash": "sha256-jWi7OwFxU5Owi4k2JmiL1sa/OuBCQtpaAesuj5LXC8w=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "7f256d7da238cb627ef189d56ed590739f42f13b", "rev": "78058d810644f5ed276804ce7ea9e82d92bee293",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -143,6 +146,21 @@
"repo": "default", "repo": "default",
"type": "github" "type": "github"
} }
},
"systems_3": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
} }
}, },
"root": "root", "root": "root",

View File

@ -68,7 +68,7 @@
}; };
in pkgs.mkShell { in pkgs.mkShell {
buildInputs = with pkgs; [ buildInputs = with pkgs; [
flutter37 pinnedJDK sdk dart # Dart flutter pinnedJDK sdk dart # Dart
gitlint # Code hygiene gitlint # Code hygiene
ripgrep # General utilities ripgrep # General utilities

View File

@ -475,7 +475,7 @@ class XmppConnection {
false, false,
false, false,
newStanza, newStanza,
TypedMap(), details.extensions ?? TypedMap(),
encrypted: details.encrypted, encrypted: details.encrypted,
shouldEncrypt: details.shouldEncrypt, shouldEncrypt: details.shouldEncrypt,
forceEncryption: details.forceEncryption, forceEncryption: details.forceEncryption,

View File

@ -117,6 +117,7 @@ class MessageManager extends XmppManagerBase {
.flattened .flattened
.toList(), .toList(),
), ),
extensions: extensions,
awaitable: false, awaitable: false,
), ),
); );

View File

@ -7,6 +7,7 @@ import 'package:moxxmpp/src/util/typed_map.dart';
class StanzaDetails { class StanzaDetails {
const StanzaDetails( const StanzaDetails(
this.stanza, { this.stanza, {
this.extensions,
this.addId = true, this.addId = true,
this.awaitable = true, this.awaitable = true,
this.shouldEncrypt = true, this.shouldEncrypt = true,
@ -19,6 +20,9 @@ class StanzaDetails {
/// The stanza to send. /// The stanza to send.
final Stanza stanza; final Stanza stanza;
/// The extension data used for constructing the stanza.
final TypedMap<StanzaHandlerExtension>? extensions;
/// Flag indicating whether a stanza id should be added before sending. /// Flag indicating whether a stanza id should be added before sending.
final bool addId; final bool addId;

View File

@ -33,8 +33,13 @@ class RoomInformation {
final String name; final String name;
} }
/// The used message-id and an optional origin-id.
typedef PendingMessage = (String, String?);
class RoomState { class RoomState {
RoomState({required this.roomJid, this.nick, required this.joined}); RoomState({required this.roomJid, this.nick, required this.joined}) {
pendingMessages = List<PendingMessage>.empty(growable: true);
}
/// The JID of the room. /// The JID of the room.
final JID roomJid; final JID roomJid;
@ -45,14 +50,5 @@ class RoomState {
/// Flag whether we're joined and can process messages /// Flag whether we're joined and can process messages
bool joined; bool joined;
RoomState copyWith({ late final List<PendingMessage> pendingMessages;
bool? joined,
String? nick,
}) {
return RoomState(
roomJid: roomJid,
joined: joined ?? this.joined,
nick: nick ?? this.nick,
);
}
} }

View File

@ -11,6 +11,7 @@ import 'package:moxxmpp/src/xeps/xep_0030/types.dart';
import 'package:moxxmpp/src/xeps/xep_0030/xep_0030.dart'; import 'package:moxxmpp/src/xeps/xep_0030/xep_0030.dart';
import 'package:moxxmpp/src/xeps/xep_0045/errors.dart'; import 'package:moxxmpp/src/xeps/xep_0045/errors.dart';
import 'package:moxxmpp/src/xeps/xep_0045/types.dart'; import 'package:moxxmpp/src/xeps/xep_0045/types.dart';
import 'package:moxxmpp/src/xeps/xep_0359.dart';
import 'package:synchronized/extension.dart'; import 'package:synchronized/extension.dart';
import 'package:synchronized/synchronized.dart'; import 'package:synchronized/synchronized.dart';
@ -33,7 +34,15 @@ class MUCManager extends XmppManagerBase {
callback: _onMessage, callback: _onMessage,
// Before the message handler // Before the message handler
priority: -99, priority: -99,
) ),
];
@override
List<StanzaHandler> getOutgoingPreStanzaHandlers() => [
StanzaHandler(
stanzaTag: 'message',
callback: _onMessageSent,
),
]; ];
/// Queries the information of a Multi-User Chat room. /// Queries the information of a Multi-User Chat room.
@ -138,11 +147,33 @@ class MUCManager extends XmppManagerBase {
return const Result(true); return const Result(true);
} }
Future<StanzaHandlerData> _onMessageSent(
Stanza message,
StanzaHandlerData state,
) async {
if (message.to == null) {
return state;
}
final toJid = JID.fromString(message.to!);
return _cacheLock.synchronized(() {
if (!_mucRoomCache.containsKey(toJid)) {
return state;
}
_mucRoomCache[toJid]!.pendingMessages.add(
(message.id!, state.extensions.get<StableIdData>()?.originId),
);
return state;
});
}
Future<StanzaHandlerData> _onMessage( Future<StanzaHandlerData> _onMessage(
Stanza message, Stanza message,
StanzaHandlerData state, StanzaHandlerData state,
) async { ) async {
final roomJid = JID.fromString(message.from!).toBare(); final fromJid = JID.fromString(message.from!);
final roomJid = fromJid.toBare();
return _mucRoomCache.synchronized(() { return _mucRoomCache.synchronized(() {
final roomState = _mucRoomCache[roomJid]; final roomState = _mucRoomCache[roomJid];
if (roomState == null) { if (roomState == null) {
@ -153,7 +184,7 @@ class MUCManager extends XmppManagerBase {
// The room subject marks the end of the join flow. // The room subject marks the end of the join flow.
if (!roomState.joined) { if (!roomState.joined) {
// Mark the room as joined. // Mark the room as joined.
_mucRoomCache[roomJid] = roomState.copyWith(joined: true); _mucRoomCache[roomJid]!.joined = true;
logger.finest('$roomJid is now joined'); logger.finest('$roomJid is now joined');
} }
@ -168,7 +199,23 @@ class MUCManager extends XmppManagerBase {
} else { } else {
if (!roomState.joined) { if (!roomState.joined) {
// Ignore the discussion history. // Ignore the discussion history.
// TODO: Implement a copyWith method return StanzaHandlerData(
true,
false,
message,
state.extensions,
);
}
// Check if this is the message reflection.
final pending =
(message.id!, state.extensions.get<StableIdData>()?.originId);
if (fromJid.resource == roomState.nick &&
roomState.pendingMessages.contains(pending)) {
// Silently drop the message.
roomState.pendingMessages.remove(pending);
// TODO(Unknown): Maybe send an event stating that we received the reflection.
return StanzaHandlerData( return StanzaHandlerData(
true, true,
false, false,

View File

@ -127,7 +127,13 @@ class StableIdManager extends XmppManagerBase {
TypedMap<StanzaHandlerExtension> extensions, TypedMap<StanzaHandlerExtension> extensions,
) { ) {
final data = extensions.get<StableIdData>(); final data = extensions.get<StableIdData>();
return data != null ? data.toXML() : []; if (data?.originId != null) {
return [
data!.toOriginIdElement(),
];
}
return [];
} }
@override @override

View File

@ -2,10 +2,10 @@ abstract class OmemoError {}
class UnknownOmemoError extends OmemoError {} class UnknownOmemoError extends OmemoError {}
class InvalidAffixElementsException with Exception {} class InvalidAffixElementsException implements Exception {}
class OmemoNotSupportedForContactException extends OmemoError {} class OmemoNotSupportedForContactException extends OmemoError {}
class EncryptionFailedException with Exception {} class EncryptionFailedException implements Exception {}
class InvalidEnvelopePayloadException with Exception {} class InvalidEnvelopePayloadException implements Exception {}

View File

@ -5,7 +5,7 @@ homepage: https://codeberg.org/moxxy/moxxmpp
publish_to: https://git.polynom.me/api/packages/Moxxy/pub publish_to: https://git.polynom.me/api/packages/Moxxy/pub
environment: environment:
sdk: '>=2.17.5 <3.0.0' sdk: '>=3.0.0 <4.0.0'
dependencies: dependencies:
collection: ^1.16.0 collection: ^1.16.0