import {
getOrdersByIdsWithFullData,
getOrderByIdWithFullData,
composeSkuName,
composeUnitNotation,
} from '@/src/dbService'
import { sendTelegramMessage } from '@/src/lib/telegram-service'
import { queueDataPusher } from '@/src/lib/queue-data-pusher'
import { ensureWorkerInit } from './worker-init';
import { getAppUrl } from '@/src/lib/env-exporter'
interface OrderIdMessage {
orderIds: number[];
}
interface CancellationMessage {
orderId: number;
cancelledBy: 'user' | 'admin';
reason: string;
cancelledAt: string;
}
const formatDateTime = (dateStr: string | null | undefined): string => {
if (!dateStr) return 'N/A';
return new Date(dateStr).toLocaleString('en-IN', {
dateStyle: 'medium',
timeStyle: 'short',
timeZone: 'Asia/Kolkata',
});
};
const buildTelegramLinks = (orderId: number, userId?: number | null): string => {
const baseUrl = getAppUrl() || 'https://ui.freshyo.in'
const orderUrl = `${baseUrl}/manage-orders/order-details/${orderId}`
const orderLink = `āŖ Order`
if (!userId) {
return orderLink
}
const userUrl = `${baseUrl}/user-management/${userId}`
const userLink = `āŖ User`
return `${orderLink} | ${userLink}`
}
const formatOrderMessageWithFullData = (ordersData: any[]): string => {
console.log('formatting the msg')
let message = 'š New Order Placed\n\n';
ordersData.forEach((order, index) => {
message += `Order ${order.id}\n`;
message += 'š¦ Items:\n';
order.orderItems?.forEach((item: any) => {
const sku = item.sku
const features = sku?.features || []
const name = composeSkuName(sku?.product?.name || 'Unknown', features)
const unit = composeUnitNotation(features)
message += ` ⢠${name} ${unit} x${item.quantity}\n`;
});
message += `\nš° Total: ā¹${order.totalAmount}\n`;
message += `š Delivery: ${
order.isFlashDelivery ? 'Flash Delivery' : formatDateTime(order.slot?.deliveryTime)
}\n`;
message += `\nš Address:\n`;
message += ` ${order.address?.name || 'N/A'}\n`;
message += ` ${order.address?.addressLine1 || ''}\n`;
if (order.address?.addressLine2) {
message += ` ${order.address.addressLine2}\n`;
}
message += ` ${order.address?.city || ''}, ${order.address?.state || ''} - ${order.address?.pincode || ''}\n`;
if (order.address?.phone) {
message += ` š ${order.address.phone}\n`;
}
message += `\n${buildTelegramLinks(order.id, order.userId)}\n`
if (index < ordersData.length - 1) {
message += '\n---\n\n';
}
});
return message;
};
const formatCancellationMessage = (orderData: any, cancellationData: CancellationMessage): string => {
const message = `ā Order Cancelled
Order #${orderData.id}
š¤ Name: ${orderData.address?.name || 'N/A'}
š Phone: ${orderData.address?.phone || 'N/A'}
š¦ Items:
${orderData.orderItems?.map((item: any) => ` ⢠${item.sku?.product?.name || 'Unknown'} x${item.quantity}`).join('\n') || ' N/A'}
š° Total: ā¹${orderData.totalAmount}
š³ Refund: ${orderData.refundStatus === 'na' ? 'N/A (COD)' : orderData.refundStatus || 'Pending'}
ā Reason: ${cancellationData.reason}
š¤ Cancelled by: ${cancellationData.cancelledBy === 'admin' ? 'Admin' : 'User'}
ā° Time: ${formatDateTime(cancellationData.cancelledAt)}
${buildTelegramLinks(orderData.id, orderData.userId)}
`;
return message;
};
export const handleOrderPlaced = async (orderIds: number[], rawMessage?: string): Promise => {
try {
const ordersData = await getOrdersByIdsWithFullData(orderIds)
const telegramMessage = formatOrderMessageWithFullData(ordersData)
await sendTelegramMessage(telegramMessage)
} catch (error) {
console.error('Failed to process order message:', error)
const fallback = rawMessage ? `ā ļø Error parsing order: ${rawMessage}` : 'ā ļø Error parsing order'
await sendTelegramMessage(fallback)
}
}
export const handleOrderCancelled = async (
cancellationData: CancellationMessage,
rawMessage?: string
): Promise => {
try {
console.log('Order cancellation received, sending to Telegram...')
const orderData = await getOrderByIdWithFullData(cancellationData.orderId)
if (!orderData) {
console.error('Order not found for cancellation:', cancellationData.orderId)
await sendTelegramMessage(`ā ļø Order ${cancellationData.orderId} was cancelled but could not be found in database`)
return
}
const refundStatus = orderData.refunds?.[0]?.refundStatus || 'pending'
const telegramMessage = formatCancellationMessage({ ...orderData, refundStatus }, cancellationData)
await sendTelegramMessage(telegramMessage)
} catch (error) {
console.error('Failed to process cancellation message:', error)
const fallback = rawMessage ? `ā ļø Error processing cancellation: ${rawMessage}` : 'ā ļø Error processing cancellation'
await sendTelegramMessage(fallback)
}
}
/**
* Start the post order handler
* Subscribes to the orders:placed channel and sends to Telegram
*/
export const publishOrder = async (orderDetails: OrderIdMessage): Promise => {
console.log('publishing order')
try {
await queueDataPusher.pushOrderPlacedQueue({
name: 'order-placed',
orderIds: orderDetails.orderIds,
})
return true;
} catch (error) {
console.error('Failed to publish order:', error);
return false;
}
};
export const publishFormattedOrder = async (
createdOrders: any[],
ordersBySlot: Map
): Promise => {
try {
const orderIds = createdOrders.map(order => order.id);
return await publishOrder({ orderIds });
} catch (error) {
console.error('Failed to format and publish order:', error);
return false;
}
};
export const publishCancellation = async (
orderId: number,
cancelledBy: 'user' | 'admin',
reason: string
): Promise => {
try {
const message: CancellationMessage = {
orderId,
cancelledBy,
reason,
cancelledAt: new Date().toISOString(),
};
await queueDataPusher.pushOrderCancelledQueue({
name: 'order-cancelled',
...message,
})
console.log('Cancellation published to queue:', orderId);
return true;
} catch (error) {
console.error('Failed to publish cancellation:', error);
return false;
}
};