Compare commits

..

2 commits

Author SHA1 Message Date
b9adcb205e
Working 2025-08-02 23:28:51 -04:00
64dd38f7aa
initial 2025-08-01 20:10:34 -04:00
15 changed files with 513 additions and 190 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"}'

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,30 +16,141 @@ 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,
),
);

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

@ -0,0 +1,12 @@
import "package:riverpod/riverpod.dart";
class LastSyncedController extends Notifier<String?> {
@override
String? build() => null;
void set(String value) => state = value;
static final provider = NotifierProvider<LastSyncedController, String?>(
LastSyncedController.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,105 +0,0 @@
import "dart:convert";
import "dart:io";
import "package:markdown/markdown.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";
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<Response> register(Request request) async {
final settings = ref.read(SettingsController.provider)!;
final registration = Registration.fromJson(
json.decode(await request.readAsString()),
);
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

@ -17,6 +17,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "7.4.5"
archive:
dependency: transitive
description:
name: archive
sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd"
url: "https://pub.dev"
source: hosted
version: "4.0.7"
args:
dependency: "direct main"
description:
@ -41,6 +49,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.13.0"
base58check:
dependency: transitive
description:
name: base58check
sha256: "6c300dfc33e598d2fe26319e13f6243fea81eaf8204cb4c6b69ef20a625319a5"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
basic_utils:
dependency: transitive
description:
@ -49,6 +65,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "5.7.0"
blurhash_dart:
dependency: transitive
description:
name: blurhash_dart
sha256: "43955b6c2e30a7d440028d1af0fa185852f3534b795cc6eb81fbf397b464409f"
url: "https://pub.dev"
source: hosted
version: "1.2.1"
boolean_selector:
dependency: transitive
description:
@ -65,6 +89,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.5.1"
build_cli_annotations:
dependency: transitive
description:
name: build_cli_annotations
sha256: b59d2769769efd6c9ff6d4c4cede0be115a566afc591705c2040b707534b1172
url: "https://pub.dev"
source: hosted
version: "2.1.0"
build_config:
dependency: transitive
description:
@ -121,6 +153,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "8.10.1"
canonical_json:
dependency: transitive
description:
name: canonical_json
sha256: d6be1dd66b420c6ac9f42e3693e09edf4ff6edfee26cb4c28c1c019fdb8c0c15
url: "https://pub.dev"
source: hosted
version: "1.1.2"
checked_yaml:
dependency: transitive
description:
@ -137,6 +177,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.1.0"
cli_config:
dependency: transitive
description:
name: cli_config
sha256: ac20a183a07002b700f0c25e61b7ee46b23c309d76ab7b7640a028f18e4d99ec
url: "https://pub.dev"
source: hosted
version: "0.2.0"
cli_tools:
dependency: "direct main"
description:
@ -177,6 +225,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.1.2"
coverage:
dependency: transitive
description:
name: coverage
sha256: "5da775aa218eaf2151c721b16c01c7676fbfdd99cebba2bf64e8b807a28ff94d"
url: "https://pub.dev"
source: hosted
version: "1.15.0"
crypto:
dependency: transitive
description:
@ -185,6 +241,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.6"
csslib:
dependency: transitive
description:
name: csslib
sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e"
url: "https://pub.dev"
source: hosted
version: "1.0.2"
dart_mappable:
dependency: transitive
description:
@ -209,6 +273,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "5.0.3"
enhanced_enum:
dependency: transitive
description:
name: enhanced_enum
sha256: "074c5a8b9664799ca91e1e8b68003b8694cb19998671cbafd9c7779c13fcdecf"
url: "https://pub.dev"
source: hosted
version: "0.2.4"
enough_convert:
dependency: transitive
description:
@ -241,6 +313,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "11.0.4"
ffi:
dependency: transitive
description:
name: ffi
sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
file:
dependency: transitive
description:
@ -257,6 +337,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.1"
flutter_rust_bridge:
dependency: transitive
description:
name: flutter_rust_bridge
sha256: b416ff56002789e636244fb4cc449f587656eff995e5a7169457eb0593fcaddb
url: "https://pub.dev"
source: hosted
version: "2.10.0"
freezed:
dependency: "direct dev"
description:
@ -297,6 +385,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.3.2"
html:
dependency: transitive
description:
name: html
sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602"
url: "https://pub.dev"
source: hosted
version: "0.15.6"
html_unescape:
dependency: transitive
description:
name: html_unescape
sha256: "15362d7a18f19d7b742ef8dcb811f5fd2a2df98db9f80ea393c075189e0b61e3"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
http:
dependency: "direct main"
description:
@ -329,6 +433,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.1.2"
image:
dependency: transitive
description:
name: image
sha256: "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928"
url: "https://pub.dev"
source: hosted
version: "4.5.4"
intl:
dependency: transitive
description:
@ -349,10 +461,10 @@ packages:
dependency: transitive
description:
name: js
sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc"
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
url: "https://pub.dev"
source: hosted
version: "0.7.2"
version: "0.6.7"
json_annotation:
dependency: "direct main"
description:
@ -401,6 +513,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.12.17"
matrix:
dependency: "direct main"
description:
name: matrix
sha256: "0c033a6ebf4ed2f56ed604769984072961fefc0cb255a802ed441dcaec490196"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
meta:
dependency: transitive
description:
@ -465,6 +585,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.5.1"
posix:
dependency: transitive
description:
name: posix
sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61"
url: "https://pub.dev"
source: hosted
version: "6.0.3"
pub_api_client:
dependency: transitive
description:
@ -489,6 +617,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.5.0"
random_string:
dependency: transitive
description:
name: random_string
sha256: "03b52435aae8cbdd1056cf91bfc5bf845e9706724dd35ae2e99fa14a1ef79d02"
url: "https://pub.dev"
source: hosted
version: "2.3.1"
rfc_6901:
dependency: transitive
description:
@ -505,6 +641,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.6.1"
sdp_transform:
dependency: transitive
description:
name: sdp_transform
sha256: "73e412a5279a5c2de74001535208e20fff88f225c9a4571af0f7146202755e45"
url: "https://pub.dev"
source: hosted
version: "0.3.2"
shelf:
dependency: "direct main"
description:
@ -529,6 +673,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.0"
slugify:
dependency: transitive
description:
name: slugify
sha256: b272501565cb28050cac2d96b7bf28a2d24c8dae359280361d124f3093d337c3
url: "https://pub.dev"
source: hosted
version: "2.0.0"
source_gen:
dependency: transitive
description:
@ -545,6 +697,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.5"
source_map_stack_trace:
dependency: transitive
description:
name: source_map_stack_trace
sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b
url: "https://pub.dev"
source: hosted
version: "2.1.2"
source_maps:
dependency: transitive
description:
name: source_maps
sha256: "190222579a448b03896e0ca6eca5998fa810fda630c1d65e2f78b3f638f54812"
url: "https://pub.dev"
source: hosted
version: "0.10.13"
source_span:
dependency: transitive
description:
@ -553,6 +721,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.10.1"
sqflite_common:
dependency: transitive
description:
name: sqflite_common
sha256: "6ef422a4525ecc601db6c0a2233ff448c731307906e92cabc9ba292afaae16a6"
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:
name: sqlite3
sha256: dd806fff004a0aeb01e208b858dbc649bc72104670d425a81a6dd17698535f6e
url: "https://pub.dev"
source: hosted
version: "2.8.0"
stack_trace:
dependency: transitive
description:
@ -625,6 +817,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.7.6"
test_core:
dependency: transitive
description:
name: test_core
sha256: "80bf5a02b60af04b09e14f6fe68b921aad119493e26e490deaca5993fef1b05a"
url: "https://pub.dev"
source: hosted
version: "0.6.11"
timing:
dependency: transitive
description:
@ -649,6 +849,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.0"
unorm_dart:
dependency: transitive
description:
name: unorm_dart
sha256: "5b35bff83fce4d76467641438f9e867dc9bcfdb8c1694854f230579d68cd8f4b"
url: "https://pub.dev"
source: hosted
version: "0.2.0"
vm_service:
dependency: transitive
description:
name: vm_service
sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60"
url: "https://pub.dev"
source: hosted
version: "15.0.2"
vodozemac:
dependency: transitive
description:
name: vodozemac
sha256: dba14017e042748fb22d270e8ab1d3e46965b89788dd3857dba938ec07571968
url: "https://pub.dev"
source: hosted
version: "0.2.0"
watcher:
dependency: transitive
description:
@ -681,6 +905,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.3"
webrtc_interface:
dependency: transitive
description:
name: webrtc_interface
sha256: "86fe3afc81a08481dfb25cf14a5a94e27062ecef25544783f352c914e0bbc1ca"
url: "https://pub.dev"
source: hosted
version: "1.2.2+hotfix.2"
xml:
dependency: transitive
description:

View file

@ -20,6 +20,8 @@ dependencies:
args: ^2.7.0
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