|
@@ -8,6 +8,7 @@ const moment = require('moment');
|
|
|
const { pool, testConnection } = require('./config/database');
|
|
|
const initDatabase = require('./config/initDb');
|
|
|
const Group = require('./models/Group');
|
|
|
+const Transaction = require('./models/Transaction');
|
|
|
|
|
|
const app = express();
|
|
|
|
|
@@ -51,6 +52,15 @@ function isAdmin(userId) {
|
|
|
// 处理消息发送
|
|
|
async function sendMessage(chatId, text, options = {}) {
|
|
|
try {
|
|
|
+ // 如果包含内联键盘,验证URL
|
|
|
+ if (options.reply_markup && options.reply_markup.inline_keyboard) {
|
|
|
+ const keyboard = generateInlineKeyboard(chatId);
|
|
|
+ if (!keyboard) {
|
|
|
+ // 如果键盘无效,发送不带键盘的消息
|
|
|
+ return await bot.sendMessage(chatId, text);
|
|
|
+ }
|
|
|
+ options.reply_markup = keyboard;
|
|
|
+ }
|
|
|
return await bot.sendMessage(chatId, text, options);
|
|
|
} catch (error) {
|
|
|
console.error('发送消息失败:', error);
|
|
@@ -66,17 +76,76 @@ async function sendMessage(chatId, text, options = {}) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// 处理所有消息
|
|
|
-bot.on('message', (msg) => {
|
|
|
- console.log('收到消息:', {
|
|
|
- chatId: msg.chat.id,
|
|
|
- chatType: msg.chat.type,
|
|
|
- chatTitle: msg.chat.title,
|
|
|
- fromId: msg.from.id,
|
|
|
- fromUsername: msg.from.username,
|
|
|
- text: msg.text,
|
|
|
- date: new Date(msg.date * 1000).toLocaleString()
|
|
|
- });
|
|
|
+// 处理快捷命令
|
|
|
+bot.on('message', async (msg) => {
|
|
|
+ if (!isGroupAllowed(msg.chat.id)) return;
|
|
|
+
|
|
|
+ const text = msg.text?.trim();
|
|
|
+ if (!text) return;
|
|
|
+
|
|
|
+ if (text.startsWith('+')) {
|
|
|
+ const amount = parseFloat(text.substring(1));
|
|
|
+ if (!isNaN(amount)) {
|
|
|
+ const transactionData = {
|
|
|
+ groupId: msg.chat.id.toString(),
|
|
|
+ groupName: msg.chat.title || '未命名群组',
|
|
|
+ amount: amount
|
|
|
+ };
|
|
|
+
|
|
|
+ try {
|
|
|
+ const result = await Transaction.deposit(transactionData);
|
|
|
+ if (result.success) {
|
|
|
+ const billMessage = await generateBillMessage(msg.chat.id);
|
|
|
+ if (billMessage) {
|
|
|
+ await sendMessage(msg.chat.id, billMessage, {
|
|
|
+ reply_markup: generateInlineKeyboard(msg.chat.id)
|
|
|
+ });
|
|
|
+ console.log(`入款成功 - 群组: ${msg.chat.title}, 金额: ${amount}, 时间: ${new Date().toLocaleString()}`);
|
|
|
+ } else {
|
|
|
+ await sendMessage(msg.chat.id, '入款成功,但获取账单信息失败');
|
|
|
+ console.log(`入款成功(无账单) - 群组: ${msg.chat.title}, 金额: ${amount}, 时间: ${new Date().toLocaleString()}`);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ await sendMessage(msg.chat.id, result.message || '入款失败');
|
|
|
+ console.log(`入款失败 - 群组: ${msg.chat.title}, 金额: ${amount}, 原因: ${result.message}, 时间: ${new Date().toLocaleString()}`);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('快捷入款失败:', error);
|
|
|
+ await sendMessage(msg.chat.id, '记录入款失败,请稍后重试');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else if (text.startsWith('-')) {
|
|
|
+ const amount = parseFloat(text.substring(1));
|
|
|
+ if (!isNaN(amount)) {
|
|
|
+ const transactionData = {
|
|
|
+ groupId: msg.chat.id.toString(),
|
|
|
+ groupName: msg.chat.title || '未命名群组',
|
|
|
+ amount: amount
|
|
|
+ };
|
|
|
+
|
|
|
+ try {
|
|
|
+ const result = await Transaction.withdrawal(transactionData);
|
|
|
+ if (result.success) {
|
|
|
+ const billMessage = await generateBillMessage(msg.chat.id);
|
|
|
+ if (billMessage) {
|
|
|
+ await sendMessage(msg.chat.id, billMessage, {
|
|
|
+ reply_markup: generateInlineKeyboard(msg.chat.id)
|
|
|
+ });
|
|
|
+ console.log(`出款成功 - 群组: ${msg.chat.title}, 金额: ${amount}, 时间: ${new Date().toLocaleString()}`);
|
|
|
+ } else {
|
|
|
+ await sendMessage(msg.chat.id, '出款成功,但获取账单信息失败');
|
|
|
+ console.log(`出款成功(无账单) - 群组: ${msg.chat.title}, 金额: ${amount}, 时间: ${new Date().toLocaleString()}`);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ await sendMessage(msg.chat.id, result.message || '出款失败');
|
|
|
+ console.log(`出款失败 - 群组: ${msg.chat.title}, 金额: ${amount}, 原因: ${result.message}, 时间: ${new Date().toLocaleString()}`);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('快捷出款失败:', error);
|
|
|
+ await sendMessage(msg.chat.id, '记录出款失败,请稍后重试');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
});
|
|
|
|
|
|
// 处理新成员加入
|
|
@@ -93,7 +162,6 @@ bot.on('new_chat_members', async (msg) => {
|
|
|
const chatIdStr = chatId.toString();
|
|
|
if (!data.allowedGroups.includes(chatIdStr)) {
|
|
|
try {
|
|
|
- // 使用 createGroup 创建新群组
|
|
|
const groupData = {
|
|
|
groupId: chatIdStr,
|
|
|
groupName: msg.chat.title || '未命名群组',
|
|
@@ -223,49 +291,82 @@ bot.onText(/\/listgroups/, async (msg) => {
|
|
|
});
|
|
|
|
|
|
// 处理入款命令
|
|
|
-bot.onText(/\/deposit (.+)/, (msg, match) => {
|
|
|
+bot.onText(/\/deposit (.+)/, async (msg, match) => {
|
|
|
+ if (!isGroupAllowed(msg.chat.id)) {
|
|
|
+ sendMessage(msg.chat.id, '该群组未授权使用此功能');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
const amount = parseFloat(match[1]);
|
|
|
if (isNaN(amount)) {
|
|
|
sendMessage(msg.chat.id, '请输入有效的金额');
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- data.deposits.push({
|
|
|
- time: new Date(),
|
|
|
- amount: amount
|
|
|
- });
|
|
|
- data.lastUpdate = new Date();
|
|
|
- saveData();
|
|
|
+ try {
|
|
|
+ const transactionData = {
|
|
|
+ groupId: msg.chat.id.toString(),
|
|
|
+ groupName: msg.chat.title || '未命名群组',
|
|
|
+ amount: amount
|
|
|
+ };
|
|
|
|
|
|
- sendMessage(msg.chat.id, generateBillMessage(), {
|
|
|
- reply_markup: generateInlineKeyboard()
|
|
|
- });
|
|
|
+ const result = await Transaction.deposit(transactionData);
|
|
|
+
|
|
|
+ if (result.success) {
|
|
|
+ sendMessage(msg.chat.id, generateBillMessage(msg.chat.id), {
|
|
|
+ reply_markup: generateInlineKeyboard(msg.chat.id)
|
|
|
+ });
|
|
|
+ console.log('机器人已准备就绪!');
|
|
|
+ } else {
|
|
|
+ sendMessage(msg.chat.id, result.message);
|
|
|
+ console.log('机器人已准备就绪!');
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('记录入款失败:', error);
|
|
|
+ sendMessage(msg.chat.id, '记录入款失败,请稍后重试');
|
|
|
+ }
|
|
|
});
|
|
|
|
|
|
// 处理下发命令
|
|
|
-bot.onText(/\/withdraw (.+)/, (msg, match) => {
|
|
|
+bot.onText(/\/withdraw (.+)/, async (msg, match) => {
|
|
|
+ if (!isGroupAllowed(msg.chat.id)) {
|
|
|
+ sendMessage(msg.chat.id, '该群组未授权使用此功能');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
const amount = parseFloat(match[1]);
|
|
|
if (isNaN(amount)) {
|
|
|
sendMessage(msg.chat.id, '请输入有效的金额');
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- data.withdrawals.push({
|
|
|
- time: new Date(),
|
|
|
- amount: amount
|
|
|
- });
|
|
|
- data.lastUpdate = new Date();
|
|
|
- saveData();
|
|
|
+ try {
|
|
|
+ const transactionData = {
|
|
|
+ groupId: msg.chat.id.toString(),
|
|
|
+ groupName: msg.chat.title || '未命名群组',
|
|
|
+ amount: amount
|
|
|
+ };
|
|
|
|
|
|
- sendMessage(msg.chat.id, generateBillMessage(), {
|
|
|
- reply_markup: generateInlineKeyboard()
|
|
|
- });
|
|
|
+ const result = await Transaction.withdrawal(transactionData);
|
|
|
+
|
|
|
+ if (result.success) {
|
|
|
+ sendMessage(msg.chat.id, generateBillMessage(msg.chat.id), {
|
|
|
+ reply_markup: generateInlineKeyboard(msg.chat.id)
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ sendMessage(msg.chat.id, result.message);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('记录下发失败:', error);
|
|
|
+ sendMessage(msg.chat.id, '记录下发失败,请稍后重试');
|
|
|
+ }
|
|
|
});
|
|
|
|
|
|
// 处理查看账单命令
|
|
|
-bot.onText(/\/bill/, (msg) => {
|
|
|
- sendMessage(msg.chat.id, generateBillMessage(), {
|
|
|
- reply_markup: generateInlineKeyboard()
|
|
|
+bot.onText(/\/bill/, async (msg) => {
|
|
|
+ const billMessage = await generateBillMessage(msg.chat.id);
|
|
|
+ sendMessage(msg.chat.id, billMessage, {
|
|
|
+ reply_markup: generateInlineKeyboard(msg.chat.id)
|
|
|
});
|
|
|
});
|
|
|
|
|
@@ -278,6 +379,10 @@ bot.onText(/\/help/, (msg) => {
|
|
|
/bill - 查看当前账单
|
|
|
/help - 显示此帮助信息
|
|
|
|
|
|
+快捷命令:
|
|
|
++<金额> - 快速记录入款(例如:+2000)
|
|
|
+-<金额> - 快速记录下发(例如:-2000)
|
|
|
+
|
|
|
管理员命令:
|
|
|
/addgroup <群组ID> - 添加允许的群组
|
|
|
/removegroup <群组ID> - 移除允许的群组
|
|
@@ -287,63 +392,105 @@ bot.onText(/\/help/, (msg) => {
|
|
|
});
|
|
|
|
|
|
// 生成账单消息
|
|
|
-function generateBillMessage() {
|
|
|
- const now = moment();
|
|
|
- const deposits = data.deposits || [];
|
|
|
- const withdrawals = data.withdrawals || [];
|
|
|
-
|
|
|
- const totalDeposit = deposits.reduce((sum, d) => sum + d.amount, 0);
|
|
|
- const totalWithdrawal = withdrawals.reduce((sum, w) => sum + w.amount, 0);
|
|
|
- const depositFee = totalDeposit * process.env.DEPOSIT_FEE_RATE;
|
|
|
- const withdrawalFee = totalWithdrawal * process.env.WITHDRAWAL_FEE_RATE;
|
|
|
- const remaining = totalDeposit - depositFee - totalWithdrawal - withdrawalFee;
|
|
|
+async function generateBillMessage(chatId) {
|
|
|
+ try {
|
|
|
+ // 获取最近的交易记录
|
|
|
+ const [records] = await pool.query(`
|
|
|
+ SELECT * FROM transactions
|
|
|
+ WHERE group_id = ?
|
|
|
+ ORDER BY time DESC
|
|
|
+ LIMIT 10
|
|
|
+ `, [chatId.toString()]);
|
|
|
+
|
|
|
+ if (!records || records.length === 0) {
|
|
|
+ return '暂无交易记录';
|
|
|
+ }
|
|
|
|
|
|
- let message = `入款(${deposits.length})笔:\n`;
|
|
|
-
|
|
|
- // 添加入款记录
|
|
|
- deposits.forEach(deposit => {
|
|
|
- message += `${moment(deposit.time).format('HH:mm:ss')} ${deposit.amount.toFixed(2)}\n`;
|
|
|
- });
|
|
|
+ const deposits = records.filter(r => r.type === 'deposit');
|
|
|
+ const withdrawals = records.filter(r => r.type === 'withdrawal');
|
|
|
+
|
|
|
+ const totalDeposit = deposits.reduce((sum, d) => sum + parseFloat(d.amount), 0);
|
|
|
+ const totalWithdrawal = withdrawals.reduce((sum, w) => sum + parseFloat(w.amount), 0);
|
|
|
+ const depositFee = totalDeposit * (process.env.DEPOSIT_FEE_RATE || 0);
|
|
|
+ const withdrawalFee = totalWithdrawal * (process.env.WITHDRAWAL_FEE_RATE || 0);
|
|
|
+ const remaining = totalDeposit - depositFee - totalWithdrawal - withdrawalFee;
|
|
|
|
|
|
- message += `\n下发(${withdrawals.length})笔:\n`;
|
|
|
-
|
|
|
- // 添加下发记录
|
|
|
- withdrawals.forEach(withdrawal => {
|
|
|
- message += `${moment(withdrawal.time).format('HH:mm:ss')} ${withdrawal.amount.toFixed(2)}\n`;
|
|
|
- });
|
|
|
+ let message = `入款(${deposits.length})笔:\n`;
|
|
|
+
|
|
|
+ // 添加入款记录
|
|
|
+ deposits.forEach(deposit => {
|
|
|
+ message += `${moment(deposit.time).format('HH:mm:ss')} ${parseFloat(deposit.amount).toFixed(2)}\n`;
|
|
|
+ });
|
|
|
+
|
|
|
+ message += `\n下发(${withdrawals.length})笔:\n`;
|
|
|
+
|
|
|
+ // 添加下发记录
|
|
|
+ withdrawals.forEach(withdrawal => {
|
|
|
+ message += `${moment(withdrawal.time).format('HH:mm:ss')} ${parseFloat(withdrawal.amount).toFixed(2)}\n`;
|
|
|
+ });
|
|
|
|
|
|
- message += `\n总入款:${totalDeposit.toFixed(2)}\n`;
|
|
|
- message += `入款费率:${(process.env.DEPOSIT_FEE_RATE * 100).toFixed(1)}%\n`;
|
|
|
- message += `下发费率:${(process.env.WITHDRAWAL_FEE_RATE * 100).toFixed(1)}%\n`;
|
|
|
- message += `应下发:${(totalDeposit - depositFee).toFixed(2)}\n`;
|
|
|
- message += `总下发:${totalWithdrawal.toFixed(2)}\n`;
|
|
|
- message += `下发单笔附加费:0.0\n`;
|
|
|
- message += `单笔附费加总计:0.0\n`;
|
|
|
- message += `余:${remaining.toFixed(2)}`;
|
|
|
+ message += `\n总入款:${totalDeposit.toFixed(2)}\n`;
|
|
|
+ message += `入款费率:${((process.env.DEPOSIT_FEE_RATE || 0) * 100).toFixed(1)}%\n`;
|
|
|
+ message += `下发费率:${((process.env.WITHDRAWAL_FEE_RATE || 0) * 100).toFixed(1)}%\n`;
|
|
|
+ message += `应下发:${(totalDeposit - depositFee).toFixed(2)}\n`;
|
|
|
+ message += `总下发:${totalWithdrawal.toFixed(2)}\n`;
|
|
|
+ message += `下发单笔附加费:0.0\n`;
|
|
|
+ message += `单笔附费加总计:0.0\n`;
|
|
|
+ message += `余:${remaining.toFixed(2)}`;
|
|
|
|
|
|
- return message;
|
|
|
+ return message;
|
|
|
+ } catch (error) {
|
|
|
+ console.error('生成账单消息失败:', error);
|
|
|
+ return '获取账单信息失败,请稍后重试';
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// 生成内联键盘
|
|
|
-function generateInlineKeyboard() {
|
|
|
- return {
|
|
|
+function generateInlineKeyboard(chatId) {
|
|
|
+ const keyboard = {
|
|
|
inline_keyboard: [
|
|
|
[
|
|
|
{
|
|
|
text: '点击跳转完整账单',
|
|
|
- url: `${process.env.BILL_PAGE_BASE_URL}?groupId=${data.allowedGroups[0]}`
|
|
|
+ callback_data: `bill_page_${chatId}`
|
|
|
}
|
|
|
],
|
|
|
[
|
|
|
{
|
|
|
text: '24小时商务对接',
|
|
|
- url: process.env.BUSINESS_ACCOUNT_URL
|
|
|
+ callback_data: 'business_contact'
|
|
|
}
|
|
|
]
|
|
|
]
|
|
|
};
|
|
|
+ return keyboard;
|
|
|
}
|
|
|
|
|
|
+// 处理内联按钮回调
|
|
|
+bot.on('callback_query', async (callbackQuery) => {
|
|
|
+ const chatId = callbackQuery.message.chat.id;
|
|
|
+ const data = callbackQuery.data;
|
|
|
+
|
|
|
+ try {
|
|
|
+ if (data.startsWith('bill_page_')) {
|
|
|
+ const groupId = data.split('_')[2];
|
|
|
+ await bot.answerCallbackQuery(callbackQuery.id, {
|
|
|
+ url: `${process.env.BILL_PAGE_BASE_URL}?groupId=${groupId}`
|
|
|
+ });
|
|
|
+ } else if (data === 'business_contact') {
|
|
|
+ await bot.answerCallbackQuery(callbackQuery.id, {
|
|
|
+ url: 'https://t.me/your_business_account'
|
|
|
+ });
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('处理内联按钮回调失败:', error);
|
|
|
+ await bot.answerCallbackQuery(callbackQuery.id, {
|
|
|
+ text: '操作失败,请稍后重试',
|
|
|
+ show_alert: true
|
|
|
+ });
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
// 保存数据
|
|
|
function saveData() {
|
|
|
try {
|