UNPKG

shadowsocks-manager

Version:

A shadowsocks manager tool for multi user and traffic control.

345 lines (329 loc) 13.8 kB
const tg = appRequire('plugins/webgui_telegram/index'); const telegram = appRequire('plugins/webgui_telegram/index').telegram; const isUser = appRequire('plugins/webgui_telegram/index').isUser; const account = appRequire('plugins/account/index'); const server = appRequire('plugins/flowSaver/server'); const config = appRequire('services/config').all(); const crypto = require('crypto'); const moment = require('moment'); const qr = require('qr-image'); const flow = appRequire('plugins/flowSaver/flow'); const knex = appRequire('init/knex').knex; const orderPlugin = appRequire('plugins/webgui_order'); const prettyFlow = number => { if(number >= 0 && number < 1000) { return number + ' B'; } else if(number >= 1000 && number < 1000 * 1000) { return (number / 1000).toFixed(1) + ' KB'; } else if(number >= 1000 * 1000 && number < 1000 * 1000 * 1000) { return (number / (1000 * 1000)).toFixed(2) + ' MB'; } else if(number >= 1000 * 1000 * 1000 && number < 1000 * 1000 * 1000 * 1000) { return (number / (1000 * 1000 * 1000)).toFixed(3) + ' GB'; } else if(number >= 1000 * 1000 * 1000 * 1000 && number < 1000 * 1000 * 1000 * 1000 * 1000) { return (number / (1000 * 1000 * 1000 * 1000)).toFixed(3) + ' TB'; } else { return number + ''; } }; const sleep = time => { return new Promise(resolve => { setTimeout(() => { resolve(); }, time); }); }; const isLogin = message => { if(!message.message || !message.message.text) { return false; } if(!message.message || !message.message.chat || !message.message.chat.type === 'private') { return false; } if(message.message.text.trim() !== 'login') { return false; } return true; }; const isGetAccount = message => { if(!message.message || !message.message.text) { return false; } if(!message.message || !message.message.chat || !message.message.chat.type === 'private') { return false; } if(message.message.text.trim() !== 'account') { return false; } return true; }; const isCallbackAccount = message => { if(!message.callback_query || !message.callback_query.data) { return false; } if(!message.callback_query.data.match(/^accountId\[\d{1,}\]$/)) { return false; } return true; }; const isCallbackServer = message => { if(!message.callback_query || !message.callback_query.data) { return false; } if(!message.callback_query.data.match(/^accountId\[\d{1,}\]serverId\[\d{1,}\]$/)) { return false; } return true; }; const isCallbackPay = message => { if(!message.callback_query || !message.callback_query.data) { return false; } if(!message.callback_query.data.match(/^alipay:accountId\[\d{1,}\]$/)) { return false; } return true; }; const isCallbackPayQrcode = message => { if(!message.callback_query || !message.callback_query.data) { return false; } if(!message.callback_query.data.match(/^alipay:qrcode:accountId\[\d{1,}\]type\[[0-9]{1,}\]$/)) { return false; } return true; }; telegram.on('message', async message => { if(isGetAccount(message)) { const telegramId = message.message.chat.id.toString(); const userId = await isUser(telegramId); const myAccount = await account.getAccount({ userId }); if(!myAccount.length) { tg.sendMessage('当前用户没有分配账号', telegramId); if(config.plugins.alipay && config.plugins.alipay.use) { tg.sendKeyboard('购买新账号', telegramId, { inline_keyboard: [[{ text: '点击这里购买', callback_data: `alipay:accountId[0]`, }]], }); } return; } const keyboard = myAccount.map(m => { return { text: m.port.toString(), callback_data: `accountId[${ m.id }]`, }; }); tg.sendKeyboard('请选择账号', telegramId, { inline_keyboard: [ keyboard ], }); const groupInfo = await knex('group').select([ 'group.multiAccount as multiAccount' ]) .leftJoin('user', 'user.group', 'group.id') .leftJoin('account_plugin', 'account_plugin.userId', 'user.id') .where({ 'user.id': userId }).then(s => s[0]); if(groupInfo && groupInfo.multiAccount) { if(config.plugins.alipay && config.plugins.alipay.use) { tg.sendKeyboard('购买新账号', telegramId, { inline_keyboard: [[{ text: '点击这里购买', callback_data: `alipay:accountId[0]`, }]], }); } } } else if(isCallbackAccount(message)) { const telegramId = message.callback_query.from.id.toString(); const accountId = message.callback_query.data.match(/^accountId\[(\d{1,})\]$/)[1]; const userId = await isUser(telegramId); const myAccount = (await account.getAccount({ userId, id: +accountId }))[0]; if(!myAccount) { return; } let servers = await server.list(); const validServers = JSON.parse(myAccount.server); servers = servers.filter(f => { if(!validServers) { return true; } else { return validServers.indexOf(f.id) >= 0; } }); let row = 4; if(servers.length <= 4) { row = 2; } else if (servers.length <= 9) { row = 3; } const serverArray = []; servers.forEach((server, index) => { if(index % row === 0) { serverArray.push([]); } serverArray[serverArray.length -1].push({ text: server.name, callback_data: `accountId[${ myAccount.id }]serverId[${ server.id }]`, }); }); if(serverArray.length > 1 && serverArray[serverArray.length - 1].length < row) { '----'.substr(0, row - serverArray[serverArray.length - 1].length).split('').forEach(f => { serverArray[serverArray.length -1].push({ text: f, callback_data: ' ', }); }); } tg.sendKeyboard('请选择服务器', telegramId, { inline_keyboard: serverArray, }); if(myAccount.type >= 2 && myAccount.type <= 5 && config.plugins.alipay && config.plugins.alipay.use) { tg.sendKeyboard('续费', telegramId, { inline_keyboard: [[{ text: '点击这里续费 ' + myAccount.port, callback_data: `alipay:accountId[${ myAccount.id }]`, }]], }); } } else if(isCallbackServer(message)) { const telegramId = message.callback_query.from.id.toString(); const accountId = message.callback_query.data.match(/^accountId\[(\d{1,})\]serverId\[\d{1,}\]$/)[1]; const serverId = message.callback_query.data.match(/^accountId\[\d{1,}\]serverId\[(\d{1,})\]$/)[1]; const userId = await isUser(telegramId); const myAccount = (await account.getAccount({ userId, id: +accountId }))[0]; const myServer = (await server.list()).filter(server => { return server.id === +serverId; }).map(server => { if(server.host.indexOf(':') >= 0) { const hosts = server.host.split(':'); const number = Math.ceil(Math.random() * (hosts.length - 1)); server.host = hosts[number]; } return server; })[0]; if(!myAccount || !myServer) { return; } let returnMessage = '账号信息\n\n'; const ssurl = 'ss://' + Buffer.from(`${ myServer.method }:${ myAccount.password }@${ myServer.host }:${ myAccount.port }`).toString('base64'); returnMessage += `地址:${ myServer.host }\n端口:${ myAccount.port }\n密码:${ myAccount.password }\n加密方式:${ myServer.method }\n\n`; tg.sendMessage(returnMessage, telegramId); if(myAccount.type >= 2 && myAccount.type <= 5) { let timePeriod = 0; if(myAccount.type === 2) { timePeriod = 7 * 86400 * 1000; } if(myAccount.type === 3) { timePeriod = 30 * 86400 * 1000; } if(myAccount.type === 4) { timePeriod = 1 * 86400 * 1000; } if(myAccount.type === 5) { timePeriod = 3600 * 1000; } const data = JSON.parse(myAccount.data); const expireTime = data.create + data.limit * timePeriod; await sleep(250); const isExpired = Date.now() >= expireTime ? ' [已过期]' : ''; const time = { '2': 7 * 24 * 3600000, '3': 30 * 24 * 3600000, '4': 24 * 3600000, '5': 3600000, }; const timeArray = [data.create, data.create + time[myAccount.type]]; if (data.create <= Date.now()) { let i = 0; while (data.create + i * time[myAccount.type] <= Date.now()) { timeArray[0] = data.create + i * time[myAccount.type]; timeArray[1] = data.create + (i + 1) * time[myAccount.type]; i++; } } const flowLimit = data.flow * (myAccount.isMultiServerFlow ? 1 : myServer.scale); const currentFlow = (await flow.getServerPortFlowWithScale(myServer.id, myAccount.id, timeArray, myAccount.multiServerFlow))[0]; tg.sendMessage(`流量:${ prettyFlow(currentFlow) } / ${ prettyFlow(flowLimit) }`, telegramId); tg.sendMessage(`过期时间:${ moment(expireTime).format('YYYY-MM-DD HH:mm') }${ isExpired }`, telegramId); } await sleep(250); tg.sendMarkdown(`[${ ssurl }](${ ssurl })`, telegramId); const qrcodeId = crypto.randomBytes(32).toString('hex'); qrcodeObj[qrcodeId] = { url: ssurl, time: Date.now() }; tg.sendPhoto(`${ config.plugins.webgui.site }/api/user/telegram/qrcode/${ qrcodeId }`, telegramId); } else if(isLogin(message)) { const telegramId = message.message.chat.id.toString(); const userId = await isUser(telegramId); const token = crypto.randomBytes(32).toString('hex'); tokens[token] = { userId: +userId, time: Date.now(), }; tg.sendKeyboard('登录', telegramId, { inline_keyboard: [[{ text: '点击这里登录网页版', url: `${ config.plugins.webgui.site }/home/login/telegram/${ token }` }]], }); } else if(isCallbackPay(message)) { const telegramId = message.callback_query.from.id.toString(); const accountId = +message.callback_query.data.match(/^alipay:accountId\[(\d{1,})\]$/)[1]; const userId = await isUser(telegramId); const groupInfo = await knex('group').select([ 'group.order as order' ]) .leftJoin('user', 'user.group', 'group.id') .leftJoin('account_plugin', 'account_plugin.userId', 'user.id') .where({ 'user.id': userId }); let accountInfo; if(accountId) { accountInfo = await knex('account_plugin').where({ id: accountId }).then(s => s[0]); if(accountInfo && accountInfo.data) { accountInfo.data = JSON.parse(accountInfo.data); } } const groupOrderInfo = groupInfo[0].order ? JSON.parse(groupInfo[0].order) : null; let orders = await orderPlugin.getOrders(); if(groupOrderInfo) { orders = orders.filter(order => { return groupOrderInfo.indexOf(order.id) > 0; }); } if(!accountId) { orders = orders.filter(order => { return !order.baseId; }); } else if(accountInfo && accountInfo.orderId) { orders = orders.filter(order => { return order.id === accountInfo.orderId || order.baseId === accountInfo.orderId; }); } else if(accountInfo && !accountInfo.orderId) { orders = orders.filter(order => { return !order.baseId; }); } const paymentArray = []; for(const order of orders) { if(order.alipay > 0) { paymentArray.push([{ text: `${ order.name } ${ order.alipay }`, callback_data: `alipay:qrcode:accountId[${ accountId }]type[${ order.id }]`, }]); } } tg.sendKeyboard('选择续费类型:', telegramId, { inline_keyboard: paymentArray, }); } else if(isCallbackPayQrcode(message)) { const alipay = appRequire('plugins/alipay/index'); const telegramId = message.callback_query.from.id.toString(); const accountId = +message.callback_query.data.match(/^alipay:qrcode:accountId\[(\d{1,})\]type\[[0-9]{1,}\]$/)[1]; const orderId = message.callback_query.data.match(/^alipay:qrcode:accountId\[\d{1,}\]type\[([0-9]{1,})\]$/)[1]; const userId = (await tg.getUserStatus(telegramId)).id; const payInfo = await alipay.createOrder(userId, accountId > 0 ? accountId : null, +orderId); const qrcodeId = crypto.randomBytes(32).toString('hex'); qrcodeObj[qrcodeId] = { url: payInfo.qrCode, time: Date.now() }; tg.sendMarkdown(`请用支付宝扫描下面二维码完成支付\n\n或者 [点击此链接](${ payInfo.qrCode }) 跳转到支付宝支付`, telegramId); tg.sendPhoto(`${ config.plugins.webgui.site }/api/user/telegram/qrcode/${ qrcodeId }`, telegramId); } }); const qrcodeObj = {}; const qrcode = (req, res) => { for(const i in qrcodeObj) { if(Date.now() - qrcodeObj[i].time >= 5 * 60 * 1000) { delete qrcodeObj[i]; } } const id = req.params.qrcodeId; const code = qr.image(qrcodeObj[id].url, { type: 'png', size: 7 }); res.setHeader('Content-type', 'image/png'); code.pipe(res); }; const tokens = {}; const login = (req, res) => { delete req.session.user; delete req.session.type; const token = req.body.token; for(const t in tokens) { if(Date.now() - tokens[t].time > 5 * 60 * 1000) { delete tokens[t]; } } if(tokens[token] && Date.now() - tokens[token].time <= 5 * 60 * 1000) { req.session.user = tokens[token].userId; req.session.type = 'normal'; req.session.save(); return res.send('success'); } else { return res.status(403).end(); } }; exports.qrcode = qrcode; exports.login = login;