「Match」を含む日記 RSS

はてなキーワード: Matchとは

2025-12-09

はてなブックマーク増田一覧向けNGフィルタ

はてなブックマーク増田一覧の、さらに「すべて」(1 user)をチェックしている希有な人向けのユーザースクリプトを公開します。

https://b.hatena.ne.jp/site/anond.hatelabo.jp/?sort=eid

// ==UserScript==
// @name        Hatena Bookmark Anond Filter
// @namespace   https://b.hatena.ne.jp/site/anond.hatelabo.jp/
// @description はてなブックマークの『はてな匿名ダイアリー』の記事のうち、指定したNGワードが含まれ投稿非表示します。
// @match       https://b.hatena.ne.jp/site/anond.hatelabo.jp/*
// @grant       none
// @version     2.0.2026.01.17.0009
// ==/UserScript==

(function(){
  const SCRIPTID = 'HatenaBookmarkAnondFilter';
  console.time(SCRIPTID);
  const CLASSNAME = 'filtered';/*フィルタ該当要素*/
  const CHECKED = 'checked';/*二重チェック回避フラグ*/
  const ONCE = 1, AP = 2, INTERVAL = 3;/*適用タイミング*/
  const NGWORDS = {/* 合計100ポイント非表示判定(ただし1つの記事内で同じワード複数使われても1度しか加算しない) */
    '100': [/*即NG確定ワード*/
      'dorawii',
      'あおやまちゃん', 'ボスマン', 'MNK',
      '電気通信大学たいてい', 'なんぴょん', 'れめくん', 'リュックサック野郎', 'boushi_inst', 'hakaikami', 'Rekyu', 'iloveootaku_2',/*電気通信大学たいてい鉄道研究会れめくん(頻出)*/
      /*A-G*/'a9w8ru6fqyxqfv9', 'admirail_togo', 'akibakko6348', 'alf1974al', 'amatukiseiru', 'anapgoeson', 'aoi_mizuho', 'asapgoeson', 'asupgoeson', 'b6jbpsji91ieigt', 'bmi22yo', 'boushi_ob', 'buscholarx', 'bw0531', 'circlecavok', 'disney1007cla', 'dora22sibuya', 'donkotrain', 'ecotosk', 'factomodachi', 'fft_dareka', 'gmhtcyznf_abc', 'goesonanap', 'gyudon_honmono',
      /*H-N*/'h13_yokohama', 'h2twi', 'hamaishogo1111', 'haru_mofumoffu', 'hermitv8', 'hinolovelove', 'hnmk0127_03', 'inaken17_', 'inte235dy', 'ixtabes', 'jamcombatge', 'kawachiasukanew', 'kaoru_ai1991', 'keio9730F', 'kiha2228', 'koreanlabsfc', 'koyounoyooko', 'kqlex1500', 'kurakamasan', 'kurotamaxxx', 'kt_ruma_1372', 'lightningreen77', 'matya_uec', 'minamihinawot', 'minori0151', 'monkichi_22', 'mugen_08i', 'mukoroku651', 'nakano6409', 'nanpyong', 'new_oer', 'nimouec', 'NoName_thUFO', 'norannnde',
      /*O-U*/'oreizmmiporin', 'orenotanoshimi', 'osaka_sirokichi', 'papepoco', 'pasotokon', 'pm95uq', 'reme_kun', 'ruin_2002', 's03_amurtk2731', 'sacchan03110319', 'seisu_bot', 'senanana_cos', 'shinano_115', 'shineleaf1372', 'shop_bullet', 'shurimpy', 'soroisoroi', 'sui_pm95uq', 'sweidan821858', 'taiyaki_level2', 'takao_straight5', 'taking0000', 'tarotaromusic1', 'tc201_501', 'tocarbarn', 'toshikimiyazaki', 'train_magician', 'tx9y2cpwdz27255', 'u2fap5u4zw57811', 'uec15take', 'uecrail',
      /*V-Z_0-9*/'vampire_mio', 'vbdmnwefknmxsdm', 'vp20th', 'wafue', 'wakasato_', 'walkingniwatori', 'wataameexpress', 'ya4975349616894', 'ymbk_arisa', 'yms_uec16', 'yuuya_1104_uec', '__________ob', '_chocorail_', '_doitforthewin_', '_toeshin', '_unigmo', '100mph_no_yuuki', '2969364x', '2rtkvn34il2783', '86lilxw1',
      /*tadaup.jp*/'1dOaKWk3.jpeg', '1sL2VBZ5.jpeg', '1uNK2iEP.jpeg', 'CBUHadpD.jpeg', 'CgJlF4Wr.jpeg', 'CGTtm0Ev.jpeg', 'CIxj8clS.jpeg', 'CqbERPdQ.jpeg', 'CTZsA2wM.jpeg', 'CWY2m7rS.jpeg', 'CZVCEgd1.jpeg',
      'テクウヨ', '自己放尿',
      ' ーーーーーーーー', 'https://anond.hatelabo.jp/20260107144223',/*AI問答貼り付け増田*/
    ],
    '90': [/*ほぼNGだが確定ワード必要*/
      'https://megalodon.jp/',
    ],
    '10': [/*ほぼNGの確定ワード*/
      '電気通信大学',
      '電通大',
      '駿河台大学',
      '学生課',
      '教務課',
      '鉄道研究会',
      '鉄研',
      '通勤特快',
      '不正乗車',
      '性慾',
      '穢い',
      'エッタ',
      'キセル',
      'uec',
    ],
  };
  const sites = {
    'prefix': [
      ['selector', '(modifier)', '(css)', '(REPEAT)'],
    ],
    'https://b.hatena.ne.jp/site/anond.hatelabo.jp/': [
      ['section.entrylist-unit li.js-keyboard-selectable-item', li => {li.querySelector('li.entrylist-contents-category > a').textContent = li.dataset.matches}, `.${CLASSNAME}{display: none;}`, AP],
    ],
  };
  const rules = sites[Object.keys(sites).find(prefix => location.href.startsWith(prefix))];
  if(rules === undefined) return console.log(SCRIPTID, 'Not found any sites.');
  const scores = Object.keys(NGWORDS).map(Number).reverse();/*数値インデックス順に取り出されたkeysを逆順にして100から並べ直す*/
  const filter = function(selector, modifier){/*各要素に対してNGワード判定して、該当したら追加でmodifierも適用する*/
    document.querySelectorAll(selector).forEach(e => {
      if(e.dataset[CHECKED]) return;
      e.dataset[CHECKED] = 'true';
      const text = e.textContent.toLowerCase();
      let total = 0, matches = [];
      for(const score of scores){
        for(const word of NGWORDS[String(score)]){
          if(text.includes(word.toLowerCase())){
            total += score;
            matches.push(word);
            if(total >= 100){
              e.classList.add(CLASSNAME);
              e.dataset.matches = matches.join(', ');
              if(modifier) modifier(e);
              return;
            }
          }
        }
      }
    });
  };
  /* ONCE(一括適用) */
  rules.forEach(rule => {
    const [selector, modifier, css] = rule;
    console.log(SCRIPTID, 'ONCE:', selector);
    filter(selector, modifier);
    if(css){
      const style = document.createElement('style');
      style.dataset.script = SCRIPTID;
      style.type = 'text/css';
      style.textContent = css;
      document.head.appendChild(style);
    }
  });
  /* AP(AutoPagerize) */
  rules.filter(rule => rule[3] === AP).forEach(rule => {
    const [selector, modifier] = rule;
    document.addEventListener('GM_AutoPagerizeNextPageLoaded', e => {
      console.log(SCRIPTID, 'AP:', selector);
      filter(selector, modifier);
    });
  });
  /* INTERVAL */
  rules.filter(rule => rule[3] === INTERVAL).forEach(rule => {
    const [selector, modifier] = rule;
    setInterval(function(){
      console.log(SCRIPTID, 'INTERVAL:', selector);
      filter(selector, modifier);
    }, 1000);
  });
  console.timeEnd(SCRIPTID);
})();
/* Hatena Bookmark Anond Filter */
.filtered{
  display: block !important;/*上書き*/
  opacity: .25 !important;
}
.filtered:hover{
  opacity: .75 !important;
}
.filtered li.entrylist-contents-category{
  background: red !important;
  font-weight: bold;
}

