[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