UNPKG

14.9 kBJavaScriptView Raw
1"use strict";
2var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3 function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4 return new (P || (P = Promise))(function (resolve, reject) {
5 function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6 function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7 function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8 step((generator = generator.apply(thisArg, _arguments || [])).next());
9 });
10};
11var __generator = (this && this.__generator) || function (thisArg, body) {
12 var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
13 return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14 function verb(n) { return function (v) { return step([n, v]); }; }
15 function step(op) {
16 if (f) throw new TypeError("Generator is already executing.");
17 while (_) try {
18 if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19 if (y = 0, t) op = [op[0] & 2, t.value];
20 switch (op[0]) {
21 case 0: case 1: t = op; break;
22 case 4: _.label++; return { value: op[1], done: false };
23 case 5: _.label++; y = op[1]; op = [0]; continue;
24 case 7: op = _.ops.pop(); _.trys.pop(); continue;
25 default:
26 if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27 if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28 if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29 if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30 if (t[2]) _.ops.pop();
31 _.trys.pop(); continue;
32 }
33 op = body.call(thisArg, _);
34 } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35 if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36 }
37};
38Object.defineProperty(exports, "__esModule", { value: true });
39var bttps_1 = require("./bttps");
40var fallbackListData_1 = require("./fallbackListData");
41var listData; // TODO add type
42var listAge = new Date();
43var extendedLogging = false;
44var useBotblockAPI = true;
45/**
46 * @param apiKeys A JSON object formatted like: {"botlist name":"API Keys for that list", etc.} ;
47 * it also includes other metadata including sharddata
48 */
49function postToAllLists(apiKeys) {
50 return __awaiter(this, void 0, void 0, function () {
51 var currentDate, tmpListData, e_1;
52 return __generator(this, function (_a) {
53 switch (_a.label) {
54 case 0:
55 currentDate = new Date();
56 if (!(!listData || listAge < currentDate)) return [3 /*break*/, 4];
57 // we try to update the listdata every day
58 // in case new lists are added but the code is not restarted
59 listAge.setDate(currentDate.getDate() + 1);
60 _a.label = 1;
61 case 1:
62 _a.trys.push([1, 3, , 4]);
63 return [4 /*yield*/, bttps_1.get('https://botblock.org/api/lists')];
64 case 2:
65 tmpListData = _a.sent();
66 // make sure we only save it if nothing goes wrong
67 if (tmpListData) {
68 listData = tmpListData;
69 }
70 else {
71 throw new Error('Got empty list from botblock.');
72 }
73 return [3 /*break*/, 4];
74 case 3:
75 e_1 = _a.sent();
76 console.error("BLAPI: " + e_1);
77 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.");
78 return [3 /*break*/, 4];
79 case 4:
80 Object.entries(listData).forEach(function (_a) {
81 var listname = _a[0];
82 if (apiKeys[listname]
83 && (listData[listname].api_post || listname === 'discordbots.org')) {
84 // we even need to check this extra because botblock gives us nulls back
85 var list = listData[listname];
86 if (listname === 'discordbots.org') {
87 list = fallbackListData_1.fallbackData[listname];
88 }
89 var apiPath = list.api_post.replace(':id', apiKeys.bot_id);
90 // creating JSON object to send, reading out shard data
91 var sendObj = {}; // TODO type
92 sendObj[list.api_field] = apiKeys.server_count;
93 if (apiKeys.shard_id && list.api_shard_id) {
94 sendObj[list.api_shard_id] = apiKeys.shard_id;
95 }
96 if (apiKeys.shard_count && list.api_shard_count) {
97 sendObj[list.api_shard_count] = apiKeys.shard_count;
98 }
99 if (apiKeys.shards && list.api_shards) {
100 sendObj[list.api_shards] = apiKeys.shards;
101 }
102 bttps_1.post(apiPath, apiKeys[listname], sendObj, extendedLogging).catch(function (e) { return console.error("BLAPI: " + e); });
103 }
104 });
105 return [2 /*return*/];
106 }
107 });
108 });
109}
110/**
111 * @param client Discord.js client
112 * @param apiKeys A JSON object formatted like: {"botlist name":"API Keys for that list", etc.}
113 * @param repeatInterval Number of minutes between each repetition
114 */
115function handleInternal(client, apiKeys, repeatInterval) {
116 return __awaiter(this, void 0, void 0, function () {
117 var unchanged, _, shardCounts, e_2, newApiKeys;
118 return __generator(this, function (_a) {
119 switch (_a.label) {
120 case 0:
121 setTimeout(/* eslint-disable-next-line @typescript-eslint/no-misused-promises */ handleInternal.bind(null, client, apiKeys, repeatInterval), 60000 * repeatInterval); // call this function again in the next interval
122 if (!client.user) return [3 /*break*/, 7];
123 /* eslint-disable camelcase */
124 apiKeys.bot_id = client.user.id;
125 if (!(client.shard && client.shard.id === 0)) return [3 /*break*/, 5];
126 apiKeys.shard_count = client.shard.count;
127 _a.label = 1;
128 case 1:
129 _a.trys.push([1, 3, , 4]);
130 return [4 /*yield*/, client.shard.broadcastEval('this.guilds.size')];
131 case 2:
132 _ = _a.sent();
133 shardCounts = _.filter(function (count) { return count !== 0; });
134 if (shardCounts.length !== client.shard.count) {
135 // If not all shards are up yet, we skip this run of handleInternal
136 return [2 /*return*/];
137 }
138 apiKeys.shards = shardCounts;
139 apiKeys.server_count = apiKeys.shards.reduce(function (prev, val) { return prev + val; }, 0);
140 return [3 /*break*/, 4];
141 case 3:
142 e_2 = _a.sent();
143 console.error('BLAPI: Error while fetching shard server counts:', e_2);
144 return [3 /*break*/, 4];
145 case 4: return [3 /*break*/, 6];
146 case 5:
147 if (!client.shard) {
148 /*
149 else if (client.ws && client.ws.shards) {
150 apiKeys.shard_count = client.ws.shards.size;
151 // Get array of shards
152 const shardCounts:Array<number> = [];
153 client.ws.shards.forEach(shard => {
154 let count = 0;
155 client.guilds.forEach(g => {
156 if (g.shardID === shard.id) count++;
157 });
158 shardCounts.push(count);
159 });
160 if (shardCounts.length !== client.ws.shards.size) {
161 // If not all shards are up yet, we skip this run of handleInternal
162 console.log(
163 "BLAPI: Not all shards are up yet, so we're skipping this run."
164 );
165 return;
166 }
167 apiKeys.shards = shardCounts;
168 apiKeys.server_count = apiKeys.shards.reduce(
169 (prev:number, val:number) => prev + val,
170 0
171 );
172 // Check if bot is not sharded at all, but still wants to send server count
173 // (it's recommended to shard your bot, even if it's only one shard)
174 } */
175 /* eslint-disable-next-line no-param-reassign */
176 apiKeys.server_count = client.guilds.size;
177 }
178 else {
179 unchanged = true;
180 } // nothing has changed, therefore we don't send any data
181 _a.label = 6;
182 case 6:
183 if (!unchanged) {
184 if (repeatInterval > 2 && useBotblockAPI) {
185 // if the interval isnt below the BotBlock ratelimit, use their API
186 bttps_1.post('https://botblock.org/api/count', 'no key needed for this', apiKeys, extendedLogging).catch(function (e) { return console.error("BLAPI: " + e); });
187 // they blacklisted botblock, so we need to do this, posting their stats manually
188 if (apiKeys['discordbots.org']) {
189 newApiKeys = {};
190 newApiKeys.bot_id = apiKeys.bot_id;
191 newApiKeys['discordbots.org'] = apiKeys['discordbots.org'];
192 newApiKeys.server_count = apiKeys.server_count;
193 if (apiKeys.shard_count) {
194 newApiKeys.shard_count = apiKeys.shard_count;
195 }
196 if (apiKeys.shards) {
197 newApiKeys.shards = apiKeys.shards;
198 }
199 postToAllLists(newApiKeys);
200 }
201 }
202 else {
203 postToAllLists(apiKeys);
204 }
205 }
206 return [3 /*break*/, 8];
207 case 7:
208 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.");
209 _a.label = 8;
210 case 8: return [2 /*return*/];
211 }
212 });
213 });
214}
215/**
216 * This function is for automated use with discord.js
217 * @param discordClient Client via wich your code is connected to Discord
218 * @param apiKeys A JSON object formatted like: {"botlist name":"API Keys for that list", etc.}
219 * @param repeatInterval Number of minutes until you want to post again, leave out to use 30
220 */
221function handle(discordClient, apiKeys, repeatInterval) {
222 // handle inputs
223 /* eslint-disable-next-line no-param-reassign */
224 if (!repeatInterval || repeatInterval < 1)
225 repeatInterval = 30;
226 return handleInternal(discordClient, apiKeys, repeatInterval);
227}
228exports.handle = handle;
229/**
230 * For when you don't use discord.js or just want to post to manual times
231 * @param guildCount Integer value of guilds your bot is serving
232 * @param botID Snowflake of the ID the user your bot is using
233 * @param apiKeys A JSON object formatted like: {"botlist name":"API Keys for that list", etc.}
234 * @param shardID (optional) The shard ID, which will be used to identify the
235 * shards valid for posting
236 * (and for super efficient posting with BLAPIs own distributer when not using botBlock)
237 * @param shardCount (optional) The number of shards the bot has, which is posted to the lists
238 * @param shards (optional) An array of guild counts of each single shard
239 * (this should be a complete list, and only a single shard will post it)
240 */
241function manualPost(guildCount, botID, apiKeys, shardID, shardCount, shards) {
242 /* eslint-disable no-param-reassign */
243 apiKeys.bot_id = botID;
244 apiKeys.server_count = guildCount;
245 // check if we want to use sharding
246 if (shardID === 0 || (shardID && !shards)) {
247 // if we don't have all the shard info in one place well try to post every shard itself
248 apiKeys.shard_id = shardID;
249 apiKeys.shard_count = shardCount;
250 if (shards) {
251 if (shards.length !== shardCount) {
252 console.error("BLAPI: Shardcount (" + shardCount + ") does not equal the length of the shards array (" + shards.length + ").");
253 return;
254 }
255 apiKeys.shards = shards;
256 apiKeys.server_count = apiKeys.shards.reduce(function (prev, val) { return prev + val; }, 0);
257 }
258 /* eslint-enable camelcase */
259 }
260 /* eslint-enable no-param-reassign */
261 if (useBotblockAPI) {
262 bttps_1.post('https://botblock.org/api/count', 'no key needed for this', apiKeys, extendedLogging).catch(function (e) { return console.error("BLAPI: " + e); });
263 }
264 else {
265 postToAllLists(apiKeys);
266 }
267}
268exports.manualPost = manualPost;
269function setLogging(setLog) {
270 extendedLogging = setLog;
271}
272exports.setLogging = setLogging;
273function setBotblock(useBotblock) {
274 useBotblockAPI = useBotblock;
275}
276exports.setBotblock = setBotblock;