漫画家志望者を描いた漫画なんだけど、黒髪と白髪の美少女ヒロイン二人に理解はされるものの、あまり励まされずそこまでは応援されない主人公の少年の描く絵が三峯徹みたいで不安を掻き立てる。見込みなさげ過ぎる。
こじかと違って舞台が高校なので、高校生くらいになると人間関係に計算高くてもまぁ、そういう奴もいるだろう…漫画だし、とそんなに不自然でもない感じ。
ちなみにこの表紙はヒロイン(黒)と担任の先生で主人公の姿はない…。というか意志を持つ主人公は黒髪の美少女の方なのか。
2017年04月
ボットのMastodon対応ひと段落
苦節4日、やっと画像付きで呟けるようになったので邪悪なエロ同人誌販促BOTに実装して一服。ちなみにエロ同人誌の紹介絵(の解像度)が小さいのはわざと。大きいのは転載禁止風潮に引っかかりそうなのと、ほぼ全部が「不適切なコンテンツ」になってしまうのでアイキャッチの意味が無い。
画像付き投稿については、https://github.com/zyuiop/mastodon-java も http://mastodonpy.readthedocs.io/en/latest/ もバギーで当てにならず、結局自分で元のドキュメント見つつ色々試すしかなかった。どっちも完成度の割に名前空間が強すぎる。まぁ早い者勝ちというか別に他所に合わせる必要もないと言えばそうだが…。
「不適切なコンテンツ」の判定処理についてはアドホックだが概ねOKに見える。これ、何も知らないと「画像認識させて教師付き学習を繰り返しおっぱい判定して…」とおおごとになってCPUを食う所だが、雑に片づけられるドメイン知識重要。
あと、今はTwitterとまったく同じ事を呟いているが、最大投稿可能文字数も違うし、ここ数日の観察によるとタグの名前も結構ノリが違うのでいずれは分けた方が良いと思う。レートリミットの影響の分離もした方が良いな。ただ、各処理のつぶやき生成側と、実際の投稿を行う常駐プロセス*1の両方を改造しないとならんので、そもそもMastodonブームがどれだけ続くのか分からないからそこまでする程かどうか…。果たして6月まで流行っているだろうか?邪悪な同人誌販促ボットではなく汎用GUIクライアントを作りたいという志の高い者も要るようだが、早く作り終えないと誰も使わないどころか当人ですらテストが終わったら使わない予感。
んで、邪悪なエロ同人誌販促BOT作り終えてこんな事を言うのも何だけど、Mstdn.jpもPawoo.netも、子供っぽい下ネタが多過ぎる。うんこちんこまんこ言い過ぎ。おまいら、メカゴジラのような手のひら回転でクラウドの牢獄から自由を取り戻して言いたい事が、うんこちんこまんこなのか。Twitterのように謎凍結が無い事を試している段階なのだろうか。果たして10年前のTwitterはそんなにうんこちんこまんこにあふれていたのだろうか…。
*1:何かの拍子に毎秒何十個とか送ると何かと問題なのでJMSに貯めておき別プロセスがゆっくり送信する方式を取っている
Pythonで画像付きToot
できたことはできた。
https://pawoo.net/@nekora2199/1865696
ほぼ、http://qiita.com/code_monkey/items/e4929ef13e2a2032d467 のまんまだけど。
mastodon.pyを改造する必要があった。メディアのアップロードに失敗するJavaのライブラリもダメダメだが、Python版もイマイチのようだな。
あとは成功時のリクエスト電文をキャプチャすれば話は終わりだと思ったんだが、これ、アップロード直後の値をそのまま使ってるので対抗にはmastodonのアップロード処理が必要であり、mastodonインスタンスとの通信はhttpsなのでそのままtcpmonでキャプチャしても意味不明な罠。
先のmedia_filesをprint()すると以下のようになっている。まさかこれを全部渡しているのか。idだけではないのか。
[{'text_url': 'https://pawoo.net/media/_Vf6DFtADky79UzLrtw', 'id': 177167, 'url': 'https://img.pawoo.net/media_attachments/files/000/177/167/original/31ed315ef2e64925.jpg?1492601655', 'preview_url': 'https://img.pawoo.net/media_attachments/files/000/177/167/small/31ed315ef2e64925.jpg?1492601655', 'type': 'image'}]
ちなみに一応渡してみたがダメだった。
ちょっと調査方向を変えて不慣れなPythonライブラリではなく、アップロードは出来ないがTootはできたJavaの方を読んでみるか。でもアレも結局googleの通信ライブラリに渡しているだけで具体的なHTTP電文は…。
public MastodonStatus postStatus(String status, int replyTo, int[] mediasIds, boolean sensitiveMedia, String spoilerText, Visibility visibility) throws IOException { Validate.isTrue(mediasIds == null || (mediasIds.length >= 0 && mediasIds.length <= 4), "maximum 4 medias"); FormDataContent content = new FormDataContent(); content.addPart("status", status); if (replyTo >= 0) content.addPart("in_reply_to_id", String.valueOf(replyTo)); if (mediasIds != null && mediasIds.length > 0) content.addPart("media_ids", Arrays.toString(mediasIds)); content.addPart("sensitive", String.valueOf(sensitiveMedia)); if (spoilerText != null) content.addPart("spoiler_text", spoilerText); if (visibility != null) content.addPart("visibility", visibility.name().toLowerCase()); return raw.post("/api/v1/statuses", content, MastodonStatus.class); }
int[] mediasIds, content.addPart("media_ids", Arrays.toString(mediasIds));
うーむ、数値の配列だ。そりゃそうだよな。全部渡す必要など無い筈だから。
今週末あたり、Java版ライブラリのバグが取れないかしら。
https://github.com/zyuiop/mastodon-java
…もう今日で15日放置されてるから微妙だな。
つかAndoroidアプリがある以上、Java版の正しいライブラリが探せばあるのではなかろうか。高々規格にあったHTTPを送るだけだし、そんなに難しい事をやる訳でも無いんだし。HTTPに詳しく英語に堪能な英米人なら容易いと思われる。
HTTPで配列渡しから攻めてみるか
http://sample/xxx?data=a&data=b&data[]=c
https://teratail.com/questions/30958
こういうものなのだろうか?今回はPOSTだけど。
Java APで画像付きToot成功
苦節三日、やっとできた。
https://pawoo.net/@nekora2199/1923722
https://pawoo.net/@nekora2199/1960858
詰まっていたmedia_idsだが、横に配列を示す[]を付けるだけの事だった。
// 画像は既にアップロード済で、以下のJSONが返ってきている。 // {"id":155370,"type":"image","url":"https://img.pawoo.net/media_attachments/files/000/155/370/original/ab37a3d25e7419e7.jpg?1492529929","preview_url":"https://img.pawoo.net/media_attachments/files/000/155/370/small/ab37a3d25e7419e7.jpg?1492529929","text_url":"https://pawoo.net/media/zRMfXUdgXsLc6vHTEUI"} try (CloseableHttpClient httpClient = HttpClients.createDefault()) { // つぶやき先URL設定 String toot_url = "https://" + host_name + "/api/v1/statuses"; logger.info(toot_url); HttpPost post = new HttpPost(toot_url); // パラメータ設定 ArrayList<NameValuePair> params = new ArrayList<>(); params.add(new BasicNameValuePair("access_token", access_token)); params.add(new BasicNameValuePair("status", status)); params.add(new BasicNameValuePair("visibility", visibility)); params.add(new BasicNameValuePair("media_ids[]", "155370")); // ★ここで画像をPOSTした時の戻りJSONのid:を入れる post.setEntity(new UrlEncodedFormEntity(params, "UTF-8")); try (CloseableHttpResponse response = httpClient.execute(post)) { if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { HttpEntity entity = response.getEntity(); logger.info(EntityUtils.toString(entity, StandardCharsets.UTF_8)); } } } catch (IOException e) { logger.log(Level.WARNING, "何か失敗した", e); }
もはや技術的問題はすべて解決。後は画像POSTした時に帰ってくるJSONからidを取り出す所を作れば画像付きTootは出来る。そうすれば https://mstdn.jp/@nekora2199 が吠える同人誌に表紙サムネイルが付く。
JSON解析も以前Jacksonをやったが、何かもう「"id":」と「,」の間の文字列を切り出せば良いような気がするな。
→よし、完全に成功 https://pawoo.net/@nekora2199/1960858
Mastodonへ画像アップロード成功時の電文
成功時のキャプチャをhttpで取得。Axisのtcpmonを使った。
POST /api/v1/media HTTP/1.1 Host: 適当なアパッチホスト名:ポート番号 User-Agent: python-requests/2.13.0 Accept: */* Connection: keep-alive Accept-Encoding: gzip, deflate Authorization: Bearer アクセスキー Content-Length: 47436 Content-Type: multipart/form-data; boundary=d81c11d8d8434ba0aae091f4078c5ae0 --d81c11d8d8434ba0aae091f4078c5ae0 Content-Disposition: form-data; name="file"; filename="mastodonpyupload_1492518567.9830065_3K59EZOH48.png" Content-Type: image/png 以下略
そしてJava版はhttps決め打ちなので無理だった。
あとマルチパートフォームのHTTPについては http://d.hatena.ne.jp/satox/20110726/1311665904 に詳しい記載が。
HTTPClient4でのマルチパートについては http://www.baeldung.com/httpclient-multipart-upload にサンプルが。
HttpPost post = new HttpPost("http://echo.200please.com"); // ちなみにここは詐欺サイトなので注意 InputStream inputStream = new FileInputStream(zipFileName); File file = new File(imageFileName); String message = "This is a multipart post"; MultipartEntityBuilder builder = MultipartEntityBuilder.create(); builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE); builder.addBinaryBody ("upfile", file, ContentType.DEFAULT_BINARY, imageFileName); builder.addBinaryBody ("upstream", inputStream, ContentType.create("application/zip"), zipFileName); builder.addTextBody("text", message, ContentType.TEXT_PLAIN); // HttpEntity entity = builder.build(); post.setEntity(entity); HttpResponse response = client.execute(post);
という訳で、やっとJavaでアップロード成功。何だったんだ。
https://img.pawoo.net/media_attachments/files/000/155/370/original/ab37a3d25e7419e7.jpg?1492529929
Python 3でToot
という訳でPython。
まぁ http://mastodonpy.readthedocs.io/en/latest/ のまんまだけど。
まず、pip3 install Mastodon.py してから、以下を実行。Pawooに吠えられる。
from mastodon import Mastodon # Create actual instance mastodon = Mastodon( client_id = 'クライアント名', client_secret = 'シークレット', access_token = 'アクセストークン', api_base_url = 'https://pawoo.net', debug_requests = True ) IDs = mastodon.media_post('test.png') #これは成功する。 mastodon.status_post(status='画像付きテスト',media_ids=IDs) #これが失敗する # mastodon.toot('ちなみに日本語 from Python') #これは成功する。
アップロード後のstatus_postが失敗するが、まぁよし。この際追求しない。
重要なmedia_postは成功した。
→https://img.pawoo.net/media_attachments/files/000/145/092/original/49910a22fd6959b3.png?1492513974
debug_requests = True にしておくとパラメータが表示されるので電文キャプチャ不要かも。
→やっぱり必要。
Javaから画像付きToot失敗する版
https://github.com/zyuiop/mastodon-java
を使って画像付きTootしようとして失敗する版
private void exec(String[] args) { logger = initLogger(); logger.info("start"); String app_name = "クライアントアプリ名"; String host_name = "ホスト名"; // pawoo.netとか String client_secret = "クライアントシークレット"; String access_token = "アクセストークン"; String img_file_name = "送信する画像ファイル.png"; int reply_to = -1; // リプライ先発言ID。ないなら-1 boolean sensitiveMedia = false; // ロリエログロ画像ならtrue String spoilerText = null; // 「死体画像注意」等の文言。無ければnull Visibility visibility = Visibility.PUBLIC; // 公開範囲 MastodonApp mstdn = new MastodonApp(host_name, app_name, client_secret); Credential credential = new Credential(BearerToken.authorizationHeaderAccessMethod()); credential.setAccessToken(access_token); MastodonSession session = new MastodonSession(mstdn, credential); // コンストラクタをpublicに改造した try { File img_file = new File(img_file_name); MastodonAttachment atm = session.uploadAttachment(img_file); // ここで鯖から500が返ってくる。 int[] img_ids = new int[1]; img_ids[0] = atm.getId(); MastodonStatus stat = session.postStatus("画像添付テスト from JavaAP ver.2 ", reply_to, img_ids, sensitiveMedia, spoilerText, visibility); logger.info(stat.toPrettyString()); } catch (IOException e) { logger.log(Level.WARNING, "なんか失敗", e); } logger.info("end"); }
画像をアップロードするところで以下のエラー。
com.google.api.client.http.HttpResponseException: 500 Internal Server Error <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>We're sorry, but something went wrong</title> <meta name="viewport" content="width=device-width,initial-scale=1"> <link href="https://fonts.googleapis.com/css?family=Roboto:400" rel="stylesheet"> <style> body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #282c37; color: #9baec8; text-align: center; margin: 0; padding: 20px; } .dialog img { display: block; margin: 20px auto; margin-top: 50px; max-width: 600px; width: 100%; height: auto; } .dialog h1 { font: 20px/28px -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; font-weight: 400; } </style> </head> <body> <div class="dialog"> <img src="/oops.png" alt="Mastodon" /> <div> <h1>We're sorry, but something went wrong.</h1> </div> </div> </body> </html>
と言われた。画像を変えても一度も成功しない。何だろう。先ほどのライブラリの使い方が不味いのか。つかそもそもアップロードAPIの使い方のドキュメントが無いし。
一応、切り分けのためmstdn.jpの方にも https://mstdn.jp/@nekora2199 を取って試したが同じ結果。やっぱ悪いのはリクエスト電文、即ち私だろう。
ソースを見たが、何の変哲もないPOSTでマルチパートで画像を送信しているようにしか見えない…。
今後の方針としては
1.とりあえず、cUrlかPythonでまず画像アップロードと画像付きTootを成功させる。
→Python でできた https://img.pawoo.net/media_attachments/files/000/145/092/original/49910a22fd6959b3.png?1492513974
2.httpsをhttpにして、我が家のVMwareの適当なApache相手にcUrlなりPythonなりで通信。404になるけどともかくそのリクエスト電文をキャプチャ
3.上のJavaサンプルでも同様にキャプチャ。
4.正誤の電文を比較すれば、ヘッダなりパラメータなり、何か足りないものや誤っているものが見つかるだろう。
JavaでMatodonのAPIにアクセス(ライブラリ利用版)
昨日は、世界的ですもんね。乗るしかない、このビッグウェーブに、と https://pawoo.net/@nekora2199 を素早く対応させるため、雑な方法をとったが、やっぱ https://github.com/zyuiop/mastodon-java を使った方が今後何かと便利だろう*1と思って調査。いずれ画像とか付けたいし、リプライで繋ぎたいしな。
http://qiita.com/yukimochi/items/f80a50a4486d0cb770dc の方法で既に、クライアント名、クライアントシークレット、アクセストークン、は予め得ている前提。
private void exec(String[] args) { logger = initLogger(); logger.info("start"); String server_name = "ホスト名"; // pawoo.netとか String app_name = "自作アプリ名"; String client_secret = "クライアントシークレット"; String access_token = "アクセストークン"; MastodonApp mstdn = new MastodonApp(server_name, app_name, client_secret); Credential credential = new Credential(BearerToken.authorizationHeaderAccessMethod()); credential.setAccessToken(access_token); MastodonSession session = new MastodonSession(mstdn, credential); try { MastodonStatus stat = session.postStatus("日本語テスト from JavaAP ver.2 "); logger.info(stat.toPrettyString()); } catch (IOException e) { logger.log(Level.WARNING, "ポスト失敗", e); } logger.info("end"); }
コンパイル通す為に MastodonSession のコンストラクタはpublicに改造している。何となくもっと調べればちゃんとしたやり方…シークレットからトークン取得して…があるような気がするのでこれはこれで結構雑ではある。が、とにかくMastdonSessionさえ得てしまえば、他のメソッド処理を自作する必要が無く使い放題という塩梅だ。そもそもアクセストークンあればシークレット要らないだろ、という気もするが、まぁ合わせた方が良いだろう。内部で何か使っていないとも限らない。
ちなみにアクセストークンの期限だが、昨夜からずっと同じのを使いっぱなしだが未だに期限が来ていない。Twitterのアクセストークンもトラブルが無ければ期限切れは無いので多分Pawooも期限は無いのだろう。
画像アップロードについてだが、小さい画像だと https://pawoo.net/@nekora2199/947533 こんな事になるので、大き目の画像の方が良いね。
*1:と言ってもこのライブラリもまだ作成途上だけど…。
JavaでMastodonにポスト
いや、https://pawoo.net/@nekora2199にbotで販促自動投稿するために。一応できたけどメモ。
最初は https://github.com/zyuiop/mastodon-java 使おうかと思って調べてたんだけど、結局使わず。
マニュアルは https://github.com/tootsuite/documentation/blob/master/Using-the-API/API.md
胆であるアクセストークンの取得は取得手順はこちら。curlを使っている。
そして一旦アクセストークンを取得してしまえば単なるApache HttpClientを使ったPOSTで充分だった。期限が気になるがアクセストークン取得時には期限の情報は無かった。
package nekora.mastodon; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.logging.FileHandler; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.Logger; import java.util.logging.SimpleFormatter; import org.apache.http.HttpEntity; import org.apache.http.HttpStatus; import org.apache.http.NameValuePair; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils; /** * https://github.com/zyuiop/mastodon-java のサンプルを使わない版 既にアクセストークン持っていることが前提 * * @author nekora * */ public class TestMain { private Logger logger; public Logger initLogger() { String logname = "log/TestMastodonClient"; Logger ret = Logger.getLogger(logname); try { Handler fileHandler = new FileHandler(logname + "%g.log", 1000000, 9); fileHandler.setFormatter(new SimpleFormatter()); ret.addHandler(fileHandler); } catch (IOException ioe) { ioe.printStackTrace(); } return ret; } /** * Mastodonのクライアントテスト * * @param args */ public static void main(String[] args) { TestMain my = new TestMain(); my.exec(args); } /** * * @param args */ private void exec(String[] args) { String host_name = "pawoo.net"; //インスタンスあるの鯖名 String access_token = "アクセストークンをここに書く"; String status = "日本語でこんにちは from JavaAP"; String visibility = "public"; //実際は他にも公開範囲は色々。 logger = initLogger(); String access_url = "https://" + host_name + "/api/v1/statuses"; logger.info(access_url); try (CloseableHttpClient httpClient = HttpClients.createDefault()) { // パラメータ設定 ArrayList<NameValuePair> params = new ArrayList<>(); params.add(new BasicNameValuePair("access_token", access_token)); params.add(new BasicNameValuePair("status", status)); params.add(new BasicNameValuePair("visibility", visibility)); // アクセス先URL設定 HttpPost post = new HttpPost(access_url); post.setEntity(new UrlEncodedFormEntity(params, "UTF-8")); // ポスト実行 try (CloseableHttpResponse response = httpClient.execute(post)) { if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { HttpEntity entity = response.getEntity(); logger.info(EntityUtils.toString(entity, StandardCharsets.UTF_8)); } } } catch (IOException e) { logger.log(Level.WARNING, "何か失敗した", e); } logger.info("end"); } }
nekora2199
https://pawoo.net/@nekora2199/581927
@nekora2199
日本語でこんにちは from JavaAP
2017年04月16日 10:15 · nekora2199_bot · 0 · 1 · Webで開く
↑貼って気付いたんだけど「2017年04月16日 10:15」って変だな。投稿したのは19時頃だったのに。Webから投稿しても同じように時間が狂うので私のせいではない。
後は画像のアップロードを覚えれば https://twitter.com/nekora2199/ に組み込んでTwitterとMastodonというかPawooに同時呟きできるという寸法。
画像無しなら今でもできるので対応。
https://github.com/tootsuite/documentation/blob/master/Using-the-API/API.md#media
お! カードっぽいのがあるな。これは一体…。
https://github.com/tootsuite/documentation/blob/master/Using-the-API/API.md#card
あ、それと画像アップロード前に閲覧注意だか18禁だか何だかのフラグを立てる方法も調べないと。
火星の人
事故でたった一人火星に取り残されたNASAの宇宙飛行士が知恵と工夫で生き抜く話。映画化された大評判の小説だ。
成程、評判通り大変面白い。こんなに面白いのは「渇きの海」「さよならジュピター (1982年)」以来だ。
普段はなるべく小説は買わないようにしておりこれも図書館から借りているんだが、これは何回か読むかも知れん、と思って本屋に行ったら、活字を大きくして上下二巻に分けた版が売られていたというかそれしか売ってない。早川書房はこれをよくやる実に阿漕な出版社なんだよな…。古い作品を新装版にするときは活字を大きくして分厚くして高く売る。翻訳ものは同じ手でしばしば上下に分けて高く売る。ダン・シモンズ作品などデフォルトで上下である。まぁあれは分厚いから仕方ないのか。が、この本は邦訳版が2014年に出版されているのに…映画化されたら速攻で上下に分けて売る。せこい、早川さん、せこい!
ちなみに最新のScientific Reportsに掲載された論文では、火星に土壌成分には細菌レベルでも生命が存在できないほどの毒性があるとのこと。ウンコ混ぜても難しそうだ。