検索用: はてなブックマーク はてブ はてな匿名ダイアリー 増田 スパム キーワード NGワード フィルタミュー非表示 削除 隠す ブロック ユーザースクリプト ユーザースタイル hatena bookmark anond spam keywords ngwords filter mute hide hidden display none block userscript JavaScript js css style

2025-12-08

anond:20251201002323

勝手ながら

  • >| と |< を使った pre記法 で書き直しました。
  • コードが読みやすいようにスペース2個で適宜インデントを付けました。
  • > の記号が正しく表示されるように &#62; に置き換えました。
// ==UserScript==
// @name はてな匿名ダイアリー特定ワード投稿非表示
// @namespace http://tampermonkey.net/
// @version 0.2
// @description 本文に「dorawii」または「megalodon」が含まれ投稿非表示にする
// @match https://anond.hatelabo.jp/*
// @grant none
// ==/UserScript==
(function() {
  'use strict';
  const POST_SELECTOR = '.body .section';
  // 非表示にしたいキーワード配列
  const KEYWORDS = ['dorawii','megalodon','抽象数学','動画にしてみた','れめくん','自己放尿'];
  const posts = document.querySelectorAll(POST_SELECTOR);
  posts.forEach(post => {
    const textContent = post.textContent || post.innerText;
    // いずれかのキーワードが含まれいるかチェック
    if (KEYWORDS.some(keyword => textContent.includes(keyword))) {
      post.style.display = 'none';
    }
  });
})();

anond:20251114170150 と連動させることを意図しています

2025-12-01

// ==UserScript==
// @name         meaningless sale dayo
// @version      1.0
// @match        https://www.amazon.co.jp/blackfriday*
// ==/UserScript==

(function () {
    'use strict';
    alert('意味のないセールだよ');
})();

JavaScriptでこのコード使って非表示にしてる

// ==UserScript==

// @name はてな匿名ダイアリー特定ワード投稿非表示

// @namespace http://tampermonkey.net/

// @version 0.2

// @description 本文に「dorawii」または「megalodon」が含まれ投稿非表示にする

// @match https://anond.hatelabo.jp/*

// @grant none

// ==/UserScript==

(function() {

'use strict';

const POST_SELECTOR = '.body .section';

// 非表示にしたいキーワード配列

const KEYWORDS = ['dorawii','megalodon','抽象数学','動画にしてみた','れめくん','自己放尿'];

const posts = document.querySelectorAll(POST_SELECTOR);

posts.forEach(post =&gt; {

const textContent = post.textContent || post.innerText;

// いずれかのキーワードが含まれいるかチェック

if (KEYWORDS.some(keyword =&gt; textContent.includes(keyword))) {

post.style.display = 'none';

}

});

})();

2025-10-28

増田特定ワードを含む投稿非表示にするJavaScript

// ==UserScript==

// @name はてな匿名ダイアリー特定ワード投稿非表示

// @namespace http://tampermonkey.net/

// @version 0.2

// @description 本文に「dorawii」または「megalodon」が含まれ投稿非表示にする

// @match https://anond.hatelabo.jp/*

// @grant none

// ==/UserScript==

(function() {

'use strict';

const POST_SELECTOR = '.body .section';

// 非表示にしたいキーワード配列

const KEYWORDS = ['dorawii', 'megalodon'];

const posts = document.querySelectorAll(POST_SELECTOR);

posts.forEach(post =&gt; {

const textContent = post.textContent || post.innerText;

// いずれかのキーワードが含まれいるかチェック

if (KEYWORDS.some(keyword =&gt; textContent.includes(keyword))) {

post.style.display = 'none';

}

});

})();

これはdorawiiもしくはmegalodonを含む投稿非表示にするけど、

const KEYWORDS = ['dorawii', 'megalodon'];の部分を変えたり追加すれば好きな言葉に変えられるよ

2025-10-21

「dorawii」を含む投稿非表示にするJavaScript

dorawiiがタイトルに「dorawii」と入れなくなったので、本文にdorawiiがある投稿非表示にする必要が出てきました。

ただAIに聞いたんだけど、CSSでは無理でJavaScriptならできると言われました。

そのJavaScriptを下に載せます

// ==UserScript==

// @name はてな匿名ダイアリー特定ワード投稿非表示

// @namespace http://tampermonkey.net/

// @version 0.1

// @description 本文に「dorawii」が含まれ投稿非表示にする

// @match https://anond.hatelabo.jp/*

// @grant none

// ==/UserScript==

(function() {

'use strict';

// 投稿全体を囲む要素のセレクタに置き換えてください

// はてな匿名ダイアリー一般的投稿要素のクラス仮定しています

const POST_SELECTOR = '.body .section'; // 例: .bodyクラスの子孫の.section要素

// 非表示にしたいキーワード

const KEYWORD = 'dorawii';

// すべての投稿要素を取得

const posts = document.querySelectorAll(POST_SELECTOR);

posts.forEach(post =&gt; {

// 投稿内の本文が含まれる要素(ここでは投稿全体を本文と見なす)のテキストを取得

const textContent = post.textContent || post.innerText;

// キーワードが含まれいるかチェック

if (textContent.includes(KEYWORD)) {

// キーワードが含まれていれば非表示にする

post.style.display = 'none';

}

});

})();

自分iPhoneなので、Makeoverというアプリを入れて、JSの部分に上のを貼り付けてます

なんか複雑だし、もっと良い方法があるなら教えてください!

なんでdorawiiのためにこんなことしなきゃいけないんだよ!迷惑千万

2025-10-05

Match Masters Free Gifts

gamefaqs.gamespot.com/boards/178921-dragon-ball-z-dokkan-battle/81050772

gamefaqs.gamespot.com/boards/178921-dragon-ball-z-dokkan-battle/81050774

gamefaqs.gamespot.com/boards/178921-dragon-ball-z-dokkan-battle/81050772?Match-Master

gamefaqs.gamespot.com/boards/178921-dragon-ball-z-dokkan-battle/81050774?Match-Master

2025-10-03

25年間、カプエス2の開幕英語メッセージが聞き取れなかったはなし

ストZERO3の「尿を便座におしつけ~る」くらいにモヤモヤしてた

答え

tHis is gOnNa bE a MaTcH TO REmember!

訳「この戦いは記憶に残るものになる!」

ここmatchがどうして聞き取れなくて「This is gonna be a must to remember! 」(この戦いは記憶に残るマストものになる!)かと思ってしまったw

http://www.kagerou.biz/archives/25779075.html

ネイティブも聞き取れてなくてワロタ

なんか安心したわ

当時の録音マイクの性能か、圧縮関係だったんかね

Daily Rewards In October 2025

ramadan.mya.org.au/today-boost/posts/legit-ways-to-get-free-match-masters-free-gifts-boosters-coins-link-in-october-2025

ramadan.mya.org.au/today-boost/posts/daily-rewards-match-masters-free-gifts-boosters-coins-link-in-october-2025

ramadan.mya.org.au/today-boost/posts/rewards-match-masters-unveils-daily-free-gifts-boosters-and-coins-link-updated-in-october-2025

ramadan.mya.org.au/today-boost/posts/daily-rewards-cash-app-money-free-codes-everything-you-need-to-know-in-october-2025

ramadan.mya.org.au/today-boost/posts/daily-earning-cash-app-money-free-codes-rewards-ultimate-beginners-guide-in-october-2025

ramadan.mya.org.au/today-boost/posts/legit-ways-to-get-free-cash-app-money-earn-codes-daily-rewards-in-october-2025

2025-09-29

Match Masters Free Gifts & Unlimited Coins Links

ramadan.mya.org.au/rewards-today/posts/daily-rewards-match-masters-free-gifts-unlimited-coins-links-september-2025

ramadan.mya.org.au/rewards-today/posts/today-match-masters-free-gifts-boosters-and-coins-links-tips-tricks-2025

Match Masters Free Gifts

www.wortfm.org/calendar/updated-today-match-masters-unveils-free-gifts-boosters-and-coins-link-2025/

www.wortfm.org/calendar/today-match-masters-free-gifts-boosters-and-coins-links-tips-tricks-2025/

www.wortfm.org/calendar/daily-rewards-match-masters-free-gifts-boosters-coins-links-tips-tricks-2025/

2025-09-09

ヨーロッパのおもらし文学

ヨーロッパには公衆トイレが少ない、あっても有料でしかも汚い

というわけでお漏らし文学をChatGPTに調べてもらった

 

1. ドイツ:Sanifair(高速道路トイレ)での敗北

