Compare commits
No commits in common. "1e7279e23b1053446eb3207dbfa8c5e62c3d5c8e" and "b92e825bc15b98d1ee576ae3a711e34e293e0fae" have entirely different histories.
1e7279e23b
...
b92e825bc1
@ -1,80 +0,0 @@
|
|||||||
import 'package:cli_repl/cli_repl.dart';
|
|
||||||
import 'package:example_dart/arguments.dart';
|
|
||||||
import 'package:example_dart/socket.dart';
|
|
||||||
import 'package:logging/logging.dart';
|
|
||||||
import 'package:moxxmpp/moxxmpp.dart';
|
|
||||||
|
|
||||||
void main(List<String> args) async {
|
|
||||||
// Set up logging
|
|
||||||
Logger.root.level = Level.ALL;
|
|
||||||
Logger.root.onRecord.listen((record) {
|
|
||||||
// ignore: avoid_print
|
|
||||||
print(
|
|
||||||
'[${record.level.name}] (${record.loggerName}) ${record.time}: ${record.message}',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
final parser = ArgumentParser()
|
|
||||||
..parser.addOption('muc', help: 'The MUC to send messages to')
|
|
||||||
..parser.addOption('nick', help: 'The nickname with which to join the MUC');
|
|
||||||
final options = parser.handleArguments(args);
|
|
||||||
if (options == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connect
|
|
||||||
final muc = JID.fromString(options['muc']! as String).toBare();
|
|
||||||
final nick = options['nick']! as String;
|
|
||||||
final connection = XmppConnection(
|
|
||||||
TestingReconnectionPolicy(),
|
|
||||||
AlwaysConnectedConnectivityManager(),
|
|
||||||
ClientToServerNegotiator(),
|
|
||||||
ExampleTCPSocketWrapper(parser.srvRecord),
|
|
||||||
)..connectionSettings = parser.connectionSettings;
|
|
||||||
|
|
||||||
// Register the managers and negotiators
|
|
||||||
await connection.registerManagers([
|
|
||||||
PresenceManager(),
|
|
||||||
DiscoManager([]),
|
|
||||||
PubSubManager(),
|
|
||||||
MessageManager(),
|
|
||||||
MUCManager(),
|
|
||||||
]);
|
|
||||||
await connection.registerFeatureNegotiators([
|
|
||||||
SaslPlainNegotiator(),
|
|
||||||
ResourceBindingNegotiator(),
|
|
||||||
StartTlsNegotiator(),
|
|
||||||
SaslScramNegotiator(10, '', '', ScramHashType.sha1),
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Connect
|
|
||||||
Logger.root.info('Connecting...');
|
|
||||||
final result =
|
|
||||||
await connection.connect(shouldReconnect: false, waitUntilLogin: true);
|
|
||||||
if (!result.isType<bool>()) {
|
|
||||||
Logger.root.severe('Authentication failed!');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Logger.root.info('Connected.');
|
|
||||||
|
|
||||||
// Join room
|
|
||||||
await connection.getManagerById<MUCManager>(mucManager)!.joinRoom(muc, nick);
|
|
||||||
|
|
||||||
final repl = Repl(prompt: '> ');
|
|
||||||
await for (final line in repl.runAsync()) {
|
|
||||||
await connection
|
|
||||||
.getManagerById<MessageManager>(messageManager)!
|
|
||||||
.sendMessage(
|
|
||||||
muc,
|
|
||||||
TypedMap<StanzaHandlerExtension>.fromList([
|
|
||||||
MessageBodyData(line),
|
|
||||||
]),
|
|
||||||
type: 'groupchat');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Leave room
|
|
||||||
await connection.getManagerById<MUCManager>(mucManager)!.leaveRoom(muc);
|
|
||||||
|
|
||||||
// Disconnect
|
|
||||||
await connection.disconnect();
|
|
||||||
}
|
|
@ -47,9 +47,6 @@ export 'package:moxxmpp/src/xeps/xep_0030/errors.dart';
|
|||||||
export 'package:moxxmpp/src/xeps/xep_0030/helpers.dart';
|
export 'package:moxxmpp/src/xeps/xep_0030/helpers.dart';
|
||||||
export 'package:moxxmpp/src/xeps/xep_0030/types.dart';
|
export 'package:moxxmpp/src/xeps/xep_0030/types.dart';
|
||||||
export 'package:moxxmpp/src/xeps/xep_0030/xep_0030.dart';
|
export 'package:moxxmpp/src/xeps/xep_0030/xep_0030.dart';
|
||||||
export 'package:moxxmpp/src/xeps/xep_0045/errors.dart';
|
|
||||||
export 'package:moxxmpp/src/xeps/xep_0045/types.dart';
|
|
||||||
export 'package:moxxmpp/src/xeps/xep_0045/xep_0045.dart';
|
|
||||||
export 'package:moxxmpp/src/xeps/xep_0054.dart';
|
export 'package:moxxmpp/src/xeps/xep_0054.dart';
|
||||||
export 'package:moxxmpp/src/xeps/xep_0060/errors.dart';
|
export 'package:moxxmpp/src/xeps/xep_0060/errors.dart';
|
||||||
export 'package:moxxmpp/src/xeps/xep_0060/helpers.dart';
|
export 'package:moxxmpp/src/xeps/xep_0060/helpers.dart';
|
||||||
|
@ -33,4 +33,3 @@ const stickersManager = 'org.moxxmpp.stickersmanager';
|
|||||||
const entityCapabilitiesManager = 'org.moxxmpp.entitycapabilities';
|
const entityCapabilitiesManager = 'org.moxxmpp.entitycapabilities';
|
||||||
const messageProcessingHintManager = 'org.moxxmpp.messageprocessinghint';
|
const messageProcessingHintManager = 'org.moxxmpp.messageprocessinghint';
|
||||||
const occupantIdManager = 'org.moxxmpp.occupantidmanager';
|
const occupantIdManager = 'org.moxxmpp.occupantidmanager';
|
||||||
const mucManager = 'org.moxxmpp.mucmanager';
|
|
||||||
|
@ -103,15 +103,14 @@ class MessageManager extends XmppManagerBase {
|
|||||||
/// data for building the message.
|
/// data for building the message.
|
||||||
Future<void> sendMessage(
|
Future<void> sendMessage(
|
||||||
JID to,
|
JID to,
|
||||||
TypedMap<StanzaHandlerExtension> extensions, {
|
TypedMap<StanzaHandlerExtension> extensions,
|
||||||
String type = 'chat',
|
) async {
|
||||||
}) async {
|
|
||||||
await getAttributes().sendStanza(
|
await getAttributes().sendStanza(
|
||||||
StanzaDetails(
|
StanzaDetails(
|
||||||
Stanza.message(
|
Stanza.message(
|
||||||
to: to.toString(),
|
to: to.toString(),
|
||||||
id: extensions.get<MessageIdData>()?.id,
|
id: extensions.get<MessageIdData>()?.id,
|
||||||
type: type,
|
type: 'chat',
|
||||||
children: _messageSendingCallbacks
|
children: _messageSendingCallbacks
|
||||||
.map((c) => c(extensions))
|
.map((c) => c(extensions))
|
||||||
.flattened
|
.flattened
|
||||||
|
@ -21,9 +21,6 @@ const discoItemsXmlns = 'http://jabber.org/protocol/disco#items';
|
|||||||
// XEP-0033
|
// XEP-0033
|
||||||
const extendedAddressingXmlns = 'http://jabber.org/protocol/address';
|
const extendedAddressingXmlns = 'http://jabber.org/protocol/address';
|
||||||
|
|
||||||
// XEP-0045
|
|
||||||
const mucXmlns = 'http://jabber.org/protocol/muc';
|
|
||||||
|
|
||||||
// XEP-0054
|
// XEP-0054
|
||||||
const vCardTempXmlns = 'vcard-temp';
|
const vCardTempXmlns = 'vcard-temp';
|
||||||
const vCardTempUpdate = 'vcard-temp:x:update';
|
const vCardTempUpdate = 'vcard-temp:x:update';
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
/// Represents an error related to Multi-User Chat (MUC).
|
|
||||||
abstract class MUCError {}
|
|
||||||
|
|
||||||
/// Error indicating an invalid (non-supported) stanza received while going
|
|
||||||
/// through normal operation/flow of an MUC.
|
|
||||||
class InvalidStanzaFormat extends MUCError {}
|
|
||||||
|
|
||||||
/// Represents an error indicating an abnormal condition while parsing
|
|
||||||
/// the DiscoInfo response stanza in Multi-User Chat (MUC).
|
|
||||||
class InvalidDiscoInfoResponse extends MUCError {}
|
|
||||||
|
|
||||||
/// Returned when no nickname was specified from the client side while trying to
|
|
||||||
/// perform some actions on the MUC, such as joining the room.
|
|
||||||
class NoNicknameSpecified extends MUCError {}
|
|
||||||
|
|
||||||
/// This error occurs when a user attempts to perform an action that requires
|
|
||||||
/// them to be a member of a room, but they are not currently joined to
|
|
||||||
/// that room.
|
|
||||||
class RoomNotJoinedError extends MUCError {}
|
|
@ -1,43 +0,0 @@
|
|||||||
import 'package:moxxmpp/src/jid.dart';
|
|
||||||
import 'package:moxxmpp/src/xeps/xep_0030/types.dart';
|
|
||||||
|
|
||||||
class RoomInformation {
|
|
||||||
/// Represents information about a Multi-User Chat (MUC) room.
|
|
||||||
RoomInformation({
|
|
||||||
required this.jid,
|
|
||||||
required this.features,
|
|
||||||
required this.name,
|
|
||||||
});
|
|
||||||
|
|
||||||
/// Constructs a [RoomInformation] object from a [DiscoInfo] object.
|
|
||||||
/// The [DiscoInfo] object contains the necessary information to populate
|
|
||||||
/// the [RoomInformation] fields.
|
|
||||||
factory RoomInformation.fromDiscoInfo({
|
|
||||||
required DiscoInfo discoInfo,
|
|
||||||
}) =>
|
|
||||||
RoomInformation(
|
|
||||||
jid: discoInfo.jid!,
|
|
||||||
features: discoInfo.features,
|
|
||||||
name: discoInfo.identities
|
|
||||||
.firstWhere((i) => i.category == 'conference')
|
|
||||||
.name!,
|
|
||||||
);
|
|
||||||
|
|
||||||
/// The JID of the Multi-User Chat (MUC) room.
|
|
||||||
final JID jid;
|
|
||||||
|
|
||||||
/// A list of features supported by the Multi-User Chat (MUC) room.
|
|
||||||
final List<String> features;
|
|
||||||
|
|
||||||
/// The name or title of the Multi-User Chat (MUC) room.
|
|
||||||
final String name;
|
|
||||||
}
|
|
||||||
|
|
||||||
class RoomState {
|
|
||||||
RoomState({
|
|
||||||
required this.roomJid,
|
|
||||||
this.nick,
|
|
||||||
});
|
|
||||||
final JID roomJid;
|
|
||||||
String? nick;
|
|
||||||
}
|
|
@ -1,112 +0,0 @@
|
|||||||
import 'package:moxlib/moxlib.dart';
|
|
||||||
import 'package:moxxmpp/src/jid.dart';
|
|
||||||
import 'package:moxxmpp/src/managers/base.dart';
|
|
||||||
import 'package:moxxmpp/src/managers/namespaces.dart';
|
|
||||||
import 'package:moxxmpp/src/namespaces.dart';
|
|
||||||
import 'package:moxxmpp/src/stanza.dart';
|
|
||||||
import 'package:moxxmpp/src/stringxml.dart';
|
|
||||||
import 'package:moxxmpp/src/xeps/xep_0030/errors.dart';
|
|
||||||
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_0045/errors.dart';
|
|
||||||
import 'package:moxxmpp/src/xeps/xep_0045/types.dart';
|
|
||||||
import 'package:synchronized/synchronized.dart';
|
|
||||||
|
|
||||||
class MUCManager extends XmppManagerBase {
|
|
||||||
MUCManager() : super(mucManager);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<bool> isSupported() async => true;
|
|
||||||
|
|
||||||
/// Map full JID to RoomState
|
|
||||||
final Map<JID, RoomState> _mucRoomCache = {};
|
|
||||||
|
|
||||||
/// Cache lock
|
|
||||||
final Lock _cacheLock = Lock();
|
|
||||||
|
|
||||||
/// Queries the information of a Multi-User Chat room.
|
|
||||||
///
|
|
||||||
/// Retrieves the information about the specified MUC room by performing a
|
|
||||||
/// disco info query. Returns a [Result] with the [RoomInformation] on success
|
|
||||||
/// or an appropriate [MUCError] on failure.
|
|
||||||
Future<Result<RoomInformation, MUCError>> queryRoomInformation(
|
|
||||||
JID roomJID,
|
|
||||||
) async {
|
|
||||||
final result = await getAttributes()
|
|
||||||
.getManagerById<DiscoManager>(discoManager)!
|
|
||||||
.discoInfoQuery(roomJID);
|
|
||||||
if (result.isType<StanzaError>()) {
|
|
||||||
return Result(InvalidStanzaFormat());
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
final roomInformation = RoomInformation.fromDiscoInfo(
|
|
||||||
discoInfo: result.get<DiscoInfo>(),
|
|
||||||
);
|
|
||||||
return Result(roomInformation);
|
|
||||||
} catch (e) {
|
|
||||||
return Result(InvalidDiscoInfoResponse);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Joins a Multi-User Chat room.
|
|
||||||
///
|
|
||||||
/// Joins the specified MUC room using the provided nickname. Sends a presence
|
|
||||||
/// stanza with the appropriate attributes to join the room. Returns a [Result]
|
|
||||||
/// with a boolean value indicating success or failure, or an [MUCError]
|
|
||||||
/// if applicable.
|
|
||||||
Future<Result<bool, MUCError>> joinRoom(
|
|
||||||
JID roomJid,
|
|
||||||
String nick,
|
|
||||||
) async {
|
|
||||||
if (nick.isEmpty) {
|
|
||||||
return Result(NoNicknameSpecified());
|
|
||||||
}
|
|
||||||
await getAttributes().sendStanza(
|
|
||||||
StanzaDetails(
|
|
||||||
Stanza.presence(
|
|
||||||
to: roomJid.withResource(nick).toString(),
|
|
||||||
children: [
|
|
||||||
XMLNode.xmlns(
|
|
||||||
tag: 'x',
|
|
||||||
xmlns: mucXmlns,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
await _cacheLock.synchronized(
|
|
||||||
() {
|
|
||||||
_mucRoomCache[roomJid] = RoomState(roomJid: roomJid, nick: nick);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
return const Result(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Leaves a Multi-User Chat room.
|
|
||||||
///
|
|
||||||
/// Leaves the specified MUC room by sending an 'unavailable' presence stanza.
|
|
||||||
/// Removes the corresponding room entry from the cache. Returns a [Result]
|
|
||||||
/// with a boolean value indicating success or failure, or an [MUCError]
|
|
||||||
/// if applicable.
|
|
||||||
Future<Result<bool, MUCError>> leaveRoom(
|
|
||||||
JID roomJid,
|
|
||||||
) async {
|
|
||||||
final nick = await _cacheLock.synchronized(() {
|
|
||||||
final nick = _mucRoomCache[roomJid]?.nick;
|
|
||||||
_mucRoomCache.remove(roomJid);
|
|
||||||
return nick;
|
|
||||||
});
|
|
||||||
if (nick == null) {
|
|
||||||
return Result(RoomNotJoinedError);
|
|
||||||
}
|
|
||||||
await getAttributes().sendStanza(
|
|
||||||
StanzaDetails(
|
|
||||||
Stanza.presence(
|
|
||||||
to: roomJid.withResource(nick).toString(),
|
|
||||||
type: 'unavailable',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
return const Result(true);
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user