Skip to content

Instantly share code, notes, and snippets.

@gpsnmeajp
Last active July 29, 2023 18:49
Show Gist options
  • Save gpsnmeajp/09f2ff0bd0c9167fb335aed79311cb26 to your computer and use it in GitHub Desktop.
Save gpsnmeajp/09f2ff0bd0c9167fb335aed79311cb26 to your computer and use it in GitHub Desktop.
DartでNostrのTLを眺めるやつ
// 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