Skip to content

Rewrite: Replace discord.js implementation with raw Discord API/Gateway client and media layer#3

Open
SyntaxGhost404 wants to merge 1 commit into
experimentalfrom
codex/inspect-and-rewrite-repository-codebase
Open

Rewrite: Replace discord.js implementation with raw Discord API/Gateway client and media layer#3
SyntaxGhost404 wants to merge 1 commit into
experimentalfrom
codex/inspect-and-rewrite-repository-codebase

Conversation

@SyntaxGhost404

Copy link
Copy Markdown
Owner

Motivation

  • Replace the previous discord.js-based bot with a lightweight implementation that speaks directly to Discord REST v10 and the Gateway to reduce dependencies and run a custom raw client.
  • Consolidate media fetching and scraping logic into a dedicated src/media.js layer to handle multiple NSFW providers and improve control over timeouts and buffering.
  • Simplify deployment by moving to a single src/ entrypoint, adding a command registration script, and trimming legacy files and large archived studies from the active tree.

Description

  • Removed the old discord.js-based application (many commands/, events/, utils/, index.js, handler code, docs and other legacy artifacts) and added a new src/ implementation that implements a raw Gateway client in src/index.js and a small REST helper in src/discord.js.
  • Introduced a modular media fetching layer in src/media.js that centralizes HTTP helpers and provider-specific fetch functions with timeouts and guarded error handling, and added src/commands.js to map slash command definitions to media operations and refresh handlers.
  • Replaced verbose logging utilities with src/logger.js, added src/register-commands.js to register commands via raw REST, updated package.json (new scripts: start, register, check) and trimmed dependencies to ws only, with corresponding package-lock.json updates.
  • Documentation and repo housekeeping: updated README.md to reflect the rewrite, added archive/README.md, updated .gitignore to include .env, *.log, and .DS_Store, and removed numerous legacy docs, studies, and the old LICENSE file from the active branch.

Testing

  • No automated tests were executed as part of this change.

Codex Task

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 308e2412bd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/media.js
async function fetchAss(id = null) { return guarded('obutts.ru', async () => { const ep = id !== null && !Number.isNaN(Number(id)) ? `http://api.obutts.ru/butts/get/${id}/` : 'http://api.obutts.ru/butts/0/1/random/'; const d = await getJson(ep); if (!d?.length) return null; return { id: d[0].id, url: `http://media.obutts.ru/${d[0].preview}` }; }); }
async function fetchPurrbot(endpoint) { return guarded('purrbot.site', async () => { const d = await getJson(endpoint); return d && !d.error && d.link ? { url: d.link } : null; }); }
async function fetchWaifu(endpoint) { if (process.env.WAIFU_PICS === 'false') return null; return guarded('waifu.pics', async () => { const d = await getJson(endpoint); return d?.url ? { url: d.url } : null; }); }
async function fetchABD(endpoint) { return guarded('n-sfw.com', async () => { const d = await getJson(endpoint); const target = d?.url_japan || d?.url_usa; if (!target) return null; return { url: target }; }); }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Upload ABD media instead of embedding its CDN URL

For every command whose pool selects abd (including ABD-only commands such as /breeding, /legs, /milf, etc.), this now returns only the url_japan/url_usa CDN URL. The previous provider fetched that media into a buffer because Discord's media proxy could not reliably embed the n-sfw S3 URL, and the command path uploaded that buffer as an attachment; with this rewrite runMedia only sets image: data.url, so these commands can acknowledge successfully while displaying a broken image. Please preserve the buffer/upload path for ABD responses.

Useful? React with 👍 / 👎.

Comment thread src/media.js
async function fetchNekosV4(endpoint) { return guarded('nekosapi.com', async () => { const d = await getJson(endpoint); return Array.isArray(d) && d[0]?.url ? { url: d[0].url, source: 'nekosv4' } : null; }); }
async function fetchNekoBot(type) { return guarded('nekobot.xyz', async () => { const d = await getJson(`https://nekobot.xyz/api/image?type=${encodeURIComponent(type)}`, { headers: { Authorization: '015445535454455354D6' } }); return d?.success && d?.message ? { url: d.message, source: 'nekobot' } : null; }); }
async function fetchSexcom(niche) { return guarded('sex.com', async () => { const page = Math.floor(Math.random() * 10) + 1; const url = new URL('https://www.sex.com/portal/api/gifs/search'); Object.entries({ 'sexual-orientation': 'straight', order: 'likeCount', search: niche, page, limit: 40 }).forEach(([k,v]) => url.searchParams.set(k, v)); const d = await getJson(url, { headers: { 'User-Agent': 'Mozilla/5.0' } }); const arr = d?.data || []; if (!arr.length) return null; const item = arr[Math.floor(Math.random() * arr.length)]; let path = item.uri; if (!path) return null; if (path.endsWith('.webp')) path = path.slice(0, -5) + '.gif'; return { id: item.id, url: `https://imagex1.sx.cdn.live${path}` }; }); }
async function fetchPorngifs() { return guarded('porngifs.com', async () => { const id = Math.floor(Math.random() * 39239) + 1; return { id: String(id), url: `https://cdn.porngifs.com/img/${id}`, source: 'porngifs' }; }); }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Verify Porngifs URLs before reporting success

