Skip to content

Commit

Permalink
Merge pull request #17 from MrBoombastic/slave
Browse files Browse the repository at this point in the history
v1.5.0
  • Loading branch information
MrBoombastic authored Mar 25, 2023
2 parents f057f6c + e90e5a6 commit 1688f3a
Show file tree
Hide file tree
Showing 16 changed files with 575 additions and 123 deletions.
9 changes: 9 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
MODE=MEMBERS
GUILD_ID=
CHANNEL_ID=
SPACING=" "
TOKEN=
DELAY=1000
DICTIONARY=" !\"#$%&'()*+,-./0123456789:;<=>?@[]^_`abcdefghijklmnopqrstuvwxyz{|}~"
DATE_FORMAT="L LTS UTCZ"
DATE_LOCALE=en
2 changes: 1 addition & 1 deletion .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
version: 2
updates:
- package-ecosystem: "npm"
target-branch: "niewolnik"
target-branch: "slave"
directory: "/"
schedule:
interval: "daily"
5 changes: 0 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
# Config
src/config*.json
src/data-*

# IDE
Expand Down Expand Up @@ -74,10 +73,6 @@ typings/
# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env
.env.test

# parcel-bundler cache (https://parceljs.org/)
.cache

Expand Down
41 changes: 25 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
![Logo.png](img.png)
![Logo.png](banner.png)
[![CodeFactor](https://www.codefactor.io/repository/github/mrboombastic/osintcord/badge)](https://www.codefactor.io/repository/github/mrboombastic/osintcord)

Just get data of (nearly) all of Discord guild members.
Just get data of (nearly) all of Discord guild members. Or track deleted and edited messages. Or why not both?

## Warning:

Expand All @@ -13,29 +13,38 @@ Using this on a user account is prohibited by the Discord TOS and can lead to th
## Usage:

- By using binaries:
- Download binary from [Releases](https://github.com/MrBoombastic/OSINTCord/releases) tab.
- Fill in the `config.json` file.
- Download binary from the [Releases](https://github.com/MrBoombastic/OSINTCord/releases) tab.
- Fill in the `.env` file.
- Run the binary.
- By using source code:
- Clone this repository.
- Make sure that you have Node.JS v18 installed.
- Install dependencies by using `yarn`.
- Fill in the `config.json` file and place it in `src` directory.
- Fill in the `.env` file and place it in `src` directory.
- Run `npm start`.

All results will be stored in the `logs` and `media` directories.

## Options:

You can use some default options from [config.example.json](config.example.json).

- `guildID`: The guild ID you want to get data from.
- `channelID`: The channel ID, which also will be used to get data from.
- `spacing`: Spacing between columns in output file.
- `token`: Your Discord account token.
- `delay`: Delay between *some* requests.
- `dictionary`: Characters used by the bruteforce method. Case-insensitive, duplicates are ignored.
- `dateFormat`: format of the parsed date (refer to the [Day.js manual](https://day.js.org/docs/en/display/format)).
- `dateLocale`: locale used in the parsed
date ([list of supported locales](https://github.com/iamkun/dayjs/tree/dev/src/locale)).
You can use some default options from the already provided [.env](.env) file.

- When you want to dump guild members, set `MODE` to `MEMBERS` and set
- `GUILD_ID`: The guild ID you want to get data from.
- `CHANNEL_ID`: The channel ID, which also will be used to get data from.
- `SPACING`: Spacing between columns in output file.
- `TOKEN`: Your Discord account token.
- `DELAY`: Delay between *some* requests.
- `DICTIONARY`: Characters used by the bruteforce method. Case-insensitive, duplicates are ignored, sorted
alphabetically.
- `DATE_FORMAT`: format of the parsed date (refer to
the [Day.js manual](https://day.js.org/docs/en/display/format)).
- `DATE_LOCALE`: locale used in the parsed
date ([list of supported locales](https://github.com/iamkun/dayjs/tree/dev/src/locale)).
- When you want to trace deleted and edited messages, set `MODE` to `WATCHDOG` and set
- `GUILD_ID`: The guild ID you want to get data from. Set to `all`, if you want to receive data from all available
guilds.
- `TOKEN`: Your Discord account token.

## FAQ:

Expand Down
Binary file added banner.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 0 additions & 10 deletions config.example.json

This file was deleted.

Binary file removed img.png
Binary file not shown.
8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "osintcord",
"version": "1.4.0",
"version": "1.5.0",
"description": "Get data of (nearly) all of Discord guild members",
"main": "src/index.js",
"scripts": {
Expand Down Expand Up @@ -28,6 +28,7 @@
"pkg": {
"outputPath": "dist",
"assets": [
"src/**/*",
"node_modules/vm2/**/*",
"node_modules/dayjs/locale/*"
]
Expand All @@ -36,6 +37,9 @@
"dependencies": {
"dayjs": "^1.11.7",
"discord.js-selfbot-v13": "https://github.com/MrBoombastic/discord.js-selfbot-v13#old-deps",
"ora": "~5.4.1"
"dotenv": "^16.0.3",
"log4js": "^6.9.1",
"ora": "~5.4.1",
"request": "^2.88.2"
}
}
29 changes: 29 additions & 0 deletions src/events/members/ready.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const {welcome, exit, saveMembers} = require("../../utils");
const {perms, overlap, bruteforce} = require("../../steps");

module.exports = async (client) => {
welcome(client);

// Getting target
const guild = await client.guilds.cache.get(process.env.GUILD_ID);
if (!guild?.available) {
console.error("ERROR: selected guild is not available!\nAvailable guilds:", client.guilds.cache.map(x => `${x.name} (${x.id})`).join(", "));
process.exit(1);
}
const channel = await guild.channels.cache.get(process.env.CHANNEL_ID);
if (!channel) {
console.warn("WARNING: selected channel is missing! 'Member list' method will be skipped\nAvailable channels: ", guild.channels.cache.filter(x => x.isText()).map(x => `${x.name} (${x.id})`).join(", "));
}

console.log(`Target acquired: ${guild.name} (${channel?.name || "NO CHANNEL"})`);

// Fetching!
await perms(guild); // Method 1 - fetching with perms
if (channel) await overlap(guild, client); // Method 2 - overlap member list fetching
if (guild.members.cache.size < guild.memberCount) await bruteforce(guild); // Method 3 - brute-force fetching

// Done!
console.log(`Fetching done! Found ${guild.members.cache.size}/${guild.memberCount} => ${guild.members.cache.size / guild.memberCount * 100}% members.`);
saveMembers(client, guild);
exit(client);
};
14 changes: 14 additions & 0 deletions src/events/watchdog/messageDelete.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const {downloadFile} = require("../../utils");

module.exports = async (client, message) => {
if (message.guildId === process.env.GUILD_ID || process.env.GUILD_ID.toLowerCase() === "all") {
let info = `[DELETED MESSAGE] Guild: ${message.guild.name} Channel: ${message.channel.name} Author: ${message.author?.tag} Bot: ${message.author?.bot}\nCONTENT: ${message.cleanContent}`;
if (message.attachments.size > 0) {
info += `\nMEDIA: ${message.attachments.map(x => x.proxyURL).join(", ")}`;
message.attachments.forEach(x => {
downloadFile(x.proxyURL);
});
}
client.logger.log(info);
}
};
19 changes: 19 additions & 0 deletions src/events/watchdog/messageUpdate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const {downloadFile} = require("../../utils");

module.exports = async (client, oldMsg, newMsg) => {
if (oldMsg.guildId === process.env.GUILD_ID || process.env.GUILD_ID.toLowerCase() === "all") {
if (oldMsg.embeds.length === 0 && newMsg.embeds.length > 0) return; //ignoring generating thumbnails for images
let info = `[EDITED MESSAGE] Guild: ${oldMsg.guild.name} Channel: ${oldMsg.channel.name} Author: ${oldMsg.author?.tag} Bot: ${oldMsg.author?.bot}
OLD CONTENT: ${oldMsg.content}
NEW CONTENT: ${newMsg.content}`;
if (oldMsg.attachments.size !== newMsg.attachments.size) {
info += `\nOLD MEDIA: ${oldMsg.attachments.map(x => x.url).join(", ")}`;
info += `\nNEW MEDIA: ${newMsg.attachments.map(x => x.url).join(", ")}`;
// Assuming that user can only remove media from existing message
oldMsg.attachments.forEach(x => {
downloadFile(x.proxyURL);
});
}
client.logger.log(info);
}
};
23 changes: 23 additions & 0 deletions src/events/watchdog/ready.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const log4js = require("log4js");
const {welcome} = require("../../utils");
module.exports = async (client) => {
welcome(client);

// Getting target
const info = process.env.GUILD_ID.toLowerCase() === "all" ? "ALL GUILDS" : await client.guilds.cache.get(process.env.GUILD_ID).name;
console.log(`Target acquired: ${info}`);

// Set up message logging
log4js.configure({
appenders: {
watchdog: {
type: "file",
layout: {type: "pattern", pattern: "[%d] %m%n"},
filename: `logs/watchdog-${process.env.GUILD_ID}.log`
}
},
categories: {default: {appenders: ["watchdog"], level: "info"}},
});

client.logger = log4js.getLogger("watchdog");
};
77 changes: 22 additions & 55 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,78 +1,45 @@
// Including libraries
const fs = require("fs");
const {Client} = require("discord.js-selfbot-v13");
global.dayjs = require("dayjs");
dayjs.extend(require("dayjs/plugin/localizedFormat"));
const {saveAndExit, checkConfig, art} = require("./utils.js");
const {bruteforce, perms, overlap} = require("./steps.js");
const {checkConfig, exit, saveMembers} = require("./utils.js");
require('dotenv').config();


// Setting up client
const client = new Client({
checkUpdate: false, partials: ["GUILD_MEMBER"]
checkUpdate: false,
});

// Config and guild are stored here
let config, guild;

// Config file validation
try {
config = JSON.parse(fs.readFileSync("./config.json"));
} catch (e) {
console.error("ERROR: missing config file!");
process.exit(1);
}
const configStatus = checkConfig(config);
if (!configStatus.ok) {
console.error(`ERROR: missing '${configStatus.prop}' in config file!`);
const configStatus = checkConfig();
if (!configStatus.success) {
console.error(`ERROR: wrong config! Reason: ${configStatus.reason}`);
process.exit(1);
}

// Preparing date formatting
try {
require(`dayjs/locale/${config.dateLocale}`);
dayjs.locale(config.dateLocale);
require(`dayjs/locale/${process.env.DATE_LOCALE}`);
dayjs.locale(process.env.DATE_LOCALE);
} catch (e) {
console.warn(`WARNING: locale '${config.dateLocale}' not found. Using 'en' as fallback.`);
console.warn(`WARNING: locale '${process.env.DATE_LOCALE}' not found. Using 'en' as fallback.`);
dayjs.locale("en");
}

// Just informational things
client.on("rateLimit", async (data) => {
console.log(data);
});

// When bot is ready
client.on("ready", async () => {
console.log(art);
console.log(`Logged in as ${client.user.tag} (${client.user?.emailAddress || "NO EMAIL"})`);

// Getting target
guild = await client.guilds.cache.get(config.guildID);
if (!guild?.available) {
console.error("ERROR: selected guild is not available!\nAvailable guilds:", client.guilds.cache.map(x => `${x.name} (${x.id})`).join(", "));
process.exit(1);
}
const channel = await guild.channels.cache.get(config.channelID);
if (!channel) {
console.warn("WARNING: selected channel is missing! 'Member list' method will be skipped\nAvailable channels: ", guild.channels.cache.map(x => `${x.name} (${x.id})`).join(", "));
}

console.log(`Target acquired: ${guild.name} (${channel?.name || "NO CHANNEL"})`);

// Fetching!
await perms(guild); // Method 1 - fetching with perms
if (channel) await overlap(guild, config, client); // Method 2 - overlap member list fetching
if ((guild.members.cache.size < guild.memberCount) && (guild.members.cache.size !== guild.memberCount)) await bruteforce(guild, config); // Method 3 - brute-force fetching

// Done!
console.log(`Fetching done! Found ${guild.members.cache.size}/${guild.memberCount} => ${guild.members.cache.size / guild.memberCount * 100}% members.`);

await saveAndExit(client, config, guild);
});
const events = {
watchdog: ["messageDelete", "messageUpdate", "ready"],
members: ["ready"]
};
for (const file of events[process.env.MODE.toLowerCase()]) {
const event = require(`./events/${process.env.MODE.toLowerCase()}/${file}`);
client.on(file, event.bind(null, client));
}

process.on("SIGINT", async () => {
console.log("\nStopped upon user's request!");
await saveAndExit(client, config, guild);
console.log("\nStopping at user's request!");
if (process.env.MODE.toLowerCase() === "members") saveMembers(client, client.guilds.cache.get(process.env.GUILD_ID));
exit(client);
});

client.login(config.token);
client.login(process.env.TOKEN);
14 changes: 7 additions & 7 deletions src/steps.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ module.exports = {
progressbar.stop();
},

bruteforce: async (guild, config) => {
bruteforce: async (guild) => {
// Dictionary info
const dictionary = (Array.from(new Set(config.dictionary.toLowerCase()))).sort(); //deduplication
const dictionary = (Array.from(new Set(process.env.DICTIONARY.toLowerCase()))).sort(); //deduplication
console.log("Using dictionary:", dictionary.join(''));

const progressbar = ora({text: "Starting 'brute-force' method!", prefixText: "[BRUTE-FORCE]"}).start();
Expand All @@ -29,14 +29,14 @@ module.exports = {
refreshLoading(progressbar, guild);
}, 500);
await guild.members.fetchBruteforce({
delay: config.delay, limit: 100, //max limit is 100 at once
delay: parseInt(process.env.DELAY), limit: 100, //max limit is 100 at once
dictionary: dictionary
});
clearInterval(stage);
progressbar.stop();
},

overlap: async (guild, config, client) => {
overlap: async (guild, client) => {
const progressbar = ora({
text: "Starting 'overlap member list' method!", prefixText: "[OVERLAP MEMBER LIST]"
}).start();
Expand All @@ -46,9 +46,9 @@ module.exports = {
refreshLoading(progressbar, guild);
}, 500);
for (let index = 0; index <= guild.memberCount; index += 100) {
await guild.members.fetchMemberList(config.channelID, index, index !== 100).catch(() => false);
if (guild.members.cache.size === guild.memberCount) break;
await client.sleep(config.delay);
await guild.members.fetchMemberList(process.env.CHANNEL_ID, index, index !== 100).catch(() => false);
if (guild.members.cache.size >= guild.memberCount) break;
await client.sleep(parseInt(process.env.DELAY));
}
clearInterval(stage);
progressbar.stop();
Expand Down
Loading

0 comments on commit 1688f3a

Please sign in to comment.