food402 1.0.2 → 1.0.4-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,396 @@
1
+ export interface Address {
2
+ id: number;
3
+ name: string;
4
+ surname: string;
5
+ phone: string;
6
+ countryPhoneCode: string;
7
+ addressLine: string;
8
+ addressName: string;
9
+ postalCode: string;
10
+ cityId: number;
11
+ cityName: string;
12
+ districtId: number;
13
+ districtName: string;
14
+ neighborhoodId: number;
15
+ neighborhoodName: string;
16
+ latitude: string;
17
+ longitude: string;
18
+ addressDescription: string;
19
+ apartmentNumber: string;
20
+ floor: string;
21
+ doorNumber: string;
22
+ addressType: string;
23
+ elevatorAvailable: boolean;
24
+ }
25
+ export interface AddressesResponse {
26
+ infoMessage: string | null;
27
+ id: string;
28
+ addresses: Address[];
29
+ }
30
+ export interface Restaurant {
31
+ id: number;
32
+ name: string;
33
+ kitchen: string;
34
+ rating: number;
35
+ ratingText: string;
36
+ minBasketPrice: number;
37
+ averageDeliveryInterval: string;
38
+ distance: number;
39
+ neighborhoodName: string;
40
+ isClosed: boolean;
41
+ campaignText?: string;
42
+ }
43
+ export interface RestaurantsResponse {
44
+ restaurants: Restaurant[];
45
+ totalCount: number;
46
+ currentPage: number;
47
+ pageSize: number;
48
+ hasNextPage: boolean;
49
+ }
50
+ export interface MenuItem {
51
+ id: number;
52
+ name: string;
53
+ description: string;
54
+ price: number;
55
+ likePercentage?: string;
56
+ }
57
+ export interface MenuCategory {
58
+ name: string;
59
+ slug: string;
60
+ items: MenuItem[];
61
+ }
62
+ export interface RestaurantInfo {
63
+ id: number;
64
+ name: string;
65
+ status: string;
66
+ rating: number;
67
+ ratingText: string;
68
+ workingHours: string;
69
+ deliveryTime: string;
70
+ minOrderPrice: number;
71
+ }
72
+ export interface RestaurantMenuResponse {
73
+ info: RestaurantInfo;
74
+ categories: MenuCategory[];
75
+ totalItems: number;
76
+ }
77
+ export interface RecommendedItem {
78
+ id: number;
79
+ name: string;
80
+ description: string | null;
81
+ price: number;
82
+ imageUrl: string;
83
+ }
84
+ export interface RecommendationCollection {
85
+ name: string;
86
+ items: RecommendedItem[];
87
+ }
88
+ export interface ProductRecommendationsResponse {
89
+ collections: RecommendationCollection[];
90
+ totalItems: number;
91
+ }
92
+ export interface ProductOption {
93
+ id: number;
94
+ name: string;
95
+ price: number;
96
+ selected: boolean;
97
+ isPopular?: boolean;
98
+ }
99
+ export interface ProductComponent {
100
+ type: "INGREDIENTS" | "MODIFIER_GROUP";
101
+ title: string;
102
+ description?: string;
103
+ modifierGroupId?: number;
104
+ options: ProductOption[];
105
+ isSingleChoice: boolean;
106
+ minSelections: number;
107
+ maxSelections: number;
108
+ }
109
+ export interface ProductDetailsResponse {
110
+ restaurantId: number;
111
+ restaurantName: string;
112
+ productId: number;
113
+ productName: string;
114
+ description: string;
115
+ imageUrl: string;
116
+ price: number;
117
+ maxQuantity: number;
118
+ components: ProductComponent[];
119
+ }
120
+ export interface IngredientExclusion {
121
+ id: number;
122
+ }
123
+ export interface ModifierProduct {
124
+ productId: number;
125
+ modifierGroupId: number;
126
+ modifierProducts: ModifierProduct[];
127
+ ingredientOptions: {
128
+ excludes: IngredientExclusion[];
129
+ includes: [];
130
+ };
131
+ }
132
+ export interface BasketItem {
133
+ productId: number;
134
+ quantity: number;
135
+ modifierProducts: ModifierProduct[];
136
+ ingredientOptions: {
137
+ excludes: IngredientExclusion[];
138
+ includes: [];
139
+ };
140
+ }
141
+ export interface AddToBasketRequest {
142
+ storeId: number;
143
+ items: BasketItem[];
144
+ isFlashSale: boolean;
145
+ storePickup: boolean;
146
+ latitude: number;
147
+ longitude: number;
148
+ }
149
+ export interface CartProduct {
150
+ productId: number;
151
+ itemId: string;
152
+ name: string;
153
+ quantity: number;
154
+ salePrice: number;
155
+ description: string;
156
+ }
157
+ export interface CartStore {
158
+ id: number;
159
+ name: string;
160
+ imageUrl: string;
161
+ rating: number;
162
+ averageDeliveryInterval: string;
163
+ minAmount: number;
164
+ }
165
+ export interface CartSummaryLine {
166
+ title: string;
167
+ amount: number;
168
+ isPromotion?: boolean;
169
+ }
170
+ export interface AddToBasketResponse {
171
+ store: CartStore;
172
+ products: CartProduct[];
173
+ summary: CartSummaryLine[];
174
+ totalProductCount: number;
175
+ totalProductPrice: number;
176
+ totalProductPriceDiscounted: number;
177
+ totalPrice: number;
178
+ deliveryPrice: number;
179
+ }
180
+ export interface SetShippingAddressRequest {
181
+ shippingAddressId: number;
182
+ invoiceAddressId: number;
183
+ }
184
+ export interface CartProductDetails extends CartProduct {
185
+ marketPrice: number;
186
+ modifierProducts: Array<{
187
+ productId: number;
188
+ modifierGroupId: number;
189
+ name: string;
190
+ price: number;
191
+ }>;
192
+ ingredientExcludes: Array<{
193
+ id: number;
194
+ name: string;
195
+ }>;
196
+ }
197
+ export interface CartStoreGroup {
198
+ store: CartStore;
199
+ products: CartProductDetails[];
200
+ }
201
+ export interface GetBasketResponse {
202
+ storeGroups: CartStoreGroup[];
203
+ summary: CartSummaryLine[];
204
+ totalProductCount: number;
205
+ totalProductPrice: number;
206
+ totalProductPriceDiscounted: number;
207
+ totalPrice: number;
208
+ deliveryPrice: number;
209
+ isEmpty: boolean;
210
+ }
211
+ export interface SearchProduct {
212
+ id: number;
213
+ name: string;
214
+ description?: string;
215
+ price: number;
216
+ imageUrl?: string;
217
+ }
218
+ export interface SearchRestaurant extends Restaurant {
219
+ products: SearchProduct[];
220
+ warning?: string;
221
+ }
222
+ export interface SearchRestaurantsResponse {
223
+ restaurants: SearchRestaurant[];
224
+ totalCount: number;
225
+ currentPage: number;
226
+ pageSize: number;
227
+ hasNextPage: boolean;
228
+ searchQuery: string;
229
+ }
230
+ export interface City {
231
+ id: number;
232
+ code: string;
233
+ name: string;
234
+ }
235
+ export interface District {
236
+ id: number;
237
+ name: string;
238
+ }
239
+ export interface Neighborhood {
240
+ id: number;
241
+ name: string;
242
+ }
243
+ export interface CitiesResponse {
244
+ cities: City[];
245
+ count: number;
246
+ }
247
+ export interface DistrictsResponse {
248
+ districts: District[];
249
+ count: number;
250
+ cityId: number;
251
+ }
252
+ export interface NeighborhoodsResponse {
253
+ neighborhoods: Neighborhood[];
254
+ count: number;
255
+ districtId: number;
256
+ }
257
+ export interface AddAddressRequest {
258
+ name: string;
259
+ surname: string;
260
+ phone: string;
261
+ apartmentNumber?: string;
262
+ floor?: string;
263
+ doorNumber?: string;
264
+ addressName: string;
265
+ addressDescription?: string;
266
+ addressLine: string;
267
+ cityId: number;
268
+ districtId: number;
269
+ neighborhoodId: number;
270
+ latitude: string;
271
+ longitude: string;
272
+ countryCode?: string;
273
+ elevatorAvailable?: boolean;
274
+ }
275
+ export interface AddAddressResponse {
276
+ success: boolean;
277
+ address?: Address;
278
+ requiresOtp?: boolean;
279
+ message: string;
280
+ }
281
+ export interface SavedCard {
282
+ cardId: number;
283
+ name: string;
284
+ maskedCardNumber: string;
285
+ cardTypeName: string;
286
+ bankName: string;
287
+ isDebitCard: boolean;
288
+ cvvRequired: boolean;
289
+ cardNetwork: string;
290
+ }
291
+ export interface SavedCardsResponse {
292
+ cards: SavedCard[];
293
+ hasCards: boolean;
294
+ message?: string;
295
+ }
296
+ export interface CheckoutReadyResponse {
297
+ ready: boolean;
298
+ store: CartStore;
299
+ products: CartProductDetails[];
300
+ summary: CartSummaryLine[];
301
+ totalPrice: number;
302
+ deliveryPrice: number;
303
+ warnings: string[];
304
+ }
305
+ export interface PlaceOrderResponse {
306
+ success: boolean;
307
+ orderId?: string;
308
+ requires3DSecure?: boolean;
309
+ redirectUrl?: string;
310
+ htmlContent?: string;
311
+ message: string;
312
+ }
313
+ export interface CustomerNoteRequest {
314
+ customerNote: string;
315
+ noServiceWare: boolean;
316
+ contactlessDelivery: boolean;
317
+ dontRingBell: boolean;
318
+ }
319
+ export interface OrderStatus {
320
+ status: string;
321
+ statusText: string;
322
+ statusColor: string;
323
+ }
324
+ export interface OrderStore {
325
+ id: number;
326
+ name: string;
327
+ }
328
+ export interface OrderPrice {
329
+ totalPrice: number;
330
+ totalPriceText: string;
331
+ refundedPrice: number;
332
+ cancelledPrice: number;
333
+ totalDeliveryPrice: number;
334
+ totalServicePrice: number;
335
+ }
336
+ export interface OrderProductSummary {
337
+ productId: number;
338
+ name: string;
339
+ imageUrl: string;
340
+ }
341
+ export interface Order {
342
+ id: string;
343
+ orderDate: string;
344
+ store: OrderStore;
345
+ status: OrderStatus;
346
+ price: OrderPrice;
347
+ productSummary: string;
348
+ products: OrderProductSummary[];
349
+ isReady: boolean;
350
+ }
351
+ export interface OrdersResponse {
352
+ orders: Order[];
353
+ pagination: {
354
+ currentPage: number;
355
+ pageSize: number;
356
+ totalCount: number;
357
+ hasNext: boolean;
358
+ };
359
+ }
360
+ export interface OrderDetailProduct {
361
+ name: string;
362
+ imageUrl: string;
363
+ salePrice: number;
364
+ salePriceText: string;
365
+ quantity: number;
366
+ description: string;
367
+ }
368
+ export interface OrderStatusStep {
369
+ status: string;
370
+ statusText: string;
371
+ }
372
+ export interface OrderShipmentItem {
373
+ status: OrderStatus;
374
+ statusSteps: OrderStatusStep[];
375
+ products: OrderDetailProduct[];
376
+ }
377
+ export interface OrderDetail {
378
+ orderId: string;
379
+ orderNumber: string;
380
+ orderDate: string;
381
+ customerNote: string;
382
+ store: OrderStore;
383
+ eta: string;
384
+ deliveredDate: string;
385
+ status: OrderStatus;
386
+ statusSteps: OrderStatusStep[];
387
+ products: OrderDetailProduct[];
388
+ price: OrderPrice;
389
+ paymentDescription: string;
390
+ deliveryAddress: {
391
+ name: string;
392
+ address: string;
393
+ districtCity: string;
394
+ phoneNumber: string;
395
+ };
396
+ }
@@ -0,0 +1,2 @@
1
+ // shared/types.ts - Common TypeScript interfaces for TGO Yemek API
2
+ export {};
@@ -0,0 +1,23 @@
1
+ import * as sharedApi from "../shared/api.js";
2
+ export type { Address, AddressesResponse, Restaurant, RestaurantsResponse, MenuItem, MenuCategory, RestaurantInfo, RestaurantMenuResponse, RecommendedItem, RecommendationCollection, ProductRecommendationsResponse, ProductOption, ProductComponent, ProductDetailsResponse, IngredientExclusion, ModifierProduct, BasketItem, AddToBasketRequest, CartProduct, CartStore, CartSummaryLine, AddToBasketResponse, SetShippingAddressRequest, CartProductDetails, CartStoreGroup, GetBasketResponse, SearchProduct, SearchRestaurant, SearchRestaurantsResponse, City, District, Neighborhood, CitiesResponse, DistrictsResponse, NeighborhoodsResponse, AddAddressRequest, AddAddressResponse, SavedCard, SavedCardsResponse, CheckoutReadyResponse, PlaceOrderResponse, CustomerNoteRequest, OrderStatus, OrderStore, OrderPrice, OrderProductSummary, Order, OrdersResponse, OrderDetailProduct, OrderStatusStep, OrderShipmentItem, OrderDetail, } from "../shared/types.js";
3
+ export declare function getAddresses(): Promise<sharedApi.AddressesResponse>;
4
+ export declare function getRestaurants(latitude: string, longitude: string, page?: number): Promise<sharedApi.RestaurantsResponse>;
5
+ export declare function getRestaurantMenu(restaurantId: number, latitude: string, longitude: string): Promise<sharedApi.RestaurantMenuResponse>;
6
+ export declare function getProductRecommendations(restaurantId: number, productIds: number[]): Promise<sharedApi.ProductRecommendationsResponse>;
7
+ export declare function getProductDetails(restaurantId: number, productId: number, latitude: string, longitude: string): Promise<sharedApi.ProductDetailsResponse>;
8
+ export declare function setShippingAddress(request: sharedApi.SetShippingAddressRequest): Promise<void>;
9
+ export declare function addToBasket(request: sharedApi.AddToBasketRequest): Promise<sharedApi.AddToBasketResponse>;
10
+ export declare function getBasket(): Promise<sharedApi.GetBasketResponse>;
11
+ export declare function removeFromBasket(itemId: string): Promise<sharedApi.GetBasketResponse>;
12
+ export declare function clearBasket(): Promise<void>;
13
+ export declare function getCities(): Promise<sharedApi.CitiesResponse>;
14
+ export declare function getDistricts(cityId: number): Promise<sharedApi.DistrictsResponse>;
15
+ export declare function getNeighborhoods(districtId: number): Promise<sharedApi.NeighborhoodsResponse>;
16
+ export declare function addAddress(request: sharedApi.AddAddressRequest): Promise<sharedApi.AddAddressResponse>;
17
+ export declare function updateCustomerNote(request: sharedApi.CustomerNoteRequest): Promise<void>;
18
+ export declare function getSavedCards(): Promise<sharedApi.SavedCardsResponse>;
19
+ export declare function getCheckoutReady(): Promise<sharedApi.CheckoutReadyResponse>;
20
+ export declare function placeOrder(cardId: number): Promise<sharedApi.PlaceOrderResponse>;
21
+ export declare function getOrders(page?: number): Promise<sharedApi.OrdersResponse>;
22
+ export declare function getOrderDetail(orderId: string): Promise<sharedApi.OrderDetail>;
23
+ export declare function searchRestaurants(searchQuery: string, latitude: string, longitude: string, page?: number): Promise<sharedApi.SearchRestaurantsResponse>;
@@ -0,0 +1,88 @@
1
+ // src/api.ts - TGO Yemek API Functions (Local wrapper using getToken)
2
+ import { getToken } from "./auth.js";
3
+ import * as sharedApi from "../shared/api.js";
4
+ // Wrapper functions that automatically inject the token
5
+ export async function getAddresses() {
6
+ const token = await getToken();
7
+ return sharedApi.getAddresses(token);
8
+ }
9
+ export async function getRestaurants(latitude, longitude, page = 1) {
10
+ const token = await getToken();
11
+ return sharedApi.getRestaurants(token, latitude, longitude, page);
12
+ }
13
+ export async function getRestaurantMenu(restaurantId, latitude, longitude) {
14
+ const token = await getToken();
15
+ return sharedApi.getRestaurantMenu(token, restaurantId, latitude, longitude);
16
+ }
17
+ export async function getProductRecommendations(restaurantId, productIds) {
18
+ const token = await getToken();
19
+ return sharedApi.getProductRecommendations(token, restaurantId, productIds);
20
+ }
21
+ export async function getProductDetails(restaurantId, productId, latitude, longitude) {
22
+ const token = await getToken();
23
+ return sharedApi.getProductDetails(token, restaurantId, productId, latitude, longitude);
24
+ }
25
+ export async function setShippingAddress(request) {
26
+ const token = await getToken();
27
+ return sharedApi.setShippingAddress(token, request);
28
+ }
29
+ export async function addToBasket(request) {
30
+ const token = await getToken();
31
+ return sharedApi.addToBasket(token, request);
32
+ }
33
+ export async function getBasket() {
34
+ const token = await getToken();
35
+ return sharedApi.getBasket(token);
36
+ }
37
+ export async function removeFromBasket(itemId) {
38
+ const token = await getToken();
39
+ return sharedApi.removeFromBasket(token, itemId);
40
+ }
41
+ export async function clearBasket() {
42
+ const token = await getToken();
43
+ return sharedApi.clearBasket(token);
44
+ }
45
+ export async function getCities() {
46
+ const token = await getToken();
47
+ return sharedApi.getCities(token);
48
+ }
49
+ export async function getDistricts(cityId) {
50
+ const token = await getToken();
51
+ return sharedApi.getDistricts(token, cityId);
52
+ }
53
+ export async function getNeighborhoods(districtId) {
54
+ const token = await getToken();
55
+ return sharedApi.getNeighborhoods(token, districtId);
56
+ }
57
+ export async function addAddress(request) {
58
+ const token = await getToken();
59
+ return sharedApi.addAddress(token, request);
60
+ }
61
+ export async function updateCustomerNote(request) {
62
+ const token = await getToken();
63
+ return sharedApi.updateCustomerNote(token, request);
64
+ }
65
+ export async function getSavedCards() {
66
+ const token = await getToken();
67
+ return sharedApi.getSavedCards(token);
68
+ }
69
+ export async function getCheckoutReady() {
70
+ const token = await getToken();
71
+ return sharedApi.getCheckoutReady(token);
72
+ }
73
+ export async function placeOrder(cardId) {
74
+ const token = await getToken();
75
+ return sharedApi.placeOrder(token, cardId);
76
+ }
77
+ export async function getOrders(page = 1) {
78
+ const token = await getToken();
79
+ return sharedApi.getOrders(token, page);
80
+ }
81
+ export async function getOrderDetail(orderId) {
82
+ const token = await getToken();
83
+ return sharedApi.getOrderDetail(token, orderId);
84
+ }
85
+ export async function searchRestaurants(searchQuery, latitude, longitude, page = 1) {
86
+ const token = await getToken();
87
+ return sharedApi.searchRestaurants(token, searchQuery, latitude, longitude, page);
88
+ }
@@ -0,0 +1,16 @@
1
+ import "dotenv/config";
2
+ /**
3
+ * Custom error class for authentication failures
4
+ */
5
+ export declare class AuthenticationError extends Error {
6
+ constructor(message: string);
7
+ }
8
+ /**
9
+ * Force a fresh login, bypassing the cache
10
+ */
11
+ export declare function login(): Promise<string>;
12
+ /**
13
+ * Get a valid authentication token
14
+ * Returns cached token if valid, otherwise performs fresh login
15
+ */
16
+ export declare function getToken(): Promise<string>;
@@ -0,0 +1,117 @@
1
+ // src/auth.ts - TGO Yemek Authentication Module
2
+ import "dotenv/config";
3
+ const CREDENTIALS = {
4
+ email: process.env.TGO_EMAIL || "",
5
+ password: process.env.TGO_PASSWORD || ""
6
+ };
7
+ const USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36";
8
+ // Token cache
9
+ let cachedToken = null;
10
+ let tokenExpiry = 0;
11
+ /**
12
+ * Custom error class for authentication failures
13
+ */
14
+ export class AuthenticationError extends Error {
15
+ constructor(message) {
16
+ super(message);
17
+ this.name = "AuthenticationError";
18
+ }
19
+ }
20
+ /**
21
+ * Parse a specific cookie from Set-Cookie header
22
+ */
23
+ function parseCookie(setCookieHeader, name) {
24
+ if (!setCookieHeader)
25
+ return null;
26
+ // Set-Cookie can have multiple cookies separated by comma (for multiple Set-Cookie headers joined)
27
+ // or be a single cookie with attributes separated by semicolon
28
+ const cookies = setCookieHeader.split(/,(?=\s*[^;]+=[^;]+)/);
29
+ for (const cookie of cookies) {
30
+ const parts = cookie.trim().split(";");
31
+ const [cookieName, ...valueParts] = parts[0].split("=");
32
+ if (cookieName.trim() === name) {
33
+ return valueParts.join("=").trim();
34
+ }
35
+ }
36
+ return null;
37
+ }
38
+ /**
39
+ * Decode JWT to get expiry timestamp
40
+ */
41
+ function getTokenExpiry(token) {
42
+ try {
43
+ const parts = token.split(".");
44
+ if (parts.length !== 3)
45
+ return 0;
46
+ const payload = JSON.parse(Buffer.from(parts[1], "base64").toString("utf-8"));
47
+ // JWT exp is in seconds, convert to milliseconds
48
+ return payload.exp ? payload.exp * 1000 : 0;
49
+ }
50
+ catch {
51
+ return 0;
52
+ }
53
+ }
54
+ /**
55
+ * Check if the cached token is still valid
56
+ * Considers token invalid if it expires within 60 seconds
57
+ */
58
+ function isTokenValid() {
59
+ if (!cachedToken)
60
+ return false;
61
+ // Add 60 second buffer before expiry
62
+ return Date.now() < tokenExpiry - 60000;
63
+ }
64
+ /**
65
+ * Force a fresh login, bypassing the cache
66
+ */
67
+ export async function login() {
68
+ // Step 1: Get CSRF token
69
+ const csrfResponse = await fetch("https://tgoyemek.com/api/auth/csrf", {
70
+ headers: {
71
+ "User-Agent": USER_AGENT
72
+ }
73
+ });
74
+ if (!csrfResponse.ok) {
75
+ throw new AuthenticationError(`Failed to fetch CSRF token: ${csrfResponse.status} ${csrfResponse.statusText}`);
76
+ }
77
+ const csrfToken = parseCookie(csrfResponse.headers.get("set-cookie"), "tgo-csrf-token");
78
+ if (!csrfToken) {
79
+ throw new AuthenticationError("CSRF token not found in response");
80
+ }
81
+ // Step 2: Login with credentials
82
+ const loginResponse = await fetch("https://tgoyemek.com/api/auth/login", {
83
+ method: "POST",
84
+ headers: {
85
+ "Content-Type": "text/plain;charset=UTF-8",
86
+ "Cookie": `tgo-csrf-token=${csrfToken}`,
87
+ "User-Agent": USER_AGENT
88
+ },
89
+ body: JSON.stringify({
90
+ username: CREDENTIALS.email,
91
+ password: CREDENTIALS.password,
92
+ csrfToken: csrfToken
93
+ })
94
+ });
95
+ if (!loginResponse.ok) {
96
+ throw new AuthenticationError(`Login failed: ${loginResponse.status} ${loginResponse.statusText}`);
97
+ }
98
+ // Step 3: Extract tgo-token from response
99
+ const token = parseCookie(loginResponse.headers.get("set-cookie"), "tgo-token");
100
+ if (!token) {
101
+ throw new AuthenticationError("Authentication token not found in login response");
102
+ }
103
+ // Cache the token
104
+ cachedToken = token;
105
+ tokenExpiry = getTokenExpiry(token);
106
+ return token;
107
+ }
108
+ /**
109
+ * Get a valid authentication token
110
+ * Returns cached token if valid, otherwise performs fresh login
111
+ */
112
+ export async function getToken() {
113
+ if (isTokenValid() && cachedToken) {
114
+ return cachedToken;
115
+ }
116
+ return login();
117
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import "dotenv/config";