add internal gomuks command support
This commit is contained in:
parent
3fc33a5ac9
commit
8650c44266
7 changed files with 168 additions and 79 deletions
2
gomuks
2
gomuks
|
|
@ -1 +1 @@
|
||||||
Subproject commit 081d5df14be6bf7131e955461207e26da7ad23c9
|
Subproject commit e745b6dc1baa45ebb62dadf577f54e6ffb2f486f
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import "package:collection/collection.dart";
|
import "package:collection/collection.dart";
|
||||||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||||
|
import "package:nexus/controllers/gomuks_commands.dart";
|
||||||
import "package:nexus/controllers/rooms.dart";
|
import "package:nexus/controllers/rooms.dart";
|
||||||
import "package:nexus/helpers/extensions/get_localpart.dart";
|
import "package:nexus/helpers/extensions/get_localpart.dart";
|
||||||
import "package:nexus/models/bot_command.dart";
|
import "package:nexus/models/bot_command.dart";
|
||||||
|
|
@ -10,12 +11,17 @@ import "package:nexus/models/content/membership.dart";
|
||||||
import "package:nexus/models/event.dart";
|
import "package:nexus/models/event.dart";
|
||||||
|
|
||||||
class BotCommandsController(final String roomId)
|
class BotCommandsController(final String roomId)
|
||||||
extends Notifier<IList<BotCommand>> {
|
extends AsyncNotifier<IList<BotCommand>> {
|
||||||
@override
|
@override
|
||||||
IList<BotCommand> build() => ref.watch(
|
Future<IList<BotCommand>> build() async {
|
||||||
|
final gomuksCommands = await ref.watch(
|
||||||
|
GomuksCommandsController.provider.future,
|
||||||
|
);
|
||||||
|
|
||||||
|
final botCommands = ref.watch(
|
||||||
RoomsController.provider.select((rooms) {
|
RoomsController.provider.select((rooms) {
|
||||||
final room = rooms[roomId];
|
final room = rooms[roomId];
|
||||||
if (room == null) return const .empty();
|
if (room == null) return const IList<BotCommand>.empty();
|
||||||
|
|
||||||
Content? membership(String userId) =>
|
Content? membership(String userId) =>
|
||||||
switch (room.state[EventType.membership.type]?[userId]) {
|
switch (room.state[EventType.membership.type]?[userId]) {
|
||||||
|
|
@ -54,12 +60,17 @@ class BotCommandsController(final String roomId)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.nonNulls
|
.nonNulls
|
||||||
.sortedBy((command) => command.description.command)
|
|
||||||
.toIList();
|
.toIList();
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
static final provider = NotifierProvider.autoDispose
|
return gomuksCommands
|
||||||
|
.addAll(botCommands)
|
||||||
|
.sortedBy((command) => command.description.command)
|
||||||
|
.toIList();
|
||||||
|
}
|
||||||
|
|
||||||
|
static final provider = AsyncNotifierProvider.autoDispose
|
||||||
.family<BotCommandsController, IList<BotCommand>, String>(
|
.family<BotCommandsController, IList<BotCommand>, String>(
|
||||||
BotCommandsController.new,
|
BotCommandsController.new,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import "package:nexus/controllers/client_state.dart";
|
||||||
import "package:nexus/controllers/settings.dart";
|
import "package:nexus/controllers/settings.dart";
|
||||||
import "package:nexus/helpers/extensions/gomuks_buffer.dart";
|
import "package:nexus/helpers/extensions/gomuks_buffer.dart";
|
||||||
import "package:nexus/main.dart";
|
import "package:nexus/main.dart";
|
||||||
|
import "package:nexus/models/content/bot_command_description.dart";
|
||||||
import "package:nexus/models/account.dart";
|
import "package:nexus/models/account.dart";
|
||||||
import "package:nexus/models/capabilities.dart";
|
import "package:nexus/models/capabilities.dart";
|
||||||
import "package:nexus/models/content/message.dart";
|
import "package:nexus/models/content/message.dart";
|
||||||
|
|
@ -240,8 +241,23 @@ class ClientController(final String? accountId) extends AsyncNotifier<int> {
|
||||||
Future<void> redactEvent(RedactEventRequest report) =>
|
Future<void> redactEvent(RedactEventRequest report) =>
|
||||||
_sendCommand("redact_event", report.toJson());
|
_sendCommand("redact_event", report.toJson());
|
||||||
|
|
||||||
Future<Event> sendMessage(SendMessageRequest request) async =>
|
/// Null for gomuks' own commands that don't reply.
|
||||||
Event.fromJson(await _sendCommand("send_message", request.toJson()));
|
Future<Event?> sendMessage(SendMessageRequest request) async =>
|
||||||
|
switch (await _sendCommand("send_message", request.toJson())) {
|
||||||
|
final Map<String, dynamic> json => .fromJson(json),
|
||||||
|
_ => null,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The MSC4391 definitions of gomuks' own commands.
|
||||||
|
Future<IList<BotCommandDescriptionContent>> getCommandDefinitions() async =>
|
||||||
|
switch (await _sendCommand("get_command_definitions")) {
|
||||||
|
final List definitions => .new(
|
||||||
|
definitions.whereType<Map<String, dynamic>>().map(
|
||||||
|
BotCommandDescriptionContent.fromJson,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
_ => const .empty(),
|
||||||
|
};
|
||||||
|
|
||||||
Future<Event> sendEvent(SendEventRequest request) async =>
|
Future<Event> sendEvent(SendEventRequest request) async =>
|
||||||
Event.fromJson(await _sendCommand("send_event", request.toJson()));
|
Event.fromJson(await _sendCommand("send_event", request.toJson()));
|
||||||
|
|
|
||||||
44
lib/controllers/gomuks_commands.dart
Normal file
44
lib/controllers/gomuks_commands.dart
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||||
|
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||||
|
import "package:nexus/controllers/client.dart";
|
||||||
|
import "package:nexus/models/bot_command.dart";
|
||||||
|
|
||||||
|
class GomuksCommandsController extends AsyncNotifier<IList<BotCommand>> {
|
||||||
|
static const unsupported = ISetConst({
|
||||||
|
// Needs gomuks web
|
||||||
|
"devtools",
|
||||||
|
"upgraderoom",
|
||||||
|
|
||||||
|
// Needs attachment stuff
|
||||||
|
"myroomavatar",
|
||||||
|
"globalavatar",
|
||||||
|
"roomavatar",
|
||||||
|
|
||||||
|
// Already in Nexus' UI with a better UX
|
||||||
|
"poll",
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<IList<BotCommand>> build() async =>
|
||||||
|
(await ref
|
||||||
|
.read(ClientController.provider.notifier)
|
||||||
|
.getCommandDefinitions())
|
||||||
|
.where(
|
||||||
|
(description) =>
|
||||||
|
description.isValid &&
|
||||||
|
!unsupported.contains(description.command),
|
||||||
|
)
|
||||||
|
.map(
|
||||||
|
(description) => BotCommand(
|
||||||
|
sender: BotCommand.gomuksSender,
|
||||||
|
botName: "gomuks",
|
||||||
|
description: description,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toIList();
|
||||||
|
|
||||||
|
static final provider =
|
||||||
|
AsyncNotifierProvider<GomuksCommandsController, IList<BotCommand>>(
|
||||||
|
GomuksCommandsController.new,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -281,6 +281,12 @@ class RoomChatController(final ChatParams info)
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
addSent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shows [event], which was just sent, until it comes down sync.
|
||||||
|
void addSent(Event? event) {
|
||||||
|
if (event case final event?) {
|
||||||
ref
|
ref
|
||||||
.read(RoomsController.provider.notifier)
|
.read(RoomsController.provider.notifier)
|
||||||
.update(
|
.update(
|
||||||
|
|
@ -292,6 +298,7 @@ class RoomChatController(final ChatParams info)
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> removeReaction(
|
Future<void> removeReaction(
|
||||||
String reaction,
|
String reaction,
|
||||||
|
|
@ -356,7 +363,6 @@ class RoomChatController(final ChatParams info)
|
||||||
Content content, {
|
Content content, {
|
||||||
bool shouldMention = true,
|
bool shouldMention = true,
|
||||||
int? repliedEventRowId,
|
int? repliedEventRowId,
|
||||||
IList<String> mentionedUserIds = const .empty(),
|
|
||||||
}) async {
|
}) async {
|
||||||
final repliedEvent = repliedEventRowId == null
|
final repliedEvent = repliedEventRowId == null
|
||||||
? null
|
? null
|
||||||
|
|
@ -375,7 +381,6 @@ class RoomChatController(final ChatParams info)
|
||||||
content: content,
|
content: content,
|
||||||
mentions: Mentions(
|
mentions: Mentions(
|
||||||
userIds: [
|
userIds: [
|
||||||
...mentionedUserIds,
|
|
||||||
if (shouldMention && repliedEvent != null) repliedEvent.sender,
|
if (shouldMention && repliedEvent != null) repliedEvent.sender,
|
||||||
].toIList(),
|
].toIList(),
|
||||||
),
|
),
|
||||||
|
|
@ -383,16 +388,7 @@ class RoomChatController(final ChatParams info)
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
ref
|
addSent(event);
|
||||||
.read(RoomsController.provider.notifier)
|
|
||||||
.update(
|
|
||||||
.new({
|
|
||||||
info.roomId: .new(
|
|
||||||
events: .new({event.rowId: event}),
|
|
||||||
clientSticky: .new({event.rowId}),
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends [image] as a sticker, as a reply to [repliedEventRowId] if set.
|
/// Sends [image] as a sticker, as a reply to [repliedEventRowId] if set.
|
||||||
|
|
@ -437,14 +433,33 @@ class RoomChatController(final ChatParams info)
|
||||||
Future<void> sendBotCommand(
|
Future<void> sendBotCommand(
|
||||||
BotCommandDraft draft, {
|
BotCommandDraft draft, {
|
||||||
int? repliedEventRowId,
|
int? repliedEventRowId,
|
||||||
}) => sendWithReply(
|
}) async {
|
||||||
.message,
|
final repliedEvent = repliedEventRowId == null
|
||||||
BotCommandMessageContent(body: draft.body, command: draft.input),
|
? null
|
||||||
shouldMention: false,
|
: ref
|
||||||
mentionedUserIds: [draft.command.sender].lock,
|
.read(RoomsController.provider)[info.roomId]
|
||||||
repliedEventRowId: repliedEventRowId,
|
?.events[repliedEventRowId];
|
||||||
|
|
||||||
|
final event = await ref
|
||||||
|
.read(ClientController.provider.notifier)
|
||||||
|
.sendMessage(
|
||||||
|
.new(
|
||||||
|
roomId: info.roomId,
|
||||||
|
text: "",
|
||||||
|
baseContent: BotCommandMessageContent(
|
||||||
|
body: draft.body,
|
||||||
|
command: draft.input,
|
||||||
|
),
|
||||||
|
mentions: .new(userIds: [draft.command.sender].lock),
|
||||||
|
relation: repliedEvent == null
|
||||||
|
? null
|
||||||
|
: .new(eventId: repliedEvent.eventId, relationType: .reply),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
addSent(event);
|
||||||
|
}
|
||||||
|
|
||||||
/// Completes once the event has come down sync.
|
/// Completes once the event has come down sync.
|
||||||
Future<void> sendPollReference(
|
Future<void> sendPollReference(
|
||||||
Event poll,
|
Event poll,
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,8 @@ class const BotCommand({
|
||||||
"htmlmd",
|
"htmlmd",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
static const gomuksSender = "@gomuks";
|
||||||
|
|
||||||
String get prefix => "/${description.command}";
|
String get prefix => "/${description.command}";
|
||||||
String get about => [botName, ?description.description].join(" · ");
|
String get about => [botName, ?description.description].join(" · ");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -300,7 +300,8 @@ class const Composer(
|
||||||
|
|
||||||
final botCommands = isEditing || hasReplacement || attachment != null
|
final botCommands = isEditing || hasReplacement || attachment != null
|
||||||
? const IList<BotCommand>.empty()
|
? const IList<BotCommand>.empty()
|
||||||
: ref.watch(BotCommandsController.provider(roomId));
|
: ref.watch(BotCommandsController.provider(roomId)).value ??
|
||||||
|
const .empty();
|
||||||
|
|
||||||
void pickBotCommand(BotCommand command) {
|
void pickBotCommand(BotCommand command) {
|
||||||
ref
|
ref
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue