From b9adcb205e8f93f89839fc2a3b8fbfd2a9c37b4c Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sat, 2 Aug 2025 23:28:51 -0400 Subject: [PATCH] Working --- .vscode/launch.json | 12 +- JUSTFILE | 8 +- bin/nexusbot.dart | 145 +++++++++++++++++-- flake.nix | 3 +- lib/controllers/client_controller.dart | 33 +++++ lib/controllers/login_controller.dart | 36 ----- lib/controllers/mail_client_controller.dart | 27 ++++ lib/controllers/registration_controller.dart | 15 ++ lib/helpers/api_helper.dart | 130 ----------------- lib/models/settings.dart | 7 +- lib/models/settings.freezed.dart | 49 ++++--- lib/models/settings.g.dart | 14 +- pubspec.lock | 8 + pubspec.yaml | 1 + 14 files changed, 274 insertions(+), 214 deletions(-) delete mode 100644 lib/controllers/login_controller.dart create mode 100644 lib/controllers/mail_client_controller.dart create mode 100644 lib/controllers/registration_controller.dart delete mode 100644 lib/helpers/api_helper.dart diff --git a/.vscode/launch.json b/.vscode/launch.json index bf9ab6a..2eb0341 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -15,8 +15,6 @@ "https://matrix.federated.nexus", "--name", "nexusbot", - "--inviteTo", - "#community:federated.nexus", "--adminRoom", "#admins:federated.nexus", "--successUri", @@ -24,7 +22,15 @@ "--botPasswordFile", "secrets/botPassword.txt", "--smtpPasswordFile", - "secrets/smtpPassword.txt" + "secrets/smtpPassword.txt", + "--adminName", + "grapevine", + "--email", + "henry@henryhiles.com", + "--mailDomain", + "mail.henryhiles.com", + "--mailName", + "Federated Nexus Registrations" ], "request": "launch", "type": "dart" diff --git a/JUSTFILE b/JUSTFILE index 5fa18e3..dff0cc6 100644 --- a/JUSTFILE +++ b/JUSTFILE @@ -1,8 +1,8 @@ +test: + curl --unix-socket ./socket http:/./ -X POST -d '{"email": "foo@henryhiles.com", "username": "name"}' + watch: dart run build_runner watch build: - dart run build_runner build - -test: - curl --unix-socket ./socket http:/root -X POST -d '{"email": "foo", "username": "bar"}' \ No newline at end of file + dart run build_runner build \ No newline at end of file diff --git a/bin/nexusbot.dart b/bin/nexusbot.dart index b7444d6..a56bc33 100644 --- a/bin/nexusbot.dart +++ b/bin/nexusbot.dart @@ -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 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)["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...", + ); + + 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}"); } diff --git a/flake.nix b/flake.nix index 603b68d..715b303 100644 --- a/flake.nix +++ b/flake.nix @@ -16,7 +16,8 @@ _module.args.pkgs = import inputs.nixpkgs {inherit system;}; devShells.default = pkgs.mkShell { - buildInputs = with pkgs; [just dart watchexec]; + packages = with pkgs; [just dart watchexec]; + LD_LIBRARY_PATH = "${pkgs.lib.makeLibraryPath (with pkgs; [sqlite])}:$LD_LIBRARY_PATH"; }; packages.default = pkgs.buildDartApplication { diff --git a/lib/controllers/client_controller.dart b/lib/controllers/client_controller.dart index e69de29..066c618 100644 --- a/lib/controllers/client_controller.dart +++ b/lib/controllers/client_controller.dart @@ -0,0 +1,33 @@ +import "dart:io"; + +import "package:matrix/matrix.dart"; +import "package:nexusbot/controllers/settings_controller.dart"; +import "package:riverpod/riverpod.dart"; +import "package:sqflite_common_ffi/sqflite_ffi.dart"; + +class ClientController extends AsyncNotifier { + @override + Future build() async { + final settings = ref.watch(SettingsController.provider)!; + final client = Client( + "nexusbot", + database: await MatrixSdkDatabase.init( + "NexusBot", + database: await databaseFactoryFfi.openDatabase(inMemoryDatabasePath), + ), + ); + + await client.checkHomeserver(settings.homeserver); + await client.login( + LoginType.mLoginPassword, + identifier: AuthenticationUserIdentifier(user: settings.name), + password: (await File(settings.botPasswordFile).readAsString()).trim(), + ); + + return client; + } + + static final provider = AsyncNotifierProvider( + ClientController.new, + ); +} diff --git a/lib/controllers/login_controller.dart b/lib/controllers/login_controller.dart deleted file mode 100644 index 3f30d7c..0000000 --- a/lib/controllers/login_controller.dart +++ /dev/null @@ -1,36 +0,0 @@ -import "dart:io"; -import "dart:convert"; -import "package:http/http.dart"; -import "package:nexusbot/controllers/settings_controller.dart"; -import "package:riverpod/riverpod.dart"; - -class LoginController extends AsyncNotifier { - @override - Future build() async { - final settings = ref.watch(SettingsController.provider)!; - final response = await post( - settings.homeserver.replace(path: "_matrix/client/v3/login"), - headers: {HttpHeaders.contentTypeHeader: "application/json"}, - body: json.encode({ - "type": "m.login.password", - "device_id": "script", - "identifier": {"type": "m.id.user", "user": settings.name}, - "password": (await File( - settings.botPasswordFile, - ).readAsString()).trim(), - }), - ); - - if (response.statusCode != 200) { - throw Exception( - "Login failed, check your name, homeserver and botPasswordFile: ${response.body}", - ); - } - - return json.decode(response.body)["access_token"]; - } - - static final provider = AsyncNotifierProvider( - LoginController.new, - ); -} diff --git a/lib/controllers/mail_client_controller.dart b/lib/controllers/mail_client_controller.dart new file mode 100644 index 0000000..18c8a4f --- /dev/null +++ b/lib/controllers/mail_client_controller.dart @@ -0,0 +1,27 @@ +import "dart:io"; +import "package:enough_mail/enough_mail.dart"; +import "package:nexusbot/controllers/settings_controller.dart"; +import "package:riverpod/riverpod.dart"; + +class MailClientController extends AsyncNotifier { + @override + Future build() async { + final settings = ref.watch(SettingsController.provider)!; + final account = MailAccount.fromManualSettings( + email: settings.email, + name: settings.mailName, + incomingHost: settings.mailDomain, + outgoingHost: settings.mailDomain, + password: (await File(settings.smtpPasswordFile).readAsString()).trim(), + ); + + final client = MailClient(account); + await client.connect(); + return client; + } + + static final provider = + AsyncNotifierProvider( + MailClientController.new, + ); +} diff --git a/lib/controllers/registration_controller.dart b/lib/controllers/registration_controller.dart new file mode 100644 index 0000000..6b3b2ff --- /dev/null +++ b/lib/controllers/registration_controller.dart @@ -0,0 +1,15 @@ +import "package:fast_immutable_collections/fast_immutable_collections.dart"; +import "package:nexusbot/models/registration.dart"; +import "package:riverpod/riverpod.dart"; + +class RegistrationController extends Notifier> { + @override + IList build() => const IList.empty(); + + void add(Registration registration) => state = state.add(registration); + + static final provider = + NotifierProvider>( + RegistrationController.new, + ); +} diff --git a/lib/helpers/api_helper.dart b/lib/helpers/api_helper.dart deleted file mode 100644 index d50f556..0000000 --- a/lib/helpers/api_helper.dart +++ /dev/null @@ -1,130 +0,0 @@ -import "dart:convert"; -import "dart:io"; -import "package:markdown/markdown.dart"; -import "package:nexusbot/controllers/last_synced_controller.dart"; -import "package:nexusbot/controllers/login_controller.dart"; -import "package:nexusbot/controllers/settings_controller.dart"; -import "package:nexusbot/models/registration.dart"; -import "package:shelf/shelf.dart"; -import "package:http/http.dart" as http; -import "package:riverpod/riverpod.dart"; -import "package:matrix/matrix.dart"; - -class ApiHelper { - final Ref ref; - ApiHelper(this.ref); - - String getTxn() => DateTime.now().millisecondsSinceEpoch.toString(); - Future> getHeaders() async => { - HttpHeaders.contentTypeHeader: "application/json", - HttpHeaders.authorizationHeader: - "Bearer ${await ref.watch(LoginController.provider.future)}", - }; - - Future getRoomId(String alias) async { - final settings = ref.watch(SettingsController.provider)!; - final response = await http.get( - settings.homeserver.replace( - path: "_matrix/client/v3/directory/room/$alias", - ), - headers: await getHeaders(), - ); - - if (response.statusCode != 200) { - throw Exception("Alias lookup failed for $alias: ${response.body}"); - } - - return json.decode(response.body)["room_id"]; - } - - Future sendMessage(message, {required String room}) async { - final settings = ref.read(SettingsController.provider)!; - final response = await http.put( - settings.homeserver.replace( - path: - "_matrix/client/v3/rooms/${await getRoomId(room)}/send/m.room.message/${getTxn()}", - ), - headers: await getHeaders(), - body: json.encode({ - "msgtype": "m.text", - "body": message, - "format": "org.matrix.custom.html", - "formatted_body": markdownToHtml(message), - }), - ); - - if (response.statusCode != 200) { - throw Exception("Message send failed for room $room: ${response.body}"); - } - - return json.decode(response.body)["event_id"]; - } - - Future react( - String reaction, { - required String eventId, - required String room, - }) async { - final settings = ref.read(SettingsController.provider)!; - final response = await http.put( - settings.homeserver.replace( - path: - "_matrix/client/v3/rooms/${await getRoomId(room)}/send/m.reaction/${getTxn()}", - ), - headers: await getHeaders(), - body: json.encode({ - "m.relates_to": { - "rel_type": "m.annotation", - "event_id": eventId, - "key": reaction, - }, - }), - ); - - if (response.statusCode != 200) { - throw Exception("Reaction failed for room $room: ${response.body}"); - } - - return json.decode(response.body)["event_id"]; - } - - Future listen() async { - final settings = ref.watch(SettingsController.provider)!; - final response = await http.get( - settings.homeserver.replace( - path: "_matrix/client/v3/sync", - queryParameters: { - "timeout": "30000", - if (ref.watch(LastSyncedController.provider) != null case final since) - "since": since, - }, - ), - headers: await getHeaders(), - ); - - if (response.statusCode != 200) { - throw Exception("Sync failed: ${response.body}"); - } - - final data = json.decode(response); - } - - Future register(Request request) async { - final settings = ref.read(SettingsController.provider)!; - final registration = Registration.fromJson( - json.decode(await request.readAsString()), - ); - - Login - - final event = await sendMessage("""# Registration request -- Username: `${registration.username}` -- Email: `${registration.email}`""", room: settings.adminRoom); - - await react("✅", eventId: event, room: settings.adminRoom); - - return Response.found(settings.successUri); - } - - static final provider = Provider(ApiHelper.new); -} diff --git a/lib/models/settings.dart b/lib/models/settings.dart index 481a410..35edbb4 100644 --- a/lib/models/settings.dart +++ b/lib/models/settings.dart @@ -10,10 +10,13 @@ abstract class Settings with _$Settings { required Uri homeserver, required Uri successUri, required String name, + required String adminName, required String adminRoom, - required String botPasswordFile, + required String email, + required String mailName, + required String mailDomain, required String smtpPasswordFile, - required String? inviteTo, + required String botPasswordFile, }) = _Settings; factory Settings.fromJson(Map json) => diff --git a/lib/models/settings.freezed.dart b/lib/models/settings.freezed.dart index d4d4767..d970287 100644 --- a/lib/models/settings.freezed.dart +++ b/lib/models/settings.freezed.dart @@ -16,7 +16,7 @@ T _$identity(T value) => value; /// @nodoc mixin _$Settings { - String get socket; Uri get homeserver; Uri get successUri; String get name; String get adminRoom; String get botPasswordFile; String get smtpPasswordFile; String? get inviteTo; + String get socket; Uri get homeserver; Uri get successUri; String get name; String get adminName; String get adminRoom; String get email; String get mailName; String get mailDomain; String get smtpPasswordFile; String get botPasswordFile; /// Create a copy of Settings /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -29,16 +29,16 @@ $SettingsCopyWith get copyWith => _$SettingsCopyWithImpl(thi @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is Settings&&(identical(other.socket, socket) || other.socket == socket)&&(identical(other.homeserver, homeserver) || other.homeserver == homeserver)&&(identical(other.successUri, successUri) || other.successUri == successUri)&&(identical(other.name, name) || other.name == name)&&(identical(other.adminRoom, adminRoom) || other.adminRoom == adminRoom)&&(identical(other.botPasswordFile, botPasswordFile) || other.botPasswordFile == botPasswordFile)&&(identical(other.smtpPasswordFile, smtpPasswordFile) || other.smtpPasswordFile == smtpPasswordFile)&&(identical(other.inviteTo, inviteTo) || other.inviteTo == inviteTo)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is Settings&&(identical(other.socket, socket) || other.socket == socket)&&(identical(other.homeserver, homeserver) || other.homeserver == homeserver)&&(identical(other.successUri, successUri) || other.successUri == successUri)&&(identical(other.name, name) || other.name == name)&&(identical(other.adminName, adminName) || other.adminName == adminName)&&(identical(other.adminRoom, adminRoom) || other.adminRoom == adminRoom)&&(identical(other.email, email) || other.email == email)&&(identical(other.mailName, mailName) || other.mailName == mailName)&&(identical(other.mailDomain, mailDomain) || other.mailDomain == mailDomain)&&(identical(other.smtpPasswordFile, smtpPasswordFile) || other.smtpPasswordFile == smtpPasswordFile)&&(identical(other.botPasswordFile, botPasswordFile) || other.botPasswordFile == botPasswordFile)); } @JsonKey(includeFromJson: false, includeToJson: false) @override -int get hashCode => Object.hash(runtimeType,socket,homeserver,successUri,name,adminRoom,botPasswordFile,smtpPasswordFile,inviteTo); +int get hashCode => Object.hash(runtimeType,socket,homeserver,successUri,name,adminName,adminRoom,email,mailName,mailDomain,smtpPasswordFile,botPasswordFile); @override String toString() { - return 'Settings(socket: $socket, homeserver: $homeserver, successUri: $successUri, name: $name, adminRoom: $adminRoom, botPasswordFile: $botPasswordFile, smtpPasswordFile: $smtpPasswordFile, inviteTo: $inviteTo)'; + return 'Settings(socket: $socket, homeserver: $homeserver, successUri: $successUri, name: $name, adminName: $adminName, adminRoom: $adminRoom, email: $email, mailName: $mailName, mailDomain: $mailDomain, smtpPasswordFile: $smtpPasswordFile, botPasswordFile: $botPasswordFile)'; } @@ -49,7 +49,7 @@ abstract mixin class $SettingsCopyWith<$Res> { factory $SettingsCopyWith(Settings value, $Res Function(Settings) _then) = _$SettingsCopyWithImpl; @useResult $Res call({ - String socket, Uri homeserver, Uri successUri, String name, String adminRoom, String botPasswordFile, String smtpPasswordFile, String? inviteTo + String socket, Uri homeserver, Uri successUri, String name, String adminName, String adminRoom, String email, String mailName, String mailDomain, String smtpPasswordFile, String botPasswordFile }); @@ -66,17 +66,20 @@ class _$SettingsCopyWithImpl<$Res> /// Create a copy of Settings /// with the given fields replaced by the non-null parameter values. -@pragma('vm:prefer-inline') @override $Res call({Object? socket = null,Object? homeserver = null,Object? successUri = null,Object? name = null,Object? adminRoom = null,Object? botPasswordFile = null,Object? smtpPasswordFile = null,Object? inviteTo = freezed,}) { +@pragma('vm:prefer-inline') @override $Res call({Object? socket = null,Object? homeserver = null,Object? successUri = null,Object? name = null,Object? adminName = null,Object? adminRoom = null,Object? email = null,Object? mailName = null,Object? mailDomain = null,Object? smtpPasswordFile = null,Object? botPasswordFile = null,}) { return _then(_self.copyWith( socket: null == socket ? _self.socket : socket // ignore: cast_nullable_to_non_nullable as String,homeserver: null == homeserver ? _self.homeserver : homeserver // ignore: cast_nullable_to_non_nullable as Uri,successUri: null == successUri ? _self.successUri : successUri // ignore: cast_nullable_to_non_nullable as Uri,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable +as String,adminName: null == adminName ? _self.adminName : adminName // ignore: cast_nullable_to_non_nullable as String,adminRoom: null == adminRoom ? _self.adminRoom : adminRoom // ignore: cast_nullable_to_non_nullable -as String,botPasswordFile: null == botPasswordFile ? _self.botPasswordFile : botPasswordFile // ignore: cast_nullable_to_non_nullable +as String,email: null == email ? _self.email : email // ignore: cast_nullable_to_non_nullable +as String,mailName: null == mailName ? _self.mailName : mailName // ignore: cast_nullable_to_non_nullable +as String,mailDomain: null == mailDomain ? _self.mailDomain : mailDomain // ignore: cast_nullable_to_non_nullable as String,smtpPasswordFile: null == smtpPasswordFile ? _self.smtpPasswordFile : smtpPasswordFile // ignore: cast_nullable_to_non_nullable -as String,inviteTo: freezed == inviteTo ? _self.inviteTo : inviteTo // ignore: cast_nullable_to_non_nullable -as String?, +as String,botPasswordFile: null == botPasswordFile ? _self.botPasswordFile : botPasswordFile // ignore: cast_nullable_to_non_nullable +as String, )); } @@ -87,17 +90,20 @@ as String?, @JsonSerializable() class _Settings implements Settings { - const _Settings({required this.socket, required this.homeserver, required this.successUri, required this.name, required this.adminRoom, required this.botPasswordFile, required this.smtpPasswordFile, required this.inviteTo}); + const _Settings({required this.socket, required this.homeserver, required this.successUri, required this.name, required this.adminName, required this.adminRoom, required this.email, required this.mailName, required this.mailDomain, required this.smtpPasswordFile, required this.botPasswordFile}); factory _Settings.fromJson(Map json) => _$SettingsFromJson(json); @override final String socket; @override final Uri homeserver; @override final Uri successUri; @override final String name; +@override final String adminName; @override final String adminRoom; -@override final String botPasswordFile; +@override final String email; +@override final String mailName; +@override final String mailDomain; @override final String smtpPasswordFile; -@override final String? inviteTo; +@override final String botPasswordFile; /// Create a copy of Settings /// with the given fields replaced by the non-null parameter values. @@ -112,16 +118,16 @@ Map toJson() { @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is _Settings&&(identical(other.socket, socket) || other.socket == socket)&&(identical(other.homeserver, homeserver) || other.homeserver == homeserver)&&(identical(other.successUri, successUri) || other.successUri == successUri)&&(identical(other.name, name) || other.name == name)&&(identical(other.adminRoom, adminRoom) || other.adminRoom == adminRoom)&&(identical(other.botPasswordFile, botPasswordFile) || other.botPasswordFile == botPasswordFile)&&(identical(other.smtpPasswordFile, smtpPasswordFile) || other.smtpPasswordFile == smtpPasswordFile)&&(identical(other.inviteTo, inviteTo) || other.inviteTo == inviteTo)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is _Settings&&(identical(other.socket, socket) || other.socket == socket)&&(identical(other.homeserver, homeserver) || other.homeserver == homeserver)&&(identical(other.successUri, successUri) || other.successUri == successUri)&&(identical(other.name, name) || other.name == name)&&(identical(other.adminName, adminName) || other.adminName == adminName)&&(identical(other.adminRoom, adminRoom) || other.adminRoom == adminRoom)&&(identical(other.email, email) || other.email == email)&&(identical(other.mailName, mailName) || other.mailName == mailName)&&(identical(other.mailDomain, mailDomain) || other.mailDomain == mailDomain)&&(identical(other.smtpPasswordFile, smtpPasswordFile) || other.smtpPasswordFile == smtpPasswordFile)&&(identical(other.botPasswordFile, botPasswordFile) || other.botPasswordFile == botPasswordFile)); } @JsonKey(includeFromJson: false, includeToJson: false) @override -int get hashCode => Object.hash(runtimeType,socket,homeserver,successUri,name,adminRoom,botPasswordFile,smtpPasswordFile,inviteTo); +int get hashCode => Object.hash(runtimeType,socket,homeserver,successUri,name,adminName,adminRoom,email,mailName,mailDomain,smtpPasswordFile,botPasswordFile); @override String toString() { - return 'Settings(socket: $socket, homeserver: $homeserver, successUri: $successUri, name: $name, adminRoom: $adminRoom, botPasswordFile: $botPasswordFile, smtpPasswordFile: $smtpPasswordFile, inviteTo: $inviteTo)'; + return 'Settings(socket: $socket, homeserver: $homeserver, successUri: $successUri, name: $name, adminName: $adminName, adminRoom: $adminRoom, email: $email, mailName: $mailName, mailDomain: $mailDomain, smtpPasswordFile: $smtpPasswordFile, botPasswordFile: $botPasswordFile)'; } @@ -132,7 +138,7 @@ abstract mixin class _$SettingsCopyWith<$Res> implements $SettingsCopyWith<$Res> factory _$SettingsCopyWith(_Settings value, $Res Function(_Settings) _then) = __$SettingsCopyWithImpl; @override @useResult $Res call({ - String socket, Uri homeserver, Uri successUri, String name, String adminRoom, String botPasswordFile, String smtpPasswordFile, String? inviteTo + String socket, Uri homeserver, Uri successUri, String name, String adminName, String adminRoom, String email, String mailName, String mailDomain, String smtpPasswordFile, String botPasswordFile }); @@ -149,17 +155,20 @@ class __$SettingsCopyWithImpl<$Res> /// Create a copy of Settings /// with the given fields replaced by the non-null parameter values. -@override @pragma('vm:prefer-inline') $Res call({Object? socket = null,Object? homeserver = null,Object? successUri = null,Object? name = null,Object? adminRoom = null,Object? botPasswordFile = null,Object? smtpPasswordFile = null,Object? inviteTo = freezed,}) { +@override @pragma('vm:prefer-inline') $Res call({Object? socket = null,Object? homeserver = null,Object? successUri = null,Object? name = null,Object? adminName = null,Object? adminRoom = null,Object? email = null,Object? mailName = null,Object? mailDomain = null,Object? smtpPasswordFile = null,Object? botPasswordFile = null,}) { return _then(_Settings( socket: null == socket ? _self.socket : socket // ignore: cast_nullable_to_non_nullable as String,homeserver: null == homeserver ? _self.homeserver : homeserver // ignore: cast_nullable_to_non_nullable as Uri,successUri: null == successUri ? _self.successUri : successUri // ignore: cast_nullable_to_non_nullable as Uri,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable +as String,adminName: null == adminName ? _self.adminName : adminName // ignore: cast_nullable_to_non_nullable as String,adminRoom: null == adminRoom ? _self.adminRoom : adminRoom // ignore: cast_nullable_to_non_nullable -as String,botPasswordFile: null == botPasswordFile ? _self.botPasswordFile : botPasswordFile // ignore: cast_nullable_to_non_nullable +as String,email: null == email ? _self.email : email // ignore: cast_nullable_to_non_nullable +as String,mailName: null == mailName ? _self.mailName : mailName // ignore: cast_nullable_to_non_nullable +as String,mailDomain: null == mailDomain ? _self.mailDomain : mailDomain // ignore: cast_nullable_to_non_nullable as String,smtpPasswordFile: null == smtpPasswordFile ? _self.smtpPasswordFile : smtpPasswordFile // ignore: cast_nullable_to_non_nullable -as String,inviteTo: freezed == inviteTo ? _self.inviteTo : inviteTo // ignore: cast_nullable_to_non_nullable -as String?, +as String,botPasswordFile: null == botPasswordFile ? _self.botPasswordFile : botPasswordFile // ignore: cast_nullable_to_non_nullable +as String, )); } diff --git a/lib/models/settings.g.dart b/lib/models/settings.g.dart index 60f17b5..1deff7c 100644 --- a/lib/models/settings.g.dart +++ b/lib/models/settings.g.dart @@ -11,10 +11,13 @@ _Settings _$SettingsFromJson(Map json) => _Settings( homeserver: Uri.parse(json['homeserver'] as String), successUri: Uri.parse(json['successUri'] as String), name: json['name'] as String, + adminName: json['adminName'] as String, adminRoom: json['adminRoom'] as String, - botPasswordFile: json['botPasswordFile'] as String, + email: json['email'] as String, + mailName: json['mailName'] as String, + mailDomain: json['mailDomain'] as String, smtpPasswordFile: json['smtpPasswordFile'] as String, - inviteTo: json['inviteTo'] as String?, + botPasswordFile: json['botPasswordFile'] as String, ); Map _$SettingsToJson(_Settings instance) => { @@ -22,8 +25,11 @@ Map _$SettingsToJson(_Settings instance) => { 'homeserver': instance.homeserver.toString(), 'successUri': instance.successUri.toString(), 'name': instance.name, + 'adminName': instance.adminName, 'adminRoom': instance.adminRoom, - 'botPasswordFile': instance.botPasswordFile, + 'email': instance.email, + 'mailName': instance.mailName, + 'mailDomain': instance.mailDomain, 'smtpPasswordFile': instance.smtpPasswordFile, - 'inviteTo': instance.inviteTo, + 'botPasswordFile': instance.botPasswordFile, }; diff --git a/pubspec.lock b/pubspec.lock index 906c12f..9fb8d42 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -729,6 +729,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.5.6" + sqflite_common_ffi: + dependency: "direct main" + description: + name: sqflite_common_ffi + sha256: "9faa2fedc5385ef238ce772589f7718c24cdddd27419b609bb9c6f703ea27988" + url: "https://pub.dev" + source: hosted + version: "2.3.6" sqlite3: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 7846fa3..d57ff46 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -21,6 +21,7 @@ dependencies: enough_mail: ^2.1.6 markdown: ^7.3.0 matrix: ^1.1.0 + sqflite_common_ffi: ^2.3.6 dev_dependencies: build_runner: ^2.4.6