Fetch message history through the client REST layer
This commit is contained in:
@@ -40,6 +40,13 @@ mobile draft at 390px.
|
|||||||
- [x] C11 preview caching: Discord-hosted image/GIF previews (WebP 320px, hash-addressed, 5 MiB
|
- [x] C11 preview caching: Discord-hosted image/GIF previews (WebP 320px, hash-addressed, 5 MiB
|
||||||
cap, one attempt) run after every walk in both modes. Live run `...-20260916090722`:
|
cap, one attempt) run after every walk in both modes. Live run `...-20260916090722`:
|
||||||
`previewed: 2`; thumbs served over `/api/media/<scrape>/<item>/thumb` (200).
|
`previewed: 2`; thumbs served over `/api/media/<scrape>/<item>/thumb` (200).
|
||||||
|
- [x] C33 Puppet history pagination. The core's `message.fetch` read the client store, which
|
||||||
|
keeps only the newest messages plus the channel's first — every page was 10 messages and the
|
||||||
|
walk stopped after one page (a 10-message, 4-media scrape of an ancient group with thousands
|
||||||
|
of messages). It now fetches through the client's REST layer first (contiguous 100-message
|
||||||
|
pages, `transport: "rest"`), store as fallback. Rebuilt and restarted the managed client;
|
||||||
|
live re-run of channel `591000745164406784`: 21+ pages / 2,100+ messages and climbing, with
|
||||||
|
hundreds of images/videos being indexed.
|
||||||
- [x] C32 Group-chat clarity and puppet refresh. Group DM scrapes already worked (live-verified
|
- [x] C32 Group-chat clarity and puppet refresh. Group DM scrapes already worked (live-verified
|
||||||
against the running daemon: `zıbb & fred` and a 1:1 DM, backend `puppet`, done, previews
|
against the running daemon: `zıbb & fred` and a 1:1 DM, backend `puppet`, done, previews
|
||||||
cached); the Group chat tab had shown an empty list while the core was offline. It now says so
|
cached); the Group chat tab had shown an empty list while the core was offline. It now says so
|
||||||
|
|||||||
+1
-1
@@ -68,7 +68,7 @@ Event line:
|
|||||||
| `dm.list` | optional `limit` | Cached DM and group DM channels with recipients, icons, and avatars. Cache-only. |
|
| `dm.list` | optional `limit` | Cached DM and group DM channels with recipients, icons, and avatars. Cache-only. |
|
||||||
| `message.send` | `channelId`, `content` | `{ channelId, messageId, sent: true }` |
|
| `message.send` | `channelId`, `content` | `{ channelId, messageId, sent: true }` |
|
||||||
| `message.read` | `channelId`, `limit` | `{ cached, messages }`. Cache-only. Messages carry the author avatar, attachment metadata (id, filename, URL, content type, size, dimensions, duration), embeds (video, image, and thumbnail URLs plus their dimensions), sticker items, forwarded snapshots, a voice-message flag, `editedAt`, and `replyTo`. |
|
| `message.read` | `channelId`, `limit` | `{ cached, messages }`. Cache-only. Messages carry the author avatar, attachment metadata (id, filename, URL, content type, size, dimensions, duration), embeds (video, image, and thumbnail URLs plus their dimensions), sticker items, forwarded snapshots, a voice-message flag, `editedAt`, and `replyTo`. |
|
||||||
| `message.fetch` | `channelId`, `limit`, optional `before` or `after` | `{ fetched, messages }`. Fetches from Discord. Same message shape. |
|
| `message.fetch` | `channelId`, `limit`, optional `before` or `after` | `{ fetched, transport, messages }`. Fetches from Discord through the client's REST layer, so pages are full and contiguous; the client store (which keeps only the newest messages plus the channel's first) is the fallback. `transport` is `rest` or `store`. Same message shape. |
|
||||||
| `reaction.add` | `channelId`, `messageId`, `emoji` | `{ added: true }` |
|
| `reaction.add` | `channelId`, `messageId`, `emoji` | `{ added: true }` |
|
||||||
| `dm.send` | `userId`, `content` | `{ userId, channelId, sent: true }` |
|
| `dm.send` | `userId`, `content` | `{ userId, channelId, sent: true }` |
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ interface ScrapeBackend {
|
|||||||
| Backend | Implementation |
|
| Backend | Implementation |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `slopbot` | discord.js REST (`channels.messages.fetch`), bucket-aware client |
|
| `slopbot` | discord.js REST (`channels.messages.fetch`), bucket-aware client |
|
||||||
| `puppet` | The core action `message.fetch` over the bridge. Expired URLs refresh with `before: id+1`, which returns exactly the target message |
|
| `puppet` | The core action `message.fetch` over the bridge: the client's REST layer paginates (100/page, contiguous), with the client store as fallback. Expired URLs refresh with `before: id+1`, which returns exactly the target message |
|
||||||
|
|
||||||
The daemon picks `slopbot` when the channel is a guild channel and slopbot is configured and in the
|
The daemon picks `slopbot` when the channel is a guild channel and slopbot is configured and in the
|
||||||
guild; otherwise `puppet`. A scrape records the backend it used. The walk can resume on the same
|
guild; otherwise `puppet`. A scrape records the backend it used. The walk can resume on the same
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import {
|
|||||||
MessageStore,
|
MessageStore,
|
||||||
PresenceStore,
|
PresenceStore,
|
||||||
ReadStateStore,
|
ReadStateStore,
|
||||||
|
RestAPI,
|
||||||
UserStore,
|
UserStore,
|
||||||
VoiceStateStore
|
VoiceStateStore
|
||||||
} from "@vencord/types/webpack/common";
|
} from "@vencord/types/webpack/common";
|
||||||
@@ -961,6 +962,47 @@ export async function handlePuppetAction(action: string, params: Record<string,
|
|||||||
const before = params.before as string | undefined;
|
const before = params.before as string | undefined;
|
||||||
const after = params.after as string | undefined;
|
const after = params.after as string | undefined;
|
||||||
|
|
||||||
|
const inRange = (message: any) => {
|
||||||
|
if (after && compareIds(message.id, after) <= 0) return false;
|
||||||
|
if (before && compareIds(message.id, before) >= 0) return false;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const normalize = (list: any[]) => {
|
||||||
|
const seen = new Set<string>();
|
||||||
|
return list
|
||||||
|
.filter(message => inRange(message) && !seen.has(message.id) && seen.add(message.id))
|
||||||
|
.sort((a, b) => compareIds(a.id, b.id))
|
||||||
|
.slice(-Number(limit));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Preferred: the client's REST layer paginates properly. The store keeps only
|
||||||
|
// the newest messages plus the channel's first, so reading it back yields tiny,
|
||||||
|
// non-contiguous pages.
|
||||||
|
let messages: any[] | null = null;
|
||||||
|
let transport = "rest";
|
||||||
|
|
||||||
|
try {
|
||||||
|
const query: Record<string, string | number> = { limit: Number(limit) };
|
||||||
|
if (before) query.before = before;
|
||||||
|
if (after) query.after = after;
|
||||||
|
|
||||||
|
const response = await RestAPI.get({
|
||||||
|
url: `/channels/${channelId}/messages`,
|
||||||
|
query: { ...query }
|
||||||
|
});
|
||||||
|
const list = Array.isArray(response)
|
||||||
|
? response
|
||||||
|
: Array.isArray((response as any)?.body)
|
||||||
|
? (response as any).body
|
||||||
|
: null;
|
||||||
|
if (list) messages = normalize(list);
|
||||||
|
} catch {
|
||||||
|
messages = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (messages === null) {
|
||||||
|
transport = "store";
|
||||||
await fetchMessages({
|
await fetchMessages({
|
||||||
channelId,
|
channelId,
|
||||||
limit: Number(limit),
|
limit: Number(limit),
|
||||||
@@ -969,16 +1011,14 @@ export async function handlePuppetAction(action: string, params: Record<string,
|
|||||||
});
|
});
|
||||||
|
|
||||||
const all = MessageStore.getMessages(channelId)?._array ?? [];
|
const all = MessageStore.getMessages(channelId)?._array ?? [];
|
||||||
const filtered = all.filter(message => {
|
messages = normalize(all);
|
||||||
if (after && compareIds(message.id, after) <= 0) return false;
|
}
|
||||||
if (before && compareIds(message.id, before) >= 0) return false;
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
return ok({
|
return ok({
|
||||||
channelId,
|
channelId,
|
||||||
fetched: filtered.length,
|
fetched: messages.length,
|
||||||
messages: filtered.slice(-Number(limit)).map(mapMessage)
|
transport,
|
||||||
|
messages: messages.map(mapMessage)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user