import "dart:io"; import "package:cli_tools/config.dart"; import "package:enough_mail/enough_mail.dart" as mail; import "package:markdown/markdown.dart"; import "package:matrix/matrix.dart"; import "package:nexusbot/controllers/client_controller.dart"; import "package:nexusbot/controllers/mail_client_controller.dart"; import "package:nexusbot/controllers/registration_controller.dart"; import "package:nexusbot/controllers/settings_controller.dart"; import "package:nexusbot/models/registration.dart"; import "package:riverpod/riverpod.dart"; import "package:shelf/shelf.dart"; import "package:shelf/shelf_io.dart"; import "package:shelf_router/shelf_router.dart"; void main(List argsRaw) async { final parser = ConfigParser() ..addOption("socket", mandatory: true) ..addOption("homeserver", mandatory: true) ..addOption("failureUri", mandatory: true) ..addOption("successUri", mandatory: true) ..addOption("name", mandatory: true) ..addOption("adminName", mandatory: true) ..addOption("adminRoom", mandatory: true) ..addOption("email", mandatory: true) ..addOption("emailAlias", mandatory: false) ..addOption("mailName", mandatory: true) ..addOption("mailDomain", mandatory: true) ..addOption("smtpPasswordFile", mandatory: true) ..addOption("botPasswordFile", mandatory: true) ..addOption("inviteTo"); final container = ProviderContainer(); container .read(SettingsController.provider.notifier) .set(parser.parse(argsRaw)); final client = await container.read(ClientController.provider.future); client.onTimelineEvent.stream.listen((event) async { final settings = container.read(SettingsController.provider)!; if (event.room.canonicalAlias != settings.adminRoom) return; if (event.senderId.startsWith("@${settings.name}:")) return; switch (event.type) { case EventTypes.Reaction: if ((event.content["m.relates_to"] as Map)["key"] != "✅") { return; } final parentEvent = await client.getOneRoomEvent( event.roomId!, event.relationshipEventId!, ); if (!parentEvent.senderId.startsWith("@${settings.name}:")) return; final registration = Registration.fromJson(parentEvent.content); container .read(RegistrationController.provider.notifier) .add(registration); await event.room.sendTextEvent( "!admin create-user ${registration.username}", ); break; case EventTypes.Message: if (!event.senderId.startsWith("@${settings.adminName}:")) break; final results = RegExp( r"@(?[a-zA-Z0-9._-]+):[^\s]+.*?password:\s+(?\S+)", ).firstMatch(event.body); if (results == null) return; final registration = container .read(RegistrationController.provider) .firstWhere( (account) => account.username == results.namedGroup("username"), ); final password = results.namedGroup("password")!; final reactionEvent = await event.room.sendReaction( event.eventId, "✉️ Sending...", ); await container .read(MailClientController.provider.notifier) .sendMessage( plainText: """Your registration for Federated Nexus has been accepted! Your credentials are: Username: ${registration.username}. Password: $password If you have any questions, check out our documentation: https://federated.nexus/services/matrix/. If you have any issues, reply to this email.""", markdown: """# Your registration for Federated Nexus has been accepted! ## Your credentials are: - ### Username: ${registration.username}. - ### Password: $password If you have any questions, check out [our documentation](https://federated.nexus/services/matrix/). If you have any issues, reply to this email.""", subject: "Your registration for Federated Nexus has been accepted!", to: mail.MailAddress(registration.username, registration.email), ); await event.room.redactEvent(reactionEvent!); await event.room.sendReaction(event.eventId, "✉️ Sent!"); if (settings.inviteTo != null) { client .getRoomByAlias(settings.inviteTo!)! .invite( RegExp( r"(?@[a-zA-Z0-9._-]+:[^\s]+)", ).firstMatch(event.body)!.namedGroup("userid")!, ); } } }); final handler = const Pipeline() .addMiddleware(logRequests()) .addHandler( (Router()..post("/", (Request request) async { final settings = container.read(SettingsController.provider)!; try { final body = await request.readAsString(); final registration = Registration.fromJson( Uri.splitQueryString( body, ).map((key, value) => MapEntry(key, value.toLowerCase())), ); final client = await container.read( ClientController.provider.future, ); final room = client.getRoomByAlias(settings.adminRoom)!; final message = """# Registration request - Username: `${registration.username}` - Email: `${registration.email}`"""; final event = await room.sendEvent({ "body": message, "msgtype": MessageTypes.Text, "format": "org.matrix.custom.html", "formatted_body": markdownToHtml(message), ...registration.toJson(), }); await room.sendReaction(event!, "✅"); return Response.found(settings.successUri); } catch (error, stackTrace) { final message = """An error occurred for this request: ${await request.readAsString()} $error $stackTrace """; try { final room = client.getRoomByAlias(settings.adminRoom)!; await room.sendTextEvent(message); } catch (_) { print(message); } return Response.found(settings.failureUri); } })) .call, ); final server = HttpServer.listenOn( await ServerSocket.bind( InternetAddress( container.read(SettingsController.provider)!.socket, type: InternetAddressType.unix, ), 0, ), ); serveRequests(server, handler); print("Bot listening at ${server.address.address}"); }