├── config.json
├── LICENSE
├── README.md
└── index.js
/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "token": "",
3 | "prefix": "a."
4 | }
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Agar.io discord bot
4 |
5 | > discord bot to create/check agar.io party servers. [invite link](https://discordapp.com/oauth2/authorize?client_id=603138922532438037&scope=bot&permissions=7232)
6 |
7 | ##
8 |
9 | ### !!! OUTDATED !!!
10 |
11 | ### How to use
12 | * create discord application [here](https://discordapp.com/developers/applications)
13 | * go to bot section and press add bot
14 | * copy your discord bot token to config.json
15 | * run the bot
16 | * invite the bot to your server using client id in general information you can use [this](https://discordapi.com/permissions.html) to generate invite link
17 |
18 | ### Commands
19 |
20 | **default prefix [ a. ]**
21 |
22 | * a.help
23 | * a.agar
24 | * a.ffa
25 | * a.exp
26 | * a.party
27 | * a.ping
28 | * a.invite
29 |
30 | ### Pictures
31 |
32 | 
33 | 
34 | 
35 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | const { Client, MessageEmbed } = require('discord.js');
2 | const bot = new Client({ disableEveryone: true });
3 | const { murmur2 } = require('murmurhash-js');
4 | const request = require('request-promise');
5 | const config = require('./config.json');
6 | const WebSocket = require('ws');
7 | require('colour');
8 |
9 | let embed = new MessageEmbed();
10 | let prefix = config.prefix;
11 |
12 | bot.on('ready', async () => {
13 | bot.user.setActivity(`${prefix}help`, { type: 'PLAYING' });
14 | console.log('Ready'.green);
15 | let xclientkey = await request('https://agar.io/mc/agario.js');
16 | let versionString = xclientkey.match(/(?<=versionString=")[^"]+/)[0];
17 | const { groups: { protoVersion } } = /proto-version.+?"(?\d+.+?)"/gm.exec(xclientkey);
18 | let versionInt = parseInt(versionString.split('.')[0]) * 10000 + parseInt(versionString.split('.')[1]) * 100 + parseInt(versionString.split('.')[2]);
19 | let core = await request('https://agar.io/agario.core.js');
20 | let protocolVersion = core.match(/d;..\(.,(\d+)\);/)[1];
21 | global.protocolVersion = protocolVersion;
22 | global.protoVersion = protoVersion;
23 | global.versionInt = versionInt;
24 | update();
25 | });
26 |
27 | bot.on('message', async msg => {
28 | if (!msg.content.toLocaleLowerCase().startsWith(prefix)) return;
29 | if (msg.author.bot || msg.channel.type === 'dm') return;
30 |
31 | const args = msg.content.toLocaleLowerCase().substring(prefix.length).split(' ');
32 |
33 | switch (args[0].toLocaleLowerCase()) {
34 |
35 | case 'ping':
36 | embed = new MessageEmbed();
37 | embed.addField('Bot ping', Math.round(bot.ws.ping));
38 | embed.setColor('RANDOM');
39 | msg.channel.send(embed);
40 | break;
41 |
42 | case 'help':
43 | embed = new MessageEmbed();
44 | embed.addField('Commands', `${prefix}party\n${prefix}ffa\n${prefix}exp\n${prefix}agar\n${prefix}invite\n${prefix}ping`);
45 | embed.setColor('RANDOM');
46 | msg.channel.send(embed);
47 | break;
48 |
49 | case 'invite':
50 | embed = new MessageEmbed();
51 | embed.setColor('RANDOM');
52 | embed.setTitle('Bot Invite Link');
53 | embed.setURL(`https://discordapp.com/oauth2/authorize?client_id=${bot.user.id}&scope=bot&permissions=7232`);
54 | msg.channel.send(embed);
55 | break;
56 |
57 | case 'agar':
58 | let latestID = await request('https://webbouncer-live-v8-0.agario.miniclippt.com/getLatestID');
59 | let webBouncer = await request('https://webbouncer-live-v8-0.agario.miniclippt.com/info');
60 | let xclientkey = await request('https://agar.io/mc/agario.js');
61 | let versionString = xclientkey.match(/(?<=versionString=")[^"]+/)[0];
62 | const { groups: { protoVersion } } = /proto-version.+?"(?\d+.+?)"/gm.exec(xclientkey);
63 | let versionInt = parseInt(versionString.split('.')[0]) * 10000 + parseInt(versionString.split('.')[1]) * 100 + parseInt(versionString.split('.')[2]);
64 | let init = new Uint8Array(new Uint32Array([versionInt]).buffer);
65 | let core = await request('https://agar.io/agario.core.js');
66 | let protocolVersion = core.match(/d;..\(.,(\d+)\);/)[1];
67 | let agar = JSON.parse(webBouncer);
68 | embed = new MessageEmbed();
69 | embed.setColor('RANDOM');
70 | embed.addField('Agar.io PC Servers', `Servers: ${agar.totals.numServers}\n Online: ${agar.totals.numEnabledServers}\nIdle: ${agar.totals.numServers - agar.totals.numEnabledServers}`);
71 | embed.addField('Players', `SG-Singapore: ${agar.regions['SG-Singapore'].numPlayers}\nUS-Atlanta: ${agar.regions['US-Atlanta'].numPlayers}
72 | EU-London: ${agar.regions['EU-London'].numPlayers}\nCN-China: ${agar.regions['CN-China'].numPlayers}\nBR-Brazil: ${agar.regions['BR-Brazil'].numPlayers}
73 | TK-Turkey: ${agar.regions['TK-Turkey'].numPlayers}\nRU-Russia: ${agar.regions['RU-Russia'].numPlayers}\nJP-Tokyo: ${agar.regions['JP-Tokyo'].numPlayers}
74 | Total: ${agar.totals.numPlayers}`);
75 | embed.addField('Info', `Proto Version: ${protoVersion}\nProtocol Key: ${versionInt}\nProtocol Version: ${protocolVersion}\n[ ${[255, init]} ]
76 | LatestID: ${latestID}\n[Config](https://configs-web.agario.miniclippt.com/live/v15/${latestID}/GameConfiguration.json)`);
77 | msg.channel.send(embed);
78 | break;
79 |
80 | case 'ffa':
81 | case 'exp':
82 | if (!args[1] || !args[1].toUpperCase().match(/EU|RU|TK|CN|US|JP|BR|SG/)) {
83 | embed = new MessageEmbed();
84 | embed.setColor('RANDOM');
85 | embed.addField('Agar.io Regions', 'CN (China)\nTK (Turkey)\nEU (London)\nUS (Atlanta)\nBR (Brazil)\nJP (Tokyo)\nRU (Russia)\nSG (Singapore)');
86 | embed.addField('Usage', `${prefix}gamemode region`);
87 | return msg.channel.send(embed);
88 | }
89 |
90 | embed = new MessageEmbed();
91 | embed.setColor('RANDOM');
92 |
93 | requestV4('findServer', generateBytes(args[1], args[0] == 'ffa' ? ':ffa' : ':experimental'), body => {
94 | let ip = `${body.endpoints.https.includes('ip') ? 'ws://' + body.endpoints.https : 'wss://' + body.endpoints.https}`;
95 | embed.addField('Server info', `Link: https://agar.io/?sip=${body.endpoints.https}\nRegion: ${generateBytes(args[1], 'region')}\nIP: ${ip}`);
96 | msg.channel.send(embed);
97 | });
98 |
99 | break;
100 |
101 | case 'party':
102 | if (!args[1] || args[1].length == 2 && !args[1].toUpperCase().match(/EU|RU|TK|CN|US|JP|BR|SG/)) {
103 | embed = new MessageEmbed();
104 | embed.setColor('RANDOM');
105 | embed.addField('Agar.io Regions', 'CN (China)\nTK (Turkey)\nEU (London)\nUS (Atlanta)\nBR (Brazil)\nJP (Tokyo)\nRU (Russia)\nSG (Singapore)');
106 | embed.addField('Creating party', `${prefix}party region`);
107 | embed.addField('Checking party', `${prefix}party code`);
108 | return msg.channel.send(embed);
109 | }
110 |
111 | embed = new MessageEmbed();
112 | embed.setColor('RANDOM');
113 |
114 |
115 | if (args[1].length == 2) {
116 |
117 | requestV4('findServer', generateBytes(args[1], ':party'), body => {
118 | if (body.status !== 'no_servers') {
119 | var ip = `${body.endpoints.https.includes('ip') ? 'ws://' + body.endpoints.https : 'wss://' + body.endpoints.https}?party_id=${body.token}`;
120 | embed.addField('Party info', `Link: https://agar.io/#${body.token}\nRegion: ${generateBytes(args[1], 'region')}\nCode: ${body.token}\nIP: ${ip}`);
121 | msg.channel.send(embed);
122 | }
123 | else {
124 | requestV4('createToken', generateBytes(args[1], ':party'), body => {
125 | let partyToken = body.token;
126 | requestV4('getToken', generateBytes(args[1], false, partyToken), body => {
127 | ip = `${body.endpoints.https.includes('ip') ? 'ws://' + body.endpoints.https : 'wss://' + body.endpoints.https}?party_id=${body.token}`;
128 | embed.addField('Party info', `Link: https://agar.io/#${partyToken}\nRegion: ${generateBytes(args[1], 'region')}\nCode: ${partyToken}\nIP: ${ip}`);
129 | msg.channel.send(embed);
130 | });
131 | });
132 | }
133 | });
134 |
135 | }
136 | else {
137 |
138 | if (args[1].startsWith('#')) args[1] = args[1].split('#')[1];
139 | requestV4('getToken', generateBytes('eu', false, args[1].toUpperCase()), body => {
140 |
141 | if (!body.status) {
142 | embed.setTitle('invalid party code');
143 | return msg.channel.send(embed);
144 | }
145 |
146 | let ip = `${body.endpoints.https.includes('ip') ? 'ws://' + body.endpoints.https : 'wss://' + body.endpoints.https}?party_id=${args[1].toUpperCase()}`;
147 |
148 | new Bot(ip, (leaderboard, friends, minimap) => {
149 | let leaderboardx = '```' + leaderboard.slice(0, 10).join('') + '```';
150 | let totalMass = 0;
151 | minimap.forEach(m => {
152 | totalMass += m.mass;
153 | });
154 | totalMass = 1e3 > totalMass ? totalMass : Math.round(totalMass / 100) / 10 + 'k';
155 | embed.addField('Party info', `\nLEADERBOARD\n\n${leaderboardx}\nIP: ${ip}\nTotal mass: ${totalMass}\nPlayers: ${leaderboard.length}\nFriends: ${friends}\nStatus: ${body.status}`);
156 | msg.channel.send(embed);
157 | });
158 |
159 | });
160 | }
161 | break;
162 |
163 | }
164 | });
165 |
166 | function requestV4(action, token, callback) {
167 | request({
168 | url: `https://webbouncer-live-v8-0.agario.miniclippt.com/v4/${action}`,
169 | method: 'POST',
170 | headers: {
171 | 'Content-Type': 'application/json',
172 | 'x-client-version': global.versionInt,
173 | 'x-support-proto-version': global.protoVersion
174 | },
175 | body: Buffer.from(token)
176 | }).then(body => {
177 | body = JSON.parse(body);
178 | callback(body);
179 | }).catch(e => {
180 | callback(e);
181 | });
182 | }
183 |
184 | function generateBytes(args, mode, token) {
185 |
186 | const regions = {
187 | sg: 'SG-Singapore',
188 | us: 'US-Atlanta',
189 | eu: 'EU-London',
190 | tk: 'TK-Turkey',
191 | br: 'BR-Brazil',
192 | ru: 'RU-Russia',
193 | jp: 'JP-Tokyo',
194 | cn: 'CN-China',
195 | };
196 |
197 | if (mode == 'region') return regions[args];
198 |
199 | const getOwnPropertyNames = function (data) {
200 | output.push(data.length);
201 | for (let value = 0; value < data.length; value++) {
202 | output.push(data.charCodeAt(value));
203 | }
204 | };
205 |
206 | if (token) {
207 | var output = [10, 4 + regions[args].length + 4, 10];
208 | getOwnPropertyNames(regions[args]);
209 | output.push(18);
210 | getOwnPropertyNames(':ffa');
211 | output.push(26, 8, 10);
212 | getOwnPropertyNames(token);
213 | return new Uint8Array(output);
214 | }
215 |
216 | output = [10, 4 + regions[args].length + mode.length, 10];
217 | getOwnPropertyNames(regions[args]);
218 | output.push(18);
219 | getOwnPropertyNames(mode);
220 | return new Uint8Array(output);
221 | }
222 |
223 | function update() {
224 | setInterval(async () => {
225 | let xclientkey = await request('https://agar.io/mc/agario.js');
226 | let versionString = xclientkey.match(/(?<=versionString=")[^"]+/)[0];
227 | let versionInt = parseInt(versionString.split('.')[0]) * 10000 + parseInt(versionString.split('.')[1]) * 100 + parseInt(versionString.split('.')[2]);
228 | const { groups: { protoVersion } } = /proto-version.+?"(?\d+.+?)"/gm.exec(xclientkey);
229 | let core = await request('https://agar.io/agario.core.js');
230 | let protocolVersion = core.match(/d;..\(.,(\d+)\);/)[1];
231 | global.protocolVersion = protocolVersion;
232 | global.protoVersion = protoVersion;
233 | global.versionInt = versionInt;
234 | }, 3.6e+6);
235 | }
236 |
237 | class Bot {
238 | constructor(server, callback) {
239 |
240 | this.encryptionKey = null;
241 | this.decryptionKey = null;
242 | this.callback = callback;
243 | this.friendsCounter = 0;
244 | this.leaderboard = [];
245 | this.server = server;
246 | this.connect();
247 | }
248 |
249 | connect() {
250 | this.ws = new WebSocket(this.server);
251 | this.ws.binaryType = 'nodebuffer';
252 |
253 | this.ws.onopen = () => {
254 | let buf = new Buffer.alloc(5);
255 | buf.writeUInt8(254, 0);
256 | buf.writeUInt32LE(global.protocolVersion, 1);
257 | this.ws.send(buf);
258 |
259 | buf = new Buffer.alloc(5);
260 | buf.writeUInt8(255, 0);
261 | buf.writeUInt32LE(global.versionInt, 1);
262 | this.ws.send(buf);
263 | };
264 |
265 | this.ws.onmessage = msg => {
266 | msg = new Buffer.from(msg.data);
267 | let offset = 0;
268 |
269 | if (this.decryptionKey) msg = this.xor(msg, this.decryptionKey);
270 |
271 | switch (msg.readUInt8(offset++)) {
272 | case 241:
273 | this.decryptionKey = global.versionInt ^ msg.readInt32LE(offset);
274 | msg = Array.from(msg).splice(5, 11);
275 |
276 | this.encryptionKey = murmur2(this.ws.url, new Uint8Array(msg), 255);
277 | break;
278 |
279 | case 54:
280 | if (!this.minimap) return;
281 | for (let i = 0; offset < msg.byteLength;) {
282 | this.sizrex = false;
283 | let flag = msg.readUInt8(offset++);
284 | if (flag & 2) {
285 | let x, d = '';
286 | while ((x = msg.readUInt8(offset++)) != 0) {
287 | d += String.fromCharCode(x);
288 | }
289 | if (!d || d == '' || d.toLowerCase().match(/agarbot|morebots/)) this.sizrex = true;
290 | if (i >= 11) this.leaderboard.push(`${i + 1}. ${this.sizrex ? 'OP-Bots.com' : decodeURIComponent(escape(d))}\n`);
291 | else this.leaderboard.push(`${i + 1}. ${this.sizrex ? 'OP-Bots.com' : decodeURIComponent(escape(d))} (${this.shortMass(this.minimap[i++].mass)})\n`);
292 | }
293 | if (flag & 4) offset += 4;
294 | if (flag & 16) this.friendsCounter++;
295 | }
296 | this.callback(this.leaderboard, this.friendsCounter, this.minimap);
297 | this.ws.close();
298 | break;
299 |
300 | case 69:
301 | let x = 0;
302 | let d = msg.readUInt16LE(offset);
303 | for (offset += 2, this.minimap = [], x = 0; x < d; x++) {
304 | let x = msg.readInt32LE(offset);
305 | offset += 4;
306 | let y = msg.readInt32LE(offset);
307 | offset += 4;
308 | let mass = msg.readInt32LE(offset);
309 | offset += 5;
310 | let size = ~~Math.sqrt(100 * mass);
311 | this.minimap.push({
312 | x: x,
313 | y: y,
314 | size: size,
315 | mass: mass,
316 | });
317 | }
318 | this.minimap.sort((a, b) => { return b.mass - a.mass; });
319 | break;
320 | }
321 | };
322 | }
323 |
324 | shortMass(e) {
325 | return 1e3 > e ? e : Math.round(e / 100) / 10 + 'k';
326 | }
327 |
328 | xor(buf, xorKey) {
329 | const newBuf = new Buffer.alloc(buf.byteLength);
330 | for (let i = 0; i < buf.byteLength; i++) newBuf.writeUInt8(buf.readUInt8(i) ^ xorKey >>> i % 4 * 8 & 255, i);
331 | return newBuf;
332 | }
333 | }
334 |
335 | bot.on('error', err => {
336 | if (err.msg === 'ECONNRESET' || err.msg === 'ERROR' || err.statusCode === 520 || !err.msg) return;
337 | console.log(err);
338 | });
339 |
340 | bot.login(config.token);
341 |
--------------------------------------------------------------------------------