import 'package:args/args.dart'; import 'package:chalkdart/chalk.dart'; import 'package:cli_repl/cli_repl.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; class TestingTCPSocketWrapper extends TCPSocketWrapper { @override bool onBadCertificate(dynamic certificate, String domain) { return true; } } 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'); final options = parser.parse(args); // 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(), TestingTCPSocketWrapper(), )..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(); }