import 'package:args/args.dart'; import 'package:chalkdart/chalk.dart'; import 'package:cli_repl/cli_repl.dart'; import 'package:example_dart/socket.dart'; import 'package:logging/logging.dart'; import 'package:moxxmpp/moxxmpp.dart'; import 'package:moxxmpp_socket_tcp/moxxmpp_socket_tcp.dart'; import 'package:omemo_dart/omemo_dart.dart' as omemo; void main(List 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 = ArgParser() ..addOption('jid') ..addOption('password') ..addOption('host') ..addOption('port') ..addOption('to') ..addOption('xmpps-srv'); final options = parser.parse(args); // Parse a potential xmpps-client SRV record here. // Format: --xmpps-srv ,,, MoxSrvRecord? srvRecord; if (options['xmpps-srv'] != null) { final parts = (options['xmpps-srv']! as String).split(','); srvRecord = MoxSrvRecord( int.parse(parts[0]), int.parse(parts[1]), parts[2], int.parse(parts[3]), ); } // Connect final jid = JID.fromString(options['jid']! as String); final to = JID.fromString(options['to']! as String).toBare(); final portString = options['port'] as String?; final connection = XmppConnection( TestingReconnectionPolicy(), AlwaysConnectedConnectivityManager(), ClientToServerNegotiator(), ExampleTCPSocketWrapper(srvRecord), )..connectionSettings = ConnectionSettings( jid: jid, password: options['password']! as String, host: options['host'] as String?, port: portString != null ? int.parse(portString) : null, ); // Generate OMEMO data omemo.OmemoManager? oom; final moxxmppOmemo = OmemoManager( () async => oom!, (toJid, _) async => toJid == to, ); oom = omemo.OmemoManager( await omemo.OmemoDevice.generateNewDevice(jid.toString(), opkAmount: 5), omemo.BlindTrustBeforeVerificationTrustManager(), moxxmppOmemo.sendEmptyMessageImpl, moxxmppOmemo.fetchDeviceList, moxxmppOmemo.fetchDeviceBundle, moxxmppOmemo.subscribeToDeviceListImpl, moxxmppOmemo.publishDeviceImpl, ); final deviceId = await oom.getDeviceId(); Logger.root.info('Our device id: $deviceId'); // Register the managers and negotiators await connection.registerManagers([ PresenceManager(), DiscoManager([]), PubSubManager(), MessageManager(), moxxmppOmemo, ]); await connection.registerFeatureNegotiators([ SaslPlainNegotiator(), ResourceBindingNegotiator(), StartTlsNegotiator(), SaslScramNegotiator(10, '', '', ScramHashType.sha1), ]); // Set up event handlers connection.asBroadcastStream().listen((event) { if (event is MessageEvent) { Logger.root.info(event.id); Logger.root.info(event.extensions.keys.toList()); final body = event.encryptionError != null ? chalk.red('Failed to decrypt message: ${event.encryptionError}') : chalk.green(event.get()?.body ?? ''); print('[${event.from.toString()}] $body'); } }); // Connect Logger.root.info('Connecting...'); final result = await connection.connect(shouldReconnect: false, waitUntilLogin: true); if (!result.isType()) { Logger.root.severe('Authentication failed!'); return; } Logger.root.info('Connected.'); // Publish our bundle Logger.root.info('Publishing bundle'); final device = await oom.getDevice(); final omemoResult = await moxxmppOmemo.publishBundle(await device.toBundle()); if (!omemoResult.isType()) { Logger.root.severe( 'Failed to publish OMEMO bundle: ${omemoResult.get()}'); return; } final repl = Repl(prompt: '> '); await for (final line in repl.runAsync()) { await connection .getManagerById(messageManager)! .sendMessage( to, TypedMap.fromList([ MessageBodyData(line), ]), ); } // Disconnect await connection.disconnect(); }