[init] Initialize repo & impl authlib
This commit is contained in:
commit
bac5932ab8
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
# Node.js
|
||||
node_modules/
|
||||
|
||||
# Editor
|
||||
.vscode/
|
||||
*.swp
|
||||
|
||||
# Credential
|
||||
credential.json
|
||||
session.json
|
19
README.md
Normal file
19
README.md
Normal file
@ -0,0 +1,19 @@
|
||||
# Compass Bot
|
||||
|
||||
## 登录
|
||||
|
||||
在项目根目录下创建 `credentials.json`,包含如下内容:
|
||||
|
||||
```json
|
||||
{
|
||||
"endpoint": "Yggdrasil API的调用点",
|
||||
"username": "用户名",
|
||||
"password": "密码"
|
||||
}
|
||||
```
|
||||
|
||||
## 运行
|
||||
|
||||
```sh
|
||||
node index.js <server_ip:server_port> --version=<minecraft_version> --owner=<bot_owner>
|
||||
```
|
109
authlib.js
Normal file
109
authlib.js
Normal file
@ -0,0 +1,109 @@
|
||||
const axios = require('axios');
|
||||
const { readJsonFile, writeJsonFile } = require('./utils');
|
||||
|
||||
async function readCredentials() {
|
||||
let credentials = await readJsonFile('credential.json');
|
||||
if (!credentials.endpoint_auth) {
|
||||
credentials.endpoint_auth = credentials.endpoint + '/authserver';
|
||||
}
|
||||
|
||||
if (!credentials.endpoint_session) {
|
||||
credentials.endpoint_session = credentials.endpoint + '/sessionserver';
|
||||
}
|
||||
|
||||
if (!credentials.handle) {
|
||||
credentials.handle = credentials.username;
|
||||
}
|
||||
|
||||
return credentials;
|
||||
}
|
||||
|
||||
async function readSessionCache() {
|
||||
return readJsonFile('session.json');
|
||||
}
|
||||
|
||||
async function storeSessionCache(data) {
|
||||
return writeJsonFile('session.json', data);
|
||||
}
|
||||
|
||||
async function yggdrailLogin(credentials) {
|
||||
let account_info = (await axios.post(`${credentials.endpoint_auth}/authenticate`, {
|
||||
username: credentials.handle,
|
||||
password: credentials.password,
|
||||
})).data;
|
||||
|
||||
let profile_info = null;
|
||||
for (let info of account_info.availableProfiles) {
|
||||
if (info.name == credentials.username) {
|
||||
profile_info = info;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let session_info = (await axios.post(`${credentials.endpoint_auth}/refresh`, {
|
||||
accessToken: account_info.accessToken,
|
||||
clientToken: account_info.clientToken,
|
||||
selectedProfile: profile_info,
|
||||
})).data;
|
||||
|
||||
return {
|
||||
accessToken: session_info.accessToken,
|
||||
clientToken: session_info.clientToken,
|
||||
selectedProfile: session_info.selectedProfile,
|
||||
};
|
||||
}
|
||||
|
||||
async function yggdrasilAuth(credentials) {
|
||||
if (credentials == null) {
|
||||
credentials = await readCredentials();
|
||||
}
|
||||
|
||||
let cache = await readSessionCache();
|
||||
if (cache != null && cache.selectedProfile?.name != credentials.username) {
|
||||
cache = null;
|
||||
}
|
||||
|
||||
if (cache != null) {
|
||||
let vres = await axios.post(`${credentials.endpoint_auth}/validate`, {
|
||||
accessToken: cache.accessToken,
|
||||
clientToken: cache.clientToken,
|
||||
}, {
|
||||
validateStatus: function (status) {
|
||||
return status === 403 || status === 204;
|
||||
}
|
||||
});
|
||||
|
||||
if (vres.status == 403) {
|
||||
cache = null;
|
||||
}
|
||||
}
|
||||
|
||||
let session_info = cache;
|
||||
if (session_info == null) {
|
||||
session_info = await yggdrailLogin(credentials);
|
||||
storeSessionCache(session_info);
|
||||
}
|
||||
|
||||
return session_info;
|
||||
}
|
||||
|
||||
async function mineflayer() {
|
||||
let credentials = await readCredentials();
|
||||
let session_info = await yggdrasilAuth(credentials);
|
||||
return {
|
||||
username: credentials.username,
|
||||
authServer: credentials.endpoint_auth,
|
||||
sessionServer: credentials.endpoint_session,
|
||||
auth: (client, options) => {
|
||||
client.username = credentials.username;
|
||||
client.session = session_info;
|
||||
options.accessToken = session_info.accessToken;
|
||||
options.clientToken = session_info.clientToken;
|
||||
options.haveCredentials = true;
|
||||
client.emit('session', session_info);
|
||||
options.connect(client);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = { readCredentials, yggdrasilAuth, mineflayer };
|
21
control.js
Normal file
21
control.js
Normal file
@ -0,0 +1,21 @@
|
||||
const mineflayer = require('mineflayer');
|
||||
const { pathfinder, Movements } = require('mineflayer-pathfinder');
|
||||
const { GoalNear } = require('mineflayer-pathfinder').goals
|
||||
|
||||
function gotoOwner(bot, context) {
|
||||
let owner = context.owner();
|
||||
if (!owner) {
|
||||
console.log('Owner is not configured or is offline');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!owner.entity) {
|
||||
console.log('Owner is out of sight');
|
||||
return false;
|
||||
}
|
||||
let pos = owner.entity.position;
|
||||
bot.pathfinder.setMovements(context.peaceful_tactic);
|
||||
bot.pathfinder.setGoal(new GoalNear(pos.x, pos.y, pos.z, 1))
|
||||
}
|
||||
|
||||
module.exports = { gotoOwner };
|
75
index.js
Normal file
75
index.js
Normal file
@ -0,0 +1,75 @@
|
||||
const authlib = require('./authlib');
|
||||
const mineflayer = require('mineflayer');
|
||||
const { pathfinder, Movements } = require('mineflayer-pathfinder');
|
||||
const { GoalNear } = require('mineflayer-pathfinder').goals
|
||||
const repl = require('repl');
|
||||
const domain = require('domain');
|
||||
const yargs = require('yargs');
|
||||
const { parseURL } = require('./utils');
|
||||
|
||||
const args = yargs.option('protocal', {
|
||||
description: 'minecraft server version',
|
||||
type: 'string',
|
||||
requiresArg: false,
|
||||
}).option('owner', {
|
||||
description: 'bot owner name',
|
||||
type: 'string',
|
||||
requiresArg: false
|
||||
}).help().alias('help', 'h').argv;
|
||||
|
||||
const [host, port] = parseURL(args._[0]);
|
||||
const version = args.protocal;
|
||||
|
||||
const repl_domain = domain.create();
|
||||
|
||||
repl_domain.on('error', (err) => {
|
||||
console.error('Caught error:', err);
|
||||
});
|
||||
|
||||
async function main() {
|
||||
let authinfo = await authlib.mineflayer();
|
||||
let bot = mineflayer.createBot({
|
||||
host, port, version,
|
||||
...authinfo,
|
||||
});
|
||||
bot.loadPlugin(pathfinder);
|
||||
|
||||
bot.on('kicked', console.warn);
|
||||
bot.on('error', console.warn);
|
||||
|
||||
bot.once('spawn', () => {
|
||||
repl_domain.run(() => {
|
||||
let r = repl.start({
|
||||
prompt: "bot > ",
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
color: true,
|
||||
terminal: true,
|
||||
});
|
||||
|
||||
const deafult_tactic = new Movements(bot);
|
||||
const peaceful_tactic = new Movements(bot);
|
||||
peaceful_tactic.canDig = false;
|
||||
peaceful_tactic.scafoldingBlocks = [];
|
||||
|
||||
r.context.deafult_tactic = deafult_tactic;
|
||||
r.context.peaceful_tactic = peaceful_tactic;
|
||||
r.context.bot = bot;
|
||||
r.context.authinfo = authinfo;
|
||||
r.context.Movements = Movements;
|
||||
r.context.GoalNear = GoalNear;
|
||||
r.context.mineflayer = mineflayer;
|
||||
r.context.owner = () => {
|
||||
if (!args.owner) {
|
||||
return null;
|
||||
}
|
||||
return bot.players[args.owner];
|
||||
};
|
||||
r.context.control = require('./control');
|
||||
r.context.Vec3 = require('vec3').Vec3;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
main();
|
||||
|
2130
package-lock.json
generated
Normal file
2130
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
9
package.json
Normal file
9
package.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"axios": "^1.6.0",
|
||||
"mineflayer": "^4.14.0",
|
||||
"mineflayer-pathfinder": "^2.4.5",
|
||||
"prismarine-viewer": "^1.25.0",
|
||||
"yargs": "^17.7.2"
|
||||
}
|
||||
}
|
22
utils.js
Normal file
22
utils.js
Normal file
@ -0,0 +1,22 @@
|
||||
const fs = require('fs/promises');
|
||||
|
||||
async function readJsonFile(path) {
|
||||
try {
|
||||
const data = await fs.readFile(path, 'utf8');
|
||||
return JSON.parse(data);
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function writeJsonFile(path, data) {
|
||||
const json_string = JSON.stringify(data);
|
||||
return fs.writeFile(path, json_string, 'utf8');
|
||||
}
|
||||
|
||||
function parseURL(url) {
|
||||
const [host, port] = url.split(':');
|
||||
return [host, port ? parseInt(port) : undefined];
|
||||
}
|
||||
|
||||
module.exports = { readJsonFile, writeJsonFile, parseURL };
|
Loading…
x
Reference in New Issue
Block a user