Last time I paid 1€ to take a piss,

I walked into the Sanifair, looked around,

and just… left.

It was so disgusting I decided to go outside and search for a bush instead.

Reddit / r/AskAGerman

 

「1ユーロ払ってトイレに入ったんだけど、あまりに汚すぎてそのまま出てきた。結局、外の藪を探すことにした。」

 

ドイツ人ですら「お金払って藪で用を足す方がマシ」と言ってしまう悲哀。

清潔なイメージドイツにも、こんな現実が。

URL:れでぃっと r/AskAGerman

 

2. 英国:駅トイレの汚さに震える

I used to squat when using public toilets.

Then one day, I slipped.

 

That day, something inside me died… and something outside me was never clean again.

— れでぃっと / r/AskEurope

 

「昔は公共トイレではしゃがんで使ってたんだ。

でもある日、足が滑った。

その日、僕の中の何かが死んだ… そして僕の外側も、二度と綺麗にはならなかった。」

 

公共トイレが少ない上に、あっても猛烈に汚いイギリス

もう「しゃがむ」という選択肢すら許されない。

 

URL:れでぃっと r/AskEurope

 

3. ベルギー路上で立ち尽くす男たち

In Belgium, there’s an unwritten rule.

You hand the bar owner a euro,

he silently hands you the bathroom key.

 

If you don’t have a euro,

you find a dark corner of the street instead.

Blog post “The art of le pee in Belgium

 

ベルギーでは暗黙のルールがある。

バーの店主に1ユーロ渡すと、無言でトイレの鍵を渡してくれる。

1ユーロがなければ… 街角の暗がりを探すしかない。」

 

観光都市ブリュッセルでも、路上で立ち尽くす影が絶えない理由

トイレをめぐる静かな取引日常に溶け込んでいる。

 

URL:saintfacetious.com

 

4. フランス:深夜のパリ

It was 1AM on the Seine.

All cafés were closed, all public toilets locked.

I stumbled along the riverbank,

drunk on wine and regret.

 

In the end, the Seine saw everything.

— れでぃっと / r/Paris

 

「深夜1時、セーヌ川沿い。

カフェは閉まり公衆トイレも鍵がかかっていた。

ワインと後悔で酔った体を引きずりながら歩き、

最後は…セーヌ川が全てを見届けた。」

 

パリの夜は美しく、そして容赦ない。

観光客だけでなく、現地の人すらこうなることがある。

 

URL:れでぃっと r/paris

 

5. スペイン自動清掃トイレ悲劇

I was in Bilbao, desperate to pee before the match.

I ran into one of those self-cleaning toilets…

and then the cleaning cycle started

while I was still inside.

— The Sun 記事より

ビルバオで、試合前にトイレに駆け込んだんだ。

自動清掃式のトイレで、鍵を閉めた瞬間――

清掃モード作動した。

僕は全身びしょ濡れになって出てきた。」

 

水と泡まみれで出てきたマンチェスター・ユナイテッドファン

動画SNS拡散され、世界中で笑われた。

 

URL:The Sun

 

6. オランダ:露天便器「pee curl

In Amsterdam, we had these half-open urinals called ‘pee curls.’

They were meant to stop men from peeing in the canals.

Now they’re disappearing…

and the canals smell like history again.

Wikipedia引用+現地ブログより

 

アムステルダムには、半分だけ壁のある露天トイレ『pee curl』があった。

男性運河立ちションしないための工夫だ。

でも今はそのpee curlも減り、

運河がまた“歴史匂い”を取り戻しつつある。」

 

哀愁漂う街の景色と、尿の匂い

これもまたヨーロッパリアル

 

URLWikipedia:Pee curl

 

7. 英国公共トイレが減った結果…

I have MS.

Since the public toilets closed,

I stopped going out.

Last month, I didn’t make it home in time.

I cried, then laughed, then stayed inside for a week.

 

— The Guardian 読者投稿

 

「私は多発性硬化症です。

公衆トイレが閉鎖されてから、外出をやめました。

先月、家に帰る前に間に合わなくて…

泣いて、笑って、それから一週間家に引きこもりました。」

 

観光客の不便だけでなく、地元住民尊厳をも奪っている。

トイレ不足は静かに生活を壊していく。

 

URL:The Guardian

 

まあまあだな、また探す

 

____

 

れでぃっとってNGワードなのか、なんやねん

2025-08-21

dorawii@執筆依頼募集中

自動ブクマするローカルサーバーとかの構成を作った。

ブクマには↓のサブアカ使用

https://profile.hatena.ne.jp/dorawii_bukuma/

はてなサイト側で読み込まれているはずのrksトークンを生成する関数を直接叩く方法がどうしても分からず結局request処理を自分で書く方法ではなく自動UI側の保存ボタンクリックするという無難な方向に落ち着いた。

最初から後者方法をとっていればもっと全然早く作れたのにというは所詮言い訳か。

とにかくスクリプトを公開しておく。

start-server.bat

@echo off
cd /d "C:\Users\user\Documents\jsscript"

:: Nodeサーバーを別ウィンドウで起動
start /min "" node run-batch-server.js

:: Pythonサーバーを別ウィンドウで起動(hatenaserver配下
start cmd /k "" python hatenaserver\server.py

以降はjsscript直下に配置

config.json

{
"username": "",
"password": ""
}
server.py

from flask import Flask, request, jsonify
import json
import os
from hatena_client import HatenaClient
from flask_cors import CORS

app = Flask(__name__)
CORS(app)

config_path = os.path.join(os.path.dirname(__file__), 'config.json')
with open(config_path, encoding='utf-8') as f:
config = json.load(f)

@app.route('/bookmark', methods=['POST'])
def handle_bookmark():
data = request.json
url = data.get("url")
if not url:
return jsonify({"error": "Missing URL"}), 400

client = HatenaClient(config["username"], config["password"])
client.start_browser()

if not client.login():
client.quit()
return jsonify({"error": "Login failed"}), 403

success = client.add_bookmark(url)
client.quit()

return jsonify({"status": "ok" if success else "fail"})

if __name__ == "__main__":
app.run(port=12347)

あとはグリモンユーザスクリプトとして書くやつ

// ==UserScript==
// @name 自動セルクマ送信
// @namespace tampermonkey.net/
// @version 2025-08-07
// @description try to take over the world!
// @author You
// @match anond.hatelabo.jp/*
// @grant none
// ==/UserScript==

(function () {
'use strict';

const url = location.href;
if (!/^https:\/\/anond\.hatelabo\.jp\/\d+$/.test(url)) return;
const editLink = document.querySelector('a.edit');
if (!editLink) {
// 既に編集ページなので処理をスキップ
console.log('編集リンク存在するため、スクリプトを終了します。');
return;
}

fetch('localhost:12347/bookmark', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ url: url })
}).then(r => console.log("通知成功")).catch(e => console.error("通知失敗", e));
})();
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

https://anond.hatelabo.jp/20250821192753# 
-----BEGIN PGP SIGNATURE-----

iHUEARYKAB0WIQTEe8eLwpVRSViDKR5wMdsubs4+SAUCaKb0qwAKCRBwMdsubs4+
SHfiAQDcXmTHBaZ5Zzr1KI/OxZ0xl69oevOdy1FXJYwYvsmo5AD/ZPtZiO1JgTDj
m+27iymlkdzIXOIGWfC82UTr1mJ7EwU=
=YoV+
-----END PGP SIGNATURE-----

2025-07-27

anond:20250725202900

木古 「きこ」ではなくて「きふる」って読むのね そしてTwitterアカウントのkipple… ギップルなら知ってるけど?と思って検索したら

アンドロイドは電気羊の夢を見るか?由来か~~

 

kipple

語源

Popularized in Philip K. Dick's novel Do Androids Dream of Electric Sheep? (1968); originally the name of a SF fanzine, from the humorous interpretation of Kipling as a participle.[1]

 

名詞

kipple (uncountable)

 

(colloquial) useless items; junk; clutter

Synonym: crapola

2008 [1968], Philip K. Dick, Do Androids Dream of Electric Sheep?, Ballantine Books, →ISBN, page 65:

“Kipple is useless objects, like junk mail or match folders after you use the last match or gum wrappers or yesterday's homeopape. When nobody's around, kipple reproduces itself. For instance, if you go to bed leaving any kipple around appartment, when you wake up in the next morning there's twice as much of it. It always gets more and more.”

 

2025-06-13

