はてなキーワード: MATCHとは
ジェシー・アイゼンバーグが『The End of the Tour』の脚本を読んだのは、あるインタビューを受けた直後だった。
そのインタビューについて彼は、
「自分について好意的なことを書くつもりはないし、自分が望むような人物像として描かれることもないだろうと分かっていた」
と語っている。
⸻
この映画は、ジェームズ・ポンソルト監督(『The Spectacular Now』)による作品で、ジャーナリストのデイヴィッド・リプスキーが書いた本を原作としている。
その本は、1996年、『Infinite Jest』という壮大なコメディ小説のプロモーション中だったデイヴィッド・フォスター・ウォレスと、リプスキーが過ごした5日間を記録したものだ。
彼自身も小説家だったが、成功は限定的で、『Rolling Stone』誌で働いていた。
彼は編集者を説得し、インディアナ州へウォレス(演:ジェイソン・シーゲル)のインタビューに行かせてもらう。
『Infinite Jest』の出版によって、当時34歳だったウォレスは文学界の有名人となった。
主要メディアは彼を「自分たちの世代の声」「天才」として絶賛した。
⸻
ピューリッツァー賞受賞劇作家ドナルド・マーグリーズによる『End of the Tour』の脚本を読んだアイゼンバーグは、
「この男を演じるのは面白いと思った。単なる無害なインタビュアーではなく、誰かを暴こうとしてそこへ向かっている人物だから」
と感じたという。
文学者同士の長い会話など、素晴らしい映画になる題材には思えないかもしれない。
しかし『End of the Tour』は、ユーモアと哀しみを交えながら展開する、二人の間の魅力的な心理戦になる。
一種のロードムービーでもあるこの作品は、ポップタルトやジャンクフードを分け合うような馬鹿げた日常的な場面と、暗く告白的な瞬間を並置している。
⸻
アイゼンバーグは、ウォレスについて人々が知っていることは、おそらく二つだけだと言う。
一つは、彼が1079ページにも及ぶ巨大な本を書いたこと。
写真では、しばしば祖母のような丸眼鏡をかけ、長い髪をバンダナでまとめている姿が写っている。
描写に満ち、魅惑的で、予想外の方向へ進む。
良くも悪くも、唯一無二の声だった。
⸻
主にコメディ俳優として知られていたシーゲルをウォレス役に選んだことには、インターネット上で反発もあった。
ポンソルト監督は、シーゲルを一つのジャンルだけに閉じ込めることは馬鹿げていると言う。
ロビン・ウィリアムズやトム・ハンクスのようなコメディ出身者が、偉大なシリアス俳優になった例を挙げながら。
またポンソルトは、もう一人の主演俳優であるアイゼンバーグについても高く評価している。
彼をダスティン・ホフマンやジーン・ハックマンになぞらえ、
だと語る。
⸻
早口で話し(ニューヨーク出身らしい特徴だ)、機転の利いた冗談をすぐ返すアイゼンバーグ(31歳)は、シーゲルとの関係について、
「映画の中の二人の人物の関係はしばしば対立的だけれど、僕たち自身はとても良い仲間意識があった」
と話す。
劇作家でもあり短編作家でもある彼は、マーグリーズの脚本を読むことを楽しみにしていた。
「登場人物たちが、本当に感情的に複雑な人生を持っていると分かっていた。台詞も良い。場面が3行程度で終わるようなものではない。こんな作品に関われる機会って、どれくらいあると思う?」
⸻
アイゼンバーグの次の仕事は、8月にカリフォルニアで撮影開始予定のウディ・アレン作品。
その後には、自身初の短編集『Bream Gives Me Hiccups』の出版ツアーが控えている。
さらに、この年には『American Ultra』『Louder Than Bombs』の2作品が公開予定で、翌年には『Batman v Superman: Dawn of Justice』で悪役レックス・ルーサーを演じる。
「バットマン映画で僕が演じる場面は、本当に面白くて魅力的なんです」
と語る。
彼は、『End of the Tour』のような小規模作品と、大作映画の両方で仕事をすることに価値を見出している。
⸻
「俳優との仕事の仕方を深く理解している、珍しいタイプの監督」
だと評価する。
また、最初はそれほどドラマチックではないと思った場面を、ポンソルトがより劇的なものに変えていくことに感銘を受けたという。
「一見すると何気ない会話の中に、生死をかけたような緊張感が生まれるんです。」
⸻
結局、リプスキーは『Rolling Stone』の記事を書く必要がなくなった。
『Although Of Course You End Up Becoming Yourself』のあとがきで、彼はウォレスと過ごした時間の中で、自分自身が抱えていた不安や劣等感を認めている。
興味深いことに、雑誌ライターとして経験豊富だったウォレスの方が、インタビューという行為についてはリプスキーよりはるかによく理解していた。
自分の発言がどのように誤解され、切り取られ、分析され、再構成される可能性があるか。
⸻
カメラの存在と同じように、回り続ける録音機は現実そのものを変えてしまう。
その意味で、二人は互いのために演じていたのだとポンソルト監督は考えている。
しかし同時に、ウォレスは「自分自身を明らかにしようとしていた」とも感じている。
「彼は本質的に警戒心の強い人でした。おそらく作家や、思慮深く神経症的な人間がするように、常に自分自身を編集していたんだと思います」
とポンソルトは語る。
⸻
『End of the Tour』はサンダンス映画祭で上映された際、好意的な評価を受けた。
特に、デイヴィッド・フォスター・ウォレス文学トラスト、彼の未亡人、そして何人かの編集者からである。
理由は複雑だ。
「ウォレスはスクリーン上で自分を描かれることを望まなかっただろう」
ということだった。
また、作家の遺産や作品を自分たちのもののように守ろうとする人々もいる。
ウォレスを直接知らず、遺産にも関係がない、ただのファンでさえそうすることがある。
⸻
ポンソルトは言う。
「多くの人がデイヴィッドを深く大切に思っていることは理解しています。私たちは何も知らずに作ったわけではありません。この映画を金儲けのために作ったわけではない。もちろんお金のためでもない。私たちはデイヴィッド・フォスター・ウォレスを愛しています。願いは、より多くの人が彼の作品を読むことです。」
⸻
作家が衝撃的な死を遂げたことを考えると、不快感を覚える人がいるのも理解できる。
しかし、文学者の自殺というものは決してウォレスだけの特殊な例ではない。
『End of the Tour』は、ウォレスが、おそらく最も力を発揮していた時期を描いている。
彼の死は遠い影として存在しているだけだ。
リプスキーの本は、5日間のインタビュー記録がほぼそのまま収録されている。
その中でウォレスはこう語る。
「作家は他の人より頭がいいわけじゃないと思う。ただ、彼らは自分の愚かさや混乱の中に、より説得力を持ってしまうんだと思う。」
そしてすぐにこう付け加える。
「でも今の言い方も、結局は音のいい言葉になるように僕が構成しているんだけどね。」
⸻
これは、ウォレスが自分の名声や、自分が作られるイメージとの間に、どれほど居心地の悪く複雑な関係を持っていたかを示しているとも言える。
「公の人物としてできる唯一の望みは、自分について物語を作る人たちが、自分に対してある種の敬意を持っていることです。そして、この場合、それは確かにそうだったと思います。」
⸻
有名人として、アイゼンバーグ自身も、自分について何が書かれるかを完全にはコントロールできないことを知っている。
実際、彼は以前、自分が不快に感じた記事を書いたインタビュアーに電話をした。
その記者は、その記事には皮肉なトーンがあったことを認めたという。
「自分がそこまで注目されるほどの価値があるとは思えなかったんです。それに、僕は特別に物議を醸すようなことをしていたわけでもありませんでした。」
Jesse Eisenberg read the script for “End of the Tour” shortly after doing an interview “that I knew was not going to say nice things about me or characterize me in a way that I would want to be characterized.”
The film, from director James Ponsoldt (“The Spectacular Now”), is an adaptation of journalist David Lipsky’s book that recounts five days in 1996 with David Foster Wallace during the promotion of the author’s epic comic novel “Infinite Jest.”
Eisenberg plays the then-30-year-old Lipsky, a novelist himself with modest but limited success, who was working at Rolling Stone. He persuades his editor to send him to interview Wallace (Jason Segel) in Indiana, where the novelist taught at a small college. The publication of “Infinite Jest” made Wallace, then 34, a literary celebrity, with major publications lauding him as the voice of his generation and a genius.
After reading the script for “End of the Tour” by Pulitzer Prize-winning playwright Donald Margulies, Eisenberg “thought it would be interesting to play this guy who was not this innocuous interviewer but is kind of going there to expose somebody.”
While a prolonged conversation between a couple of literary guys doesn’t sound like the stuff of great cinema, “End of the Tour” becomes a fascinating fencing match between the two, punctuated by humor and pathos. A quasi-road-trip movie, it juxtaposes silly and mundane concerns — they share Pop Tarts and junk food — with dark and confessional moments.
If people know anything about Wallace, it’s that he wrote a big book — 1,079-pages — and hanged himself in 2008 at 46, observes Eisenberg. The author was an eccentric figure. His photos often show him wearing granny glasses, his long hair wrapped in a bandana. His writing was electric, trippy, with descriptive passages, seductive and unexpected, for better or worse a singular voice.
The choice of Segel, mostly known for comedies, to play Wallace engendered some protests on the Internet, but the actor proves riveting in his portrayal. Ponsoldt thinks it is ridiculous to box Segel into one category, pointing out that comic talents like Robin Williams and Tom Hanks proved to be great dramatic actors.
Ponsoldt also has high praise for his other star, Eisenberg, comparing him to Dustin Hoffman and Gene Hackman, “guys you wouldn’t think could become leading men.”
A fast talker (a New York City native) and ready with a quip, Eisenberg, 31, says he and Segel had “a nice camaraderie even though the relationship of the characters in the movie is often contentious.”
A playwright and short story writer himself, the actor was excited to see the script from Margulies.
“I knew the characters would have a real emotionally complicated life, that there would be good dialogue, that the scenes were more than three lines long. How often do you get that chance to do something like that?”
Next up for Eisenberg is a Woody Allen film slated to begin shooting in California in August, and then a book tour for his first collection of short stories, “Bream Gives Me Hiccups.” He’s got two more movies coming out this year — “American Ultra” and “Louder Than Bombs” — and next year will be seen as the arch-villain Lex Luthur in “Batman v. Superman: Dawn of Justice.”
“The scenes I have in the Batman movie are so interesting and compelling,” says Eisenberg, who finds positives in working in both big films and smaller ones like “End of the Tour.”
The actor credits Ponsoldt as “an unusual director with keen insight into how to work with actors.” Eisenberg adds he was impressed with how Ponsoldt could make scenes more dramatic than he thought at first. “There becomes these life-or-death stakes in what is seemingly casual interaction.”
Lipsky, as it turned out, never had to write the Rolling Stone article. He published his interviews in book form after the author’s death. In his afterward to “Although of Course You End Up Becoming Yourself,” he acknowledges his own insecurities during their time together.
Interestingly, Wallace — a veteran magazine writer himself — was far more experienced with the interviewing process. It’s easy to see how acutely aware the author was of how everything he said could be (mis)interpreted, parsed, repackaged, etc. etc. He was a wordsmith after all.
Like the presence of a camera, a running tape recorder alters reality. In that sense, the two were performing for each other, the director thinks, but also feels Wallace was “trying to reveal himself,” while trying to come to grips with his sudden celebrity. “He was an inherently guarded person, probably self-editing the way writers and thoughtful neurotic people do,” says Ponsoldt.
“End of the Tour,” which received positive reviews when screened at the Sundance Film Festival, has drawn objections from some camps, notably from the David Foster Wallace Literary Trust, his widow and some of his editors, none of whom took part in the making of the film.
The reasons are complicated. For some it comes down to saying Wallace would not want to be portrayed on screen. There are others who are proprietary about the author’s legacy and writings, even those who are just fans and never knew him and have no stake in his estate.
“I understand that a lot of people care deeply about David,” says Ponsoldt. “We didn’t go into it naïvely. We didn’t make this movie for mercenary purposes, and it certainly wasn’t money. We love David Foster Wallace. Our hope is that more people read him.”
Some people might be uncomfortable since the author died in a shocking way, though literary suicides are hardly unique.
“End of the Tour” finds Wallace at, perhaps, the height of his powers, with his death a distant shadow. In Lipsky’s book, which is mostly the transcriptions of the five-days of interviews, Wallace says, “I don’t think writers are any smarter than other people. I think they more compelling in their stupidity, or in their confusion.” And then immediately admits, “I’m structuring that into a sound bite.”
That might be construed as the author having an uncomfortable, complicated relationship with his fame and image.
“The only hope you have as a public figure is the people making a story about you have some reverence for you, which in our case would be true,” says Eisenberg.
As a celebr
ソースコードがシンタックスハイライト付きで書けるようになった 記念に、はてな向けのコードを貼ってみるテスト。
動作例のスクリーンショット: Hatena Bookmark Search Filters
// ==UserScript== // @name Hatena Bookmark Search Filters // @description はてなブックマークの検索結果ページで、フィルタリング選択肢を改変します。 // @namespace knoa.jp // @include https://b.hatena.ne.jp/q/* // @version 2.0.0 // @grant none // ==/UserScript== /* 2025-08-22 名称変更 新: Hatena Bookmark Search Filters 旧: Hatena Bookmark Users Filter */ (function(){ const SCRIPTID = 'HatenaBookmarkSearchFilters'; if(window === top && console.time) console.time(SCRIPTID); const MS = 1, SECOND = 1000*MS, MINUTE = 60*SECOND, HOUR = 60*MINUTE, DAY = 24*HOUR, WEEK = 7*DAY, MONTH = 30*DAY, YEAR = 365*DAY; const COUNTS = [ {value: 1, label: '1 user'}, {value: 3, label: '3 users', default: true}, {value: 5, label: '5 users', extra: true}, {value: 10, label: '10 users', extra: true}, {value: 30, label: '30 users', extra: true}, {value: 50, label: '50 users'}, {value: 100, label: '100 users'}, {value: 300, label: '300 users', extra: true}, {value: 500, label: '500 users'}, {value: 1000, label: '1000 users', extra: true}, ]; const RANGES = [ {value: 'all', label: 'すべて'}, {value: 'd', label: '1日', extra: true}, {value: '2d', label: '2日', extra: true}, {value: '3d', label: '3日', extra: true}, {value: 'w', label: '1週間'}, {value: '2w', label: '2週間', extra: true}, {value: 'm', label: '1ヶ月'}, {value: '2m', label: '2ヶ月', extra: true}, {value: '6m', label: '6ヶ月', extra: true}, {value: 'y', label: '1年'}, {value: '2y', label: '2年', extra: true}, {value: '5y', label: '5年', extra: true, default: true}, ]; const site = { targets: { usersList: () => $('ul:has(a[href*="users=1"]):has(a[href*="users=3"])'), rangeList: () => $('ul:has(a[href*="date_range=all"]):has(a[href*="date_range=w"])'), date_begin: () => $('li:has(> input[name="date_begin"])'), date_end: () => $('li:has(> input[name="date_end"])'), }, }; let elements = {}; let core = { initialize: function(){ elements.html = document.documentElement; elements.html.classList.add(SCRIPTID); core.ready(); }, ready: function(){ core.getTargets(site.targets).then(() => { console.log(SCRIPTID, "I'm ready."); core.rebuildUsers(); core.rebuildRanges(); core.addStyle(); }).catch(e => { console.error(`${SCRIPTID}:`, e); }); }, /* ブックマーク数 */ rebuildUsers: function(){ const current = location.href.includes('users=') ? parseInt(location.href.match(/users=([0-9]+)/)[1]) : COUNTS.find(c => c.default).value; const ul = elements.usersList, template = ul.children[0].cloneNode(true); while(ul.children.length > 0) ul.removeChild(ul.lastElementChild); COUNTS.forEach(c => { const li = template.cloneNode(true); const a = li.querySelector('a'); a.classList.toggle('extra', c.extra === true); a.classList.toggle('is-current', c.value === current); a.href = a.href.replace(/(users)=1\b/, '$1=' + c.value); a.textContent = c.label; ul.appendChild(li); }); }, /* 期間指定 */ rebuildRanges: function(){ const current = location.href.includes('date_range=') ? location.href.match(/date_range=(all|[0-9]*[dwmy])/)[1] : (['date_begin=', 'date_end='].some(p => location.href.includes(p)) ? null : RANGES.find(r => r.default).value); const ul = elements.rangeList, template = ul.children[0].cloneNode(true); while(ul.children.length > 0) ul.removeChild(ul.lastElementChild); RANGES.forEach(r => { const li = template.cloneNode(true); const a = li.querySelector('a'); a.classList.toggle('extra', r.extra === true); a.classList.toggle('is-current', r.value === current); a.href = a.href.replace(/(date_range)=all/, '$1=' + r.value); a.textContent = r.label; ul.appendChild(li); }); /* 日付指定(から/まで) */ elements.date_begin.classList.toggle('is-current', location.href.match(/date_begin=[0-9-]+/) !== null); elements.date_end.classList.toggle('is-current', location.href.match(/date_end=[0-9-]+/) !== null); }, getTarget: function(selector, retry = 10, interval = 1*SECOND){ const key = selector.name; const get = function(resolve, reject){ let selected = selector(); if(selected === null || selected.length === 0){ if(--retry) return console.log(SCRIPTID, `Not found: ${key}, retrying... (${retry})`), setTimeout(get, interval, resolve, reject); else return reject(new Error(`Not found: ${selector.name}, I give up.`)); }else{ if(selected.nodeType === Node.ELEMENT_NODE) selected.dataset.selector = key;/* element */ else selected.forEach((s) => s.dataset.selector = key);/* elements */ elements[key] = selected; resolve(selected); } }; return new Promise(function(resolve, reject){ get(resolve, reject); }); }, getTargets: function(selectors, retry = 10, interval = 1*SECOND){ return Promise.all(Object.values(selectors).map(selector => core.getTarget(selector, retry, interval))); }, addStyle: function(name = 'style', d = document){ if(html[name] === undefined) return; if(d.head){ let style = createElement(html[name]()), id = SCRIPTID + '-' + name, old = d.getElementById(id); style.id = id; d.head.appendChild(style); if(old) old.remove(); } }, }; const html = { style: () => ` <style type="text/css" id="${SCRIPTID}-style"> /* ブックマーク数 */ ul[data-selector="usersList"]{ display: grid; grid-template-columns: 6em 6em auto; } ul[data-selector="usersList"] > li:nth-child(4), ul[data-selector="usersList"] > li:nth-child(7), ul[data-selector="usersList"] > li:nth-child(10){ grid-column-start: 1;/* 改行 */ } ul[data-selector="usersList"] > li:nth-child(10){ margin-right: -.5em;/* 1000 users のみ横幅を追加 */ } ul[data-selector="usersList"] > li > a.extra{ text-decoration: underline dotted; text-underline-offset: .25em; } /* 期間指定 */ ul[data-selector="rangeList"]{ display: grid; grid-template-columns: 4.5em 4.5em 4.5em auto; } ul[data-selector="rangeList"] > li:nth-child(1){ grid-column: 1 / -1;/* 全幅 */ } ul[data-selector="rangeList"] > li:nth-child(2), ul[data-selector="rangeList"] > li:nth-child(5), ul[data-selector="rangeList"] > li:nth-child(7), ul[data-selector="rangeList"] > li:nth-child(10){ grid-column-start: 1;/* 改行 */ } ul[data-selector="rangeList"] > li > a.extra{ text-decoration: underline dotted; text-underline-offset: .25em; } form.js-entrysearch-datepicker-form li.is-current{ background: #f6f7f8; color: #333; font-weight: 700; } form.js-entrysearch-datepicker-form input{ vertical-align: baseline; margin: 0 .25em 6px .5em !important; } /* セーフサーチが遅延読み込みされるせいで期間指定の位置がズレる問題を回避 */ .left-container{ display: flex; flex-direction: column; } .left-container .js-safe-search-div{ order: 999;/* いちばん最後でよい */ } .left-container ul.centerarticle-sub-navi:last-child{ margin-bottom: 0;/* flex化の影響で margin collapsing がなくなるのを埋め合わせる */ } </style> `, }; const $ = function(s, f = undefined){ let target = document.querySelector(s); if(target === null) return null; return f ? f(target) : target; }; const $$ = function(s, f = undefined){ let targets = document.querySelectorAll(s); return f ? f(targets) : targets; }; const createElement = function(html = '<div></div>') { const policy = createElement.policy ??= trustedTypes.createPolicy(SCRIPTID, {createHTML: s => s}); const template = document.createElement('template'); template.innerHTML = policy.createHTML(html); return template.content.firstElementChild; }; core.initialize(); if(window === top) console.timeEnd(SCRIPTID); })();
テスト用:
https://b.hatena.ne.jp/q/%E3%81%AF%E3%81%A6%E3%81%AA%20?target=all&sort=recent
ボクシングのThe DayがPPV数でキックボクシングのThe Matchを上回ったという報道を見て、The Matchを実現した榊原さんが怒るのは当たり前。
The Matchの真似をしてThe Dayという興行名をつければ、両興行が比較されるのは当然なので、The Dayのプロモーターが知らんぷりをするなら、The Match を侮辱していると受け取られてもしょうがない。
The Dayのプロモーターは、The DayのPPV数を公開してあとは報道に任せるのが一番スマートだが、そうしないなら、興行の名前を真似たThe Match側に相応のリスペクトを示すのが常識的対応。
最高の技術戦が見られたとは言え、井上・中谷戦は団体を背負うもの同士の試合ではないし、そもそも中谷はチャンピオンでさえない。あくまでボクシングの枠の中の試合。
The Matchはそれぞれの団体(会社)を背負うチャンピオン同士が共通ルールでガチンコの試合をしたのだから、試合の重みはThe Dayとはそもそも種類が違って比較にならない。
The Dayと比べられるのは、去年の大リーグのワールドシリーズのドジャース対ブルージェイズの最終戦とか、それぞれのスポーツの枠内の試合。
本当に(社会的な評価として)全てを得るか失うか、どちらかの結果しかなかった(中谷のように負けて評価を上げるとかはあり得なかった)The MatchをThe Dayと比較するのは、失礼。
こう考えると、通常のボクシングのタイトルマッチにすぎないのに、軽々しくThe Matchの名前を真似て、The Dayという興行名をつけるべきではなかった。他にもつけられる名前はあったでしょ。
// ==UserScript==
// @namespace http://tampermonkey.net/
// @version 2026-05-03
// @match https://booklive.jp/search/keyword/keyword/*
// @grant GM_getValue
// @grant GM_setValue
// ==/UserScript==
// ==UserScript==
// @namespace http://tampermonkey.net/
// @version 2026-05-04
// @grant GM_getValue
// @grant GM_setValue
// ==/UserScript==
(function() {
'use strict';
const STORAGE_KEY = 'buttonPosition';
function isMobilePage() {
if (document.querySelector(
'meta[name="apple-mobile-web-app-title"][content="ブックライブ"]'
)) {
return true;
}
return false;
}
const btn = document.createElement('button');
btn.textContent = 'bl-pager下端へ移動';
btn.style.position = 'fixed';
btn.style.zIndex = 99999;
btn.style.opacity = '0.9';
document.body.appendChild(btn);
const saved = GM_getValue(STORAGE_KEY, null);
if (saved && saved.left && saved.top) {
} else {
}
// ---- クリック処理 ----
btn.addEventListener('click', e => {
e.preventDefault();
e.stopPropagation();
return;
}
// ---- ここから変更:PC版は2番目の .bl-pager.page_nav を使う ----
let target;
if (isMobilePage()) {
console.log('スマホ版として処理');
target = document.querySelector(
'.bk_pageing.section_content.bl-pager.clearfix'
);
} else {
console.log('PC版として処理');
const targets = document.querySelectorAll('.bl-pager.page_nav');
// 2番目を使用
target = targets[1];
}
if (!target) {
alert('対象の bl-pager が見つかりません');
return;
}
// ---- ここまで変更:PC版は2番目の .bl-pager.page_nav を使う ----
const rect = target.getBoundingClientRect();
// 要素下端のページ全体基準座標
const absoluteBottom = rect.bottom + window.scrollY;
// 要素下端をウィンドウ下端へ一致
const destination = absoluteBottom - window.innerHeight;
window.scrollTo({
behavior: 'auto'
});
});
// ---- ドラッグ処理 ----
let dragging = false;
let startX = 0;
let startY = 0;
let offsetX = 0;
let offsetY = 0;
btn.addEventListener('mousedown', e => {
dragging = true;
startX = e.clientX;
startY = e.clientY;
const rect = btn.getBoundingClientRect();
offsetX = e.clientX - rect.left;
offsetY = e.clientY - rect.top;
e.preventDefault();
});
document.addEventListener('mousemove', e => {
if (!dragging) return;
if (
Math.abs(e.clientX - startX) > 5 ||
Math.abs(e.clientY - startY) > 5
) {
}
btn.style.left = `${e.clientX - offsetX}px`;
btn.style.top = `${e.clientY - offsetY}px`;
}
});
document.addEventListener('mouseup', () => {
if (!dragging) return;
dragging = false;
GM_setValue(STORAGE_KEY, {
});
}
});
})();
まともに指示すればAIはこれぐらいお手の物に出せるようになったな。
手動でスクロールしてるとつい事故って下の方のランダム表示のおすすめ作品欄が目に入る位置までスクロールしちゃって、ランダムだから後で気になったとしてもまた見れるとは限らないからとスクショが増える羽目になっていたのが、これで防げるようになった。
dorawiiより
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA512 https://anond.hatelabo.jp/20260504175957# -----BEGIN PGP SIGNATURE----- iHUEARYKAB0WIQTEe8eLwpVRSViDKR5wMdsubs4+SAUCafhgDQAKCRBwMdsubs4+ SDc1AQCfvf159m/85ngaE1SU7DnOiPC03RG4l9qI2Ce8Bp0j7QEAzIp52/FQfpXI vmRTMM52g1VEwry0pmg+4p0QQZlkEAk= =FTvE -----END PGP SIGNATURE-----
「はじけよう」は、2001年6月1日にリリースされたCHARCOAL FILTERの6枚目のシングル。 ...
日本語が使われる、または日本人が多いsubredditの人数を見て、日本の人口億1.2000人を基にすると、Redditを利用している日本人は0.01%以下だと言えます。これは他の国と比べても非常に少ないと考えられます。
なぜこのような状況になっていると思いますか?
日本人の多くがRedditを使うには年齢が高すぎるからでしょうか? すでに他のSNSが日本人の関心を独占しているからでしょうか? Redditのルールや雰囲気が日本人の価値観に合わないからでしょうか? 皆さんの意見を楽しみにしています!
Looking at the number of people on subreddits where Japanese is used or where there are many Japanese people, based on Japan's population of 120 million, we can say that less than 0.01% of Japanese people use Reddit. This is considered very low compared to other countries.
Why do you think this is the case?
Is it because most Japanese are too old to use Reddit? Is it because other social networking sites already dominate the Japanese interest? Is it because Reddit's rules and atmosphere do not match Japanese values? I look forward to your opinions!
大学は女子学生の割合が60%にまで上昇し、同時に進歩的な単一文化へと変貌を遂げた。若い女性が最も信頼する大学は、世界観が形成される時期に、深刻な反対を受けることなく、単一のイデオロギーを植え付けている。
FIREのキャンパス言論調査は、このパターンを明確に示しています。学生は自己検閲を行い、意見を表明することに不安を感じ、容認できる意見に群がっています。これは女性に限ったことではありませんが、現在、女性は男性よりも高等教育に深く関わっており、女性が優勢な分野(人文科学、社会科学、教育学、人事学)は、最もイデオロギー的に均質化しています。
同じことを信じる仲間に囲まれた4年間。教授陣も皆同じ考え。読書リストも一つの方向を指し示す。意見の相違は珍しいことではなく、社会的に罰せられる。受け入れられる意見をパターンマッチングし、それを実践することを学ぶ。
その後、彼女たちは女性優位の分野、例えば人事、メディア、教育、医療、非営利団体へと進み、そこでも単一文化が続く。18歳から35歳まで、多くの女性は尊敬する人から継続的な反対意見を出されることはなく、フィードバックループは永遠に続く。
男性は異なる道を歩んだ。貿易、エンジニアリング、金融、軍事。合意よりも結果が重視される分野。意見の相違が許容され、報われる分野。単一文化に捕らわれなかったのは、捕らわれている組織に属していなかったからだ(主に追い出されたためだが、それはまた別の話だ)。
Universities flipped to 60% female while simultaneously becoming progressive monoculture. The institution young women trust most, during the years their worldview forms, feeds them a single ideology with no serious opposition.
FIRE's campus speech surveys show the pattern clearly: students self-censor, report fear of expressing views, cluster toward acceptable opinions. This isn't unique to women, but women are more embedded in higher education than men now, and the fields they dominate (humanities, social sciences, education, HR) are the most ideologically uniform.
Four years surrounded by peers who all believe the same thing. Professors who all believe the same thing. Reading lists pointing one direction. Disagreement is not even rare, it's socially punished. You learn to pattern-match the acceptable opinions and perform them.
Then they graduate into female-dominated fields: HR, media, education, healthcare, non-profits, where the monoculture continues. From 18 to 35, many women never encounter sustained disagreement from people they respect. The feedback loop never breaks.
Men took different paths. Trades. Engineering. Finance. Military. Fields where results matter more than consensus. Fields where disagreement is tolerated or even rewarded. The monoculture didn't capture them because they weren't in the institutions being captured. (mostly because they were kicked out of them, but that's a different piece)
はてなブックマークの増田一覧の、さらに「すべて」(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.1.2026.07.14.0036 // ==/UserScript== /* 2.1.2026.03.25.0023 正規表現に対応。10 users 以上の人気記事を除外判定。 2.0.2026.01.28.0015 ポイント制を導入。 1.0.2025.12.09.0000 公開。 */ (function(){ const SCRIPTID = 'HatenaBookmarkAnondFilter'; console.time(SCRIPTID); const FILTERED = 'filtered';/* フィルタ該当要素クラス */ const CHECKED = 'checked';/* 二重チェック回避フラグ */ const USERS = 10;/* 人気記事なら誤検知やスパム解説などの可能性があるので除外する */ const POPULAR = 'popular';/* 人気記事クラス */ const ONCE = 1, AP = 2, INTERVAL = 3;/* 適用タイミング */ const NGWORDS = {/* 合計100ポイントで非表示判定(ただし1つの記事内で同じワードが複数使われても1度しか加算しない) */ '100': [/* 即NG確定ワード */ 'dorawii', 'あおやまちゃん', 'アオヤマチャン', 'ボスマン', '大学たいてい', 'なんぴょん', 'れめくん', 'れめきゅん', 'えめくん', 'るまさん', 'るまおねえちゃん', '眠りの民', 'リュックサック野郎', 'boushi_inst', 'hakaikami', 'Rekyu', 'iloveootaku_2', 'osaka-sirokichi',/* 電気通信大学たいてい鉄道研究会れめくん(頻出) */ /*A-G*/'a9w8ru6fqyxqfv9', 'admirail_togo', 'akibakko6348', 'alf1974al', 'amatukiseiru', 'anapgoeson', 'aoi_mizuho', 'asapgoeson', 'asupgo', 'asupgoeson', 'avoid_bds_kk', 'b6jbpsji91ieigt', 'bmi22yo', 'boushi_instrail', 'boushi_ob', 'buscholarx', 'bw0531', 'chihiro_love415', 'circlecavok', 'disney1007cla', 'dora22sibuya', 'donkotrain', 'ecotosk', 'electlone', 'factomodachi', 'fft_dareka', 'gmhtcyznf_abc', 'goesonanap', 'gyudon_honmono', /*H-N*/'h13_yokohama', 'h2twi', 'H2TWR', 'hamaishogo1111', 'haru_mofumoffu', 'hermitv8', 'hide1798038', 'hirabiscus', 'hinolovelove', 'hnmk0127_03', 'inaken17_', 'inte235dy', 'ixtabes', 'jamcombatge', 'kawachiasukanew', 'kaoru_ai1991', 'keio9730F', 'kiha2228', 'kihatena200', 'koreanlabsfc', 'koyounoyooko', 'kqlex1500', 'kunugiyamaosake', 'kurakamasan', 'kurotamaxxx', 'kt_ruma_1372', 'kt_up_date', 'lightningreen77', 'luckyyusha', 'mamadoll_kun', 'matya_uec', 'michee_n', 'minamihinawot', 'miniminicot', 'minori0151', 'mizunyanpanda', 'monkichi_22', 'mugen_08i', 'mukoroku651', 'nakano6409', 'nanpyong', 'new_oer', 'nimouec', 'NoName_thUFO', 'norannnde', /*O-U*/'oreizmmiporin', 'orenotanoshimi', 'osaka_sirokichi', 'papepoco', 'pasotokon', 'pm95uq', 'portrail', 'reme_kun', 'ruin_2002', 'rx00shiratama03', 's03_amurtk2731', 'sacchan03110319', 'sacchanenjoy', 'seishinyamate_', 'seisu_bot', 'senanana_cos', 'shinano_115', 'shineleaf1372', 'Shirasagi494', 'shop_bullet', 'shurimpy', 'soroisoroi', 'sui_pm95uq', 'sweidan821858', 'taiyaki_level2', 'takao_straight5', 'taking0000', 'tarotaromusic1', 'tc201_501', 'thomas_returnee', 'tocarbarn', 'tokusatsu_fan_0', 'toshikimiyazaki', 'train_magician', 'tx9y2cpwdz27255', 'u2fap5u4zw57811', 'uec15take', 'uecdaisuki', 'UECert', 'uecrail', /*V-Z_0-9*/'vampire_mio', 'vbdmnwefknmxsdm', 'vp20th', 'wafue', 'wakasato_', 'walkingniwatori', 'wataameexpress', 'ya4975349616894', 'yayoiulc', 'ymbk_arisa', 'yms_uec16', 'yorozun', 'yuuya_1104_uec', '__________ob', '_chocorail_', '_doitforthewin_', '_toeshin', '_unigmo', '100mph_no_yuuki', '16887y', '169_D51_protect', '205musashino', '2969364x', '2rtkvn34il2783', '36kyo', '86lilxw1', /(?!(.)\1{19,})(.{20,})\2{2}/,/* 20文字以上の文を3回以上繰り返す */ /(?!(.)\1{39,})(.{80,}).*\2/,/* 80文字以上の文が2回以上現れる */ /^ https:\/\/tadaup\.jp/m,/* 冒頭限定 */ 'テクウヨ', '自己放尿', ' ーーーーーーーーーーーーーーーーーーーー', 'https://anond.hatelabo.jp/20260107144223',/* AI問答貼り付け増田 */ '†噛み締めて行こうな†', 'https://anond.hatelabo.jp/20250826121213#', '困難女性(コンジョ)', '困難女性(コンジョ)', 'コンジョ自警団', 'ボディオブナレッジ', ' Boost', ' Cash', ' Coin Master', ' Free', ' Follower', ' Generator', ' Gift', ' Guide',/* 英文スパム */ 'altyazili', 'assistir', 'bgsub', 'dublado', 'espanol', 'espaol', 'fhd', 'filme', 'filmi', 'filmul', 'filmyzilla', 'full-hd', 'fullhd', 'fullstory', 'gratis', 'izle', 'minh', 'motchill', 'pelicula', 'phim', 'portugues', 'portugus', 'subthai', 'subtitrat', 'thaidub', 'vietsub', 'vostfr', ], '90': [/* ほぼNG */ 'megalodon.jp', 'tadaup.jp', '鉄道研究会', '鉄研', '通勤特快', '不正乗車', 'こども料金', '性慾', '穢い', 'エッタ', 'オソソ', 'キセル', 'uec', ], '10': [/* NG */ '電気通信大学', '電通大', '駿河台大学', '武蔵野美術大学', '武蔵美', '大学院', '学生課', '教務課', '朝鮮', '統合失調症', 'ケド。', ], }; 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 => { if(parseInt(li.querySelector('span.entrylist-contents-users > a > span').textContent) >= USERS) li.classList.add(POPULAR); else li.querySelector('li.entrylist-contents-category > a').textContent = li.dataset.matches; }, `.${FILTERED}:not(.${POPULAR}){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)]){ switch(true){ case(typeof word === 'string' && text.includes(word.toLowerCase())): case(word instanceof RegExp && word.test(text)): total += score; matches.push(word); if(total >= 100){ e.classList.add(FILTERED); e.dataset.matches = matches.join(', '); if(modifier) modifier(e); return; } break; } } } }); }; /* 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:not(.popular){ display: block !important;/*上書き*/ opacity: .25 !important; } .filtered:not(.popular):hover{ opacity: .75 !important; } .filtered:not(.popular) 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
勝手ながら
// ==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 と連動させることを意図しています。
// ==UserScript== // @name meaningless sale dayo // @version 1.0 // @match https://www.amazon.co.jp/blackfriday* // ==/UserScript== (function () { 'use strict'; alert('意味のないセールだよ'); })();
// ==UserScript==
// @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';
}
});
})();
// ==UserScript==
// @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';
}
});
})();
これはdorawiiもしくはmegalodonを含む投稿を非表示にするけど、
const KEYWORDS = ['dorawii', 'megalodon'];の部分を変えたり追加すれば好きな言葉に変えられるよ
dorawiiがタイトルに「dorawii」と入れなくなったので、本文にdorawiiがある投稿も非表示にする必要が出てきました。
ただAIに聞いたんだけど、CSSでは無理でJavaScriptならできると言われました。
そのJavaScriptを下に載せます。
// ==UserScript==
// @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 posts = document.querySelectorAll(POST_SELECTOR);
posts.forEach(post => {
// 投稿内の本文が含まれる要素(ここでは投稿全体を本文と見なす)のテキストを取得
const textContent = post.textContent || post.innerText;
if (textContent.includes(KEYWORD)) {
post.style.display = 'none';
}
});
})();
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
ストZERO3の「尿を便座におしつけ~る」くらいにモヤモヤしてた
答え
tHis is gOnNa bE a MaTcH TO REmember!
ここmatchがどうして聞き取れなくて「This is gonna be a must to remember! 」(この戦いは記憶に残るマストなものになる!)かと思ってしまったw
なんか安心したわ
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
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
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/
というわけでお漏らし文学をChatGPTに調べてもらった
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
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
でもある日、足が滑った。
その日、僕の中の何かが死んだ… そして僕の外側も、二度と綺麗にはならなかった。」
もう「しゃがむ」という選択肢すら許されない。
In Belgium, there’s an unwritten rule.
You hand the bar owner a euro,
he silently hands you the bathroom key.
you find a dark corner of the street instead.
— Blog post “The art of le pee in Belgium”
バーの店主に1ユーロ渡すと、無言でトイレの鍵を渡してくれる。
観光都市ブリュッセルでも、路上で立ち尽くす影が絶えない理由。
URL:saintfacetious.com
All cafés were closed, all public toilets locked.
I stumbled along the riverbank,
In the end, the Seine saw everything.
— れでぃっと / r/Paris
「深夜1時、セーヌ川沿い。
ワインと後悔で酔った体を引きずりながら歩き、
パリの夜は美しく、そして容赦ない。
観光客だけでなく、現地の人すらこうなることがある。
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
僕は全身びしょ濡れになって出てきた。」
水と泡まみれで出てきたマンチェスター・ユナイテッドファン。
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.
「アムステルダムには、半分だけ壁のある露天トイレ『pee curl』があった。
でも今はそのpee curlも減り、
I have MS.
Since the public toilets closed,
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
まあまあだな、また探す
____
https://profile.hatena.ne.jp/dorawii_bukuma/
はてなのサイト側で読み込まれているはずのrksトークンを生成する関数を直接叩く方法がどうしても分からず結局request処理を自分で書く方法ではなく自動でUI側の保存ボタンをクリックするという無難な方向に落ち着いた。
最初から後者の方法をとっていればもっと全然早く作れたのにというは所詮言い訳か。
とにかくスクリプトを公開しておく。
@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
{
"username": "",
"password": ""
}
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-----
木古 「きこ」ではなくて「きふる」って読むのね そして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.”
一度投稿したうえで別タブを開いてプログラム的(fetch)に送信してその別タブが閉じられる仕組み。
// ==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 });
})();
// ==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) => {
return new Promise((resolve, reject) => {
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) => {
e.preventDefault(); // HTML標準のsubmitをキャンセル
const bodyText = textarea?.value || '';
// reCAPTCHA トークンの取得
const recaptchaToken = await new Promise((resolve) => {
grecaptcha.enterprise.ready(() => {
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);
}
})();
const http = require('http'); const { exec } = require('child_process'); const querystring = require('querystring'); const server = http.createServer((req, res) => { if (req.method === 'GET' && req.url === '/ping') { res.writeHead(200); res.end('pong'); } else if (req.method === 'POST' && req.url === '/run-batch') { let body = ''; req.on('data', chunk => { body += chunk.toString(); }); req.on('end', () => { const parsed = querystring.parse(body); const value = parsed.value || 'default'; // 値を引数としてバッチに渡す exec(`C:\\Users\\hoge\\Desktop\\makesign.bat "${value}"`, { encoding: 'utf8' }, (err, stdout, stderr) => { 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, () => { 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 > nul goto signloop ) :postprocess powershell -nologo -command ^ "$header = '>|'; $footer = '|<'; $body = Get-Content '%outfile%' -Raw; Write-Output ($header + \"`r`n\" + $body + $footer)" powershell -nologo -command ^ "$header = '>|'; $footer = '|<'; $body = Get-Content 'signed.asc' -Raw; Set-Clipboard -Value ($header + \"`r`n\" + $body + $footer)" endlocal exit /b
#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-----