generated at
100件以上のメッセージを取得する方法

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)); }).map(e => [e.id, e])); } 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 }); return array2Collection([messages, ...await Promise.all([backward, forward])].flatMap( e => [...e.values()] )); } 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 件のメッセージを取得して出力する
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'))

解説
MessageManager#fetchChannelLogsQueryOptionsを取るやつと揃えるつもりで書いた。tig
まずAPIの設計として before after around がある。
around というのは与えた引数の前後 limit/2 件のメッセージを取得するもの。
limit に偶数を与えると limit+1 件結果が返ってくる。
これらは択一で複数を与えることはできない。
before after は指定したIDのメッセージを含まない。
before after の場合は単純でAPIをその方向に繰り返し呼べば良い。
around は多少複雑で初回のリクエストを around で行ったあとその両端から続くメッセージを (limit-100)/2 件取得する。
件数は切り捨てで良い
実験すればわかるし考えてもわかる(
ここでは実装に再帰を用いている
古いメッセージがコレクションの後ろに来るようになっている
これは単に実装がそうなってるだけな気がしなくもないが気にしないことにする
つまりIDが小さいメッセージが先頭にくる
そうなるように sort している

関連
再帰関数を使う例も書きたいかな。/yuta0801/非同期タイマー再帰関数

井戸端
解説は私かく?yutoさん書く?tig
語彙力があまりないので伝わりにくい解説になってしまいそう(yuto0214w
それは私が書いてもそう。tig
というか本質的に難しいんだよこれ()tig
解説書くとしたらどこから説明したらいいんだろyuto0214w
aroundの再帰部分?でも見たらわかるやん()tig
あとは関数の中に関数を入れるという邪悪なテクニックについて?tig
yuto0214w
邪悪ですねyuto0214w.icon
いい感じに書き直してくれてもいいのよ?tig
D.js さんはどんな風にしてるんだろ
彼の仕事はdiscord apiを呼び出すことなので()tig
なるほどyuto0214w
会話になってて草octo-uro
Wikipedia の井戸端みたいなかんじyuto0214w
解説にあると邪魔なので解説を上にあげよう(tig
同時に作業するとこうなってしまうのか(yuto0214w
ところでsort順序逆だったかもしれねぇtig
逆だわtig
fixed(tig
sort をもう少し分かりやすくしたものがあってもよさそうyuto0214w
fetchMany って discord.js にもあるんだ(関数名)yuto0214w
内部実装としてあるはずtig(当然ここから持ってきた
なるほどねyuto0214w
forwardとbackwardを明示してみたtig
:+1: yuto0214w
終わった?octo-uro
しらん(tig
私はだいたい満足したtig
とてもよさそうyuto0214w
( 'ω')octo-uro
適当にアイコン作ってきたけどみずらocto-uro
こっそりforをpush(可変長引数)に変えた(tig
yuto0214w
砂場とかあると良いんですかね?tig
それくらい自分でproject作れば良い気がするtig
それはそうyuto0214w
ESのexportsとcommonjsのrequireを併用するなどの過ちを犯していたtig

というかこのプロジェクト全体の井戸端があっても良いか?tig
そうだね あってもよさそうyuto0214w