When /gif chooses the Porngifs.com branch, this function now immediately returns a random cdn.porngifs.com/img/<id> URL without fetching it, checking the status/size, or using the DNS/SNI workaround the old provider used before it returned a buffer. Any missing, blocked, or over-limit random ID is therefore treated as a successful fetch and sent to Discord as an embed, leaving users with a broken GIF instead of retrying or showing an error.

Useful? React with 👍 / 👎.

Comment thread src/index.js
if (sessionId && resumeGatewayUrl) {
ws = new WebSocket(`${resumeGatewayUrl}?v=10&encoding=json`);
ws.on('open', () => send(6, { token: process.env.BOT_TOKEN, session_id: sessionId, seq }));
ws.on('message', raw => { const p = JSON.parse(raw.toString()); if (p.s !== null && p.s !== undefined) seq = p.s; if (p.op === 10) { clearInterval(heartbeatTimer); heartbeatTimer = setInterval(heartbeat, p.d.heartbeat_interval); heartbeat(); } if (p.op === 0 && p.t === 'INTERACTION_CREATE') handleInteraction(p.d); });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Handle invalid Gateway resumes

When this resume connection receives an Invalid Session (op === 9) or Reconnect (op === 7) payload, it currently ignores it because the compact handler only processes Hello and dispatch events. After a session timeout or invalid sequence, Discord sends Invalid Session and expects the client to disconnect and identify again; leaving this socket open without identifying means the bot stops receiving interactions until the process is restarted.

Useful? React with 👍 / 👎.

Comment thread src/commands.js
async function runGif(interaction, update=false){ await d.defer(interaction, update); const n=Math.floor(Math.random()*4); let data, link; if(n===0){ data=await media.fetchSexcom(pick(NICHES)); link=data?.id?`https://www.sex.com/pin/${data.id}/`:data?.url; } else if(n===1){ data=await media.fetchPorngifs(); link=data?.url; } else if(n===2){ data=await media.fetchNekoBot('pgif'); link=data?.url; } else { data=await media.fetchPorngifsTv(); link=data?.url; } if(!data||data.error) return d.edit(interaction,{embeds:[{title:'❌ ▸ Error',description:'Failed to fetch image.',color:0xff0000}],components:[]}); return d.edit(interaction,{embeds:[d.embed({title:'🔞 ▸ NSFW GIF',image:data.url,user:interaction.member?.user||interaction.user})],components:d.buttons('refresh_gif',link||data.url)}); }
async function runSolo(interaction, update=false, saved=null){ await d.defer(interaction, update); const type=saved||d.getOption(interaction,'type'); const sources= type==='male' ? [source('purrbot',{endpoint:'https://purrbot.site/api/img/nsfw/solo_male/gif'})] : type==='female' ? [source('purrbot',{endpoint:'https://purrbot.site/api/img/nsfw/solo/gif'}),source('abd',{endpoint:'https://api.n-sfw.com/nsfw/masturbation'}),source('nekosv4',{endpoint:'https://api.nekosapi.com/v4/images/random?limit=1&rating=explicit,suggestive&tags=masturbating'})] : [source('purrbot',{endpoint:'https://purrbot.site/api/img/nsfw/solo/gif'}),source('abd',{endpoint:'https://api.n-sfw.com/nsfw/masturbation'}),source('purrbot',{endpoint:'https://purrbot.site/api/img/nsfw/solo_male/gif'}),source('nekosv4',{endpoint:'https://api.nekosapi.com/v4/images/random?limit=1&rating=explicit,suggestive&tags=masturbating'})]; const data=await fetchSource(pick(sources)); if(!data||data.error) return d.edit(interaction,{embeds:[{title:'❌ ▸ Error',description:'Failed to fetch image.',color:0xff0000}],components:[]}); return d.edit(interaction,{embeds:[d.embed({title:'🔞 ▸ NSFW Solo Image',image:data.url,user:interaction.member?.user||interaction.user})],components:d.buttons(`refresh_solo_${type||'random'}`,data.url)}); }
async function runThreesome(interaction, update=false, saved=null){ await d.defer(interaction, update); const chosen=saved||d.getOption(interaction,'type'); const type=chosen||pick(['fff','ffm','mmf','nekosv4']); const src=type==='nekosv4'?source('nekosv4',{endpoint:'https://api.nekosapi.com/v4/images/random?limit=1&rating=explicit,suggestive&tags=threesome'}):source('purrbot',{endpoint:`https://purrbot.site/api/img/nsfw/threesome_${type}/gif`}); const data=await fetchSource(src); if(!data||data.error) return d.edit(interaction,{embeds:[{title:'❌ ▸ Error',description:'Failed to fetch image.',color:0xff0000}],components:[]}); return d.edit(interaction,{embeds:[d.embed({title:'🔞 ▸ NSFW Threesome Image',image:data.url,user:interaction.member?.user||interaction.user})],components:d.buttons(`refresh_threesome_${chosen||'random'}`,data.url)}); }
async function runUtility(name, interaction){ if(name==='ping') return d.callback(interaction,d.CALLBACK.CHANNEL_MESSAGE_WITH_SOURCE,{embeds:[{title:'🏓 ▸ Pong',description:`Gateway and REST bot is online. Uptime: ${Math.round(process.uptime())}s`,color:d.randomColor()}]}); if(name==='invite'){ const url=`https://discord.com/oauth2/authorize?client_id=${process.env.CLIENT_ID}&scope=bot%20applications.commands&permissions=2147485696`; return d.callback(interaction,d.CALLBACK.CHANNEL_MESSAGE_WITH_SOURCE,{embeds:[{title:'🔗 ▸ Invite',description:`[Add this bot to your server](${url})`,color:d.randomColor()}]}); } if(name==='help') return d.callback(interaction,d.CALLBACK.CHANNEL_MESSAGE_WITH_SOURCE,{embeds:[{title:'📖 ▸ Command Directory',description:Object.keys({...COMMANDS,gif:1,solo:1,threesome:1}).sort().map(c=>`\`/${c}\``).join(' • '),color:d.randomColor()}]}); }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restore the invite's required bot permissions

The generated invite now requests permission value 2147485696, which only grants SEND_MESSAGES and USE_APPLICATION_COMMANDS and omits the permissions the old invite explicitly requested for this bot's responses, especially EMBED_LINKS and ATTACH_FILES. In servers or channels where those permissions are not already inherited from @everyone, users who add the bot through /invite can run commands but the bot's embed/media responses will fail or be stripped.

Useful? React with 👍 / 👎.

Comment thread src/index.js
if (packet.t === 'INTERACTION_CREATE') await handleInteraction(packet.d);
});
ws.on('close', (code, reason) => { clearInterval(heartbeatTimer); log.warn(`Gateway closed (${code}) ${reason}`); setTimeout(start, 5000); });
ws.on('error', err => log.error(`Gateway error: ${err.message}`));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Detect missed heartbeat ACKs

This treats heartbeat ACKs as a no-op and never tracks whether an ACK arrived before the next heartbeat. If the gateway TCP connection becomes zombied without a close event, Discord expects clients to terminate and resume; this implementation will keep sending heartbeats on the dead socket and stop receiving interactions until an external restart.

Useful? React with 👍 / 👎.

Comment thread src/index.js
const user = interaction.member?.user || interaction.user;
const name = interaction.data?.name || interaction.data?.custom_id;
log.command(name, user, interaction.guild_id || 'DM');
if (interaction.guild_id && interaction.channel && interaction.channel.nsfw === false && !['help','invite','ping'].includes(interaction.data?.name)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Enforce NSFW parents for thread interactions

This check only rejects channels whose partial payload explicitly has nsfw === false; Discord thread payloads do not set nsfw and inherit that setting from the parent channel. A refresh/component interaction in a guild thread under a non-NSFW parent therefore bypasses the guard and can fetch NSFW media in a SFW location unless the parent channel is fetched or thread parents are otherwise denied.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant