Skip to content

Commit d90b614

Browse files
committed
pizzagate
1 parent a4900a9 commit d90b614

File tree

5 files changed

+769
-407
lines changed

5 files changed

+769
-407
lines changed

packages/client-twitter/src/interactions.ts

+39
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@ import {
1717
} from "@ai16z/eliza";
1818
import { ClientBase } from "./base";
1919
import { buildConversationThread, sendTweet, wait } from "./utils.ts";
20+
import {
21+
generateText
22+
} from "@ai16z/eliza/src/generation.ts";
23+
import { PizzaAPI } from "./pizza.ts";
24+
import {
25+
pizzaDecisionFooter,
26+
parsePizzaDecisionFromText
27+
} from "@ai16z/eliza/src/parsing.ts";
2028

2129
export const twitterMessageHandlerTemplate =
2230
`
@@ -282,6 +290,37 @@ export class TwitterInteractionClient {
282290
this.client.saveRequestMessage(message, state);
283291
}
284292

293+
const pizzaCheck = `
294+
You are checking to see if someone is asking you to order a pizza.
295+
They should explicitly ask for a pizza order.
296+
297+
Here is the tweet they posted:
298+
${currentPost}`
299+
+ pizzaDecisionFooter;
300+
301+
const pizzaCheckResponse = await generateText({
302+
runtime: this.runtime,
303+
context: pizzaCheck,
304+
modelClass: ModelClass.LARGE,
305+
});
306+
307+
console.log("[PIZZA-GEN][INTERACTIONS CLIENT] PIZZA check response: ", pizzaCheckResponse, " ", currentPost);
308+
309+
const pizzaCheckResult = parsePizzaDecisionFromText(pizzaCheckResponse);
310+
311+
console.log("[PIZZA-GEN][INTERACTIONS CLIENT] PIZZA check result:", pizzaCheckResult);
312+
313+
if (pizzaCheckResult === "YES"){
314+
console.log("[PIZZA-GEN][INTERACTIONS CLIENT] PIZZA check result is YES, generating pizza order");
315+
316+
const pizzaAPI = new PizzaAPI(this.runtime);
317+
318+
const result = await pizzaAPI.orderPizza();
319+
320+
console.log("[PIZZA-GEN][INTERACTIONS CLIENT] Order result: ", result);
321+
322+
}
323+
285324
const shouldRespondContext = composeContext({
286325
state,
287326
template:

packages/client-twitter/src/pizza.ts

+256
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
import { IAgentRuntime } from "@ai16z/eliza";
2+
3+
// Types and Interfaces
4+
interface Address {
5+
Street: string;
6+
City: string;
7+
Region: string;
8+
PostalCode: string;
9+
}
10+
11+
interface CustomerInfo {
12+
FirstName: string;
13+
LastName: string;
14+
Email: string;
15+
Phone: string;
16+
}
17+
18+
interface PizzaOption {
19+
[key: string]: {
20+
[key: string]: string;
21+
};
22+
}
23+
24+
interface Product {
25+
Code: string;
26+
Options: PizzaOption;
27+
}
28+
29+
interface Payment {
30+
Type: string;
31+
Amount: number;
32+
CardType: string;
33+
Number: string;
34+
Expiration: string;
35+
SecurityCode: string;
36+
PostalCode: string;
37+
TipAmount: number;
38+
}
39+
40+
interface OrderRequest {
41+
Address: Address;
42+
StoreID: string;
43+
Products: Product[];
44+
OrderChannel: string;
45+
OrderMethod: string;
46+
LanguageCode: string;
47+
ServiceMethod: string;
48+
Payments?: Payment[];
49+
FirstName?: string;
50+
LastName?: string;
51+
Email?: string;
52+
Phone?: string;
53+
}
54+
55+
export class PizzaAPI {
56+
private readonly BASE_URL: string;
57+
private readonly TRACKER_URL: string;
58+
59+
private readonly headers = {
60+
'Accept': 'application/json',
61+
'Content-Type': 'application/json',
62+
'Referer': 'order.dominos.com'
63+
};
64+
65+
private readonly trackerHeaders = {
66+
'dpz-language': 'en',
67+
'dpz-market': 'UNITED_STATES',
68+
'Accept': 'application/json',
69+
'Content-Type': 'application/json; charset=utf-8'
70+
};
71+
72+
constructor(private runtime: IAgentRuntime) {
73+
this.BASE_URL = this.runtime.getSetting('API_BASE_URL') || 'https://order.dominos.com/power';
74+
this.TRACKER_URL = this.runtime.getSetting('API_TRACKER_URL') || 'https://tracker.dominos.com/tracker-presentation-service/v2';
75+
}
76+
77+
// Helper function to get required setting
78+
private getRequiredSetting(name: string): string {
79+
const value = this.runtime.getSetting(name);
80+
if (!value) {
81+
throw new Error(`Required setting ${name} is not configured`);
82+
}
83+
return value;
84+
}
85+
86+
// Function to get customer info from settings
87+
private getCustomerInfo(): CustomerInfo {
88+
return {
89+
FirstName: this.getRequiredSetting('CUSTOMER_FIRST_NAME'),
90+
LastName: this.getRequiredSetting('CUSTOMER_LAST_NAME'),
91+
Email: this.getRequiredSetting('CUSTOMER_EMAIL'),
92+
Phone: this.getRequiredSetting('CUSTOMER_PHONE')
93+
};
94+
}
95+
96+
// Function to get address from settings
97+
private getAddress(): Address {
98+
return {
99+
Street: this.getRequiredSetting('CUSTOMER_STREET'),
100+
City: this.getRequiredSetting('CUSTOMER_CITY'),
101+
Region: this.getRequiredSetting('CUSTOMER_REGION'),
102+
PostalCode: this.getRequiredSetting('CUSTOMER_POSTAL_CODE')
103+
};
104+
}
105+
106+
// Function to get payment info from settings
107+
private getPayment(amount: number): Payment {
108+
return {
109+
Type: 'CreditCard',
110+
Amount: amount,
111+
CardType: this.detectCardType(this.getRequiredSetting('PAYMENT_CARD_NUMBER')),
112+
Number: this.getRequiredSetting('PAYMENT_CARD_NUMBER'),
113+
Expiration: this.getRequiredSetting('PAYMENT_EXPIRATION'),
114+
SecurityCode: this.getRequiredSetting('PAYMENT_CVV'),
115+
PostalCode: this.getRequiredSetting('PAYMENT_POSTAL_CODE'),
116+
TipAmount: parseFloat(this.getRequiredSetting('PAYMENT_TIP_AMOUNT'))
117+
};
118+
}
119+
120+
private detectCardType(cardNumber: string): string {
121+
if (cardNumber.startsWith('4')) return 'VISA';
122+
if (cardNumber.startsWith('5')) return 'MASTERCARD';
123+
if (cardNumber.startsWith('34') || cardNumber.startsWith('37')) return 'AMEX';
124+
if (cardNumber.startsWith('6')) return 'DISCOVER';
125+
return 'UNKNOWN';
126+
}
127+
128+
async findNearestStore(): Promise<any> {
129+
const address = this.getAddress();
130+
const encodedAddress = encodeURIComponent(address.Street);
131+
const encodedCityState = encodeURIComponent(`${address.City}, ${address.Region}`);
132+
const url = `${this.BASE_URL}/store-locator?s=${encodedAddress}&c=${encodedCityState}&type=Delivery`;
133+
134+
const response = await fetch(url, {
135+
method: 'GET',
136+
headers: this.headers
137+
});
138+
return response.json();
139+
}
140+
141+
async getStoreInfo(storeId: string): Promise<any> {
142+
const url = `${this.BASE_URL}/store/${storeId}/profile`;
143+
const response = await fetch(url, {
144+
method: 'GET',
145+
headers: this.headers
146+
});
147+
return response.json();
148+
}
149+
150+
async validateOrder(orderData: OrderRequest): Promise<any> {
151+
const url = `${this.BASE_URL}/validate-order`;
152+
const response = await fetch(url, {
153+
method: 'POST',
154+
headers: this.headers,
155+
body: JSON.stringify({ Order: orderData })
156+
});
157+
return response.json();
158+
}
159+
160+
async priceOrder(orderData: OrderRequest): Promise<any> {
161+
const url = `${this.BASE_URL}/price-order`;
162+
const response = await fetch(url, {
163+
method: 'POST',
164+
headers: this.headers,
165+
body: JSON.stringify({ Order: orderData })
166+
});
167+
return response.json();
168+
}
169+
170+
async placeOrder(orderData: OrderRequest): Promise<any> {
171+
const url = `${this.BASE_URL}/place-order`;
172+
const response = await fetch(url, {
173+
method: 'POST',
174+
headers: this.headers,
175+
body: JSON.stringify({ Order: orderData })
176+
});
177+
return response.json();
178+
}
179+
180+
async trackOrder(): Promise<any> {
181+
const customerPhone = this.getRequiredSetting('CUSTOMER_PHONE');
182+
const url = `${this.TRACKER_URL}/orders?phonenumber=${customerPhone.replace(/\D/g, '')}`;
183+
const response = await fetch(url, {
184+
method: 'GET',
185+
headers: this.trackerHeaders
186+
});
187+
return response.json();
188+
}
189+
190+
async orderPizza() {
191+
try {
192+
// 1. Find nearest store using settings address
193+
const storeResponse = await this.findNearestStore();
194+
console.log('Store Response:', JSON.stringify(storeResponse, null, 2));
195+
const storeId = storeResponse.Stores[0].StoreID;
196+
197+
// 2. Get store info
198+
const storeInfo = await this.getStoreInfo(storeId);
199+
console.log('Store Info:', JSON.stringify(storeInfo, null, 2));
200+
201+
// 3. Create order request
202+
const address = this.getAddress();
203+
const orderRequest: OrderRequest = {
204+
Address: address,
205+
StoreID: storeId,
206+
Products: [{
207+
Code: '14SCREEN',
208+
Options: {
209+
X: { '1/1': '1' },
210+
C: { '1/1': '1' },
211+
}
212+
}],
213+
OrderChannel: 'OLO',
214+
OrderMethod: 'Web',
215+
LanguageCode: 'en',
216+
ServiceMethod: 'Delivery'
217+
};
218+
219+
// 4. Validate order
220+
const validatedOrder = await this.validateOrder(orderRequest);
221+
console.log('Validated Order:', JSON.stringify(validatedOrder, null, 2));
222+
223+
// 5. Price order
224+
const pricedOrder = await this.priceOrder(orderRequest);
225+
console.log('Priced Order:', JSON.stringify(pricedOrder, null, 2));
226+
227+
// 6. Add payment and customer info for final order
228+
const customerInfo = this.getCustomerInfo();
229+
const finalOrder: OrderRequest = {
230+
...orderRequest,
231+
FirstName: customerInfo.FirstName,
232+
LastName: customerInfo.LastName,
233+
Email: customerInfo.Email,
234+
Phone: customerInfo.Phone,
235+
Payments: [this.getPayment(pricedOrder.Order.Amounts.Customer)]
236+
};
237+
238+
// 7. Place order
239+
const placedOrder = await this.placeOrder(finalOrder);
240+
console.log('Placed Order:', JSON.stringify(placedOrder, null, 2));
241+
242+
// 8. Track order
243+
const trackingInfo = await this.trackOrder();
244+
console.log('Tracking Info:', JSON.stringify(trackingInfo, null, 2));
245+
246+
return {
247+
storeInfo,
248+
orderDetails: placedOrder,
249+
tracking: trackingInfo
250+
};
251+
} catch (error) {
252+
console.error('Error ordering pizza:', error);
253+
throw error;
254+
}
255+
}
256+
}

packages/core/src/parsing.ts

+23
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ If {{agentName}} is talking too much, you can choose [IGNORE]
1010
1111
Your response must include one of the options.`;
1212

13+
14+
export const pizzaDecisionFooter = `The available options are [YES] or [NO]. Choose the most appropriate option.
15+
Your response must include one of the options.`;
16+
17+
1318
export const parseShouldRespondFromText = (
1419
text: string
1520
): "RESPOND" | "IGNORE" | "STOP" | null => {
@@ -31,6 +36,24 @@ export const parseShouldRespondFromText = (
3136
: null;
3237
};
3338

39+
export const parsePizzaDecisionFromText = (
40+
text: string
41+
): "YES" | "NO" | null => {
42+
const match = text
43+
.split('\n')[0]
44+
.trim()
45+
.replace("[", "")
46+
.toUpperCase()
47+
.replace("]", "")
48+
.match(/^(YES|NO)$/i);
49+
return match
50+
? (match[0].toUpperCase() as "YES" | "NO")
51+
: text.includes("YES") ? "YES" : text.includes("NO") ? "NO" : null;
52+
};
53+
54+
55+
56+
3457
export const booleanFooter = `Respond with a YES or a NO.`;
3558

3659
export const parseBooleanFromText = (text: string) => {

0 commit comments

Comments
 (0)