100件以上のメッセージを取得する方法
code: js
const { Collection } = require("@discordjs/collection");
function array2Collection(messages) {
return new Collection(messages.slice().sort((a, b) => {
const a_id = BigInt(a.id);
const b_id = BigInt(b.id);
return (a_id > b_id ? 1 : (a_id === b_id ? 0 : -1));
}
module.exports = async function fetchMany(channel, options = { limit: 50 }) {
if ((options.limit ?? 50) <= 100) {
return channel.messages.fetch(options);
}
if (typeof options.around === "string") {
const messages = await channel.messages.fetch({ ...options, limit: 100 });
const limit = Math.floor((options.limit - 100) / 2);
if (messages.size < 100) {
return messages;
}
const backward = fetchMany(channel, { limit, before: messages.last().id });
const forward = fetchMany(channel, { limit, after: messages.first().id });
));
}
let temp;
function buildParameter() {
const req_cnt = Math.min(options.limit - messages.length, 100);
if (typeof options.after === "string") {
const after = temp
? temp.first().id : options.after
return { ...options, limit: req_cnt, after };
}
const before = temp
? temp.last().id : options.before;
return { ...options, limit: req_cnt, before };
}
const messages = [];
while (messages.length < options.limit) {
const param = buildParameter();
temp = await channel.messages.fetch(param);
messages.push(...temp.values());
if (param.limit > temp.size) {
break;
}
}
return array2Collection(messages);
}
使い方
fetchMany(<TextChannel | DMChannel | NewsChannel | Thread>, <?ChannelLogsQueryOptions>)
例
メッセージID 818529905184604180 より前 (818529905184604180 は含まれない) の 400 件のメッセージを取得して出力する
code: js
let messages = []
const fetchedMessages = await fetchMany(channel, {
limit: 400,
before: "818529905184604180"
})
fetchedMessages.forEach(msg =>
messages.push(${msg.author.username} | ${msg.content})
)
console.log(messages.join('\n'))
解説
まずAPIの設計としてbefore、after、aroundがある。
aroundというのは与えた引数の前後limit/2件のメッセージを取得するもの。
limitに偶数を与えるとlimit+1件結果が返ってくる。
これらは択一で複数を与えることはできない。
beforeとafterは指定したIDのメッセージを含まない。
beforeとafterの場合は単純でAPIをその方向に繰り返し呼べば良い。
aroundは多少複雑で初回のリクエストをaroundで行ったあとその両端から続くメッセージを(limit-100)/2件取得する。
件数は切り捨てで良い
実験すればわかるし考えてもわかる(
古いメッセージがコレクションの後ろに来るようになっている
これは単に実装がそうなってるだけな気がしなくもないが気にしないことにする
つまりIDが小さいメッセージが先頭にくる
そうなるようにsortしている
関連
井戸端
解説は私かく?yutoさん書く?tig.icon
語彙力があまりないので伝わりにくい解説になってしまいそう(yuto0214w.icon
それは私が書いてもそう。tig.icon
というか本質的に難しいんだよこれ()tig.icon
解説書くとしたらどこから説明したらいいんだろyuto0214w.icon
aroundの再帰部分?でも見たらわかるやん()tig.icon
あとは関数の中に関数を入れるという邪悪なテクニックについて?tig.icon
yuto0214w.icon
邪悪ですねyuto0214w.icon
いい感じに書き直してくれてもいいのよ?tig.icon
D.js さんはどんな風にしてるんだろ
彼の仕事はdiscord apiを呼び出すことなので()tig.icon
なるほどyuto0214w.icon
会話になってて草octo-uro.icon
解説にあると邪魔なので解説を上にあげよう(tig.icon
同時に作業するとこうなってしまうのか(yuto0214w.icon
ところでsort順序逆だったかもしれねぇtig.icon
逆だわtig.icon
fixed(tig.icon
sort をもう少し分かりやすくしたものがあってもよさそうyuto0214w.icon
fetchMany って discord.js にもあるんだ(関数名)yuto0214w.icon
内部実装としてあるはずtig.icon(当然ここから持ってきた
なるほどねyuto0214w.icon
forwardとbackwardを明示してみたtig.icon
:+1: yuto0214w.icon
終わった?octo-uro.icon
しらん(tig.icon
私はだいたい満足したtig.icon
とてもよさそうyuto0214w.icon
( 'ω')octo-uro.icon
適当にアイコン作ってきたけどみずらocto-uro.icon
こっそりforをpush(可変長引数)に変えた(tig.icon
草yuto0214w.icon
それくらい自分でproject作れば良い気がするtig.icon
それはそうyuto0214w.icon
ESのexportsとcommonjsのrequireを併用するなどの過ちを犯していたtig.icon
というかこのプロジェクト全体の井戸端があっても良いか?tig.icon そうだね あってもよさそうyuto0214w.icon