Add the slopbot scaffold and discord.js dependency, unused
This commit is contained in:
@@ -18,7 +18,8 @@ media-scrape/
|
||||
configurable with `VESKSLOP_MEDIA_DB_DIR` and never committed. See
|
||||
[../docs/media.md](../docs/media.md).
|
||||
|
||||
The daemon imports this folder. discord.js is declared in `poc/package.json`, like the MCP SDK.
|
||||
The daemon imports this folder. `media-scrape/` is its own pnpm workspace root (like `poc/`), so
|
||||
`discord.js` is declared in `media-scrape/package.json` and resolves from `media-scrape/node_modules`.
|
||||
|
||||
## Docs
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "veskslop-media-scrape",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"description": "Media archive module for Veskslop: scraper engine, store, Web UI routes, and slopbot.",
|
||||
"dependencies": {
|
||||
"discord.js": "^14.27.0"
|
||||
}
|
||||
}
|
||||
Generated
+218
@@ -0,0 +1,218 @@
|
||||
lockfileVersion: '9.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
dependencies:
|
||||
discord.js:
|
||||
specifier: ^14.27.0
|
||||
version: 14.27.0
|
||||
|
||||
packages:
|
||||
|
||||
'@discordjs/builders@1.14.1':
|
||||
resolution: {integrity: sha512-gSKkhXLqs96TCzk66VZuHHl8z2bQMJFGwrXC0f33ngK+FLNau4hU1PYny3DNJfNdSH+gVMzE85/d5FQ2BpcNwQ==}
|
||||
engines: {node: '>=16.11.0'}
|
||||
|
||||
'@discordjs/collection@1.5.3':
|
||||
resolution: {integrity: sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ==}
|
||||
engines: {node: '>=16.11.0'}
|
||||
|
||||
'@discordjs/collection@2.1.1':
|
||||
resolution: {integrity: sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
'@discordjs/formatters@0.6.2':
|
||||
resolution: {integrity: sha512-y4UPwWhH6vChKRkGdMB4odasUbHOUwy7KL+OVwF86PvT6QVOwElx+TiI1/6kcmcEe+g5YRXJFiXSXUdabqZOvQ==}
|
||||
engines: {node: '>=16.11.0'}
|
||||
|
||||
'@discordjs/rest@2.6.3':
|
||||
resolution: {integrity: sha512-wvOylxNYJkwKjctS/Mn5GP1w9r3/rzyH+ThD1JlAca6zEdlHs8QWBBUQJpU5Q+W6DoIj/Ljh1IPlZs7hTU+UAg==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
'@discordjs/util@1.2.0':
|
||||
resolution: {integrity: sha512-3LKP7F2+atl9vJFhaBjn4nOaSWahZ/yWjOvA4e5pnXkt2qyXRCHLxoBQy81GFtLGCq7K9lPm9R517M1U+/90Qg==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
'@discordjs/ws@1.2.3':
|
||||
resolution: {integrity: sha512-wPlQDxEmlDg5IxhJPuxXr3Vy9AjYq5xCvFWGJyD7w7Np8ZGu+Mc+97LCoEc/+AYCo2IDpKioiH0/c/mj5ZR9Uw==}
|
||||
engines: {node: '>=16.11.0'}
|
||||
|
||||
'@sapphire/async-queue@1.5.5':
|
||||
resolution: {integrity: sha512-cvGzxbba6sav2zZkH8GPf2oGk9yYoD5qrNWdu9fRehifgnFZJMV+nuy2nON2roRO4yQQ+v7MK/Pktl/HgfsUXg==}
|
||||
engines: {node: '>=v14.0.0', npm: '>=7.0.0'}
|
||||
|
||||
'@sapphire/shapeshift@4.0.0':
|
||||
resolution: {integrity: sha512-d9dUmWVA7MMiKobL3VpLF8P2aeanRTu6ypG2OIaEv/ZHH/SUQ2iHOVyi5wAPjQ+HmnMuL0whK9ez8I/raWbtIg==}
|
||||
engines: {node: '>=v16'}
|
||||
|
||||
'@sapphire/snowflake@3.5.5':
|
||||
resolution: {integrity: sha512-xzvBr1Q1c4lCe7i6sRnrofxeO1QTP/LKQ6A6qy0iB4x5yfiSfARMEQEghojzTNALDTcv8En04qYNIco9/K9eZQ==}
|
||||
engines: {node: '>=v14.0.0', npm: '>=7.0.0'}
|
||||
|
||||
'@types/node@26.5.1':
|
||||
resolution: {integrity: sha512-CzNm2FezW4VR/LjG6yUdiEgLE/rAQ9Slj5gCu/C2VrdcW7I0ahNZ8DRbHT7zOZ6r3ONgd/bsQIeSaoDGrd1C6g==}
|
||||
|
||||
'@types/ws@8.18.1':
|
||||
resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==}
|
||||
|
||||
'@vladfrangu/async_event_emitter@2.4.7':
|
||||
resolution: {integrity: sha512-Xfe6rpCTxSxfbswi/W/Pz7zp1WWSNn4A0eW4mLkQUewCrXXtMj31lCg+iQyTkh/CkusZSq9eDflu7tjEDXUY6g==}
|
||||
engines: {node: '>=v14.0.0', npm: '>=7.0.0'}
|
||||
|
||||
discord-api-types@0.38.55:
|
||||
resolution: {integrity: sha512-ytuaRTzdnHUCXJ6KjL9MrItQX0xKncBKEeYI1Bst4+ud47eejH3cG6gaesYakjpPcUhh68XYS2YwGq3mmujFsA==}
|
||||
|
||||
discord.js@14.27.0:
|
||||
resolution: {integrity: sha512-qHbFlFG2N7y3LjPySYsL6A1+BnX6bkTVgo842EX0CqVPk/KTMwZkojPHEXKsQUpWZNyz5BISNHK1cPpQw0+m4A==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
fast-deep-equal@3.1.3:
|
||||
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
|
||||
|
||||
lodash.snakecase@4.1.1:
|
||||
resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==}
|
||||
|
||||
lodash@4.18.1:
|
||||
resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==}
|
||||
|
||||
magic-bytes.js@1.13.1:
|
||||
resolution: {integrity: sha512-x5sn4UX2k5gCWlcfmoFwG4TPie8+dctESyqOBdhB5p6MsgWXdBKGmt9nXPObj/JI50TTL928lc5Yt1WntMn1bw==}
|
||||
|
||||
ts-mixer@6.0.4:
|
||||
resolution: {integrity: sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA==}
|
||||
|
||||
tslib@2.8.1:
|
||||
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
||||
|
||||
undici-types@8.9.0:
|
||||
resolution: {integrity: sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==}
|
||||
|
||||
undici@6.28.1:
|
||||
resolution: {integrity: sha512-zWpdTVD54H48CIybL0rWQ3ukpb9d23wM7eH5RtfdmeP70cWHNjtfo7P4vZX+5CoDcO53J4Pu5uXp7lNfjc6DRA==}
|
||||
engines: {node: '>=18.17'}
|
||||
|
||||
ws@8.21.3:
|
||||
resolution: {integrity: sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
peerDependencies:
|
||||
bufferutil: ^4.0.1
|
||||
utf-8-validate: '>=5.0.2'
|
||||
peerDependenciesMeta:
|
||||
bufferutil:
|
||||
optional: true
|
||||
utf-8-validate:
|
||||
optional: true
|
||||
|
||||
snapshots:
|
||||
|
||||
'@discordjs/builders@1.14.1':
|
||||
dependencies:
|
||||
'@discordjs/formatters': 0.6.2
|
||||
'@discordjs/util': 1.2.0
|
||||
'@sapphire/shapeshift': 4.0.0
|
||||
discord-api-types: 0.38.55
|
||||
fast-deep-equal: 3.1.3
|
||||
ts-mixer: 6.0.4
|
||||
tslib: 2.8.1
|
||||
|
||||
'@discordjs/collection@1.5.3': {}
|
||||
|
||||
'@discordjs/collection@2.1.1': {}
|
||||
|
||||
'@discordjs/formatters@0.6.2':
|
||||
dependencies:
|
||||
discord-api-types: 0.38.55
|
||||
|
||||
'@discordjs/rest@2.6.3':
|
||||
dependencies:
|
||||
'@discordjs/collection': 2.1.1
|
||||
'@discordjs/util': 1.2.0
|
||||
'@sapphire/async-queue': 1.5.5
|
||||
'@sapphire/snowflake': 3.5.5
|
||||
'@vladfrangu/async_event_emitter': 2.4.7
|
||||
discord-api-types: 0.38.55
|
||||
magic-bytes.js: 1.13.1
|
||||
tslib: 2.8.1
|
||||
undici: 6.28.1
|
||||
|
||||
'@discordjs/util@1.2.0':
|
||||
dependencies:
|
||||
discord-api-types: 0.38.55
|
||||
|
||||
'@discordjs/ws@1.2.3':
|
||||
dependencies:
|
||||
'@discordjs/collection': 2.1.1
|
||||
'@discordjs/rest': 2.6.3
|
||||
'@discordjs/util': 1.2.0
|
||||
'@sapphire/async-queue': 1.5.5
|
||||
'@types/ws': 8.18.1
|
||||
'@vladfrangu/async_event_emitter': 2.4.7
|
||||
discord-api-types: 0.38.55
|
||||
tslib: 2.8.1
|
||||
ws: 8.21.3
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- utf-8-validate
|
||||
|
||||
'@sapphire/async-queue@1.5.5': {}
|
||||
|
||||
'@sapphire/shapeshift@4.0.0':
|
||||
dependencies:
|
||||
fast-deep-equal: 3.1.3
|
||||
lodash: 4.18.1
|
||||
|
||||
'@sapphire/snowflake@3.5.5': {}
|
||||
|
||||
'@types/node@26.5.1':
|
||||
dependencies:
|
||||
undici-types: 8.9.0
|
||||
|
||||
'@types/ws@8.18.1':
|
||||
dependencies:
|
||||
'@types/node': 26.5.1
|
||||
|
||||
'@vladfrangu/async_event_emitter@2.4.7': {}
|
||||
|
||||
discord-api-types@0.38.55: {}
|
||||
|
||||
discord.js@14.27.0:
|
||||
dependencies:
|
||||
'@discordjs/builders': 1.14.1
|
||||
'@discordjs/collection': 1.5.3
|
||||
'@discordjs/formatters': 0.6.2
|
||||
'@discordjs/rest': 2.6.3
|
||||
'@discordjs/util': 1.2.0
|
||||
'@discordjs/ws': 1.2.3
|
||||
'@sapphire/snowflake': 3.5.5
|
||||
discord-api-types: 0.38.55
|
||||
fast-deep-equal: 3.1.3
|
||||
lodash.snakecase: 4.1.1
|
||||
magic-bytes.js: 1.13.1
|
||||
tslib: 2.8.1
|
||||
undici: 6.28.1
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- utf-8-validate
|
||||
|
||||
fast-deep-equal@3.1.3: {}
|
||||
|
||||
lodash.snakecase@4.1.1: {}
|
||||
|
||||
lodash@4.18.1: {}
|
||||
|
||||
magic-bytes.js@1.13.1: {}
|
||||
|
||||
ts-mixer@6.0.4: {}
|
||||
|
||||
tslib@2.8.1: {}
|
||||
|
||||
undici-types@8.9.0: {}
|
||||
|
||||
undici@6.28.1: {}
|
||||
|
||||
ws@8.21.3: {}
|
||||
@@ -0,0 +1,2 @@
|
||||
# Keep media-scrape its own workspace root, separate from the Vesktop app.
|
||||
packages: []
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Slopbot — the dedicated bot core for media scraping.
|
||||
*
|
||||
* Scaffold only: nothing imports this yet. The scrape backend lands at
|
||||
* checkpoint C4 (see ../CURRENT.md). Setup, limits, and security: docs/slopbot.md.
|
||||
*/
|
||||
|
||||
import { readFileSync } from "node:fs";
|
||||
|
||||
export interface SlopbotConfig {
|
||||
/** Kill switch. Off unless VESKSLOP_SLOPBOT_ENABLED=1 and a token exists. */
|
||||
enabled: boolean;
|
||||
/** Bot token. Never logged, never committed. */
|
||||
token: string | null;
|
||||
/** Application ID, used to render the invite URL. */
|
||||
clientId: string | null;
|
||||
}
|
||||
|
||||
interface SlopbotStateFile {
|
||||
token?: string;
|
||||
clientId?: string;
|
||||
}
|
||||
|
||||
function readStateFile(path: string): SlopbotStateFile {
|
||||
try {
|
||||
return JSON.parse(readFileSync(path, "utf8")) as SlopbotStateFile;
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
/** Read the slopbot settings from the environment, then from the state file. */
|
||||
export function loadSlopbotConfig(
|
||||
env: NodeJS.ProcessEnv = process.env,
|
||||
stateFilePath = env.VESKSLOP_SLOPBOT_STATE
|
||||
): SlopbotConfig {
|
||||
const state = stateFilePath ? readStateFile(stateFilePath) : {};
|
||||
const token = env.VESKSLOP_SLOPBOT_TOKEN ?? state.token ?? null;
|
||||
const clientId = env.VESKSLOP_SLOPBOT_CLIENT_ID ?? state.clientId ?? null;
|
||||
|
||||
return {
|
||||
enabled: env.VESKSLOP_SLOPBOT_ENABLED === "1" && token !== null,
|
||||
token,
|
||||
clientId
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Slopbot entry point.
|
||||
*
|
||||
* Scaffold only: the daemon does not import this yet. Checkpoint C4 adds the
|
||||
* scrape backend; docs/slopbot.md is the module contract.
|
||||
*/
|
||||
|
||||
import type { Client } from "discord.js";
|
||||
import { loadSlopbotConfig, type SlopbotConfig } from "./config.js";
|
||||
|
||||
/** Kept as a type so the discord.js dependency is type-checked before C4 uses it. */
|
||||
export type SlopbotClient = Client;
|
||||
|
||||
export { loadSlopbotConfig, type SlopbotConfig };
|
||||
|
||||
/** View Channels + Read Message History. The bot reads only; it never sends. */
|
||||
const PERMISSIONS = String((1 << 10) | (1 << 16));
|
||||
|
||||
/** The invite URL the Bot tab renders once a client ID is configured. */
|
||||
export function slopbotInviteUrl(clientId: string): string {
|
||||
return `https://discord.com/oauth2/authorize?client_id=${encodeURIComponent(
|
||||
clientId
|
||||
)}&scope=bot%20applications.commands&permissions=${PERMISSIONS}`;
|
||||
}
|
||||
Reference in New Issue
Block a user