wip: Sicherungs-Commit aller Änderungen seit April + Arbeitsstruktur (CLAUDE.md, ROADMAP.md, PROGRESS.md, Autopilot)
6 Wochen uncommittete Arbeit (Voice-Channels, LiveKit-Manager, Settings-Modal u.v.m.) als ein WIP-Commit gesichert, damit nichts verloren geht und der Pi den aktuellen Stand klonen kann. Thematische Aufarbeitung: siehe ROADMAP M0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CPrAGBxBT6GfPXzeWQ4AXb
This commit is contained in:
@@ -0,0 +1,238 @@
|
||||
# 11 — Suche & Discovery
|
||||
|
||||
---
|
||||
|
||||
## Inhaltsverzeichnis
|
||||
- [Nachrichten in einem Raum suchen](#nachrichten-in-einem-raum-suchen)
|
||||
- [Globale Nachrichten-Suche](#globale-nachrichten-suche)
|
||||
- [User-Suche](#user-suche)
|
||||
- [Öffentliches Raumverzeichnis](#öffentliches-raumverzeichnis)
|
||||
- [Matrix-URI auflösen](#matrix-uri-auflösen)
|
||||
- [Raum per Alias finden](#raum-per-alias-finden)
|
||||
|
||||
---
|
||||
|
||||
## Nachrichten in einem Raum suchen
|
||||
|
||||
```dart
|
||||
// In der lokalen Timeline suchen (gecacht)
|
||||
final found = timeline.events
|
||||
.where((e) =>
|
||||
e.type == EventTypes.Message &&
|
||||
e.body.toLowerCase().contains(query.toLowerCase()))
|
||||
.toList();
|
||||
|
||||
// Auf dem Server suchen (Matrix-Suchendpoint)
|
||||
final result = await client.searchEvents(
|
||||
Categories(
|
||||
roomEvents: RoomEventsCriteria(
|
||||
searchTerm: 'suchbegriff',
|
||||
roomId: room.id, // optional: nur in einem Raum
|
||||
orderBy: SearchOrder.recent, // oder .rank
|
||||
keys: [SearchKey.contentBody], // oder .contentName, .contentTopic
|
||||
filter: RoomEventFilter(
|
||||
limit: 20,
|
||||
rooms: [room.id],
|
||||
notRooms: [],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Ergebnisse
|
||||
for (final result in result.roomEvents?.results ?? []) {
|
||||
print(result.result.body);
|
||||
print(result.result.originServerTs);
|
||||
print(result.result.senderId);
|
||||
}
|
||||
|
||||
// Paginierung
|
||||
final nextToken = result.roomEvents?.nextBatch;
|
||||
if (nextToken != null) {
|
||||
final more = await client.searchEvents(
|
||||
/* gleiche Criteria */,
|
||||
nextBatch: nextToken,
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Globale Nachrichten-Suche
|
||||
|
||||
```dart
|
||||
// Über alle Räume suchen
|
||||
final result = await client.searchEvents(
|
||||
Categories(
|
||||
roomEvents: RoomEventsCriteria(
|
||||
searchTerm: 'flutter matrix',
|
||||
orderBy: SearchOrder.recent,
|
||||
includeState: false,
|
||||
groupings: Groupings(
|
||||
groupBy: [Group(key: GroupKey.roomId)], // Gruppiert nach Raum
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Gruppen-Ergebnisse
|
||||
for (final entry in result.roomEvents?.groups?.entries ?? []) {
|
||||
final groupKey = entry.key; // 'room_id'
|
||||
for (final roomGroup in entry.value.entries) {
|
||||
final roomId = roomGroup.key;
|
||||
print('Treffer in $roomId: ${roomGroup.value.count}');
|
||||
// roomGroup.value.results: List<String> (Event-IDs)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## User-Suche
|
||||
|
||||
```dart
|
||||
// User-Verzeichnis durchsuchen
|
||||
final response = await client.searchUserDirectory(
|
||||
'alice', // Suchbegriff (Name oder MXID)
|
||||
limit: 20,
|
||||
);
|
||||
|
||||
for (final user in response.results) {
|
||||
print(user.userId); // @alice:server
|
||||
print(user.displayName); // Alice
|
||||
print(user.avatarUrl); // mxc://...
|
||||
|
||||
// Avatar als HTTP-URL
|
||||
final avatarHttp = user.avatarUrl?.getThumbnail(
|
||||
client,
|
||||
width: 56,
|
||||
height: 56,
|
||||
method: ThumbnailMethod.crop,
|
||||
);
|
||||
}
|
||||
|
||||
print(response.limited); // true wenn weitere Ergebnisse vorhanden
|
||||
|
||||
// MXID direkt laden (ohne Suche)
|
||||
final profile = await client.getProfileFromUserId('@alice:server');
|
||||
print(profile.displayname);
|
||||
print(profile.avatarUrl);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Öffentliches Raumverzeichnis
|
||||
|
||||
```dart
|
||||
// Öffentliche Räume (des eigenen Servers)
|
||||
final result = await client.queryPublicRooms(
|
||||
limit: 50,
|
||||
);
|
||||
|
||||
// Mit Suchbegriff
|
||||
final searched = await client.queryPublicRooms(
|
||||
limit: 20,
|
||||
filter: PublicRoomQueryFilter(
|
||||
genericSearchTerm: 'flutter',
|
||||
),
|
||||
);
|
||||
|
||||
// Anderen Server durchsuchen
|
||||
final remote = await client.queryPublicRooms(
|
||||
server: 'matrix.org',
|
||||
limit: 20,
|
||||
filter: PublicRoomQueryFilter(genericSearchTerm: 'linux'),
|
||||
);
|
||||
|
||||
// Paginierung
|
||||
var batch = await client.queryPublicRooms(limit: 50);
|
||||
while (batch.nextBatch != null) {
|
||||
batch = await client.queryPublicRooms(
|
||||
limit: 50,
|
||||
since: batch.nextBatch,
|
||||
);
|
||||
}
|
||||
|
||||
// Einzelnes Ergebnis
|
||||
for (final room in result.chunk) {
|
||||
print(room.roomId);
|
||||
print(room.name);
|
||||
print(room.topic);
|
||||
print(room.numJoinedMembers);
|
||||
print(room.worldReadable);
|
||||
print(room.guestCanJoin);
|
||||
print(room.avatarUrl);
|
||||
print(room.canonicalAlias);
|
||||
print(room.joinRule); // 'public' | 'knock' | etc.
|
||||
print(room.roomType); // 'm.space' | null
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Matrix-URI auflösen
|
||||
|
||||
```dart
|
||||
// Matrix-to URI parsen
|
||||
// https://matrix.to/#/@alice:server
|
||||
// https://matrix.to/#/!roomId:server
|
||||
// https://matrix.to/#/#alias:server
|
||||
// matrix:u/alice:server
|
||||
// matrix:r/alias:server
|
||||
// matrix:roomid/roomId:server
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
final uri = MatrixUri.parse('https://matrix.to/#/#kanal:example.org');
|
||||
if (uri != null) {
|
||||
switch (uri.primaryType) {
|
||||
case MatrixUriType.room: // Raum-Alias
|
||||
case MatrixUriType.roomId: // Raum-ID
|
||||
case MatrixUriType.user: // User-MXID
|
||||
case MatrixUriType.event: // Event-Link
|
||||
}
|
||||
|
||||
final identifier = uri.primaryIdentifier;
|
||||
// z.B. '#kanal:example.org' oder '!roomId:server'
|
||||
|
||||
// Beitreten (bei Raum)
|
||||
if (uri.primaryType == MatrixUriType.room ||
|
||||
uri.primaryType == MatrixUriType.roomId) {
|
||||
await client.joinRoomOrAlias(
|
||||
identifier,
|
||||
serverName: uri.viaServers, // föderierte Server
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Matrix-URI generieren
|
||||
final roomUri = MatrixUri(
|
||||
primaryType: MatrixUriType.roomId,
|
||||
primaryIdentifier: room.id,
|
||||
viaServers: ['example.org'],
|
||||
).toUri().toString();
|
||||
// → matrix:roomid/roomId:server?via=example.org
|
||||
|
||||
// Matrix.to Link generieren
|
||||
final matrixToLink = 'https://matrix.to/#/${Uri.encodeComponent(room.canonicalAlias ?? room.id)}';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Raum per Alias finden
|
||||
|
||||
```dart
|
||||
// Alias → Raum-ID auflösen
|
||||
final result = await client.getRoomIdByAlias('#alias:server');
|
||||
print(result.roomId);
|
||||
print(result.servers); // Via-Server für Beitritt
|
||||
|
||||
// Direkt beitreten via Alias
|
||||
await client.joinRoomOrAlias('#alias:server');
|
||||
|
||||
// Lokal prüfen (gecacht)
|
||||
final localRoom = client.rooms.firstWhere(
|
||||
(r) => r.canonicalAlias == '#alias:server',
|
||||
orElse: () => throw Exception('Nicht gefunden'),
|
||||
);
|
||||
```
|
||||
Reference in New Issue
Block a user