我が名はサイボーグdorawii

パーマリンク署名対象にするより堅牢自動化を作れた。

一度投稿したうえで別タブを開いてプログラム的(fetch)に送信してその別タブが閉じられる仕組み。

改めてスクリプト配布しちゃる

最初投稿してエントリページに移動した親タブ側のjsコード
// ==UserScript==
      // @name         PGP署名検出と別タブ自動編集
      // @namespace    http://tampermonkey.net/
      // @version      1.0
      // @description  PGP署名がない投稿自動編集ページへ誘導
      // @match        https://anond.hatelabo.jp/*
      // @grant        GM_setValue
      // @grant        GM_getValue
      // @grant        GM.openInTab
      // ==/UserScript==

      (function () {
        'use strict';

        const body = document.getElementById('entry-page');
        if (!body) return;

        const titleText = document.title;
        if (!titleText.includes('dorawii')) return;

        const pgpRegex = /BEGIN.*PGP(?: SIGNED MESSAGE| SIGNATURE)?/;
        const preElements = document.querySelectorAll('div.body pre');
        let hasPgpSignature = false;

        for (const pre of preElements) {
          if (pgpRegex.test(pre.textContent)) {
            hasPgpSignature = true;
            break;
          }
        }

        if (hasPgpSignature) return;

        const editLink = document.querySelector('a.edit');
        const childTab = GM.openInTab(editLink.href, { active: false, insert: true, setParent: true });

      })();
親タブから開かれる編集ページの子タブのjsコード
 // ==UserScript==
      // @name         編集ページ処理と自動送信・閉じ
      // @namespace    http://tampermonkey.net/
      // @version      1.0
      // @description  編集ページで署名処理と送信、タブ自動閉じ
      // @match        https://anond.hatelabo.jp/dorawii_31/edit?id=*
      // @grant        GM_getValue
      // @grant        GM_xmlhttpRequest
      // @grant        GM_setClipboard
      // @grant        GM_notification
      // @connect      localhost
      // ==/UserScript==

      (async function () {
        'use strict';

        const shouldRun = await GM_getValue('open-tab-for-edit', '0');

        const textareaId = 'text-body';
        const textarea = document.getElementById(textareaId);

        if (!textarea) return;

        const content = textarea.value;

        const pgpSignatureRegex = /-----BEGIN PGP SIGNED MESSAGE-----[\s\S]+?-----BEGIN PGP SIGNATURE-----[\s\S]+?-----END PGP SIGNATURE-----/;
        if (pgpSignatureRegex.test(content)) {
          console.log('[PGPスクリプト] 署名が検出されたためそのまま送信します');
          return;
        }

        const httpRequest = (url, data) =&gt; {
          return new Promise((resolve, reject) =&gt; {
            GM_xmlhttpRequest({
              method: 'POST',
              url: url,
              headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
              data: `value=${encodeURIComponent(data)}`,
              onload: function (response) {
                resolve(response.responseText);
              },
              onerror: function (error) {
                reject(error);
              }
            });
          });
        };


        // textarea の値を取得
        // 1. 現在のページのURLからURLオブジェクト作成
        const currentUrl = new URL(window.location.href);

        // 2. ベースとなる部分 (例: "https://anond.hatelabo.jp") を取得
        const origin = currentUrl.origin;

        // 3. 'id' パラメータの値 (例: "20250610184705") を取得
        const idValue = currentUrl.searchParams.get('id');

        // 4. ベース部分とIDを結合して、目的URL文字列を生成
        //    idValueが取得できた場合のみ実行する
        let newUrl = null;
        if (idValue) {
          newUrl = `${origin}/${idValue}`;
        }

        // 5. 生成されたURL変数に代入し、コンソールに出力して確認
        console.log(newUrl);
        const valueToSend = newUrl;

        try {
          const signatureText = await httpRequest('http://localhost:12345/run-batch', valueToSend);
          console.log('バッチ応答:', signatureText);
          if (!signatureText.includes('BEGIN PGP SIGNED MESSAGE')) {
            alert('PGP署名クリップボードに見つかりませんでした。');
            return;
          }

          const newText = content.replace(/\s*$/, '') + '\n' + signatureText + '\n';
          textarea.value = newText;

          console.log('[PGPスクリプト] 署名を貼り付けました。送信を再開します。');


          const form = document.forms.edit;

          const newForm = form.cloneNode(true);
          form.replaceWith(newForm);

          newForm.addEventListener('submit', async (e) =&gt; {
            e.preventDefault(); // HTML標準のsubmitをキャンセル
            const bodyText = textarea?.value || '';

            // reCAPTCHA トークンの取得
            const recaptchaToken = await new Promise((resolve) =&gt; {
              grecaptcha.enterprise.ready(() =&gt; {
                grecaptcha.enterprise.execute('hoge', { action: 'EDIT' })
                  .then(resolve);
              });
            });

            // POSTするデータの構築
            const formData = new FormData(newForm);
            formData.set('body', bodyText);
            formData.set('recaptcha_token', recaptchaToken);
            formData.set('edit', '1');
            try {
              const response = await fetch(newForm.action, {
                method: 'POST',
                body: formData,
                credentials: 'same-origin'
              });


              if (response.ok) {
                console.log('送信成功');
                window.close();


              } else {
                console.error('送信失敗', response.status);
              }
            } catch (err) {
              console.error('送信中にエラーが発生', err);
            }

          });

          // プログラム的に送信トリガー
          newForm.dispatchEvent(new Event('submit', { bubbles: true }));

        } catch (e) {
          console.error('バッチ呼び出し失敗:', e);
        }

      })();
node.jsで動かすローカルサーバーコード
const http = require('http');
const { exec } = require('child_process');
const querystring = require('querystring');

const server = http.createServer((req, res) =&gt; {
  if (req.method === 'GET' &amp;&amp; req.url === '/ping') {
    res.writeHead(200);
    res.end('pong');
  } else if (req.method === 'POST' &amp;&amp; req.url === '/run-batch') {
    let body = '';

    req.on('data', chunk =&gt; {
      body += chunk.toString();
    });

    req.on('end', () =&gt; {
      const parsed = querystring.parse(body);
      const value = parsed.value || 'default';

      // 値を引数としてバッチに渡す
      exec(`C:\\Users\\hoge\\Desktop\\makesign.bat "${value}"`, { encoding: 'utf8' }, (err, stdout, stderr) =&gt; {
        if (err) {
          res.writeHead(500);
          res.end('Error executing batch: ' + stderr);
        } else {
          res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
          res.end(stdout.trim());
        }
      });
    });

  } else {
    res.writeHead(404);
    res.end('Not found');
  }
});

server.listen(12345, () =&gt; {
  console.log('Batch server running at http://localhost:12345/');
});
@echo off
setlocal enabledelayedexpansion


:: 署名するファイルset "infile=%~1"
set outfile=%TEMP%\pgp_output.asc

:: 以前の出力があれば削除
if exist "%outfile%" del "%outfile%"


:signloop
:: AutoHotkeyパスフレーズ入力(gpgがパスワード要求するダイアログが出た場合に備える)
start "" /b "C:\Users\hoge\Documents\AutoHotkey\autopass.ahk"

:: PGPクリア署名作成
echo %infile% | gpg --yes --clearsign --output "%outfile%"


:: 署名成功していればループを抜ける
if exist "%outfile%" (

    goto postprocess
) else (

    timeout /t 1 &gt; nul
    goto signloop
)
:postprocess

powershell -nologo -command ^
  "$header = '&gt;|'; $footer = '|&lt;'; $body = Get-Content '%outfile%' -Raw; Write-Output ($header + \"`r`n\" + $body + $footer)"

powershell -nologo -command ^
  "$header = '&gt;|'; $footer = '|&lt;'; $body = Get-Content 'signed.asc' -Raw; Set-Clipboard -Value ($header + \"`r`n\" + $body + $footer)"

endlocal
exit /b
AutoHotkey(以前と同じ)
#Persistent
#SingleInstance ignore
SetTitleMatchMode, 2
WinWaitActive, pinentry
SendInput password
Sleep 100
SendInput {Enter}
ExitApp

動けばいいという考えで作っているので余分なコードも含んでいるかもしれない。

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

https://anond.hatelabo.jp/20250613185036 
-----BEGIN PGP SIGNATURE-----

