diff --git a/bun.lockb b/bun.lockb
index 632f472..731ac5b 100755
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/package.json b/package.json
index 708177d..1c4b798 100644
--- a/package.json
+++ b/package.json
@@ -14,16 +14,20 @@
"@snazzah/davey": "^0.1.10",
"crc-32": "^1.2.2",
"discord.js-selfbot-v13": "^3.7.1",
+ "express": "^5.2.1",
"ffmpeg-static": "^5.3.0",
"fluent-ffmpeg": "^2.1.3",
"libsodium-wrappers": "^0.8.2",
"node-crc": "^4.0.0",
"opusscript": "^0.1.1",
"prism-media": "2.0.0-alpha.0",
- "sodium-native": "^4.3.2"
+ "sodium-native": "^4.3.2",
+ "ws": "^8.20.1"
},
"devDependencies": {
"@types/bun": "latest",
- "@types/fluent-ffmpeg": "^2.1.28"
+ "@types/express": "^5.0.6",
+ "@types/fluent-ffmpeg": "^2.1.28",
+ "@types/ws": "^8.18.1"
}
}
\ No newline at end of file
diff --git a/public/index.html b/public/index.html
new file mode 100644
index 0000000..8fff19f
--- /dev/null
+++ b/public/index.html
@@ -0,0 +1,273 @@
+
+
+
+
+
+ Discord Audio Transmitter
+
+
+
+
+
Audio Transmitter
+
Transmit your microphone to Discord Voice
+
+
+
+
+
+
+
Listen to Discord
+
+
+
Click button to listen
+
+
+
+
+
+
+
+
+
+
diff --git a/src/index.ts b/src/index.ts
index 8b41e84..e769749 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,6 +1,9 @@
import { Client } from "discord.js-selfbot-v13";
import { startRecording } from "./recorder";
import { config } from "./config";
+import { startWebserver } from "./webserver";
+import { discordPlayer } from "./player";
+import { getVoiceConnection } from "@discordjs/voice";
// Validasi environment variables
const token = process.env.DISCORD_TOKEN;
@@ -42,6 +45,16 @@ client.on("ready", async () => {
console.log(`[bot] Joining voice channel: #${channel.name} (${channel.id})`);
}
await startRecording(client, channel as any);
+
+ // Set up player connection
+ const connection = getVoiceConnection(guildId!);
+ if (connection) {
+ discordPlayer.setConnection(connection);
+ console.log("[bot] Player connected to voice channel");
+ }
+
+ // Start Webserver
+ startWebserver(3000);
});
client.on("error", (err) => {
diff --git a/src/player.ts b/src/player.ts
new file mode 100644
index 0000000..e3c52c4
--- /dev/null
+++ b/src/player.ts
@@ -0,0 +1,47 @@
+import {
+ createAudioPlayer,
+ createAudioResource,
+ AudioPlayerStatus,
+ VoiceConnection,
+ AudioPlayer,
+ StreamType
+} from "@discordjs/voice";
+import { Readable } from "stream";
+
+export class DiscordPlayer {
+ private player: AudioPlayer;
+ private connection: VoiceConnection | null = null;
+
+ constructor() {
+ this.player = createAudioPlayer();
+
+ this.player.on(AudioPlayerStatus.Playing, () => {
+ console.log("[player] Audio player is now playing!");
+ });
+
+ this.player.on("error", error => {
+ console.error(`[player] Error: ${error.message}`);
+ });
+ }
+
+ public setConnection(connection: VoiceConnection) {
+ this.connection = connection;
+ this.connection.subscribe(this.player);
+ }
+
+ public playStream(stream: Readable) {
+ // We assume the stream is Opus or PCM.
+ // For MediaRecorder (webm/opus), we might need to parse it.
+ // But let's start with a simple resource.
+ const resource = createAudioResource(stream, {
+ inputType: StreamType.WebmOpus,
+ });
+ this.player.play(resource);
+ }
+
+ public stop() {
+ this.player.stop();
+ }
+}
+
+export const discordPlayer = new DiscordPlayer();
diff --git a/src/recorder.ts b/src/recorder.ts
index 17fe94b..1b5f6e7 100644
--- a/src/recorder.ts
+++ b/src/recorder.ts
@@ -139,6 +139,13 @@ export async function startRecording(client: Client, channel: VoiceChannel): Pro
// Pipe: audioStream -> packetFilter -> oggStream -> out
audioStream.pipe(packetFilter).pipe(oggStream).pipe(out);
+ // Also forward to web listeners
+ oggStream.on('data', (chunk) => {
+ if ((global as any).broadcastToWeb) {
+ (global as any).broadcastToWeb(chunk);
+ }
+ });
+
if (config.verbose) {
console.log(`[recorder] Recording user ${userId} → ${filename}`);
}
diff --git a/src/webserver.ts b/src/webserver.ts
new file mode 100644
index 0000000..de016d6
--- /dev/null
+++ b/src/webserver.ts
@@ -0,0 +1,59 @@
+import express from "express";
+import { WebSocketServer } from "ws";
+import http from "http";
+import path from "path";
+import { PassThrough } from "stream";
+import { discordPlayer } from "./player";
+
+export function startWebserver(port: number = 3000) {
+ const app = express();
+ const server = http.createServer(app);
+ const wss = new WebSocketServer({ server });
+
+ const listeners = new Set();
+
+ app.use(express.static(path.join(__dirname, "../public")));
+
+ // Endpoint for receiving (listening) audio from Discord
+ app.get("/listen", (req, res) => {
+ res.setHeader("Content-Type", "audio/ogg");
+ listeners.add(res);
+ console.log(`[webserver] New listener connected. Total: ${listeners.size}`);
+
+ req.on("close", () => {
+ listeners.delete(res);
+ console.log(`[webserver] Listener disconnected. Total: ${listeners.size}`);
+ });
+ });
+
+ // Function to broadcast audio chunks to all listeners
+ (global as any).broadcastToWeb = (chunk: Buffer) => {
+ listeners.forEach(res => res.write(chunk));
+ };
+
+ wss.on("connection", (ws) => {
+ console.log("[webserver] New WebSocket connection");
+
+ const audioStream = new PassThrough();
+ discordPlayer.playStream(audioStream);
+
+ ws.on("message", (data: Buffer) => {
+ // Write incoming audio chunks to the stream
+ audioStream.write(data);
+ });
+
+ ws.on("close", () => {
+ console.log("[webserver] WebSocket connection closed");
+ audioStream.end();
+ });
+
+ ws.on("error", (err) => {
+ console.error("[webserver] WebSocket error:", err);
+ audioStream.end();
+ });
+ });
+
+ server.listen(port, () => {
+ console.log(`[webserver] Server listening on http://localhost:${port}`);
+ });
+}