UNPKG

8.64 kBJavaScriptView Raw
1const { join } = require('path');
2const bttps = require(join(__dirname, 'bttps.js'));
3const fallbackListData = require('./fallbackListData.json');
4
5let listData;
6const listAge = new Date();
7let extendedLogging = false;
8let useBotblockAPI = true;
9
10/**
11 * @param {Object} apiKeys A JSON object formatted like: {"botlist name":"API Keys for that list", etc.} ; it also includes other metadata including sharddata
12 */
13const postToAllLists = async apiKeys => {
14 // make sure we have all lists we can post to and their apis
15 const currentDate = new Date();
16 if (!listData || listAge < currentDate) {
17 listAge.setDate(currentDate.getDate() + 1); // we try to update the listdata every day, in case new lists are added but the code is not restarted
18 try {
19 const tmpListData = await bttps.get('https://botblock.org/api/lists');
20 // make sure we only save it if nothing goes wrong
21 if (tmpListData) {
22 listData = tmpListData;
23 } else {
24 throw new Error('Got empty list from botblock.');
25 }
26 } catch (e) {
27 console.error(`BLAPI: ${e}`);
28 console.error('BLAPI : Something went wrong when contacting BotBlock for the API of the lists, so we\'re using an older preset. Some lists might not be available because of this.');
29 }
30 }
31 for (const listname in listData) {
32 if (apiKeys[listname] && (listData[listname]['api_post'] || listname === 'discordbots.org')) { // we even need to check this extra because botblock gives us nulls back
33 let list = listData[listname];
34 if (listname === 'discordbots.org') {
35 list = fallbackListData[listname];
36 }
37 const apiPath = list.api_post.replace(':id', apiKeys.bot_id);
38 // creating JSON object to send, reading out shard data
39 const sendObj = {};
40 sendObj[list.api_field] = apiKeys.server_count;
41 if (apiKeys.shard_id && list.api_shard_id) {
42 sendObj[list.api_shard_id] = apiKeys.shard_id;
43 }
44 if (apiKeys.shard_count && list.api_shard_count) {
45 sendObj[list.api_shard_count] = apiKeys.shard_count;
46 }
47 if (apiKeys.shards && list.api_shards) {
48 sendObj[list.api_shards] = apiKeys.shards;
49 }
50 bttps.post(apiPath, apiKeys[listname], sendObj, extendedLogging).catch(e => console.error(`BLAPI: ${e}`));
51 }
52 }
53};
54
55/**
56 * @param {Client} client Discord.js client
57 * @param {Object} apiKeys A JSON object formatted like: {"botlist name":"API Keys for that list", etc.}
58 * @param {number} repeatInterval Number of minutes between each repetition
59 */
60const handleInternal = async (client, apiKeys, repeatInterval) => {
61 setTimeout(handleInternal.bind(null, client, apiKeys, repeatInterval), 60000 * repeatInterval); // call this function again in the next interval
62 let unchanged;
63
64 if (client.user) {
65 /* eslint-disable camelcase */
66 apiKeys.bot_id = client.user.id;
67
68 // Checks if bot is sharded
69 if (client.shard && client.shard.id === 0) {
70 apiKeys.shard_count = client.shard.count;
71
72 // This will get as much info as it can, without erroring
73 const _ = await client.shard.broadcastEval('this.guilds.size').catch(e => console.error('BLAPI: Error while fetching shard server counts:', e)); const shardCounts = _.filter(count => count !== 0);
74 if (shardCounts.length !== client.shard.count) {
75 // If not all shards are up yet, we skip this run of handleInternal
76 return;
77 }
78
79 apiKeys.shards = shardCounts;
80 apiKeys.server_count = apiKeys.shards.reduce((prev, val) => prev + val, 0);
81
82 // Checks if bot is sharded with internal sharding
83 } else if (client.ws && client.ws.shards) {
84 apiKeys.shard_count = client.ws.shards.size;
85 // Get array of shards
86 const shardCounts = [];
87 client.ws.shards.forEach(shard => {
88 let count = 0;
89 client.guilds.forEach(g => {
90 if (g.shardID === shard.id) count++;
91 });
92 shardCounts.push(count);
93 });
94 if (shardCounts.length !== client.ws.shards.size) {
95 // If not all shards are up yet, we skip this run of handleInternal
96 console.log("BLAPI: Not all shards are up yet, so we're skipping this run.");
97 return;
98 }
99 apiKeys.shards = shardCounts;
100 apiKeys.server_count = apiKeys.shards.reduce((prev, val) => prev + val, 0);
101 // Check if bot is not sharded at all, but still wants to send server count (it's recommended to shard your bot, even if it's only one shard)
102 } else if (!client.shard) {
103 apiKeys.server_count = client.guilds.size;
104 } else { unchanged = true; } // nothing has changed, therefore we don't send any data
105 /* eslint-enable camelcase */
106 if (!unchanged) {
107 if (repeatInterval > 2 && useBotblockAPI) { // if the interval isnt below the BotBlock ratelimit, use their API
108 bttps.post('https://botblock.org/api/count', 'no key needed for this', apiKeys, extendedLogging).catch(e => console.error(`BLAPI: ${e}`));
109
110 // they blacklisted botblock, so we need to do this, posting their stats manually
111 if (apiKeys['discordbots.org']) {
112 const newApiKeys = {};
113 /* eslint-disable camelcase */
114 newApiKeys.bot_id = apiKeys.bot_id;
115 newApiKeys['discordbots.org'] = apiKeys['discordbots.org'];
116 newApiKeys.server_count = apiKeys.server_count;
117 if (apiKeys.shard_count) {
118 newApiKeys.shard_count = apiKeys.shard_count;
119 }
120 if (apiKeys.shards) {
121 newApiKeys.shards = apiKeys.shards;
122 }
123 /* eslint-enable camelcase */
124 postToAllLists(newApiKeys);
125 }
126 } else {
127 postToAllLists(apiKeys);
128 }
129 }
130 } else {
131 console.error(`BLAPI : Discord client seems to not be connected yet, so we're skipping this run of the post. We will try again in ${repeatInterval} minutes.`);
132 }
133};
134
135module.exports = {
136 /**
137 * This function is for automated use with discord.js
138 * @param {Client} discordClient Client via wich your code is connected to Discord
139 * @param {Object} apiKeys A JSON object formatted like: {"botlist name":"API Keys for that list", etc.}
140 * @param {number} repeatInterval Number of minutes until you want to post again, leave out to use 30
141 */
142 handle: (discordClient, apiKeys, repeatInterval) => {
143 // handle inputs
144 if (!repeatInterval || repeatInterval < 1) repeatInterval = 30;
145 handleInternal(discordClient, apiKeys, repeatInterval);
146 },
147 /**
148 * For when you don't use discord.js or just want to post to manual times
149 * @param {number} guildCount Integer value of guilds your bot is serving
150 * @param {string} botID Snowflake of the ID the user your bot is using
151 * @param {Object} apiKeys A JSON object formatted like: {"botlist name":"API Keys for that list", etc.}
152 * @param {number} shardID (optional) The shard ID, which will be used to identify the shards valid for posting (and for super efficient posting with BLAPIs own distributer when not using botBlock)
153 * @param {number} shardCount (optional) The number of shards the bot has, which is posted to the lists
154 * @param {number[]} shards (optional) An array of guild counts of each single shard (this should be a complete list, and only a single shard will post it)
155 */
156 manualPost: (guildCount, botID, apiKeys, shardID, shardCount, shards) => {
157 /* eslint-disable camelcase */
158 apiKeys.bot_id = botID;
159 apiKeys.server_count = guildCount;
160 // check if we want to use sharding
161 if (shardID === 0 || (shardID && !shards)) { // if we don't have all the shard info in one place well try to post every shard itself
162 apiKeys.shard_id = shardID;
163 apiKeys.shard_count = shardCount;
164 if (shards) {
165 if (shards.length !== shardCount) {
166 console.error(`BLAPI: Shardcount (${shardCount}) does not equal the length of the shards array (${shards.length}).`);
167 return;
168 }
169 apiKeys.shards = shards;
170 apiKeys.server_count = apiKeys.shards.reduce((prev, val) => prev + val, 0);
171 }
172 /* eslint-enable camelcase */
173 }
174 if (useBotblockAPI) {
175 bttps.post('https://botblock.org/api/count', 'no key needed for this', apiKeys, extendedLogging).catch(e => console.error(`BLAPI: ${e}`));
176 } else {
177 postToAllLists(apiKeys);
178 }
179 },
180 setLogging: setLogging => {
181 extendedLogging = setLogging;
182 },
183 setBotblock: useBotblock => {
184 useBotblockAPI = useBotblock;
185 }
186};