カテゴリ: java

ボットを作ろうとアカウント取った。ただのメアドで取れる。
ちなみにメアドを入れたのにアクティベーションメール的なものも送られてこないでそのまま使える。
Javaから https://github.com/uakihir0/bsky4j ってライブラリを使ってみたが、
{"error":"AuthenticationRequired","message":"Invalid identifier or password"}
と言われて弾かれる。
ちなみに、pythonから
https://github.com/MarshalX/atproto をそのまんま実行したら何の問題も無かったので、パスワードとIDは正しい筈。ライブラリの使い方がまずいのかライブラリにバグがあるのか。
そういえばこのライブラリ、jitpackを使っててivyからjarをダウンロードできず自分でコンパイルしたからちょっと怪しいんだよな…。まずは正しいjarの入手からだな。gradle入れるしかないというのか。


このエントリーをはてなブックマークに追加 mixiチェック

メモ:
java.util.concurrent.ExecutionException: java.lang.NoClassDefFoundError: org/bouncycastle/jce/provider/BouncyCastleProvider
が出る時は、bcprov-jdk15on-1.70.jar をクラスパスに入れること。
https://a4dosanddos.hatenablog.com/entry/2015/06/30/002123


このエントリーをはてなブックマークに追加 mixiチェック

1.Nostrには「クライアントが表示する前にユーザにワンクッション置かせる指定」の規格NIP-36があるのでそれを設定する。
2.iPad用のメジャーなNostrクライアント、Damusは上記のNIP-36に対応しておらず、代わりにハッシュタグ「#nsfw」を見ているらしいので、それを付けるのが親切。
3.ハッシュタグ(NIP-12)は本文中にTwitter同様「#hoge」等と記載し、更にtag「t」を使う。tのvalueには「#」は含めない
4.画像を添付したい時は、インターネット上のどこかに画像をアップロードしてその画像のURIを本文中に含める

つまりはこう





このエントリーをはてなブックマークに追加 mixiチェック

先日Nostrへの書き込みに成功したのでボットをNostrに対応させた
リレーはよくわからんのでこの辺からを適当に羅列したものの中から毎回2つをランダムに選択して送信させているんだけど、同一イベントを例えば100カ所に送るとか駄目なのかしら。あるいは各クライアントのデフォルトに全部送るとか?ただしい作法がわからない。
あと、ハッシュタグに未対応なのでこれは対応しないとな。
まぁ…Nostrのノリが分からぬのでまぁ最初は静かにしておくべし。



このエントリーをはてなブックマークに追加 mixiチェック

