-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbot.js
More file actions
239 lines (230 loc) · 9.16 KB
/
bot.js
File metadata and controls
239 lines (230 loc) · 9.16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
const TelegramBot = require('node-telegram-bot-api')
require('dotenv').config()
const supabase = require('./supabaseClient')
const bot = new TelegramBot(process.env.TELEGRAM_BOT_TOKEN, { polling: true })
bot.onText(/\/start/, (msg) => {
bot.sendMessage(msg.chat.id, "👋🏽 Welcome to FundiFix! You'll get bookings here.")
})
// You'll add booking notification logic next
bot.on('message', (msg) => {
console.log('New Telegram message:', msg.text);
});
async function notifyFundi({ name, phone, service, location, date, time, id }) {
const chatId = process.env.TELEGRAM_CHAT_ID;
const message = `📢 New Booking!\n\n👤 Name: ${name}\n📞 Phone: ${phone}\n🛠️ Service: ${service}\n📍 Location: ${location}\n📅 Date: ${date}\n⏰ Time: ${time}`;
if (!chatId) throw new Error('TELEGRAM_CHAT_ID not set');
const opts = {
reply_markup: {
inline_keyboard: [[
{ text: '✅ Accept', callback_data: `accept_${id}` },
{ text: '❌ Decline', callback_data: `decline_${id}` }
]]
}
};
await bot.sendMessage(chatId, message, opts);
}
// Helper to message client on Telegram
async function notifyClientTelegram({ contact_value, name, service, time, status }) {
if (!contact_value) return;
let msg = '';
if (status === 'accepted') {
msg = `Hi ${name}, your fundi has accepted your request for ${service} at ${time}. They'll arrive prepared. Please be ready and let us know if anything changes. Thank you for using FundiFix!`;
} else if (status === 'declined') {
msg = `Hi ${name}, unfortunately your booking was declined as the fundi is currently unavailable. We'll notify you once someone is available. Thank you for your patience!`;
} else if (status === 'finished') {
msg = `Hi ${name}, your work is finished! Please proceed to payment. Thank you for using FundiFix!`;
}
// Always use numeric ID for direct messages
let chatId = Number(contact_value);
try {
await bot.sendMessage(chatId, msg);
console.log(`Sent client notification to ${chatId}: ${msg}`);
} catch (e) {
console.error('Failed to notify client on Telegram:', e.message);
}
}
// Track review state for clients
const waitingForReview = {};
bot.on('callback_query', async (query) => {
const chatId = query.message.chat.id;
const messageId = query.message.message_id;
const data = query.data;
const match = data.match(/^(accept|decline|finish|payment_sent|confirm_payment|leave_review|review_5|review_3|review_2|review_1)_(.+)$/);
if (!match) return;
const action = match[1];
const bookingId = match[2];
let newStatus;
let paymentStatus;
if (action === 'accept') newStatus = 'accepted';
else if (action === 'decline') newStatus = 'declined';
else if (action === 'finish') newStatus = 'finished';
else if (action === 'payment_sent') paymentStatus = 'sent';
else if (action === 'confirm_payment') paymentStatus = 'confirmed';
else if (action === 'leave_review') {
// Prompt client for review text
if (booking.contact_type === 'telegram' && booking.contact_value) {
let clientChatId = Number(booking.contact_value);
waitingForReview[clientChatId] = bookingId;
await bot.sendMessage(clientChatId, 'Please type your review and send it as a message.');
}
}
// Update booking status in Supabase
if (newStatus) {
const { error } = await supabase
.from('bookings')
.update({ status: newStatus })
.eq('id', bookingId);
if (error) {
await bot.answerCallbackQuery(query.id, { text: 'Failed to update booking status.' });
return;
}
}
if (paymentStatus) {
const { error } = await supabase
.from('bookings')
.update({ payment_status: paymentStatus })
.eq('id', bookingId);
if (error) {
await bot.answerCallbackQuery(query.id, { text: 'Failed to update payment status.' });
return;
}
}
await bot.editMessageReplyMarkup({ inline_keyboard: [] }, { chat_id: chatId, message_id: messageId });
await bot.answerCallbackQuery(query.id, { text: `${action.replace('_', ' ')}!` });
// Fetch booking for client/fundi notification
const { data: booking, error: fetchError } = await supabase
.from('bookings')
.select('*')
.eq('id', bookingId)
.single();
if (!fetchError && booking) {
// Payment and review flow
if (action === 'accept') {
// Send 'Booking has been accepted.' and 'Mark as Finished' button to fundi
await bot.sendMessage(process.env.TELEGRAM_CHAT_ID, 'Booking has been accepted.', {
reply_markup: {
inline_keyboard: [[
{ text: '✅ Mark as Finished', callback_data: `finish_${bookingId}` }
]]
}
});
// Notify client of acceptance
if (booking.contact_type === 'telegram' && booking.contact_value) {
await notifyClientTelegram({
contact_value: booking.contact_value,
name: booking.name,
service: booking.service,
time: booking.time,
status: 'accepted'
});
}
} else if (action === 'decline') {
// Notify client of decline
if (booking.contact_type === 'telegram' && booking.contact_value) {
await notifyClientTelegram({
contact_value: booking.contact_value,
name: booking.name,
service: booking.service,
time: booking.time,
status: 'declined'
});
}
} else if (action === 'finish') {
// Send payment button to client
if (booking.contact_type === 'telegram' && booking.contact_value) {
let clientChatId = Number(booking.contact_value);
await bot.sendMessage(clientChatId, 'Please process payment.', {
reply_markup: {
inline_keyboard: [[
{ text: '✅ Payment Sent', callback_data: `payment_sent_${bookingId}` }
]]
}
});
// Notify client of finished work
await notifyClientTelegram({
contact_value: booking.contact_value,
name: booking.name,
service: booking.service,
time: booking.time,
status: 'finished'
});
}
} else if (action === 'payment_sent') {
// Notify fundi to confirm payment WITH BUTTON
await bot.sendMessage(process.env.TELEGRAM_CHAT_ID, 'Client has sent payment. Please confirm receipt.', {
reply_markup: {
inline_keyboard: [[
{ text: '✅ Confirm Payment', callback_data: `confirm_payment_${bookingId}` }
]]
}
});
console.log('Sent Confirm Payment button to fundi');
} else if (action === 'confirm_payment') {
// Send review buttons to client
if (booking.contact_type === 'telegram' && booking.contact_value) {
let clientChatId = Number(booking.contact_value);
await bot.sendMessage(clientChatId, 'Payment confirmed! Please leave a review.', {
reply_markup: {
inline_keyboard: [
[
{ text: '⭐️⭐️⭐️⭐️⭐️ Excellent', callback_data: `review_5_${bookingId}` },
{ text: '⭐️⭐️⭐️ Good', callback_data: `review_3_${bookingId}` }
],
[
{ text: '⭐️⭐️ Fair', callback_data: `review_2_${bookingId}` },
{ text: '⭐️ Poor', callback_data: `review_1_${bookingId}` }
]
]
}
});
}
} else if (action.startsWith('review_')) {
// Button-based review
const reviewMap = {
'5': '⭐️⭐️⭐️⭐️⭐️ Excellent',
'3': '⭐️⭐️⭐️ Good',
'2': '⭐️⭐️ Fair',
'1': '⭐️ Poor'
};
const reviewScore = action.split('_')[1];
const reviewText = reviewMap[reviewScore] || 'Review';
// Save review to Supabase
const { error } = await supabase
.from('bookings')
.update({ review: reviewText })
.eq('id', bookingId);
if (!error) {
// Notify client and fundi
if (booking.contact_type === 'telegram' && booking.contact_value) {
let clientChatId = Number(booking.contact_value);
await bot.sendMessage(clientChatId, 'Thank you for your review!');
}
await bot.sendMessage(process.env.TELEGRAM_CHAT_ID, `New review for booking ${bookingId}:\n${reviewText}`);
console.log('Sent review to fundi:', reviewText);
}
}
}
});
// Listen for review text from client
bot.on('message', async (msg) => {
const clientChatId = msg.chat.username ? `@${msg.chat.username}` : msg.chat.id;
if (waitingForReview[clientChatId]) {
const bookingId = waitingForReview[clientChatId];
// Save review to Supabase
const { error } = await supabase
.from('bookings')
.update({ review: msg.text })
.eq('id', bookingId);
if (!error) {
await bot.sendMessage(clientChatId, 'Thank you for your review!');
// Notify fundi with the review
await bot.sendMessage(process.env.TELEGRAM_CHAT_ID, `New review for booking ${bookingId}:
${msg.text}`);
console.log('Sent review to fundi:', msg.text);
} else {
await bot.sendMessage(clientChatId, 'Failed to save your review. Please try again.');
}
delete waitingForReview[clientChatId];
}
});
module.exports = { bot, notifyFundi };