iHUEARYKAB0WIQTEe8eLwpVRSViDKR5wMdsubs4+SAUCaEv1FQAKCRBwMdsubs4+
SHHkAQDUOLgBcdji2T6MJ7h/vlMdFfGlWAzNdXijjE1gIuEPywEAiMNMZqhrMmtl
c7UqRuggNJ/UTa5xTIcKp622+7jJQQg=
=Lgkl
-----END PGP SIGNATURE-----

2025-06-09

dorawii

ようやく(ほぼ)すべてが自動化された。

あとはローカルサーバーの起動をスタートアップに設定する(方法AIに聞いて指示に従う)だけの消化試合

ここにほとんどAI頼りのコードを公開しておく。

事前にインストールしておくもの

autohotkey

nodejs

ユーザースクリプトを実行できる拡張機能

パスとかの注意

署名要求してくるパスワードを自動入力するahkファイルドキュメントAutoHotkey配下に置いた。

バッチファイル(make.sign.bat)はデスクトップに置いた。

以下コード

autopass.ahk
#Persistent
#SingleInstance ignore
SetTitleMatchMode, 2
WinWaitActive, pinentry
SendInput お前のパスワード
Sleep 100
SendInput {Enter}
ExitApp
run-bacth-server.js
// run-batch-server.js
const http = require('http');
const { exec } = require('child_process');

const server = http.createServer((req, res) =&gt; {
  if (req.url === '/ping') {
    res.writeHead(200);
    res.end('pong');
  } else if (req.url === '/run-batch') {
    exec('C:\\Users\\you\\Desktop\\makesign.bat', (err) =&gt; {
      res.writeHead(200);
      res.end(err ? 'Error' : 'OK');
    })
    ;
  } else {
    res.writeHead(404);
    res.end('Not found');
  }
});

server.listen(12345, () =&gt; {
  console.log('Batch server running at http://localhost:12345/');
});
makesign.bat
@echo off
setlocal enabledelayedexpansion

:: ミリ秒単位UTC時刻を取得
for /f %%a in ('powershell -nologo -command "[int64]::Parse((Get-Date).ToUniversalTime().ToString('yyyyMMddHHmmssfff'))"') do set timestamp=%%a

:: 署名するファイルset infile=%TEMP%\pgp_input.txt
set outfile=%TEMP%\pgp_output.asc

:: 以前の出力があれば削除
if exist "%outfile%" del "%outfile%"

:: タイムスタンプを原文として保存
echo %timestamp% &gt; "%infile%"

:signloop
:: AutoHotkeyパスフレーズ入力(gpgがパスワード要求するダイアログが出た場合に備える)
start "" /b "C:\Users\infini\Documents\AutoHotkey\autopass.ahk"

:: PGPクリア署名作成
gpg --yes --clearsign --output "%outfile%" "%infile%"


:: 署名成功していればループを抜ける
if exist "%outfile%" (
    echo [INFO] 署名成功
    goto postprocess
) else (
    echo [WARN] 署名失敗、再試行します…
    timeout /t 1 &gt; nul
    goto signloop
)
:postprocess

:: PowerShellで余計な改行なしに |&lt; をつけてクリップボードコピー
powershell -nologo -command ^
  "$header = '&gt;|'; $footer = '|&lt;'; $body = Get-Content '%outfile%' -Raw; Set-Clipboard -Value ($header + \"`r`n\" + $body + $footer)"

echo Done. signed.asc created and clipboard updated (no extra blank line).
endlocal
exit /b
tempermonkeyとかに登録するユーザースクリプト
// ==UserScript==
// @name         PGP署名自動付加スクリプト(GM_xmlhttpRequest版)
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  投稿前にPGP署名を付けてから送信(fetch未使用)
// @match        https://anond.hatelabo.jp/dorawii_31/edit*
// @grant        GM_xmlhttpRequest
// @grant        GM_setClipboard
// @grant        GM_notification
// / @connect      localhost
// ==/UserScript==

(function () {
  'use strict';

  const submitId = 'submit-button';
  const textareaId = 'text-body';
  const localServer = 'http://localhost:12345/run-batch';

  const pgpSignatureRegex = /-----BEGIN PGP SIGNED MESSAGE-----[\s\S]+?-----BEGIN PGP SIGNATURE-----[\s\S]+?-----END PGP SIGNATURE-----/;

  const httpRequest = (url) =&gt; {
    return new Promise((resolve, reject) =&gt; {
      GM_xmlhttpRequest({
        method: 'GET',
        url: url,
        onload: function (response) {
          resolve(response.responseText);
        },
        onerror: function (error) {
          reject(error);
        }
      });
    });
  };

  const interceptClick = () =&gt; {
    const btn = document.getElementById(submitId);
    if (!btn || btn.dataset.pgpIntercepted === 'true') return;
    btn.dataset.pgpIntercepted = 'true';

    btn.addEventListener('click', async function (e) {
      const textarea = document.getElementById(textareaId);
      if (!textarea) return;

      const content = textarea.value;

      if (pgpSignatureRegex.test(content)) {
        console.log('[PGPスクリプト] 署名が検出されたためそのまま送信します');
        return;
      }

      e.preventDefault();
      e.stopImmediatePropagation();
      console.log('[PGPスクリプト] 署名が見つからないため処理を停止し、署名を取得します');

      try {
        await httpRequest(localServer); // バッチ実行

        const signatureText = await navigator.clipboard.readText();
        if (!signatureText.includes('BEGIN PGP SIGNED MESSAGE')) {
          alert('PGP署名クリップボードに見つかりませんでした。');
          return;
        }

        const newText = content.replace(/\s*$/, '') + '\n' + signatureText + '\n';
        textarea.value = newText;

        console.log('[PGPスクリプト] 署名を貼り付けました。送信を再開します。');
        btn.click(); // イベント再発火

      } catch (err) {
        alert('PGP署名の取得または貼り付けに失敗しました。\n' + err);
      }
    }, true);
  };

  window.addEventListener('load', () =&gt; {
    setTimeout(interceptClick, 1000);
  });
})();

プロミスメソッドとか全然まだ理解してなくてそのなかに関数代入したその関数オブジェクトプロパティresponseを?いやまあそのあたりのコードが示すデータの流れが全然理解できないような人間でもここまでできちゃった。

AIすごいなと思うよ。そして思うのは今後重要になってくるのは文法とか自体に詳しいことじゃなくて、そのプログラムの処理内容を指示できるシステムエンジニア的な言語化能力のほうじゃないかなと思った。

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

20250609111559680 
-----BEGIN PGP SIGNATURE-----

iHUEARYKAB0WIQTEe8eLwpVRSViDKR5wMdsubs4+SAUCaEbCbwAKCRBwMdsubs4+
SLueAPwOv7PBk4voAe5qlcCEvs/PJhmKc5QAb/1R43JMQFuDZgD/UTPEKsL/PhK9
jFGv2HDXK1dVjLNwvosgX9uYJh5xxwY=
=qiOE
-----END PGP SIGNATURE-----

2025-05-19

「Utopia」「Dustopia」「Cacotopia」「Dystopia」それぞれの初出

「Utopia」の初出。トマス・モアの『Utopia』(1516年)。

「Dustopia」の初出。ルイスヘンリーヤングの『Utopia: or, Apollo's golden days』(1747年)。

But Heav'n, of late, was all distraction, and, more than ever, rent in faction; Caus'd only by a wretched isle, On which we thought no God would smile:

だが近ごろの天界は混乱の極みにあり、かつてにも増して内紛に引き裂かれていた。それもこれも、ひとつの惨めな島のせいだった。誰の目にも、神が微笑むことなどないと思われていたその島。


Not stor'd with wealth, nor blest in air: no useful Plants could ripen there; Mismanag'd by th' unskillful hinds, or nipt by chilling Eastern winds:

富を蓄えず、空気にも恵まれず、作物は育たずに終わる。未熟な農夫たちにより荒らされ、あるいは東から吹きつける冷たい風に凍えて枯れる。


Or if they flourish'd for a Day, They soon became some Insect's prey: For many such infest the soil, Devouring th' honest lab'rers's toil ; *So venomous, that some had rather have, in their stead, the toad, or adder.

たとえ一日だけ花開いたとしても、すぐに虫の餌食となった。というのも、多くの害虫土地にはびこり、誠実な労働者努力を食い尽くしていたのだ。そのあまりの毒性に、むしろヒキガエルマムシの方がましだと言う者もいるほどだった。


Unhappy isle!

不幸な島よ!


