优化项目结构,修改注册流程,添加管理员指令
continuous-integration/drone/push Build is failing Details

This commit is contained in:
Qumolama.d 2022-07-05 01:35:51 +08:00
parent 32f7577a07
commit 904b906b63
Signed by: Lama3L9R
GPG Key ID: 1762AFC05157CE18
10 changed files with 369 additions and 484 deletions

View File

@ -27,6 +27,6 @@
},
"scripts": {
"dev": "node launch-development.js",
"test": "cross-env NODE_OPTIONS=--experimental-vm-modules UNIT_TEST=1 NODE_NO_WARNINGS=1 jest",
"test": "cross-env NODE_OPTIONS=--experimental-vm-modules UNIT_TEST=1 NODE_NO_WARNINGS=1 jest"
}
}

View File

@ -3,7 +3,7 @@ export const config = {
url: '您的mongodb链接',
},
server: {
port: 3000,
port: 30,
skinDomain: [ "assets.lama.icu", 'textures.minecraft.net' ],
serverName: "老色批世界树",
advanced: { // 详情可见 -> https://github.com/yushijinhun/authlib-injector/wiki/Yggdrasil-%E6%9C%8D%E5%8A%A1%E7%AB%AF%E6%8A%80%E6%9C%AF%E8%A7%84%E8%8C%83#meta-%E4%B8%AD%E7%9A%84%E5%85%83%E6%95%B0%E6%8D%AE

View File

@ -3,17 +3,14 @@ import { mongoose } from 'mongoose'
import * as Hooks from './hooks.js'
import * as AuthenticateRoutings from './routes/authenticate.js'
import * as SessionServerRoutings from './routes/session.js'
import * as AdvancedRoutings from './routes/advanced.js'
import * as APIRoutings from './routes/api.js'
import * as WebAPIRoutings from './routes/web-api.js'
import { config } from './config.js'
import { readFileSync } from 'fs'
import { Scenes, session, Telegraf } from 'telegraf'
import { allScenes, registerAllPlayerCommands } from './telegram/player-commands.js'
import { registerAllPlayerCommands } from './telegram/player-commands.js'
import { Player } from './models/player.js'
import fastifySwagger from '@fastify/swagger'
import { S3Client } from '@aws-sdk/client-s3'
import multipart from '@fastify/multipart'
import pino from 'pino'
String.prototype._split = String.prototype.split
@ -99,12 +96,6 @@ export const setup = async () => {
server.addContentTypeParser('image/png', (_, payload, done) => {
done(null, payload)
})
server.register(multipart, {
limit: {
fileSize: 5 * 1024 * 1024 // 5MB/40Mb
}
})
server.register(fastifySwagger, {
routePrefix: '/docs',
swagger: {
@ -132,12 +123,11 @@ export const setup = async () => {
server.route(SessionServerRoutings.join)
server.route(SessionServerRoutings.hasJoined)
server.route(SessionServerRoutings.profile)
server.route(SessionServerRoutings.profiles)
server.route(AdvancedRoutings.meta)
server.route(AdvancedRoutings.status)
server.route(APIRoutings.profiles)
server.route(WebAPIRoutings.meta)
server.route(WebAPIRoutings.status)
server.route(WebAPIRoutings.telegramBind)
server.route(WebAPIRoutings.login)
server.route(WebAPIRoutings.register)
server.route(WebAPIRoutings.textures)
@ -157,20 +147,14 @@ export const setup = async () => {
cape: 'assets.lama.icu/textures/cape/default.png'
},
registerDate: Date.now(),
permissions: [{ node: 'login', allowed: true, duration: 0, eternal: true, startDate: Date.now(), highPriority: false }],
telegramBind: {
username: 'test',
permissions: ['login'],
binding: {
platform: 'telegram',
username: 'Qumolama',
verified: true,
}
}).save()
}
const stage = new Scenes.Stage([ ...allScenes ])
telegraf.use(session())
telegraf.use(stage.middleware())
// server.log.info("老色批世界树 > 初始化中...")
registerAllPlayerCommands()
}

View File

@ -16,9 +16,12 @@ export const Player = mongoose.model("Player", new mongoose.Schema({
cape: String,
},
registerDate: Number,
permissions: [{ node: String, allowed: Boolean, duration: Number, startDate: Number, highPriority: Boolean }], // ban -> true
telegramBind: {
permissions: [{
type: String
}],
binding: {
username: String,
platform: String,
verified: Boolean,
}
}))

View File

@ -1,130 +0,0 @@
import { config, getOverrideHandler, getOverridePreHandler } from "../config.js"
export const meta = {
method: "GET",
url: "/",
schema: {
summary: "获取 Meta 信息",
description: "获取服务器元数据。详情请见 authlib-injector 文档。",
tags: [ "api" ],
response: {
200: {
"type": "object",
"properties": {
"meta": {
"type": "object",
"properties": {
"serverName": {
"type": "string"
},
"implementationName": {
"type": "string"
},
"implementationVersion": {
"type": "string"
}
}
},
"skinDomains": {
"type": "array",
"items": {
"type": "string"
}
},
"signaturePublickey": {
"type": "string"
}
}
}
}
},
preHandler: getOverridePreHandler("/"),
handler: getOverrideHandler("/") ?? async function(req, rep) {
rep.code(200).send({
meta: {
serverName: config.server.serverName,
implementationName: "lsp-yggdrasil",
implementationVersion: "1.0",
},
skinDomains: config.server.skinDomain,
signaturePublickey: this.keys.publicKey
})
}
}
export const status = {
method: "GET",
url: "/status",
schema: {
summary: "获取服务器信息",
description: "获取服务器基本信息",
tags: [ "webapi" ],
response: {
200: {
"type": "object",
"properties": {
"public": {
"type": "string"
},
"version": {
"type": "string"
},
"api": {
"type": "object",
"properties": {
"yggdrasil": {
"type": "string"
},
"webapi": {
"type": "string"
}
}
},
"name": {
"type": "string"
},
"server": {
"type": "string"
},
"env": {
"type": "object",
"properties": {
"node": {
"type": "string"
},
"os": {
"type": "string"
},
"arch": {
"type": "string"
},
"t": {
"type": "number"
},
}
}
}
}
}
},
preHandler: getOverridePreHandler("/status"),
handler: getOverrideHandler("/status") ?? async function(req, rep) {
rep.code(200).send({
public: this.keys.publicKey,
version: "1.0",
name: config.server.serverName,
api: {
yggdrasil: "authlib-injector",
webapi: "standard-1.0"
},
server: "lsp-yggdrasil",
env: {
"node": process.version,
"os": process.platform,
"arch": process.arch,
"t": Date.now(),
}
})
}
}

View File

@ -1,73 +0,0 @@
import { getOverrideHandler, getOverridePreHandler } from "../config.js"
import { uuidToNoSymboUUID } from "../generator.js"
import { Player } from "../models/player.js"
export const profiles = {
method: 'POST',
url: '/api/profiles/minecraft',
schema: {
summary: "获取玩家信息",
description: "获取玩家信息。详情请见 authlib-injector 文档。",
tags: [ "api" ],
body: {
type: 'array',
items: {
type: 'string'
}
},
response: {
200: {
type: 'array',
items: {
type: 'object',
properties: {
id: {
type: 'string'
},
name: {
type: 'string'
}
}
}
}
}
},
preHandler: getOverridePreHandler('/api/profiles/minecraft') ?? async function(req, rep) {
if(req.body.length >= 3) {
await rep.code(400).send({
error: "IllegalArgumentException",
errorMessage: "请求内容过长, 最大请求玩家数: 2",
cause: "请求内容过长, 最大请求玩家数: 2"
})
}
},
handler: getOverrideHandler('/api/profiles/minecraft') ?? async function (req, rep) {
const { body } = req
const profiles = await Player.find({ username: { $in: body } })
return await rep.code(200).send(profiles.map(profile => ({
id: uuidToNoSymboUUID(profile.uuid),
name: profile.username
})))
}
}
export const telegramBind = {
method: 'GET',
url: '/api/telegram/bind/:uuid',
schema: {
summary: "获取 Telegram 绑定",
description: "获取玩家对应 Telegram 绑定,返回纯文本。",
tags: [ "webapi" ],
response: {
200: {
type: 'string'
}
}
},
preHandler: getOverridePreHandler('/api/telegram/bind/:uuid'),
handler: getOverrideHandler('/api/telegram/bind/:uuid') ?? async function (req, rep) {
const { uuid } = req.params
const player = await Player.findOne({ uuid: uuidToNoSymboUUID(uuid) })
await rep.code(200).send(player?.telegram ?? "")
}
}

View File

@ -72,9 +72,7 @@ export const authenticate = {
}
const player = await PlayerModel.Player.findOne({ email: username, password: createHash('sha256').update(password).digest().toString('hex').toLowerCase() })
if(!player || !player.permissions.some((it) => {
return it.node === 'login' && it.allowed && (it.duration === 0 || it.startDate + it.duration > Date.now())
})) {
if(!player || player.permissions.indexOf("login") === -1) {
return await rep.code(401).send({
error: "Unauthorized",
errorMessage: "用户名或密码错误",
@ -82,11 +80,11 @@ export const authenticate = {
})
}
if(!player.telegramBind || !player.telegramBind.verified) {
if(!player.binding || !player.binding.verified) {
return await rep.code(401).send({
error: "Unauthorized",
errorMessage: "未绑定 Telegram 账号或账号验证未通过,登录请求已禁止",
cause: "未绑定 Telegram 账号或账号验证未通,登录请求已禁止"
errorMessage: "您的账号未验证,登录请求已禁止",
cause: "您的账号未验证,登录请求已禁止"
})
}

View File

@ -1,5 +1,5 @@
import { getOverrideHandler, getOverridePreHandler } from '../config.js'
import { toSymboUUID } from '../generator.js'
import { toSymboUUID, uuidToNoSymboUUID } from '../generator.js'
import { getPlayerSerialization, Player, PlayerSeriliazationSchema } from '../models/player.js'
import { Token } from '../models/token.js'
@ -177,3 +177,52 @@ export const profile = {
}
}
export const profiles = {
method: 'POST',
url: '/api/profiles/minecraft',
schema: {
summary: "获取玩家信息",
description: "获取玩家信息。详情请见 authlib-injector 文档。",
tags: [ "api" ],
body: {
type: 'array',
items: {
type: 'string'
}
},
response: {
200: {
type: 'array',
items: {
type: 'object',
properties: {
id: {
type: 'string'
},
name: {
type: 'string'
}
}
}
}
}
},
preHandler: getOverridePreHandler('/api/profiles/minecraft') ?? async function(req, rep) {
if(req.body.length >= 3) {
await rep.code(400).send({
error: "IllegalArgumentException",
errorMessage: "请求内容过长, 最大请求玩家数: 2",
cause: "请求内容过长, 最大请求玩家数: 2"
})
}
},
handler: getOverrideHandler('/api/profiles/minecraft') ?? async function (req, rep) {
const { body } = req
const profiles = await Player.find({ username: { $in: body } })
return await rep.code(200).send(profiles.map(profile => ({
id: uuidToNoSymboUUID(profile.uuid),
name: profile.username
})))
}
}

View File

@ -3,9 +3,12 @@ import { Player } from "../models/player.js"
import { createHash } from "crypto"
import { generateToken, uuid } from "../generator.js"
import { Token } from "../models/token.js"
import { s3Instance } from "../index.js"
import { s3Instance, server } from "../index.js"
import { ImageSecurity } from "../secure.js"
import { PutObjectCommand } from "@aws-sdk/client-s3"
import crypto from 'crypto'
const defaultSkin = "https://textures.minecraft.net/texture/ddb8684e59f771666bde5f411fcb2e495c452f2ecabc31981bc132ac71bdd394"
const BASE_RESPONSE = {
err: {
@ -149,9 +152,7 @@ export const login = {
});
}
if(!user.permissions.some((it) => {
return it.node === 'login' && it.allowed && (it.duration === 0 || it.startDate + it.duration > Date.now())
})) {
if(user.permissions.indexOf("login") === -1) {
return await rep.code(401).send({
err: 0.337187,
msg: "泻药,宁滴账号已被封禁"
@ -211,9 +212,13 @@ export const register = {
type: 'string',
description: '邮箱'
},
telegramId: {
invitationCode: {
type: 'string',
description: 'telegramId'
description: 'invitationCode'
},
validationCode: {
type: 'string',
description: 'invitationCode'
},
textureMigrations: {
anyOf: [
@ -244,68 +249,27 @@ export const register = {
200: {
type: 'object',
properties: {
...BASE_RESPONSE,
extra: {
username: {
type: 'string',
description: '用户名'
},
password: {
type: 'string',
description: '密码'
},
email: {
type: 'string',
description: '邮箱'
},
telegramId: {
type: 'string',
description: 'telegramId'
},
textureMigrations: {
anyOf: [
{
type: 'object',
description: '纹理迁移',
properties: {
skin: {
anyOf: [
{ type: 'string', description: '皮肤' },
{ type: 'null' }
]
},
cape: {
anyOf: [
{ type: 'string', description: '披风' },
{ type: 'null' }
]
}
}
},
{ type: 'null' }
]
}
}
...BASE_RESPONSE
}
},
},
},
preHandler: getOverridePreHandler('/api/register'),
handler: getOverrideHandler('/api/register') ?? async function(req, rep) {
const { username, password, email, telegramId, textureMigrations } = req.body
const { username, password, email, invitationCode, textureMigrations, validationCode } = req.body
const user = await Player.findOne({ $or: [
{ email: email }, { username: username }
] })
if (user) {
return await rep.code(401).send({
err: 1,
err: 1.143688,
msg: "用户名已存在"
})
}
if(username == 0 || password == 0 || email == 0 || telegramId == 0) {
return await rep.code(401).send({
err: 1,
err: 1.143688,
msg: "用户名/密码/邮箱/telegramId不能为空"
})
}
@ -313,15 +277,41 @@ export const register = {
const textues = { }
if(textureMigrations) {
if(textureMigrations.skin != 0 && textureMigrations.skin) {
textues.skin = textureMigrations.skin
}
textues.skin = textureMigrations.skin ?? defaultSkin
if(textureMigrations.cape != 0 && textureMigrations.cape) {
textues.cape = textureMigrations.cape
}
}
/*
Examples:
{
p(latform): "name",
n(ame): "name",
t(o): "email",
}
v -> Signature
*/
const { p, n, t, v } = JSON.parse(crypto.privateDecrypt(server.keys.privateKey, Buffer.from(invitationCode)).toString())
if(!crypto.createVerify('rsa-sha1').update(Buffer.from(invitationCode)).verify(server.keys.publicKey, Buffer.from(v, 'hex'))) {
return await rep.code(401).send({
err: 1.143688,
msg: "邀请码验证失败!非法邀请码!"
})
}
if(t !== email) {
return await rep.code(401).send({
err: 1.143688,
msg: "邀请码验证失败!这邀请码不属于你!"
})
}
const newUser = new Player({
username,
password: createHash("sha256").update(password).digest('hex'),
@ -329,10 +319,11 @@ export const register = {
uuid: uuid('LSPlayer:' + email),
textues,
registerDate: Date.now(),
permissions: [{ node: 'login', allowed: true, duration: -1, startDate: Date.now(), highPriority: false }],
telegramBind: {
username: telegramId,
verified: false
permissions: ['login'],
binding: {
platform: p,
username: n,
verified: true,
}
});
await newUser.save()
@ -464,4 +455,164 @@ export const uploadTexture = {
msg: ""
})
}
}
export const telegramBind = {
method: 'GET',
url: '/api/binding/:uuid',
schema: {
summary: "获取 Telegram 绑定",
description: "获取玩家对应 Telegram 绑定,返回纯文本。",
tags: [ "webapi" ],
response: {
200: {
type: 'object',
properties: {
username: {
type: 'string'
},
platform: {
type: 'string'
},
verified: {
type: 'boolean'
}
}
}
}
},
preHandler: getOverridePreHandler('/api/binding/:uuid'),
handler: getOverrideHandler('/api/binding/:uuid') ?? async function (req, rep) {
const { uuid } = req.params
return await rep.code(200).send((await Player.findOne({ uuid }))?.binding ?? { username: '', platform: '', verified: false })
}
}
export const meta = {
method: "GET",
url: "/",
schema: {
summary: "获取 Meta 信息",
description: "获取服务器元数据。详情请见 authlib-injector 文档。",
tags: [ "api" ],
response: {
200: {
"type": "object",
"properties": {
"meta": {
"type": "object",
"properties": {
"serverName": {
"type": "string"
},
"implementationName": {
"type": "string"
},
"implementationVersion": {
"type": "string"
}
}
},
"skinDomains": {
"type": "array",
"items": {
"type": "string"
}
},
"signaturePublickey": {
"type": "string"
}
}
}
}
},
preHandler: getOverridePreHandler("/"),
handler: getOverrideHandler("/") ?? async function(req, rep) {
rep.code(200).send({
meta: {
serverName: config.server.serverName,
implementationName: "lsp-yggdrasil",
implementationVersion: "1.0",
},
skinDomains: config.server.skinDomain,
signaturePublickey: this.keys.publicKey
})
}
}
export const status = {
method: "GET",
url: "/status",
schema: {
summary: "获取服务器信息",
description: "获取服务器基本信息",
tags: [ "webapi" ],
response: {
200: {
"type": "object",
"properties": {
"public": {
"type": "string"
},
"version": {
"type": "string"
},
"api": {
"type": "object",
"properties": {
"yggdrasil": {
"type": "string"
},
"webapi": {
"type": "string"
}
}
},
"name": {
"type": "string"
},
"server": {
"type": "string"
},
"env": {
"type": "object",
"properties": {
"node": {
"type": "string"
},
"os": {
"type": "string"
},
"arch": {
"type": "string"
},
"t": {
"type": "number"
},
}
}
}
}
}
},
preHandler: getOverridePreHandler("/status"),
handler: getOverrideHandler("/status") ?? async function(req, rep) {
rep.code(200).send({
public: this.keys.publicKey,
version: "1.0",
name: config.server.serverName,
api: {
yggdrasil: "authlib-injector",
webapi: "standard-1.0"
},
server: "lsp-yggdrasil",
env: {
"node": process.version,
"os": process.platform,
"arch": process.arch,
"t": Date.now(),
}
})
}
}

View File

@ -1,205 +1,108 @@
import { Scenes } from 'telegraf'
import { telegraf, server } from '../index.js'
import { createHash } from 'crypto'
import axios from 'axios'
import { uuid } from '../generator.js'
import { Player } from '../models/player.js'
import crypto from 'crypto'
export const registerAllPlayerCommands = async () => {
await register()
export const registerAllPlayerCommands = () => {
adminCreateInvitation()
adminBan()
adminRevokeBan()
userCreateInvitation()
}
const defaultSkin = "http://textures.minecraft.net/texture/ddb8684e59f771666bde5f411fcb2e495c452f2ecabc31981bc132ac71bdd394"
const registerWizard = new Scenes.WizardScene('REGISTRIATION_WIZARD',
(ctx) => {
ctx.reply('欢迎来到注册流程,如果想中途取消使用指令 "/cancle" ,请先告诉我您的用户名')
ctx.scene.session.data = {
username: ctx.from.username
}
return ctx.wizard.next()
},
(ctx) => {
if(!ctx.message.text || ctx.message.text < 3) {
return ctx.reply('用户名不能少于 3 个字符!请重新输入')
const adminCreateInvitation = () => {
telegraf.command('invite', async (ctx) => {
const args = ctx.update.message.text.split(' ').slice(1)
if(args.length < 2) {
ctx.reply('Usage: /invite <username> <email> [platform]')
return
}
const username = ctx.from.username === 'Lapis_Apple' ? 'LSP' : ctx.message.text
ctx.reply(`请设置玩家 ${username} 的密码`)
ctx.scene.session.data.playerUsername = username
const player = await Player.findOne({ 'binding.platform': 'telegram', 'binding.username': ctx.message.from.username })
return ctx.wizard.next()
},
async (ctx) => {
if(!ctx.message.text || ctx.message.text < 6) {
return ctx.reply('密码不能少于 6 个字符!请重新输入')
if(!player || player.permissions.indexOf('admin') === -1) {
return ctx.reply('配钥匙吗?什么?你配?哦不你不配!')
}
ctx.scene.session.data.password = createHash('sha256').update(ctx.message.text).digest('hex')
const duplicated = await Player.findOne({ password: ctx.scene.session.data.password })
if(duplicated) {
return ctx.reply(`该密码hash为${ctx.scene.session.data.password.substring(3,10)} 已检测到数据库中存在重复密码(${JSON.stringify({d: duplicated})}),为了安全请重新输入`)
}
ctx.reply(`该密码hash为${ctx.scene.session.data.password.substring(3,10)} 请确认是否正确,若正确请输入 'confirm'`)
return ctx.wizard.next()
},
(ctx) => {
if(!ctx.message.text || ctx.message.text !== 'confirm') {
return ctx.reply('请输入 "confirm" 确认您的密码,或者输入 /cancle 取消注册')
}
ctx.replyWithMarkdownV2("请输入您的邮箱。**注意:我们并不会向您发送注册确认,邮箱仅作为符合 authlib\\-injector 登陆规范在登陆时使用**")
return ctx.wizard.next()
},
(ctx) => {
if(!ctx.message.text || ctx.message.text.length < 6) {
return ctx.reply('邮箱不能少于 6 个字符!请重新输入')
}
ctx.scene.session.data.email = ctx.message.text
ctx.reply(`是否需要同步同用户名正版玩家的皮肤和披风? 输入 'yes' 或者 'no'`)
return ctx.wizard.next()
},
(ctx) => {
if(!ctx.message.text || ctx.message.text !== 'yes' && ctx.message.text !== 'no') {
return ctx.reply('请输入 "yes" 或者 "no"')
}
ctx.scene.session.data.sync = ctx.message.text === 'yes'
ctx.reply(`请问您是否有注册邀请码? 有请输入邀请码,没有输入 'skip'`)
return ctx.wizard.next()
},
(ctx) => {
if(!ctx.message.text || ctx.message.text === 'skip') {
ctx.scene.session.data.inviteCode = null
} else {
if(!ctx.message.text || ctx.message.text.length < 6) {
return ctx.reply('邀请码不能少于 6 个字符!请重新输入')
}
ctx.scene.session.data.inviteCode = ctx.message.text
}
ctx.reply(
`好的现在来确认一下信息:\n`+
`用户名:${ctx.scene.session.data.playerUsername}\n`+
`密码:${ctx.scene.session.data.password.substring(3,10)}\n`+
`邮箱:${ctx.scene.session.data.email}\n`+
`同步皮肤和披风:${ctx.scene.session.data.sync ? '是' : '否'}\n`+
`邀请码:${ctx.scene.session.data.inviteCode ?? '无'}\n`+
`是否确认? 输入 'yes' 或者 'no'`
)
return ctx.wizard.next()
},
async (ctx) => {
if(!ctx.message.text || ctx.message.text !== 'yes' && ctx.message.text !== 'no') {
return ctx.reply('请输入 "yes" 或者 "no"')
}
if(ctx.message.text === 'no') {
ctx.reply("已取消注册")
return ctx.scene.leave()
} else {
let message = await ctx.reply("正在注册...")
let skin = defaultSkin
let cape = ""
if(ctx.scene.session.data.sync) {
try{
message = await telegraf.telegram.editMessageText(ctx.chat.id, message.message_id, null, '获取对应正版玩家皮肤中...')
const uuid = await axios({
method: 'GET',
url: "https://api.mojang.com/users/profiles/minecraft/" + ctx.scene.session.data.playerUsername,
})
if(uuid.status === 400) {
message = await telegraf.telegram.editMessageText(ctx.chat.id, message.message_id, null, '对应正版玩家不存在!已跳过皮肤获取')
} else {
const skinResponse = await axios({
method: 'GET',
url: `https://sessionserver.mojang.com/session/minecraft/profile/${uuid.data.id}`,
responseType: 'json',
})
const skinData = JSON.parse(Buffer.from(skinResponse.data.properties[0].value, 'base64').toString())
skin = skinData.textures.SKIN.url
cape = skinData.textures.CAPE?.url
message = await telegraf.telegram.editMessageText(ctx.chat.id, message.message_id, null, '成功同步皮肤和披风')
}
} catch(err) {
server.log.info(err.toJSON())
}
}
let verified = false
if(ctx.scene.session.data.inviteCode) {
await telegraf.telegram.editMessageText(ctx.chat.id, message.message_id, null, message.text + '\n验证邀请码中...')
const invi = Invitation.findOne({
token: ctx.scene.session.data.inviteCode
})
if(invi) {
message = await telegraf.telegram.editMessageText(ctx.chat.id, message.message_id, null, message.text + '\n邀请码验证成功邀请人: ' + invi.by)
verified = true
} else {
message = await telegraf.telegram.editMessageText(ctx.chat.id, message.message_id, null, message.text + '\n邀请码无效! 已跳过')
}
}
const player = new Player({
uuid: uuid("LSP-yggdrasil:" + ctx.scene.session.data.playerUsername),
username: ctx.scene.session.data.playerUsername,
password: ctx.scene.session.data.password,
email: ctx.scene.session.data.email,
textures: {
skin: skin,
cape: cape
},
registerDate: Date.now(),
permissions: [{ node: 'login', allowed: true, duration: 0, eternal: true, startDate: Date.now(), highPriority: false }],
telegramBind: {
username: ctx.from.username,
verified: verified
}
})
player.save()
message = await telegraf.telegram.editMessageText(ctx.chat.id, message.message_id, null, message.text + '\n注册成功')
ctx.scene.leave()
}
}
)
export const allScenes = [
registerWizard,
]
const register = async () => {
registerWizard.command('cancel', (ctx) => {
ctx.reply("注册已取消")
return ctx.scene.leave()
const [invitation, v] = makeInvitation(args[0], args[1], args[2] || 'telegram')
ctx.replyWithMarkdownV2('邀请码:\n```' + invitation + '```\n\n验证码\n```' + v + "```")
})
}
telegraf.command('register', async (ctx) => {
const username = ctx.from.username
if(!username) {
return ctx.reply("请设置 Telegram 用户名!")
const adminBan = () => {
telegraf.command('banplr', async (ctx) => {
const player = await Player.findOne({ 'binding.platform': 'telegram', 'binding.username': ctx.message.from.username })
if(!player || player.permissions.indexOf('admin') === -1) {
return ctx.reply('配钥匙吗?什么?你配?哦不你不配!')
}
const player = await Player.findOne({ "telegramBind.username": username })
server.log.info(player)
if(!player) {
return ctx.scene.enter('REGISTRIATION_WIZARD')
} else {
return ctx.reply(`您已绑定用户: ${player.username} (${player.uuid})`)
const args = ctx.update.message.text.split(' ').slice(1)
if(args.length < 1) {
ctx.reply('Usage: /banplr <yggdrasil-player-username>')
return
}
await Player.updateOne({ username: args[0] }, { $pull: { permissions: 'login' } })
ctx.reply("已枪毙玩家 " + args[0])
})
}
const adminRevokeBan = () => {
telegraf.command('revokeban', async (ctx) => {
const player = await Player.findOne({ 'binding.platform': 'telegram', 'binding.username': ctx.message.from.username })
if(!player || player.permissions.indexOf('admin') === -1) {
return ctx.reply('配钥匙吗?什么?你配?哦不你不配!')
}
const args = ctx.update.message.text.split(' ').slice(1)
if(args.length < 1) {
ctx.reply('Usage: /revokeban <yggdrasil-player-username>')
return
}
await Player.updateOne({ username: args[0], permissions: { $nin: [ 'login' ] } }, { $push: { permissions: 'login' } })
ctx.reply("已复活玩家 " + args[0])
})
}
const userCreateInvitation = () => {
telegraf.command('inviteme', async (ctx) => {
const args = ctx.update.message.text.split(' ').slice(1)
if(args.length === 0) {
ctx.reply('Usage: /inviteme <email>')
return
}
if(ctx.message.chat.id === -1001780498838) {
const [i, v] = makeInvitation(ctx.message.from.username, args[0], 'telegram')
ctx.replyWithMarkdownV2('邀请码:\n```' + i + '```\n\n验证码将发送到私聊如果您未私聊启动过bot则无法接收到消息请 /start 后再次执行此指令。')
try {
await telegraf.telegram.sendMessage(ctx.message.from.id, '验证码:\n```' + v + "```", { parse_mode: 'Markdown' })
} catch (_) { }
} else {
ctx.reply('您不符合自我邀请最低要求,无法获取邀请码')
}
})
}
/*
{
p(latform): "name",
n(ame): "name",
t(o): "email",
}
*/
const makeInvitation = (username, platform, email) => {
const invitation = crypto.publicEncrypt(server.keys.publicKey, Buffer.from(JSON.stringify({
p: platform,
n: username,
t: email,
}))).toString('hex')
const v = crypto.createSign('RSA-SHA1').update(invitation).sign(server.keys.privateKey, 'hex')
return [invitation, v]
}