Java製のクライアントを作ってNostrに投稿したい(既存のJava製のボットにNostr投稿機能を追加したいので調べた。
一応動作したのでNostrのJava製クライアントに関する日本語情報が少ない事だしここに記す。ちなみにエラー対応不足のため書き込みに失敗すると子スレッドが残ってしまう。
■1.nostr-javaのClientクラス
やれ~/.nostr-java配下にプロパティファイルを作れだのハンドラを作れだのmodule-info.javaを作れと喧しい上に実行したら
Exception in thread "main" java.lang.NoClassDefFoundError: org/bouncycastle/jce/provider/BouncyCastleProvider
と言われhttps://www.bouncycastle.org/latest_releases.htmlからjarをダウンロードして入れたり
java.util.concurrent.ExecutionException: javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested targetr
と言われ実行時に
-Djavax.net.ssl.trustStore=C:\Program Files\Java\jdk-21.0.1\lib\security\cacerts
を入れたはいいがその後もトラブルが起きて解決できず。

■2.JavaでNostrのリレーに1件だけテキストイベントを投稿するコードを書いてみた
…nostr-javaのサンプルよりは分かりやすいけれど、まずビルドが通らない。多分JettyのWebSocketのライブラリとHTTPClientのライブラリのバージョンの不一致あたりだと思うけどそもそもimport文も省略されているので想像で入れたりしたけれど結局通らず。

■3.Jetty 12.x のWebSocketClientのサンプルコードを育てて自作
2番はビルドできないとは言え大いに参考にしつつ、まずはビルドや実行が通るように慎重に本家Jettyのサンプルから育てていく。
とりあえず最後まで動作して相手リレーサーバから
["OK","fdf83a3115ae12c3331acd6390641a2ab075b85bf21a44d7e10ce4d0379b0dc6",true,""]
なる電文が返って来たしNostterにも表示されたので動作はした模様
0103c

ちなみにWebSocketのクローズの辺りは少々あやしい。
NostrSendMain.java
/**
 * Nostr書き込み処理サンプル。
 */
package nekora.nostr;

import java.io.IOException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;

import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.websocket.api.Callback;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.StatusCode;
import org.eclipse.jetty.websocket.client.WebSocketClient;

import nostr.base.PrivateKey;
import nostr.base.PublicKey;
import nostr.crypto.schnorr.Schnorr;
import nostr.util.NostrUtil;

/**
 * 
 */
public class NostrSendMain {
	//final String RELAY_URL = "wss://relay.nostr.wirednet.jp";
	final String RELAY_URL = "wss://relay-jp.nostr.wirednet.jp";
	final String CONTENT = "初カキコ…ども…from My Java Client";

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		NostrSendMain my = new NostrSendMain();
		my.exec(args);
	}

	private void exec(String[] args) {
		// Use a standard, HTTP/1.1, HttpClient.
		HttpClient httpClient = new HttpClient();

		// Create and start WebSocketClient.
		WebSocketClient webSocketClient = new WebSocketClient(httpClient);
		try {
			webSocketClient.start();
			// The client-side WebSocket EndPoint that
			// receives WebSocket messages from the server.
			MyAutoDemandListenerEndPoint myendp = new MyAutoDemandListenerEndPoint();
			// The server URI to connect to.
			URI serverURI = URI.create(RELAY_URL);

			// クライアントエンドポイントをサーバーに接続.
			CompletableFuture clientSessionPromise = webSocketClient.connect(myendp, serverURI);

			// 接続処理完了を待つ
			// Thread.sleep(1500); // これ要らない気がするので削除してみた

			// セッションを取得
			try (Session session = clientSessionPromise.join()) {
				if (!session.isOpen()) {
					System.err.println("接続できませんでした");
					return;
				}
				System.out.println("接続OK…" + session);

				// Nonstr特化通信
				nostrExec(session);
				System.out.println("Nostrに送信完了");

			} catch (Exception e) {
				throw e;
			}

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// クローズ
			try {
				webSocketClient.stop();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		System.out.println("すべて完了");
	}

	/**
	 * Nostrリレーサーバと通信 https://zenn.dev/memory_of_snow/articles/1f0ddd964c5f83 参考
	 * 
	 * @param webSocketClient
	 * @param session
	 */
	private void nostrExec(Session session) {
		// テスト用秘密鍵(=アカウント)をランダムに作成。
		PrivateKey secKey = new PrivateKey(Schnorr.generatePrivateKey());
		//PrivateKey secKey = new PrivateKey("アカウント固定する場合はエンコードされた秘密鍵文字列");

		try {
			// 秘密鍵から公開鍵を作成
			PublicKey publicKey = new PublicKey(Schnorr.genPubKey(secKey.getRawData()));
			String publicKeyHex = publicKey.toString();

			System.out.println("SecKey(HEX) = " + secKey);
			System.out.println("publicKey(HEX) = " + publicKey);

			long created_at = Instant.now().getEpochSecond();
			int kind = 1;
			List> tags = new ArrayList<>();

			// id計算に使う要素を集める
			// [0,"公開鍵(HEX)",投稿時間,kind(プレーンテキスト投稿は1),タグ群,"投稿内容"]
			String partsForId = "[0,\"" + publicKeyHex + "\"," + created_at + "," + kind + "," + tags + ",\"" + CONTENT
					+ "\"]";
			byte[] idSourceBytes = partsForId.getBytes(StandardCharsets.UTF_8);
			String id = NostrUtil.bytesToHex(NostrUtil.sha256(idSourceBytes));
			byte[] signedHashedSerializedEvent = Schnorr.sign(NostrUtil.sha256(idSourceBytes), secKey.getRawData(),
					NostrUtil.createRandomByteArray(32));
			String sig = NostrUtil.bytesToHex(signedHashedSerializedEvent);

			// メッセージは["EVENT",{
			// "id":"id",
			// "pubkey":"公開鍵(Hex)",
			// "created_at":作成時間(UnixTimeStamp),
			// "kind":kind(プレーンテキストは1),
			// "tags":[],
			// "content":"投稿内容",
			// "sig":"署名"
			// }]
			String message = "[\"EVENT\",{\"id\":\"" + id + "\",\"pubkey\":\"" + publicKeyHex + "\","
					+ "\"created_at\":" + created_at + ",\"kind\":" + kind + ",\"tags\":" + tags + ","
					+ "\"content\":\"" + CONTENT + "\",\"sig\":\"" + sig + "\"}]";

			String displayMessage = message.replace(",", ",\n");
			System.out.println("組み立てたメッセージ:\n" + displayMessage);

			// session.getRemote().sendString(message); // こんなメソッドは無いと言われる
			MySendCallBack calbak = new MySendCallBack(message);
			session.sendText(message, calbak);

			// 受信時間稼ぎ
			Thread.sleep(1500); // これ必要なのか分からないがサンプルに従い残す。

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}
MyAutoDemandListenerEndPoint.java
package nekora.nostr;

import org.eclipse.jetty.websocket.api.Session;

/**
 * 接続用エンドポイント 以下をコピペ
 * https://eclipse.dev/jetty/documentation/jetty-12/programming-guide/index.html#pg-client-http
 */

public class MyAutoDemandListenerEndPoint implements Session.Listener.AutoDemanding {
	private Session session;

	@Override
	public void onWebSocketOpen(Session session) {
		this.session = session;
		// No need to demand here, because this endpoint is auto-demanding.
	}

	@Override
	public void onWebSocketText(String message) {
		System.out.println("受信メッセージ=" + message);
		// No need to demand here, because this endpoint is auto-demanding.
	}

}
MySendCallBack.java
package nekora.nostr;

import org.eclipse.jetty.websocket.api.Callback;

/**
 * 送信用コールバック
 * https://eclipse.dev/jetty/documentation/jetty-12/programming-guide/index.html#pg-websocket-session-send
 */
public class MySendCallBack implements Callback {
	private String message;

	public MySendCallBack(String message) {
		this.message = message;
	}

	@Override
	public void succeed() {
		System.out.println("テキスト送信成功." + message);
	}

	@Override
	public void fail(Throwable x) {
		// No need to rethrow or close the session.
		System.out.println("テキスト送信失敗." + message + " エラー=" + x.getMessage());
	}

}
ビルド時にIvy.xmlに指定するのは以下のみ(Mavenならpom.xml)。
dependency org="org.eclipse.jetty.websocket" name="jetty-websocket-jetty-client" rev="12.0.5"
あと、nostr-javaも使っているので事前にimportして、参照させる必要がある。
0104

自分の秘密鍵をhexにしたい時は下記を使う
https://nak.nostr.com/



このエントリーをはてなブックマークに追加 mixiチェック

いつぞやはサンプルがまだ未熟で利用は時期尚早だったnostr-javaだけれど、そろそろよかんべと再挑戦。
今度はMavenのままEclipseにインポートした。nostr-javaのGitHubページからZIPでダウンロードして、展開したディレクトリをそのままEclipseのワークスペースディレクトリにコピーして、Mavenプロジェクトとしてそのディレクトリを指定。すると、ディレクトリ配下に含まれる各Nostr-java-xxプロジェクトがEclipseにインポートされる。
Mavenプロジェクトは、いつものEclipseのJavaプロジェクトと同じように他プロジェクトから参照可能。
いずれ我がボットとなるショボいHello Worldもビルドは出来たけど、久々のSSLにこいつが出て来た…。

java.util.concurrent.ExecutionException: javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

at java.base/java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:396)

at java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:2073)

at nostr.ws.Connection.connect(Connection.java:111)



自分でHTTPClientでクローラとか作る時は、相手サーバやその証明書チェックはスキップしてたんだけど…。
一応、以下のオプションは付けているが駄目。
javax.net.ssl.trustStore=C:/Program Files/Java/jdk-21.0.1/lib/security/cacerts
com.sun.net.ssl.checkRevocation=false

keytoolで何か証明書をインポートするんだっけか…。

Javaビルドツール入門 Maven/Gradle/SBT/Bazel対応
掌田津耶乃
秀和システム
2018-03-01

このエントリーをはてなブックマークに追加 mixiチェック

JavaのLTS、21が出たので我が家のJDKを片っ端から更新。
特に不具合は出ていない。
しかしLTSが2年になったのか…。
家のだから別に良いけど、仕事だと大変だよな。


このエントリーをはてなブックマークに追加 mixiチェック

開発用PCとアフィのサーバのJavaを19から20にしたが、特に変わった事は無し。不具合も起きず。
たしか20からグリーンスレッドが使えるらしいんだけど、特に使う用事もないし。

Effective Java 第3版
柴田芳樹
丸善出版
2021-07-13

このエントリーをはてなブックマークに追加 mixiチェック

いよいよTwitter APIの無料アクセスポイントも最期の時がせまりつつあるので、NostrとかMisskey.ioとかに移行せねばならぬが、misskey4j がgradleとJitPackというナウなヤングのイカしたツールとリポジトリを使っているのでAnt+Ivyに移植(?)が割と厄介だ。特にJitPackとデフォルトリポジトリの両立。
いやGradleとは言わずともいい加減Mavenに移行しろという話もあるがEclipseは相変わらずプロジェクトのデフォはAntなので、当然過去のは全部Ant+Ivyだし。
もう面倒なので依存先のプロジェクトもソースごとローカルでビルドしようかしら。

Javaビルドツール入門 Maven/Gradle/SBT/Bazel対応
掌田津耶乃
秀和システム
2018-03-01


このエントリーをはてなブックマークに追加 mixiチェック

※結局成功した日の日記はこちら

先日ビルドが通ったので
とりあえずサンプルを実行。これ元はNetBeansのMavenプロジェクトなんだけど私はEclipse+Ivyに修正して使っている。何故か複数プロジェクトに分かれていてサンプルはnostr-examplesプロジェクトだ。
※追記:ちなみに結局面倒になってWebSocketから自作した。nostr-javaのClientは私が使うには分厚すぎる。

秘密鍵や表示名などの自分の情報はnostr-idプロジェクト配下のprofile.properties、接続先(?)サーバは同じくrelays.propertiesに記述してクラスパスの通った所に配置する。
あとnostr-examplesのlogging.propertiesも同じく。
relays.propertiesには何を書けば良いのかよくわからないが、nostr.watchで近い所のサーバ名を入れれば多分良いのではなかろうか。ポート番号がわからないけど、とりあえずホスト名だけ変えて後はサンプルのままとする。7000番と9090番。最近のWindowsは煩いからもしかするとFW設定とか変えないと駄目かも知れないが…。
ではとりあえず実行。

3月 05, 2023 10:20:24 午後 nostr.examples.NostrExamples main

普通: ================= The Beginning

3月 05, 2023 10:20:24 午後 nostr.id.Identity$ProfileConfiguration getPrivateKey

普通: Reading the private key...

Exception in thread "main" java.lang.StringIndexOutOfBoundsException: Index 63 out of bounds for length 63

at java.base/jdk.internal.util.Preconditions$1.apply(Preconditions.java:55)

at java.base/jdk.internal.util.Preconditions$1.apply(Preconditions.java:52)

at java.base/jdk.internal.util.Preconditions$4.apply(Preconditions.java:213)

at java.base/jdk.internal.util.Preconditions$4.apply(Preconditions.java:210)

at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:98)

at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:106)

at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:302)

at java.base/java.lang.String.checkIndex(String.java:4557)

at java.base/java.lang.StringLatin1.charAt(StringLatin1.java:46)

at java.base/java.lang.String.charAt(String.java:1515)

at nostr.util.NostrUtil.hexToBytes(NostrUtil.java:36)

at nostr.id.Identity$ProfileConfiguration.getPrivateKey(Identity.java:226)

at nostr.id.Identity.<init>(Identity.java:66)

at nostr.id.Identity.<init>(Identity.java:62)

at nostr.examples.NostrExamples.main(NostrExamples.java:65)


んーむ…。通信に行く以前の秘密鍵の文字列処理で落ちている。サンプルに同梱されている秘密鍵より、自分の秘密鍵が1文字少ないのが宜しくないのだろう。
まぁそのうち直るだろう。
またnostr-javaのリポジトリが更新されたら試そう(自分で治す気ナッシング)。
つか https://snort.social/p/npub1ur9pa83tulgwwcgcs8wp7hsckw89pf2tm889h3k8tj000e30x89qm5lklu とか激烈遅いのでこのサービス?自体、イマイチ使い物になる気がしない…。


このエントリーをはてなブックマークに追加 mixiチェック

↑このページのトップヘ