This commit is contained in:
Henry Hiles 2025-08-02 23:28:51 -04:00
commit b9adcb205e
No known key found for this signature in database
14 changed files with 274 additions and 214 deletions

View file

@ -1,7 +1,14 @@
import "dart:convert";
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/helpers/api_helper.dart";
import "package:nexusbot/models/registration.dart";
import "package:riverpod/riverpod.dart";
import "package:shelf/shelf.dart";
import "package:shelf/shelf_io.dart";
@ -9,35 +16,145 @@ import "package:shelf_router/shelf_router.dart";
void main(List<String> argsRaw) async {
final parser = ConfigParser()
..addOption("socket", abbr: "s", mandatory: true)
..addOption("homeserver", abbr: "h", mandatory: true)
..addOption("successUri", abbr: "u", mandatory: true)
..addOption("name", abbr: "n", mandatory: true)
..addOption("adminRoom", abbr: "a", mandatory: true)
..addOption("botPasswordFile", abbr: "b", mandatory: true)
..addOption("smtpPasswordFile", abbr: "p", mandatory: true)
..addOption("inviteTo", abbr: "i");
..addOption("socket", mandatory: true)
..addOption("homeserver", mandatory: true)
..addOption("successUri", mandatory: true)
..addOption("name", mandatory: true)
..addOption("adminName", mandatory: true)
..addOption("adminRoom", mandatory: true)
..addOption("email", mandatory: true)
..addOption("mailName", mandatory: true)
..addOption("mailDomain", mandatory: true)
..addOption("smtpPasswordFile", mandatory: true)
..addOption("botPasswordFile", mandatory: true);
final container = ProviderContainer();
container
.read(SettingsController.provider.notifier)
.set(parser.parse(argsRaw));
final apiHelper = container.read(ApiHelper.provider);
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<String, dynamic>)["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"@(?<username>[a-zA-Z0-9._-]+):[^\s]+.*?password:\s+(?<password>\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...",
);
final from = mail.MailAddress(settings.mailName, settings.email);
final mailClient = await container.read(
MailClientController.provider.future,
);
await mailClient.sendMessageBuilder(
mail.MessageBuilder.prepareMultipartAlternativeMessage(
plainText:
"""Your registration for Federated Nexus has been accepted! Your credentials are:
Username: ${registration.username}.
Password: $password""",
htmlText: markdownToHtml(
"""# 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!"
..sender = from,
from: from,
recipients: [
mail.MailAddress(registration.username, registration.email),
],
);
await event.room.redactEvent(reactionEvent!);
await event.room.sendReaction(event.eventId, "✉️ Sent!");
}
});
final handler = const Pipeline()
.addMiddleware(logRequests())
.addHandler((Router()..post("/", apiHelper.register)).call);
.addHandler(
(Router()..post("/", (Request request) async {
final settings = container.read(SettingsController.provider)!;
final registration = Registration.fromJson(
json.decode(await request.readAsString()),
);
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);
}))
.call,
);
final settings = container.read(SettingsController.provider)!;
final server = HttpServer.listenOn(
await ServerSocket.bind(
InternetAddress(settings.socket, type: InternetAddressType.unix),
InternetAddress(
container.read(SettingsController.provider)!.socket,
type: InternetAddressType.unix,
),
0,
),
);
apiHelper.listen();
serveRequests(server, handler);
print("Bot listening at ${server.address.address}");
}