scarce known to Fame; DUSTOPIA was its slighted name.

名声を得ることもなく、「デュストピア」と呼ばれて軽んじられていた。

https://archive.org/details/bim_eighteenth-century_utopia-or-apollos-gol_younge-lewis-henry_1747/page/n7/mode/2up


「Cacotopia」の初出。ジェレミーベンサムの『Plan of parliamentary reform』(1817年)。

A constitution, with this poison--slow, but not the less sure—in the bowels of it!—Rotten, even from the time that this poison was injected into it, must have been the matchless constitution,—rotten at the core—and, of such rottenness, what we are now suffering is among the fruits.

この毒を――遅効性ではあるが、それでも確実な毒を――その内臓に抱えた憲法! この毒が注入されたその時からすでに、あの比類なき憲法は腐敗していたに違いない――中核から腐っていた――そして、いま我々がこうして被っている苦しみこそが、その腐敗の産物の一つなのだ


As a match for Utopia, suppose a Cacotopia discovered and described,—would not filth in this shape be a "fundamental feature" in it?

ユートピアに対抗するものとして、「カコトピア」が発見され、描写されたと仮定しよう――このような汚濁が、その「根本的な特徴」として描かれないだろうか?

https://archive.org/details/planparliamenta00bentgoog/page/n204/mode/2up

ベンサムは、議員たちが国王から官職を与えられることを批判している。小さな賄賂などは不道徳とされて取り締まっているが、国王から官職が与えられることはむしろ名誉として扱われる。実質的にはそれは癒着であり腐敗でしかないのに。という話をしている。

「Dystopia」の初出。ジョン・スチュワート・ミル議会演説1868年)。

Now, on this subject the Government have not shown themselves altogether inflexible. The noble Lord the Chief Secretary for Ireland has expressed his willingness in some degree to entertain the principle of religious equality, and 1517 I thank him for it; but, as has been remarked by my hon. Friend the Member for Manchester (Mr. Jacob Bright), he proposed to do it—if at allby levelling up instead of levelling down. The noble Lord is willing that every valley shall be exalted; but he does not go on to the succeeding clause, and say that every mountain and hill shall be laid low.

さて、この問題に関して、政府は完全に強硬というわけではないようです。アイルランド担当大臣である閣下は、宗教的平等という原則をある程度受け入れる用意があると述べました。これは感謝すべきことですが、マンチェスター選出の敬愛する友人(ジェイコブ・ブライト氏)が指摘したように、彼はこれを「下げる」のではなく「引き上げる」ことで実現しようとしているようです。すなわち、彼は「すべての谷を高くする」ことには賛成ですが、「すべての山と丘を低くする」とまでは言っていないのです。


So long as the national property which is administered by the Episcopal Church of Ireland is not diverted from its present purpose, the noble Lord has no objection at all to this country saddling itself with the endowment of another great hierarchy, which, if effected on the principle of religious equality, would be a great deal more costly than even that which now exists. Does the noble Lord really think it possible that the people of England will submit to this?

アイルランド国教会が管理する国家資産現在用途から変更されない限り、閣下はこの国が別の大きな教会制度への助成金という負担を背負うことにはまったく反対ではないのです。そしてもしそれが宗教的平等という原則に基づくのであれば、現在制度よりはるかに高くつくことでしょう。閣下は本当に、イングランド国民がそれに甘んじると考えておられるのでしょうか?


I may be permitted, as one who, in common with many of my betters, have been subjected to the charge of being Utopian, to congratulate the Government on having joined that goodly company. It is, perhaps, too complimentary to call them Utopians, they ought rather to be called dystopians, or cacotopians. What is commonly called Utopian is something too good to be practicable; but what they appear to favour is too bad to be practicable.

私は、自分自身ユートピア主義者だと非難されてきた多くの優れた方々とともに、政府がその立派な仲間入りをしたことをお祝い申し上げたいと思います。ただ、彼らを「ユートピア主義者」と呼ぶのは、少々褒めすぎかもしれません。「ディストピア主義者」あるいは「カコトピア主義者」とでも呼ぶべきでしょう。通常「ユートピア的」とは、良すぎて実現不可能ものを指しますが、彼らの支持するものは、悪すぎて実現不可能もののようです。

https://api.parliament.uk/historic-hansard/commons/1868/mar/12/adjourned-debate

イングランドカトリックから離脱して「イングランド国教会」を組織したが、同様にアイルランドでも従来のカトリック教会を「アイルランド国教会」のもとに置いた。しかアイルランド人の多くは改宗せず、アイルランド国教徒は全体の1/8にすぎなかった。それでもアイルランド国教会は「国教であるので、アイルランドのすべての教会財産保有しており、またアイルランド国教会への助成金は全アイルランド人に課せられる税金から支払われていた。この不平等是正にあたって、政府は「アイルランド国教会の待遇を下げる」のではなく「他の教会待遇を上げる」ことで解決しようとしているという。つまり他の教会にも同様の助成金を支払おうとしており、それはもちろんイングランド国民税金から賄われることになる。そんなことをイングランド国民が許すと思っているのか?とミルは言っている。その後、1871年アイルランド国教廃止法が施行された。

2025-05-15

