Last active
July 29, 2023 18:49
-
-
Save gpsnmeajp/09f2ff0bd0c9167fb335aed79311cb26 to your computer and use it in GitHub Desktop.
DartでNostrのTLを眺めるやつ
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// flutter pub add nostr | |
import 'dart:convert'; | |
import 'dart:io'; | |
import 'package:nostr/nostr.dart'; | |
//自分の公開鍵(16進数。https://damus.io/key/ で変換できる) | |
const yourPubKeyHex = | |
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; | |
class User { | |
User(this.pubkey, this.name, this.displayName, this.nip05, this.createdAt); | |
String pubkey = ""; | |
String nip05 = ""; | |
String displayName = ""; | |
String name = ""; | |
int createdAt = 0; | |
@override | |
String toString() { | |
return "$pubkey : $displayName : @$name <$nip05>"; | |
} | |
} | |
void main(List<String> arguments) async { | |
List<Event> receivedEvents = List.empty(growable: true); | |
Map<String, User> userMap = {}; | |
bool isEOSE = false; | |
bool timelineMode = false; | |
print('Hello'); | |
//リレーサーバーに接続する | |
WebSocket webSocket = await WebSocket.connect( | |
'wss://nos.lol', | |
); | |
//メッセージを待ち受ける | |
webSocket.listen((payload) { | |
if (payload is String) { | |
if (!timelineMode) { | |
//受信モード | |
print('Received event: $payload'); | |
} | |
//ここで種類別のデシリアライズも完了する | |
Message m = Message.deserialize(payload); | |
switch (m.type) { | |
case "EVENT": | |
receivedEvents.add(m.message); | |
break; | |
case "EOSE": | |
isEOSE = true; | |
break; | |
case "OK": | |
print('OK!'); | |
break; | |
} | |
} | |
}); | |
//Event event = Event.from(kind: kind, tags: tags, content: content, privkey: privkey) | |
//リレーから受信したい情報を指定する | |
int epochTimeSec_before30day = | |
((DateTime.now().subtract(Duration(days: 30))).millisecondsSinceEpoch / | |
1000) | |
.floor(); | |
String followListSubscriptionId = generate64RandomHexChars().substring(0, 32); | |
//リレーサーバーから最新のフォローリストを取得する | |
Request followListRequest = Request(followListSubscriptionId, [ | |
Filter( | |
//3=連絡先情報 | |
kinds: [3], | |
authors: [yourPubKeyHex], | |
//受信する最低時刻 | |
since: epochTimeSec_before30day, | |
//最初に受信するイベント数の上限 | |
limit: 450, | |
) | |
]); | |
//要求を送信 | |
isEOSE = false; | |
receivedEvents = List.empty(growable: true); | |
webSocket.add(followListRequest.serialize()); | |
//完了するまで待つ | |
while (!isEOSE) { | |
await Future.delayed(Duration(milliseconds: 30)); | |
} | |
//要求解除を送信 | |
webSocket.add(Close(followListSubscriptionId).serialize()); | |
//最新順にソート | |
receivedEvents.sort((a, b) => b.createdAt.compareTo(a.createdAt)); | |
//何らかの理由でフォローリスト以外のものが出てきたとき | |
if (receivedEvents[0].kind != 3) { | |
throw "not contact(3)"; | |
} | |
//最新のフォローリストを決定 | |
String relayList = receivedEvents[0].content; | |
List<List<String>> followList = receivedEvents[0].tags; | |
//受信先リストを生成 | |
List<String> followedPubKeys = List.empty(growable: true); | |
for (var element in followList) { | |
if (element[0] == "p") { | |
followedPubKeys.add(element[1]); | |
} | |
} | |
print("==> $followedPubKeys"); | |
//------------- | |
//ユーザー名とアイコンを取得する | |
String profileSubscriptionId = generate64RandomHexChars().substring(0, 32); | |
Request profileRequest = Request(profileSubscriptionId, [ | |
Filter( | |
//0=メタデータ | |
kinds: [0], | |
//受信する最低時刻 | |
since: epochTimeSec_before30day, | |
//フォローしてる人リスト | |
authors: followedPubKeys, | |
//最初に受信するイベント数の上限 | |
limit: 450, | |
) | |
]); | |
//要求を送信 | |
isEOSE = false; | |
receivedEvents = List.empty(growable: true); | |
webSocket.add(profileRequest.serialize()); | |
//完了するまで待つ | |
while (!isEOSE) { | |
await Future.delayed(Duration(milliseconds: 30)); | |
} | |
//要求解除を送信 | |
webSocket.add(Close(profileSubscriptionId).serialize()); | |
//解析 | |
for (var element in receivedEvents) { | |
Map<String, dynamic> json = jsonDecode(element.content); | |
//存在しないか古い場合は上書きする | |
if (!userMap.containsKey(element.pubkey) || | |
userMap[element.pubkey]!.createdAt < element.createdAt) { | |
userMap[element.pubkey] = User( | |
element.pubkey, | |
json["name"] ?? "", | |
json["display_name"] == "" | |
? (json["name"] ?? "") | |
: (json["display_name"] ?? json["name"] ?? ""), | |
json["nip05"] ?? "", | |
element.createdAt); | |
print(userMap[element.pubkey]); | |
} | |
} | |
print("====================="); | |
int epochTimeSec_before30min = | |
((DateTime.now().subtract(Duration(minutes: 30))).millisecondsSinceEpoch / | |
1000) | |
.floor(); | |
String messageSubscriptionId = generate64RandomHexChars().substring(0, 32); | |
Request request = Request(messageSubscriptionId, [ | |
Filter( | |
//1=テキスト, 2=推奨リレー | |
//5=削除イベント, 7=リアクション(+1) | |
kinds: [1, 2, 5], | |
//受信する最低時刻 | |
since: epochTimeSec_before30min, | |
//フォローしてる人リスト | |
authors: followedPubKeys, | |
//最初に受信するイベント数の上限 | |
limit: 450, | |
) | |
]); | |
//要求を送信 | |
isEOSE = false; | |
receivedEvents = List.empty(growable: true); | |
webSocket.add(request.serialize()); | |
//完了するまで待つ | |
while (!isEOSE) { | |
await Future.delayed(Duration(milliseconds: 30)); | |
} | |
//最新順にソート | |
receivedEvents.sort((a, b) => a.createdAt.compareTo(b.createdAt)); | |
print("====================="); | |
//以降来たら順に出す | |
timelineMode = true; | |
while (true) { | |
await Future.delayed(Duration(seconds: 1)); | |
if (receivedEvents.isNotEmpty) { | |
for (var message in receivedEvents) { | |
//TLモード (この実装は極めてイケていない) | |
String name = userMap[message.pubkey]?.displayName ?? message.pubkey; | |
String nip05 = userMap[message.pubkey]?.nip05 ?? "-"; | |
print( | |
'\n========== ${name} <${nip05}> ========== \n ${message.content}'); | |
} | |
receivedEvents = List.empty(growable: true); | |
} | |
} | |
//要求解除を送信 | |
webSocket.add(Close(messageSubscriptionId).serialize()); | |
webSocket.close(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment