This commit is contained in:
@ -39,6 +39,9 @@ export const config = {
|
||||
preHooks: (fastify) => {}, // 在这里添加自定义hook
|
||||
preRouting: (fastify) => {}, // 在这里添加自定义路由
|
||||
postRouting: (fastify) => {}, // 我也不知道你在这里写了有啥用...
|
||||
},
|
||||
telegram: {
|
||||
token: '5161996862:AAHY79HdmI9A9Xoumv4E8hzi5UUrY38n5h0',
|
||||
}
|
||||
}
|
||||
|
||||
|
19
src/index.js
19
src/index.js
@ -8,6 +8,8 @@ import * as AdvancedRoutings from './routes/advanced.js'
|
||||
import * as APIRoutings from './routes/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';
|
||||
|
||||
export const server = fastify({
|
||||
logger: {
|
||||
@ -16,6 +18,8 @@ export const server = fastify({
|
||||
}
|
||||
})
|
||||
|
||||
export const telegraf = new Telegraf(config.telegram.token)
|
||||
|
||||
export const setup = async () => {
|
||||
const mongooseClient = await mongoose.connect(config.database.url)
|
||||
const models = registerModels(mongooseClient)
|
||||
@ -61,15 +65,27 @@ 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 }]
|
||||
permissions: [{ node: 'login', allowed: true, duration: 0, eternal: true, startDate: Date.now(), highPriority: false }],
|
||||
telegramBind: {
|
||||
username: 'test',
|
||||
verified: true,
|
||||
}
|
||||
}).save()
|
||||
}
|
||||
|
||||
const stage = new Scenes.Stage([ ...allScenes ])
|
||||
telegraf.use(session())
|
||||
telegraf.use(stage.middleware())
|
||||
|
||||
registerAllPlayerCommands()
|
||||
}
|
||||
|
||||
const launch = async () => {
|
||||
process.on('SIGINT', () => {
|
||||
new Promise(shutdown)
|
||||
})
|
||||
|
||||
telegraf.launch()
|
||||
|
||||
await server.listen(config.server.port, config.server.url)
|
||||
|
||||
@ -78,6 +94,7 @@ const launch = async () => {
|
||||
|
||||
export const shutdown = async () => {
|
||||
server.close()
|
||||
telegraf.stop()
|
||||
mongoose.disconnect()
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,11 @@
|
||||
import { TokenSchema } from "./token.js";
|
||||
import { PlayerSchema } from "./player.js";
|
||||
import { InviteTokenSchema } from "./invite.js";
|
||||
|
||||
export function registerModels(mongoose) {
|
||||
return {
|
||||
Token: mongoose.model("Token", TokenSchema),
|
||||
Player: mongoose.model("Player", PlayerSchema),
|
||||
Invitation: mongoose.model('Invitation', InviteTokenSchema)
|
||||
}
|
||||
}
|
9
src/models/invite.js
Normal file
9
src/models/invite.js
Normal file
@ -0,0 +1,9 @@
|
||||
import mongoose from 'mongoose'
|
||||
const { Schema } = mongoose
|
||||
|
||||
export const InviteTokenSchema = new Schema({
|
||||
by: String, // Telegram username
|
||||
token: String
|
||||
})
|
||||
|
||||
// { from: string, id: number, sign: string }
|
@ -14,6 +14,10 @@ export const PlayerSchema = new Schema({
|
||||
},
|
||||
registerDate: Number,
|
||||
permissions: [{ node: String, allowed: Boolean, duration: Number, startDate: Number, highPriority: Boolean }], // ban -> true
|
||||
telegramBind: {
|
||||
username: String,
|
||||
verified: Boolean,
|
||||
}
|
||||
})
|
||||
|
||||
export const PlayerSeriliazationSchema = {
|
||||
|
@ -78,6 +78,14 @@ export const authenticate = {
|
||||
})
|
||||
}
|
||||
|
||||
if(!player.telegramBind || !player.telegramBind.verified) {
|
||||
return await rep.code(401).send({
|
||||
error: "Unauthorized",
|
||||
errorMessage: "未绑定 Telegram 账号或账号验证未通过,登录请求已禁止",
|
||||
cause: "未绑定 Telegram 账号或账号验证未通,登录请求已禁止"
|
||||
})
|
||||
}
|
||||
|
||||
if(!clientToken) {
|
||||
clientToken = createHash('sha256').update( "" + Math.random() * 1.048596).digest().toString('hex')
|
||||
}
|
||||
|
204
src/telegram/player-commands.js
Normal file
204
src/telegram/player-commands.js
Normal file
@ -0,0 +1,204 @@
|
||||
import { Scenes } from 'telegraf'
|
||||
import { telegraf, server } from '../index.js'
|
||||
import { createHash } from 'crypto'
|
||||
import axios from 'axios'
|
||||
import { uuid } from '../generator.js'
|
||||
|
||||
export const registerAllPlayerCommands = async () => {
|
||||
await register()
|
||||
}
|
||||
|
||||
|
||||
|
||||
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 username = ctx.from.username === 'Lapis_Apple' ? 'LSP' : ctx.message.text
|
||||
ctx.reply(`请设置玩家 ${username} 的密码`)
|
||||
ctx.scene.session.data.playerUsername = username
|
||||
|
||||
return ctx.wizard.next()
|
||||
},
|
||||
async (ctx) => {
|
||||
if(!ctx.message.text || ctx.message.text < 6) {
|
||||
return ctx.reply('密码不能少于 6 个字符!请重新输入')
|
||||
}
|
||||
|
||||
ctx.scene.session.data.password = createHash('sha256').update(ctx.message.text).digest('hex')
|
||||
|
||||
const duplicated = await server.models.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 = server.models.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 server.models.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()
|
||||
})
|
||||
|
||||
telegraf.command('register', async (ctx) => {
|
||||
const username = ctx.from.username
|
||||
if(!username) {
|
||||
return ctx.reply("请设置 Telegram 用户名!")
|
||||
}
|
||||
|
||||
const player = await server.models.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})`)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
Reference in New Issue
Block a user