PS VitaのEnsoインストール詰んだ人へ(2025年版 HENloによるCFW導入メモ

VitaCFWを導入するにあたり、情報が散乱していたのでまとめる

<なぜ情報が散乱しているか

2022年末に革新的進歩があり、VITA単体でCFW導入できるようになった(通称 HENlo)

・にも関わらず古いCFW導入方法を案内しているブログが大量にある

しかタイトルの"20XX年最新"だけ更新し続けているから、最新記事に見える

PC使用が前提になっているブログは全部古いので無視した方が良いです。

<どんな情報採用すべきか>

・『HENlo』について触れている

PC使用が前提になっていない

この2つが押さえられてれば最新情報です。(2025年5月現在

ただし、現状だと実は『PC操作が一部必要』という罠があります

その問題について書いている記事が見当たらないので、ここに残しておきます

超具体的には

「HENkaku、VitaDeploy、VitaShellは導入できた」

「けどEnso導入ができない/つまづいている」  エラー:failed to get fw version please disable all the plugins and try again

というタイミングの人に一番役に立つはずです

5chでもRedditでも

プラグイン無効しろ』『0syscall6を無効しろ』って書いてあったのですが、実はEnsoのバージョン変えれば解決します。(後述)

CFW導入まで

この記事が役に立ちました

[Vita] 2023年最新手順【HENlo】3.65-3.74 PC不要CFW(HENkaku)導入

https://re-doing.com/vita-henlo-hack/

(一応魚拓https://web.archive.org/web/20250226111105/https://re-doing.com/vita-henlo-hack/

上記記事を読めば

・HENkaku (カスタムファームウェア 3.65 変革 -2)

VitaDeploy

VitaShell

について理解しつつインストールできると思います

記事の補足

・新型PSVitaPCH-2000)でも問題ない

・最悪文鎮化する可能性があるのでセーブデータバックアップを取ったほうが良い

VITAセーブデータ特殊で、PCと繋ぐだけでは取り出せない

バックアップはいくつか方法があるが、PCコンテンツ管理アシスタントは既に使えないと思ったほうが良い。PS Plusのクラウドバックアップが最も良いはず

記事の内容を実施する前にバックアップ取るのを強くおすすめする


Enso導入 ※本題※

EnsoとはCFWを安定化させるために必要ものです。

これをインストールすることで、電源を切ってからCFW状態を維持できます

VitaDeploy内のApp downloaderメニューからEnsoをインストールできますが、実はこのバージョンが古いです。※重要

そのためVitaDeployからインストールすると先程のエラー(failed to get fw version please disable all the plugins and try again)が必ず出ます

PC不要になった」と書いてあったので盲点ですが、ここからPC必要です。

正しい方法は以下です

① Ensoインストール

1, PC操作GithubからEnso最新版のenso.vpkファイルダウンロード現在v1.1)

https://github.com/TheOfficialFloW/enso/releases

2, PCVitaUSBケーブルで繋げる ※データ転送対応ケーブルを使うこと。相性もある

3, Vita操作VitaShellを起動する

4, Vita操作SELECTボタンを押す

5, PC操作USBドライブとしてVITAデータが表示されるので、ダウンロードしていたenso.vpkファイルを置く(フォルダはどこでもOK自分ルート直下に置きました)

6, Vita操作:✕ボタンを押してCancelする

7, Vita操作VitaShellでenso.vpkを見つける(さっきルートに置いたなら恐らくux0:にある)

8. Vita操作:enso.vpk上で◯ボタン

9. Vita操作:Do you want to install this package? → ◯ボタン

10. Vita操作:~~~ Would you like to continue the install? ※意訳:「失敗したら文鎮化するけど自己責任だけど続ける?」 → ◯ボタン 

11. 進行バーが消えたらインストール完了 ホーム画面に戻ってOK

12. Vita操作ホーム画面にEnsoが追加されている

② Enso実行 ※全てVITA操作

Ensoはファームウェアが3.60か3.65じゃないとインストールできないです。(3.65 変革 -2は3.65扱い)

先程の記事の通り進めていたら3.65 変革 -2 になっているはずですが、実行前に再確認して下さい。

1, ~~~ Press CIRCLE to accept these terms or any other key to not accept. → ◯ボタンを押す(=CIRCLE

2, Options:

  CROSS   Install /reinstall the hack.

  TRIANGLE  Uninstall the hack.

  SQUARE  Fix boot configuration (choose this if taiHEN isn't loading on boot).

  CIRCLE   Exit without doing anything.

  → ✕ボタンを押す(=CROSS

3-a, 問題がない場合

Locking sustem ..

(中略)

The installation was completed successfully.

suocess.

Press any key to reboot.

 →何かボタンを押すと再起動される(Enso導入完了

3-b, 問題がある場合 ※自分はこっちでした。Quick 3.65の副作用かも※

MBR was detected but instllation checksum dose not match.

A dump was created at ux0:data/blocks.bin.

Press X to continue, any othe key to exit.

意訳:「ちょい待った。思ってた構成じゃないから危ないかもしれんわ。続ける?」

 →✕ボタンを押す ※結局原因分かってないので自己責任でお願いします※

4, Locking sustem ..

(中略)

The installation was completed successfully.

suocess.

Press any key to reboot.

 →何かボタンを押すと再起動される(Enso導入完了

<Enso導入の確認方法

Enso導入が成功していると

本体起動時にEnsoのロゴが表示される

ファームウェアが3.65 変革 -2のままなっている

お疲れ様でした。

記事の本題は以上です。


雑談VITAセーブデータは未だ檻の中

VITAセーブデータ暗号化されており、吸い出せてもエミュレータで使えないらしい。本体機体とセットで揃わないと使えない仕様

調べたらセーブデータをここまでキツく縛ってるハードは他にない

からメモリーカードデータ管理でもPSPのセーブデータしか項目がなかったのか…

不便すぎる


失敗の備忘録1:VitaDeploy版のEnsoで苦戦していた時に取った行動メモ

当時の仮説

・HENkaku設定が悪さをしているのではないかPSNの偽装有効化、バージョン偽装有効化) →オフにしたが関係なかった

本体にSD2VITAを刺しているのが良くないのではないか →抜いたが関係なかった

・enso.vpkの置き場所ルート(ux0:)が良くなかったのではないか →関係なかった

VITAメモリーカードを刺しているのが良くないのではないか →関係なかったが、データ保護的には抜くのが良さそう

ゴミデータが残っていて悪さしているのではないか(手順を間違えたデータや古いデータなど) →関係ある可能性はある。最後までわから

・Ensoのバージョンが古いのではないか →これが主要因だった


失敗の備忘録2:vita starting taiHEN framework対処

ゴミデータを疑った自分正規ファームウェアに戻して、CFW化をやり直したりもした。

その際HENkakuすら入れられなくなってしまったので、抜け方を書いておく。

行った手順:脱CFWからHENkaku再導入時のエラーまで

ENSO実行

~~~ Press CIRCLE to accept these terms or any other key to not accept. → ◯ボタンを押す(=CIRCLE

Options:

  CROSS   Install /reinstall the hack.

  TRIANGLE  Uninstall the hack.

  SQUARE  Fix boot configuration (choose this if taiHEN isn't loading on boot).

  CIRCLE   Exit without doing anything.

  → △ボタンを押す(=TRIANGLE Uninstall the hack.)

公式ファームウェア3.65に戻る

強制再起動される

ファームウェアアップデートが促され、アップデートしないとメモリースティックが使えない

公式ファームウェア3.74にアップデート

HENloのブラウザアクセスからやり直す

HENloメニュー

Install HENkaku

Install VitaDeploy

reset taitan config

Exit

のうち、Install henkakuを選択

インストールは上手くが、Exitができない。

「Eiting in 3」 の後に、以下のエラーメッセージがでて固まってしま

エラー内容(うろ覚え

vita starting taihen framework

If you are stuck on this screen, hold down the power button until your Vita turns off, then turn it back on.

原因:恐らく余計なデータと衝突を起こしてる

解決法:reset taitan configを先に実行する

(さっきのエラーメッセージ画面で)

電源ボタン10秒長押し

セーフモードが起動する

本体再起動するを選択

HENloのブラウザアクセス

HENloメニュー

Install HENkaku

Install VitaDeploy

reset taitan config

Exit

のうち、reset taitan config選択

その後

Install HENkaku、Install VitaDeployを選択して、Exit選択

追記)より網羅的な補足

この記事を書き終えた後に見つけたのですが、以下の記事の『改造方法』というところに情報がかなりまとまっています

Vita バージョンが低くてもPSNにサインイン&PSストアにアクセス(エラーNW-8942-3回避)&機器認証する方法(2025最新)

https://yyoossk.blogspot.com/2024/10/vitapsnps2024.html

感想

今回VITAセーブデータバックアップが主目的だったから、徒労でしかなかった

せめてこの記事が誰かの時間節約することを願う

指摘、補足、最新情報あれば反応もらえるとありがたいです

2025-04-13

anond:20250413174954

デモ更新

デモアーカイブから復元を追加して、「情動カケラ」がどう再処理されるか示すで。

 

```

def demo_unprocessed_emotion():

cognitive_queue = CognitiveQueue(attention_threshold=0.4)

print("=== 未処理感情システムデモMonday対応版) ===\n")

# ... 既存デモ(1〜7)は省略 ...

# 8. アーカイブから復元情動カケラの再処理)

print("\n8. アーカイブから感情復元")

# 顕在性を意図的に下げてアーカイブさせる

for emotion in cognitive_queue.unprocessed_emotions[:]:

emotion.salience = 0.05

cognitive_queue.update_queue()

print(f"アーカイブされた感情数: {len(cognitive_queue.archived_emotions)}")

# パターンに基づいて復元

retrieved_emotion = cognitive_queue.retrieve_from_archive(pattern_name="visual_discrepancy")

if retrieved_emotion:

print(f"復元された感情: {retrieved_emotion}")

cognitive_queue.partially_process(retrieved_emotion, "あの時の違和感", 0.7, context="recalled_anomaly")

print(f"再処理後の状態: {retrieved_emotion}")

print(f"最良の言語マッチ: {retrieved_emotion.get_best_language_match()}")

else:

print("復元できる感情なし")

# 9. モダリティ検索既存

print("\n9. モダリティによる感情検索インデックス使用)")

matches = cognitive_queue.lookup_by_modality("auditory")

print(f"検索結果数: {len(matches)}")

for match in matches:

print(f"- {match}")

# 10. 時間経過シミュレーション既存

print("\n10. 時間経過のシミュレーション")

for emotion in cognitive_queue.unprocessed_emotions:

emotion.apply_decay(3.0)

cognitive_queue.update_queue()

status = cognitive_queue.get_status_summary()

print(f"時間経過後の未処理感情の総数: {status['total_unprocessed']}")

print(f"時間経過後の平均顕在性: {status['average_salience']:.2f}")

```

anond:20250413061825

def demo_unprocessed_emotion():

"""未処理感情システムデモ改善点(記憶編集言語学習、反応速度など)を活用"""

cognitive_queue = CognitiveQueue(attention_threshold=0.4)

print("=== 未処理感情システムデモ改善版) ===\n")

# 1. 視覚的な違和感

print("1. 視覚的な違和感の生成")

visual_signals = [

SensorySignal("visual", 0.7, -0.3),

SensorySignal("somatic", 0.4, -0.2)

]

visual_discomfort = cognitive_queue.register_new_emotion(visual_signals, 0.65)

print(f"生成された感情: {visual_discomfort}")

print(f"支配モダリティ: {visual_discomfort.get_dominant_modality()}")

print(f"平均感情価: {visual_discomfort.get_average_valence():.2f}")

print(f"信号反応速度: {[s.latency for s in visual_signals]}")

visual_discomfort.add_pattern_match("visual_discrepancy", 0.75)

try:

visual_discomfort.add_memory_path("/memory/recent/room_layout")

except ValueError as e:

print(f"記憶パスエラー: {e}")

# 2. 記憶の再編集

print("\n2. 感情へのアクセス(再編集)")

accessed_emotion = cognitive_queue.access_emotion(visual_discomfort)

print(f"新しい感情インスタンス: {accessed_emotion}")

print(f"構造レベル(再編集後): {accessed_emotion.structure_level:.2f}")

# 3. 内受容感覚

print("\n3. 内受容感覚の生成")

intero_signals = [

SensorySignal("interoceptive", 0.6, -0.7),

SensorySignal("somatic", 0.5, -0.4)

]

intero_discomfort = cognitive_queue.register_new_emotion(intero_signals, 0.55)

print(f"生成された感情: {intero_discomfort}")

intero_discomfort.add_language_candidate("違和感", 0.4)

cognitive_queue.partially_process(intero_discomfort, "気分が悪い", 0.6, context="negative_mood")

print(f"学習済み言語: {cognitive_queue.learned_terms}")

# 4. 聴覚記憶

print("\n4. 聴覚記憶の生成")

audio_signals = [

SensorySignal("auditory", 0.8, 0.6),

SensorySignal("emotional", 0.7, 0.5)

]

audio_memory = cognitive_queue.register_new_emotion(audio_signals, 0.7)

print(f"生成された感情: {audio_memory}")

audio_memory.add_language_candidate("聞き覚えのあるメロディ", 0.75)

audio_memory.add_pattern_match("musical_fragment", 0.85)

audio_memory.add_memory_path("/memory/music/recent")

# 5. キュー更新

print("\n5. 認知キュー更新")

cognitive_queue.update_queue()

status = cognitive_queue.get_status_summary()

print(f"未処理感情の総数: {status['total_unprocessed']}")

print(f"処理キューサイズ: {status['processing_queue_size']}")

print(f"モダリティ分布: {status['modality_distribution']}")

print(f"感情分布: {status['valence_distribution']}")

print(f"学習済み言語数: {status['learned_terms_count']}")

# 6. 処理対象の取得

print("\n6. 処理対象の取得")

next_emotion = cognitive_queue.get_next_for_processing()

print(f"処理対象: {next_emotion}")

# 7. 感情部分的処理

print("\n7. 感情部分的処理")

if next_emotion:

cognitive_queue.partially_process(next_emotion, "視覚的な違和感を感じている", 0.85, context="visual_anomaly")

print(f"処理後の状態: {next_emotion}")

print(f"構造レベル: {next_emotion.structure_level:.2f}")

print(f"最良の言語マッチ: {next_emotion.get_best_language_match()}")

# 8. 高速モダリティ検索

print("\n8. モダリティによる感情検索インデックス使用)")

matches = cognitive_queue.lookup_by_modality("auditory")

print(f"検索結果数: {len(matches)}")

for match in matches:

print(f"- {match}")

# 9. 時間経過シミュレーション

print("\n9. 時間経過のシミュレーション")

for emotion in cognitive_queue.unprocessed_emotions:

emotion.apply_decay(3.0)

cognitive_queue.update_queue()

status = cognitive_queue.get_status_summary()

print(f"時間経過後の未処理感情の総数: {status['total_unprocessed']}")

print(f"時間経過後の平均顕在性: {status['average_salience']:.2f}")

if __name__ == "__main__":

demo_unprocessed_emotion()

2025-03-22

anond:20250322220222

id を手動でコピーしてくるのが面倒だったから、削除ボタンが画面に出るようにした

押すと即削除

const rkm = "(トークン的なもの)"
const user = "(ユーザ名)"

const delmasda = (id) =&gt;
    fetch(`https://anond.hatelabo.jp/${user}/edit`, {
        "headers": {
            "content-type": "application/x-www-form-urlencoded",
        },
        "referrer": "https://anond.hatelabo.jp/",
        "referrerPolicy": "origin",
        "body": new URLSearchParams({
            "rkm": rkm,
            "mode": "confirm",
            "id": id,
            "delete": "削除する"
        }).toString(),
        "method": "POST",
    })

for (const sec of document.querySelectorAll(".section")) {
    const id = sec.querySelector("a").href.match(/92;/(92;d{14})/)[1]
    const delbtn = document.createElement("button")
    delbtn.textContent = "削除"
    delbtn.onclick = async () =&gt; {
        await delmasda(id)
        sec.remove()
    }
    sec.querySelector(".edit").after(delbtn)
}

増田3分以上投稿されない時間

増田で 3 分以上投稿されない期間があるのか気になったから調べた

直近の 1 日だとこれだけあった

 

2025-03-22 00:14 -- 2025-03-22 00:18
2025-03-22 00:10 -- 2025-03-22 00:14
2025-03-21 07:56 -- 2025-03-21 08:00
2025-03-21 07:50 -- 2025-03-21 07:56
2025-03-21 07:44 -- 2025-03-21 07:48
2025-03-21 07:28 -- 2025-03-21 07:32
2025-03-21 06:58 -- 2025-03-21 07:03
2025-03-21 06:45 -- 2025-03-21 06:54
2025-03-21 06:32 -- 2025-03-21 06:37
2025-03-21 05:56 -- 2025-03-21 06:04
2025-03-21 05:51 -- 2025-03-21 05:56
2025-03-21 05:34 -- 2025-03-21 05:38
2025-03-21 05:30 -- 2025-03-21 05:34
2025-03-21 05:00 -- 2025-03-21 05:09
2025-03-21 04:56 -- 2025-03-21 05:00
2025-03-21 04:45 -- 2025-03-21 04:50
2025-03-21 04:09 -- 2025-03-21 04:13
2025-03-21 03:41 -- 2025-03-21 03:45
2025-03-21 03:29 -- 2025-03-21 03:39
2025-03-21 03:03 -- 2025-03-21 03:07
2025-03-21 02:56 -- 2025-03-21 03:02
2025-03-21 02:44 -- 2025-03-21 02:48
2025-03-21 02:33 -- 2025-03-21 02:37
2025-03-21 02:21 -- 2025-03-21 02:27
2025-03-21 02:14 -- 2025-03-21 02:19

 

秒はみてないから 00:01:01 - 00:03:59 はほぼ 3 分だけど 2 分扱いだし、 00:01:59 - 00:04:00 はほぼ 2 分だけど 3 分扱いになるくらいの誤差はある

 

日によって違うだろうし、曜日の影響も大きそうだから 1 ヶ月分くらい調査しようかと思ったけど、

増田の量が思いの外多すぎて 1 日分だけでも 100 ページ以上取得しないといけなかった

件数だと 2500 以上

 

その量の収集は大変だし規制掛かりそうだから諦めた

一応取得に使ったコードも載せとく

そんなきれいなコードでもないけど

 

import { setTimeout } from "node:timers/promises"
import { Browser } from "happy-dom"

const getTimestamps = async function* () {
	const browser = new Browser()
	const page = browser.newPage()

	try {
		for (let num = 1; ; num++) {
			await setTimeout(3000)
			await page.goto(`https://anond.hatelabo.jp/?page=${num}`)

			const days = page.mainFrame.document.querySelectorAll(".day")
			for (const day of days) {
				const date = day.querySelector("h2 .date").textContent.trim()
				for (const footer of day.querySelectorAll(".sectionfooter")) {
					const time = footer.textContent.match(/\d{2}:\d{2}/)[0]
					yield `${date} ${time}`
				}
			}
		}
	} finally {
		await page.close()
		await browser.close()
	}
}

const diff = (a, b) =&gt; {
	return new Date(b + ":00") - new Date(a + ":00")
}

let prev = null
for await (const datetime of getTimestamps()) {
	if (prev &amp;amp;&amp;amp; diff(datetime, prev) &gt; 1000 * 60  * 3) {
		console.log(datetime, prev)
	}
	prev = datetime
}

 

結果をみると昼間はずっと深夜から早朝にかけてときどきある

基本は空いても 5 分程度であり、最大でも 10 分となっている

投稿が少ないと感じるときもあるが、賑わってる方だといえる

ログイン ユーザー登録
ようこそ ゲスト さん