はてなキーワード: 文字列とは
勝手に tampermonkey とかに突っ込んで使ってヨロ
スクリプト保守とかするつもりないから、保守とかするつもりのあるパワーの溢れた人が
これ参考とかにしてもっとかっちょよくしたのを greasy fork あたりに公開してくれ
そしたら俺もそれ使う
localStorage.hatebu_ng_word_list に非表示のトリガーになる文字列を|区切りで登録する。
localStorage.hatebu_ng_word_list = "池田信夫|フェミ|弱者男性|やまもといちろう"
大なり小なり(>)が実体参照で表示されるのはよくわからん。使う人で適宜コードを直してくれ。
// ==UserScript== // @name はてブの一覧NG記事非表示 // @namespace http://tampermonkey.net/ // @version 0.1 // @description try to take over the world! // @author masuda // @match https://b.hatena.ne.jp/* // @icon https://www.google.com/s2/favicons?sz=64&domain=hatena.ne.jp // @grant none // ==/UserScript== (function() { 'use strict'; if (!localStorage.hatebu_ng_word_list) { return; } console.log("はてブの一覧NG記事非表示", localStorage.hatebu_ng_word_list); /* * 例: * localStorage.hatebu_ng_word_list = * "池田信夫|フェミ|弱者男性|やまもといちろう|togetter.com"; */ let words = localStorage.hatebu_ng_word_list.split('|').map(w => new RegExp(w)); function entryDelete(els) { els.forEach(el => { let hit = false; words.forEach(w => { hit = hit|| w.test(el.textContent); }); if (hit) { el.remove(); } }); } // entrylist-header-main li 1つ目のアイテム entryDelete(document.querySelectorAll('.entrylist-header-main > li')); // 2つ目以降の li アイテム entryDelete(document.querySelectorAll('.entrylist-item > li')); })();
^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])$
(出典:https://www.javadrive.jp/regex-basic/sample/index4.html)
0~255を判定している部分は置いといて、0~255を判定する記述が二つに分かれているのが気になる。
間に"."が挟まるからだ。
IPアドレスでは先頭に"."があってもおかしいし、末尾に"."があってもおかしい。
そのため、まずipアドレスの先頭三組("0~255".)だけドット付きで判定し、最後の一つだけドットなしで判定させている。
しかしそれなら、間に"."が挟まっても挟まらなくてもどっちでもマッチするように書いたあと、
その後、マッチした文字列の全体のフォーマットが正しいかを判定すればいいのではないか?
例えば以下のように。
^(?=^(\d+\.){3}\d+$)((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.?){4}$
0~255を判定する部分が一つになっている。にもかかわらず末尾に"."がついてしまうパターンがマッチしないのは
(または、途中の組で"."がつかないパターンがマッチしないのは)、
先読みで正しいフォーマットだけにマッチするようにして、それ以外の文字列をはじいているからだ。
正規表現が単純に短くなったし、「全体のフォーマットを判定する先読み部」と「各8bit+"\.?"の複雑な判定部分」に分けて描くことができた為、
先読みを使うことで、判定対象となる文字列の、「各部分判定の複雑さ」と「全体のフォーマットの判定の複雑さ」を分けることができる。
次に、0~255判定も複雑になっている。
正規表現では"0以上255以下"のような、複数桁にまたがる数の大小判定は出来ない。
例えば"2"と"10"では、数としては当然"10"が大きいが、辞書順で考えれば"2"の方が後に来る。
つまり、辞書順に並んだ文字列の中から、0~255の範囲だけにマッチするような正規表現を書かないといけない。そのため複雑になる。
しかし、0~255のような複数桁にまたがる数の大小判定ができないとしても、
全ての0~255に当てはまる厳密なパターンを書く((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9]))より、
ある程度絞れる範囲だけ書いた後、除外したいパターンを書いて弾いた方が、読みやすく、意図が理解しやすくなるのではないか?
例えは以下のように。
^(?![3-9]\d\d)(?!2[6-9]\d)(?!25[6789])(?!0\d)\d{1,3}$
まず、上記正規表現末尾の\d{1,3}で、1~3桁のあらゆる数字列にマッチする。
するように記述されている。
「元々の0~255判定部分より複雑じゃん」と言われればそうなのだが、読み方が異なっている。
元々の正規表現は、
「250以上255以下、または、240以上249以下、または、100以上199以下、または、0~99」という読み方になる。
「3桁の数字列にマッチする、かつ、300以上999以下を除外、かつ、260以上299以下を除外、かつ、256以上259以下を除外、かつ、先頭が"0"の2ケタ以上の数字列を除外」という読み方になる。
個人的な意見になるが、「~、または、~」の連言では、「対象となる範囲の全体感と、除外されるべきパターン」が見えないため、どういった範囲の話をしているのかがピンとこない(場合もある)。
一方、「~、かつ、~~を除外」では、「対象となる範囲の全体感と、除外されるべきパターン」がそのまま書かれているため、対象文字列のどの部分のことを言及しているのか(比較的)理解しやすい(、と言いたい)。
ちょっと無理がある言い方か。
二つの変更を組み合わせたIPアドレス(IPv4)を判定する正規表現は以下になる。
^(?=^(\d+\.){3}\d+$)((?![3-9]\d\d)(?!2[6-9]\d)(?!25[6789])(?!0\d)(\d{1,3})\.?){4}$
"(?=..."や、"(?!..."で始まる部分は、「除外または許可されるべきパターンを指定している部分」であり、文字マッチを行っていない。
先読み部分を無視して、文字とマッチする部分だけに注目すると、この正規表現がマッチしようとしている文字列の全体感が把握できる。
((\d{1,3})\.?){4}
・できる人もできない可能性がある(ライブコーディングによる緊張・ふだんの一般的な環境との相違・一般的な条件との相違)
・できない人もできてしまう可能性がある(実務とは関係ない問題であり、paizaとかやってる土日ちょっと勉強勢のほうがむしろよく見かける問題)
最悪だな。
まともなエンジニアを採用する方法はただ一つ。コーディング面接だよ。簡単な問題で良いんだよ。文字列を反転させなさいとか木を巡回しなさいとか。そんなんで良いんだよ。経験10年とか言ってるおっさんも意外とできないもんなんだよ。土日に勉強とか言ってるアホを弾く一番簡単な方法だよ。
割りとマジだよねと思う出来事をふと思い出したので書いてみる。
といっても後輩が俺の思ってもいないところでつまづいて、それに俺がカルチャーショックを受けたというだけの話。
問題の話なんだけど、とある有名サービスのJSON APIを叩いて呼び出し結果を手元のオブジェクトにマッピングするというただそれだけのコードを書くというもの。
普通に考えて一日もしないで出来ると思うような代物だけど、三日以上悩んで彼はそれでも出来なかった。
{ ..., "count": 10000000000000000000000000000000000000, ...}
という感じで多倍長整数がリテラルとして書かれているのを前提として受け取る仕様だった。
JavaScriptの通常の整数と違って、JSONの整数リテラルは仕様上大きさの制限の記載がないので、上のようなのも合法。
で、彼の使ってたプログラミング言語のオブジェクト から JSONの変換ライブラリが、多倍長整数を文字列("")としてシリアライズするような仕様なことがわかって、彼は行き詰まった。
そこで何をやり始めたかというと、JSONの整数がそのまま1000000000000000みたいにシリアライズされるライブラリ探し始めたんだけど、それは見つからないまま。
というわけで「増田さん、詰まってるんですけど……」と言われて助け舟出すことになったはいいものの、彼のコード見るとJSONの抽象構文木クラスがそのまま使えるようだった。
なので、
String serialiaze(Ast.JsValue value) { return switch(value) { case Ast.JsNull nullValue-> "null"; case Ast.JsInt bigIntValue -> bigIntValue.toString(); case Ast.JsArray arrayValue -> arrayValue.stream().map(v -> serialize(v)).collect(Collectors.joining(", ", "[", "]")); // 他のJSONの木についても同様に処理 default -> throw new RuntimeException("cannot reach") }; }
1時間しない内にこんな感じのコード(言語はJavaじゃなかったけど、だいたいこういう感じ)を書いて無事問題解決。細かいタイポとかあるかもだけど、日記では確認してないのでそれはおいといて)。
結局、JSONの形が期待と違って、しかも既存のAPIじゃいいのがなかったのに延々API探すことしか出来なかったのが問題解決できなかった原因だけど、このくらいのは割りとちょこちょこある。
きっと、それから一週間放置しても問題解決できなかっただろうし、どうも同じチームの同僚も問題を解決できなかったようだった。
最近、APIは叩けるけど、そこでトラブルとどうにもならなくエンジニアにちょくちょく遭遇するんだけど、やっぱりもうちょっと基礎出来てないと駄目だなと思った出来事だった。
自動で安価をつけて返信するプログラムでもこんなに長く複雑になる(一部抜粋)
/**************************************
以下のCSV_DIR, FILE_PATHS, SETTINGSを書き換えてね。 <h3>o- *************************************/</h3>
//CSVファイルが置かれてるディレクトリのパス。投稿前にエラー出たら大体ここの設定ミス。 例:"C:\\Users\\sakuraimasahiro\\Documents\\iMacros\\Macros\\rentou\\";
'C:\\Users\\USER\\Desktop\\iMacros\\Macros\\rentou\\';
//ファイルのパス。CSVは絶対パスで、拡張子も必要。iimは相対パスでよく、拡張子不要。
const FILE_PATHS = {
textCsv: CSV_DIR + 'textNoAnker.csv',
//レス用投稿文が書かれたCSV。通常とレス用で分けないなら同じファイルを使えばいい。
replyTextCsv: CSV_DIR + 'textReply.csv',
};
baseWaitTime: 5,
//baseWaitTime+0~waitTimeRange(ランダム)だけ待つ
waitTimeRange: 5,
//連投しすぎだと忠告された場合に処理を一時停止させる時間(秒)
waitTimeForAvoidingPunishment: 60 * 30,
//メール
mail: 'sage',
//名前設定
name: '',
//以下、偽装ワッチョイ設定。浪人でワッチョイを非表示にしてるときだけtrueにしてね。
//妙なニックネーム(ワッチョイ、アウアウウーなど)をランダムで決めて付加するかどうか。true=付加する。false=付加しない。
//妙なニックネームの後に付く8桁の文字列をランダムで決めて付加するかどうか。
},
//アンカー無し投稿をするならtrue。しないならfalse。noAnkerPostかreplyPostのどちらかはtrueにすること(両方trueでもOK)。
//アンカー付き投稿(返信)をするならtrue。しないならfalse。もしnoAnkerPostとreplyPostの両方がtrueの場合、投稿は返信が優先され、返信対象が見つからなくなったらアンカー無し投稿をする。
//最初に取得するアンカー無し投稿文CSVファイルの行番号。もし返信用と同じCSVファイルを使うなら-1と入力。
noAnkerPostTextCsvStartRow: 1,
//最初に取得する返信用投稿文CSVファイルの行番号。もしアンカー無しと同じCSVファイルを使うなら-1と入力。
//テキストCSV/返信用テキストCSVの取得行が最終行に達したら最初の行まで戻るかどうか。true=戻る。false=マクロ終了。
//返信する場合、これより小さなレス番には返信しない。返信を投稿すると、この数値は前回の返信先のレス番に更新される。
minAnker: 895,
//返信する場合、名前に以下の文字列を含む投稿にアンカーをつけて返信する(ワッチョイやIPなど名前フィールドにあるものならなんでも可)。配列で複数指定可能。指定無しなら空配列([])。filterNamesとfilterNamesNotIncluded共に無指定ならレス番1から順に返信していく(minAnkerが設定されてればそこから順に)。以下のfilter系は全て併用可能。
//↑とは逆に、名前に以下の文字列を含まない投稿にアンカーをつけて返信する。↑と併用も可能。
//返信する場合、本文に以下の文字列を含む投稿にアンカーをつけて返信する。
filterText: ['自演かな', '自演わらわら', 'スクリプト使うの', '安価ガバ', '>>660', '自演で擁護', '最後' ,'あいうえお', 'かきくけこ', 'さしすせそ', 'なにぬねの', 'はひふへほ', 'まみむめも', 'やいゆえよ', 'やゆよ', 'らりるれろ', 'わいうえを', 'わをん', 'わいうえをん'],
},
//自分のIPアドレスの確認。VPNとかでIPを変更してマクロを動かしてるとき、突然VPNが作動しなくなってIPが元に戻ったときにマクロを止めるためのもの。
//以下の文字列が自分の現在のIPアドレスに含まれている場合、マクロを一時停止する。基本的に自分の本当のIPアドレスを入力。
},
//浪人設定。最後に動作を確認したのは5年くらい前で、今も同じように動作するかは、浪人を持ってないから確認できずわからない。
//浪人にログインしてるかどうかをチェックするかどうか。trueならする。falseならしない。trueにしていてもし浪人にログインしていないことを確認したらログインしにいく。
password: '1234',
},
};
/**************************************
設定箇所終わり。
https://info.5ch.net/index.php/%E6%9B%B8%E3%81%8D%E8%BE%BC%E3%82%81%E3%81%AA%E3%81%84%E6%99%82%E3%81%AE%E6%97%A9%E8%A6%8B%E8%A1%A8 <h3>o- *************************************/</h3>
/**************************************
・NULL演算子(??)は使えない。論理積(&&)は使える。
・オブジェクトの分割代入はできない。
・importはできない。 <h3>o- *************************************/</h3>
/**************************************
関数 <h3>o- *************************************/</h3>
/**
* ここから始まる。
*/
checkSettings();
var _TextCsvCursors = new TextCsvCursors(
SETTINGS.postSettings.noAnkerPostTextCsvStartRow > 0
? SETTINGS.postSettings.noAnkerPostTextCsvStartRow - 1
: SETTINGS.postSettings.noAnkerPostTextCsvStartRow,
SETTINGS.postSettings.textCsvLoop,
),
SETTINGS.postSettings.replyPostTextCsvStartRow > 0
? SETTINGS.postSettings.replyPostTextCsvStartRow - 1
: SETTINGS.postSettings.replyPostTextCsvStartRow,
SETTINGS.postSettings.textCsvLoop,
),
);
var _LoopStatuses = new LoopStatuses(0, SETTINGS.postSettings.minAnker);
const _MyPosterName = new MyPosterName({
name: SETTINGS.nameSettings.name,
});
const _ThreadUrl = openPromptThreadUrl();
//ループ
while (true) {
SETTINGS.ipSettings.checkIp && checkCurrentIpNotTheIp();
//スレを開く
openUrl(_ThreadUrl.fullUrlHttps());
//浪人にログインする設定なら、浪人にログインしているかどうかを確認し、していなければログインしにいく。
if (SETTINGS.roninSettings.checkLogin) {
}
}
if (SETTINGS.postSettings.replyPost) {
const targetAnkerNumber = createPostDOMList()
.filterPostnumberHigher(_LoopStatuses.currentMinAnker())
.filterByPostername(SETTINGS.postSettings.filterNames)
.filterByPosternameNotIncluded(
SETTINGS.postSettings.filterNamesNotIncluded,
)
.filterByText(SETTINGS.postSettings.filterText)
if (targetAnkerNumber !== null) {
const r = _TextCsvCursors.takeNextRowTextAsReply(targetAnkerNumber);
messageDisplay(`返信対象有り。アンカー先: ${targetAnkerNumber}`);
return {
...r,
updatedLoopStatuses:
_LoopStatuses.updateMinAnker(targetAnkerNumber),
};
}
}
if (SETTINGS.postSettings.noAnkerPost) {
//返信対象無し、或いは返信しない設定の場合。アンカー無し投稿文を作る。
const r = _TextCsvCursors.takeNextRowTextAsNoAnker();
messageDisplay('返信対象無し。アンカー無し投稿。');
return {
...r,
updatedLoopStatuses: _LoopStatuses,
};
}
return null;
})();
if (p) {
//投稿。
nickname: SETTINGS.nameSettings.nickname,
korokoro: SETTINGS.nameSettings.korokoro,
area: SETTINGS.nameSettings.area,
}),
SETTINGS.mail,
p.text,
);
//_TextCsvCursorsと_LoopStatusesを更新。
_TextCsvCursors = p.updatedTextCsvCursors;
_LoopStatuses = p.updatedLoopStatuses.incrementPostCount();
`投稿回数: ${_LoopStatuses.currentPostCount()}`,
`minAnker: ${_LoopStatuses.currentMinAnker()}`,
`今回アンカー無し投稿取得行: ${_TextCsvCursors.currentRows().noAnker}`,
`今回アンカー有り投稿取得行: ${_TextCsvCursors.currentRows().reply}`,
]);
} else {
`返信対象が現われるのを待機中...。`,
`投稿回数: ${_LoopStatuses.currentPostCount()}`,
`minAnker: ${_LoopStatuses.currentMinAnker()}`,
`今回アンカー無し投稿取得行: ${_TextCsvCursors.currentRows().noAnker}`,
`今回アンカー有り投稿取得行: ${_TextCsvCursors.currentRows().reply}`,
]);
}
wait(SETTINGS.baseWaitTime + randomRange(0, SETTINGS.waitTimeRange));
}
}
/**
* 投稿処理と投稿結果を見てリトライしたりマクロ終了したり。
* @param {string} serverName サーバー名
* @param {MyPosterName} _MyPosterName
* @param {string} postMail メール
*/
serverName,
postMail,
_MyText,
retryTimes = 0,
) {
const r =
retryTimes === 0
? new ValuesOfPost(serverName, _MyPosterName, postMail, _MyText).post(
postTo5chTread,
)
serverName,
postMail,
_MyText,
).postSubstring(retryTimes, postTo5chTread, postConfirm);
if (r) {
back();
return;
}
wait(7);
const error = createPostErrorMessage().analyze();
messageDisplay(error.message);
if (error.order === 'KILL') {
kill();
} else if (error.order === 'SKIP') {
return;
} else if (error.order === 'TRUNCATE') {
back();
serverName,
postMail,
_MyText,
retryTimes + 1,
);
} else if (error.order === 'WAIT') {
wait(SETTINGS.waitTimeForAvoidingPunishment);
serverName,
postMail,
_MyText,
retryTimes,
);
} else if (error.order === 'LOGIN') {
serverName,
postMail,
_MyText,
retryTimes,
);
}
return;
}
/**
* 現在のIPアドレスに、SETTINGS.ipSettings.avoidTheIpの値が含まれていないことを確認する。含まれていたらマクロを一時停止。
* @returns
*/
function checkCurrentIpNotTheIp() {
openUrl('https://www.cman.jp/network/support/go_access.cgi');
const _IpAdress = createIpAdressFromCMan();
if (_IpAdress.includes(SETTINGS.ipSettings.avoidTheIp)) {
pause('現在のIPに指定した値が含まれていることを確認。');
}
return;
}
/**
* @returns
*/
if (
SETTINGS.postSettings.noAnkerPost === false &&
SETTINGS.postSettings.replyPost === false
) {
return kill('設定エラー。noAnkerPostとreplyPost両方ともfalseになってる。');
}
if (
SETTINGS.postSettings.noAnkerPostTextCsvStartRow < 0 &&
SETTINGS.postSettings.replyPostTextCsvStartRow < 0
) {
return kill(
'設定エラー。noAnkerPostTextCsvStartRowとreplyPostTextCsvStartRow両方とも-1になってる。',
);
}
if (
SETTINGS.postSettings.noAnkerPostTextCsvStartRow === 0 ||
SETTINGS.postSettings.replyPostTextCsvStartRow === 0
) {
return kill(
'設定エラー。noAnkerPostTextCsvStartRow/replyPostTextCsvStartRowの初期値は-1或いは1以上で。',
);
}
}
/**
* 入力フォームを表示して入力されたスレのURLを受け取る。
*/
function openPromptThreadUrl() {
const url = prompt('スレURLを入力');
}
/**
* 開いてるスレのレス全て読み取ってPostListインスタンスを作って返す。
* 重すぎるので使うのやめ。どうやらインスタンスの大量生成が原因な模様。
*/
const posts = window.document.getElementsByClassName('post');
return new PostList(Array.from(posts).map((e) => new Post(e)));
}
/**
* 開いてるスレのレス全て取得してPostDOMListに格納して返す。
* @returns
*/
function createPostDOMList() {
const posts = window.document.getElementsByClassName('post');
for (let index = 0; index < posts.length; index++) {
//HTMLCollectionからElementを1つずつ抽出して配列に。
arrPostDOMList.push(posts.item(index));
}
return new PostDOMList(arrPostDOMList);
}
/**
* 開いてる投稿結果画面に表示されてるエラーを読み取ってPostErrorMessageインスタンスを作って返す。
*/
function createPostErrorMessage() {
window.document
5chの連投スクリプトが送ってくるグロ画像は、まあグロい。グロくなかったらグロ画像じゃなくなるからだ。人間がバーンしたり、ズギャーンしたり、バコーンしたりしてる画像がたくさんある。体感ではボコッが一番多い。
少し前になんJだかなんGだかで以前猛威を振るっていたのが、エロ画像スレを建てると見せかけてグロ画像を貼るというスクリプトだ。まあ、エロ画像とグロ画像は一文字違いだから大して変わらないかもしれない。
基本的に「巨」と「乳」をNGワードに入れるか性欲を捨てるかすれば簡単に回避できる荒らしだから、この害悪度の道を歩んだID腹筋スレみたいなのはあまり相手にされなかった。しかし俺は相手にした。「乳」をNGワードに入れることも性欲を捨てることもできなかったからだ。開いた。グロ画像だった。閉じた。開いた。グロ画像だった。閉じた。開いた。ID腹筋スレだった。一周回って安心感みたいなものを覚えながら、冷たい床に両手をついた。そんな風にして、流れ的に俺はグロ画像を踏みまくった。そして、「>>1 グロ」というレスを残した。
しかし、最初の数回の時点で、俺はスクリプトの異変に気付いた。スレタイ時点ではいつもと変わらず、「【悲報】おっぱいかけ算九九 乳の段」みたいなザ・電子ゴミって感じの益体もない文字列だった。しかしザ・電子ゴミって感じの益体もない文字列をタップした俺の目に入ってきたものは、グロ画像でもID腹筋スレでも、エロ画像でもなかった。
マグロだった。
紅とは言えないが赤とはいえるくらいの、少し薄い色をしたマグロの寿司が、スーパーか何かのトレイに入って、グロ画像の代わりに俺を見ていた。
目を擦ったしリロードもしたが、マグロはマグロのまま、低めの解像度で俺のスマホのディスプレイを占有していた。俺は仕方なく「>>1 マグロ」とレスして、その場を去った。
そのあとも、マグロは定期的に現れた。
スクリプトが何かの間違いを犯した、としか考えられなかった。俺が落ち込んでいる時も舞い上がっている時も、マグロは等しくそこに現れて、めちゃくちゃ美味そうというほどでもないが普通に食べられそうな外見で、俺に何かの感情を抱かせようとしてくる。その感情が何か知りたくても、俺は知ることができなくて、そのまま次のグロ画像ないしID腹筋スレへと旅立つしかなかった。
いつしか、俺はマグロを求めるようになっていた。やったエロ画像だと考えながら、あるいはどうせグロ画像だと考えながら開いたスレの>>1に存在するマグロを見た時の、あのどうしようもない感情に名前を付けたかった。十連ガチャを引いているような感覚もあった。いつしか、俺にとって「マグロは当たり」という考えが刷り込まれるようになった。スレタイがエロ画像である以上、当たりはエロ画像ということになり、そうなると三段論法でマグロはエロ画像ということになる。マグロはエロ画像だった。
あれから数か月、俺の観測範囲内では、偽装エロ画像スレを見ることもなくなってしまった。今のスクリプトは夏目漱石か何かから生成した支離滅裂な文章を連投したり、マグロ抜きのもっと厳選したグロ画像を連投したり、そういうことをするのに留まってしまった。けれども俺が夢の中とか、街角とか、ふと目に入ったネット広告とかで、ふと赤々としたマグロを発見した時に、何度でも去来する感情だけは、決して途絶えることもなく、延々と人格の中で残響している。
ああ、マグロ。
現在5chに出現するスクリプトには複数の種類があり、それぞれ特徴があります
ただし出現するスレッドは手動で決めているようで、野球スレには基本出現しない
よって「益虫ではないか」という意見もあるが、パート化しているウマ娘スレや遊戯王スレにも湧かないため「ただのオタクのなんじゃないか」とも言われている
住民は多少巻き込みをしてでも末尾dを丸ごと非表示にせざるを得ない状況になっている
ちなみにグロ画像の選定は過去に安価で「グロ」と書かれた画像から選んでいるため、ちょいちょいマグロの画像が混ざる
出現当初は書き込み数もおとなしめでグロスクリプトの影に隠れている存在であった
出現スレッドは完全にランダムであり、野球スレにも出現するスクリプトであるものの
「まぁ実況する分には無視できるしどうでもいいわ」という評価だった
なんG(J)民のアイデンティティである「やきう」を阻害するスクリプトと化している
末尾dだったりa(au回線)だったりM(貧乏回線)だったりする
画像は貼らないものの文字列の選定や出現傾向はグロスクリプトに似ているため同一人物による犯行ではないかと囁かれている
厄介なのは『末尾0』ということ
これは固定回線を示すIDなので丸ごと非表示にする対策を取るとほとんどのレスが見えなくなってしまう
こちらも末尾0
とにかく書き込み速度が半端ではないため出現するとスレッドは機能不全になる
出現するスレ傾向は未だ掴めず
/etc/systemd/system に corekeeperds.service って名前で以下の内容のファイルを作る。
[Unit] Description=Core Keeper Dedicated Server Documentation= [Service] Type=exec User=steam Group=steam TimeoutStartSec=0 Restart=always RestartSec=30s WorkingDirectory=/home/steam/Steam/steamapps/common/Core Keeper Dedicated Server/ ExecStartPre=/home/steam/steamcmd +login anonymous +app_update 1007 +app_update 1963720 +quit ExecStart=/usr/bin/env "/home/steam/Steam/steamapps/common/Core Keeper Dedicated Server/_launch.sh" SyslogIdentifier=CoreKeeperDS [Install] WantedBy=multi-user.target
※ ExecStart はバグっているので空白のあるパスを食わせるとうまく処理できない。なので /usr/bin/env 経由で文字列を食わせている。古の知識。
作ったら、
chmod 755 /etc/systemd/system/corekeeperds.service systemctl daemon-reload
して、完了。
あとは
systemctl start corekeeperds.service
でサービス開始。
systemdがプロセスを死活監視して、異常終了した場合(この辺の挙動は 後述の Note 参照)に勝手に再起動してくれるようになります。
状態は
systemctl status corekeeperds.service
で見て、
止めるときは
systemctl stop corekeeperds.service
で。(時間かかる)
Restart=on-failure で大丈夫だと思うのですが、うまく上がってこないときは always とかにした方がいいかもしれません。
Restart の各設定値の挙動については以下が詳しいです。
https://tex2e.github.io/blog/linux/systemd-restart-config
ぼっちなので、私のサーバーには Dedicated Server が落ちるほどプレイヤーが来ません。
なので Systemd への登録で、ちゃんと再起動するかどうかは確認できてない感じです。
出来なかったらごめんなさい。