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

12
.vscode/launch.json vendored
View file

@ -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"

View file

@ -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"}'
dart run build_runner build

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}");
}

View file

@ -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 {

View file

@ -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<Client> {
@override
Future<Client> 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, Client>(
ClientController.new,
);
}

View file

@ -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<String> {
@override
Future<String> 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, String>(
LoginController.new,
);
}

View file

@ -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<MailClient> {
@override
Future<MailClient> 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, MailClient>(
MailClientController.new,
);
}

View file

@ -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<IList<Registration>> {
@override
IList<Registration> build() => const IList.empty();
void add(Registration registration) => state = state.add(registration);
static final provider =
NotifierProvider<RegistrationController, IList<Registration>>(
RegistrationController.new,
);
}

View file

@ -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<Map<String, String>> getHeaders() async => {
HttpHeaders.contentTypeHeader: "application/json",
HttpHeaders.authorizationHeader:
"Bearer ${await ref.watch(LoginController.provider.future)}",
};
Future<String> 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<String> 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<String> 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<String> 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<Response> 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>(ApiHelper.new);
}

View file

@ -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<String, dynamic> json) =>

View file

@ -16,7 +16,7 @@ T _$identity<T>(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<Settings> get copyWith => _$SettingsCopyWithImpl<Settings>(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<String, dynamic> 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<String, dynamic> 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,
));
}

View file

@ -11,10 +11,13 @@ _Settings _$SettingsFromJson(Map<String, dynamic> 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<String, dynamic> _$SettingsToJson(_Settings instance) => <String, dynamic>{
@ -22,8 +25,11 @@ Map<String, dynamic> _$SettingsToJson(_Settings instance) => <String, dynamic>{
'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,
};

View file

@ -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:

View file

@ -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