import dotenv from 'dotenv' dotenv.config() import { Client, GatewayIntentBits } from 'discord.js' import { logger, formatCustomDate } from './utils.js' import OpenAI from 'openai' import moment from 'moment-timezone' const openai = new OpenAI() import { showDailyReservations } from './reservations_delete.js' import { createReservation as supabaseCreateReservation, getReservationsForPeriod, } from './supabase/reservationsHelper.js' import functions from './openai/reservation_functions.js' const client = new Client({ intents: [ GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent, GatewayIntentBits.GuildMessageReactions, ], }) const botToken = process.env.DISCORD_BOT_TOKEN // Function to extract intent and parameters from a message using OpenAI export async function extractIntentAndParameters(messageContent) { const date = new Date() const dayOfWeek = date.toLocaleDateString('bg-BG', { weekday: 'long' }) const currentDate = `${date.toISOString().split('T')[0]} (${dayOfWeek})` const messages = [ { role: 'system', content: `You are a reservation assistant bot. When given a message, identify the intended action and extract the parameters required to perform the action. Important: - Do not infer any information that is not explicitly mentioned. - When dates are relative like "утре", "другата седмица", etc., infer the actual date based on the current date (${currentDate}) in YYYY-MM-DD format. - Output the parameters in English for consistency. Respond by calling one of the functions with the extracted parameters.`, }, { role: 'user', content: messageContent }, ] try { const response = await openai.chat.completions.create({ model: 'gpt-4o', messages: messages, functions: functions, function_call: 'auto', }) const message = response.choices[0].message if (message.function_call) { const action = message.function_call.name let parameters try { parameters = JSON.parse(message.function_call.arguments) } catch (parseErr) { console.error('Error parsing function arguments:', parseErr) throw new Error( 'Не можах да разбера вашата заявка. Моля, опитайте отново с повече детайли.' ) } logger(`Action: ${action}, Parameters:`) console.dir(parameters, { depth: null }) return { action, parameters } } else { throw new Error( 'Не можах да разбера вашата заявка. Моля, опитайте отново с повече детайли.' ) } } catch (error) { console.error('Error calling OpenAI API:', error) throw new Error( error.message || 'Грешка при обработка на вашата заявка. Моля, опитайте отново по-късно.' ) } } // Function to save or update the reservation to Supabase async function createReservation(reservation, channel) { let { person_name, phone, from_date, to_date, persons, caparo, description } = reservation if (!person_name || person_name.trim() === '') { person_name = 'Клиент' reservation.person_name = person_name } // Convert from_date and to_date to UTC from_date = moment.tz(from_date, 'Europe/Sofia').utc().format() if (to_date) { to_date = moment.tz(to_date, 'Europe/Sofia').utc().format() } try { const result = await supabaseCreateReservation({ person_name, phone, from_date, to_date, persons, caparo, description }) if (result.success) { channel.send('Резервацията е създадена успешно.') // update reservations channel message if the reservation is for today const today = new Date().toISOString().split('T')[0] if (from_date.split('T')[0] === today) { showDailyReservations(channel) } } else { channel.send('Хм, записването не мина по план. Нека опитаме пак! 🤔') } } catch (err) { console.error('Error saving the reservation:', err) channel.send('Хм, записването не мина по план. Нека опитаме пак! 🤔') } } // Function to display reservations within a date range from Supabase export const showReservations = async (startDate, endDate, channel) => { try { const result = await getReservationsForPeriod(startDate, endDate) if (result.error) { channel.send('Неуспешно извличане на резервации.') return } const reservations = result.data if (reservations.length === 0) { channel.send('Няма резервации за избрания период.') } else { // Order reservations by from_date reservations.sort((a, b) => new Date(a.from_date) - new Date(b.from_date)) const reservationMessages = reservations.map((reservation) => { const formattedDate = formatCustomDate(reservation.from_date) const time = moment.tz(reservation.from_date, 'UTC').tz('Europe/Sofia').format('HH:mm') let reservationDetails = `📅 ${formattedDate} ${time} | **${ reservation.person_name || '' }** | 👥 ${reservation.persons} гости | ${ reservation.description || 'Без информация' }` if (reservation.phone) { reservationDetails += ` | 📞 ${reservation.phone}` } return reservationDetails }) const dateRange = startDate === endDate ? `за ${formatCustomDate(startDate)}` : `за периода от ${formatCustomDate(startDate)} до ${formatCustomDate( endDate )}` const message = `Резервации ${dateRange}:\n\n${reservationMessages.join( '\n' )}` channel.send(message) } } catch (error) { console.error('Error processing reservations:', error) channel.send('Неуспешно извличане на резервации.') } } export const handleReservationMessage = async (message) => { const content = message.content.trim() try { const { action, parameters } = await extractIntentAndParameters(content) switch (action) { case 'CreateReservation': await createReservation(parameters, message.channel) await showReservations( parameters.from_date.split('T')[0], parameters.from_date.split('T')[0], message.channel ) break case 'ShowReservations': showReservations( parameters.startDate, parameters.endDate, message.channel ) break case 'UknownAction': message.channel.send(parameters.message) break default: logger('Unknown action:', action) message.channel.send('Не разпознавам командата.') } } catch (error) { console.error('Error processing message:', error) message.channel.send( error.message || 'Съжалявам, възникна грешка при обработката на вашата заявка.' ) } } // Start the bot client.login(botToken)