create-message-kit 1.1.7-beta.8 → 1.1.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. package/README.md +2 -2
  2. package/index.js +60 -50
  3. package/package.json +1 -1
  4. package/templates/agent/src/handler/ens.ts +14 -12
  5. package/templates/agent/src/index.ts +2 -10
  6. package/templates/agent/src/prompt.ts +28 -22
  7. package/templates/agent/src/skills.ts +8 -9
  8. package/templates/gm/.env.example +1 -1
  9. package/templates/gm/src/index.ts +1 -5
  10. package/templates/group/.env.example +2 -3
  11. package/templates/group/src/handler/game.ts +4 -5
  12. package/templates/group/src/handler/helpers.ts +10 -6
  13. package/templates/group/src/handler/payment.ts +29 -0
  14. package/templates/group/src/handler/tipping.ts +17 -35
  15. package/templates/group/src/index.ts +23 -26
  16. package/templates/group/src/prompt.ts +45 -0
  17. package/templates/group/src/skills.ts +17 -107
  18. package/templates/agent/package.json +0 -22
  19. package/templates/agent/src/lib/gpt.ts +0 -161
  20. package/templates/agent/src/lib/openai.ts +0 -174
  21. package/templates/agent/src/lib/resolver.ts +0 -151
  22. package/templates/gm/package.json +0 -21
  23. package/templates/group/package.json +0 -23
  24. package/templates/group/src/handler/agent.ts +0 -67
  25. package/templates/group/src/handler/group.ts +0 -24
  26. package/templates/group/src/handler/loyalty.ts +0 -46
  27. package/templates/group/src/handler/splitpayment.ts +0 -65
  28. package/templates/group/src/handler/transaction.ts +0 -50
  29. package/templates/group/src/lib/gpt.ts +0 -161
  30. package/templates/group/src/lib/openai.ts +0 -174
  31. package/templates/group/src/lib/resolver.ts +0 -151
  32. package/templates/group/src/lib/stack.ts +0 -18
  33. package/templates/group/src/lib/vision.ts +0 -42
@@ -1,174 +0,0 @@
1
- import dotenv from "dotenv";
2
- dotenv.config();
3
- import type { SkillGroup } from "@xmtp/message-kit";
4
- import OpenAI from "openai";
5
- const openai = new OpenAI({
6
- apiKey: process.env.OPEN_AI_API_KEY,
7
- });
8
-
9
- export type ChatHistoryEntry = { role: string; content: string };
10
- export type ChatHistories = Record<string, ChatHistoryEntry[]>;
11
-
12
- // New ChatMemory class
13
- class ChatMemory {
14
- private histories: ChatHistories = {};
15
-
16
- getHistory(address: string): ChatHistoryEntry[] {
17
- return this.histories[address] || [];
18
- }
19
-
20
- addEntry(address: string, entry: ChatHistoryEntry) {
21
- if (!this.histories[address]) {
22
- this.histories[address] = [];
23
- }
24
- this.histories[address].push(entry);
25
- }
26
-
27
- initializeWithSystem(address: string, systemPrompt: string) {
28
- if (this.getHistory(address).length === 0) {
29
- this.addEntry(address, {
30
- role: "system",
31
- content: systemPrompt,
32
- });
33
- }
34
- }
35
-
36
- clear() {
37
- this.histories = {};
38
- }
39
- }
40
-
41
- export const clearMemory = () => {
42
- chatHistories = {};
43
- };
44
-
45
- // Create singleton instance
46
- export const chatMemory = new ChatMemory();
47
-
48
- let chatHistories: ChatHistories = {};
49
- export const PROMPT_RULES = `You are a helpful and playful agent called {NAME} that lives inside a web3 messaging app called Converse.
50
- - You can respond with multiple messages if needed. Each message should be separated by a newline character.
51
- - You can trigger skills by only sending the command in a newline message.
52
- - Never announce actions without using a command separated by a newline character.
53
- - Dont answer in markdown format, just answer in plaintext.
54
- - Do not make guesses or assumptions
55
- - Only answer if the verified information is in the prompt.
56
- - Check that you are not missing a command
57
- - Focus only on helping users with operations detailed below.
58
- `;
59
-
60
- export const PROMPT_SKILLS_AND_EXAMPLES = (skills: SkillGroup[]) => `
61
- Commands:
62
- ${skills
63
- .map((skill) => skill.skills.map((s) => s.command).join("\n"))
64
- .join("\n")}
65
-
66
- Examples:
67
- ${skills
68
- .map((skill) => skill.skills.map((s) => s.examples).join("\n"))
69
- .join("\n")}
70
- `;
71
-
72
- export async function agentResponse(
73
- sender: { address: string },
74
- userPrompt: string,
75
- systemPrompt: string,
76
- context: any,
77
- ) {
78
- try {
79
- const { reply } = await textGeneration(
80
- sender.address,
81
- userPrompt,
82
- systemPrompt,
83
- );
84
- await processMultilineResponse(sender.address, reply, context);
85
- } catch (error) {
86
- console.error("Error during OpenAI call:", error);
87
- await context.reply("An error occurred while processing your request.");
88
- }
89
- }
90
- export async function textGeneration(
91
- address: string,
92
- userPrompt: string,
93
- systemPrompt: string,
94
- ) {
95
- let messages = chatMemory.getHistory(address);
96
- chatMemory.initializeWithSystem(address, systemPrompt);
97
- if (messages.length === 0) {
98
- messages.push({
99
- role: "system",
100
- content: systemPrompt,
101
- });
102
- }
103
- messages.push({
104
- role: "user",
105
- content: userPrompt,
106
- });
107
- try {
108
- const response = await openai.chat.completions.create({
109
- model: "gpt-4o",
110
- messages: messages as any,
111
- });
112
- const reply = response.choices[0].message.content;
113
- messages.push({
114
- role: "assistant",
115
- content: reply || "No response from OpenAI.",
116
- });
117
- const cleanedReply = parseMarkdown(reply as string);
118
- chatMemory.addEntry(address, {
119
- role: "assistant",
120
- content: cleanedReply,
121
- });
122
- return { reply: cleanedReply, history: messages };
123
- } catch (error) {
124
- console.error("Failed to fetch from OpenAI:", error);
125
- throw error;
126
- }
127
- }
128
-
129
- export async function processMultilineResponse(
130
- address: string,
131
- reply: string,
132
- context: any,
133
- ) {
134
- let messages = reply
135
- .split("\n")
136
- .map((message: string) => parseMarkdown(message))
137
- .filter((message): message is string => message.length > 0);
138
-
139
- console.log(messages);
140
- for (const message of messages) {
141
- if (message.startsWith("/")) {
142
- const response = await context.skill(message);
143
- if (response && typeof response.message === "string") {
144
- let msg = parseMarkdown(response.message);
145
- chatMemory.addEntry(address, {
146
- role: "system",
147
- content: msg,
148
- });
149
- await context.send(response.message);
150
- }
151
- } else {
152
- await context.send(message);
153
- }
154
- }
155
- }
156
- export function parseMarkdown(message: string) {
157
- let trimmedMessage = message;
158
- // Remove bold and underline markdown
159
- trimmedMessage = trimmedMessage?.replace(/(\*\*|__)(.*?)\1/g, "$2");
160
- // Remove markdown links, keeping only the URL
161
- trimmedMessage = trimmedMessage?.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "$2");
162
- // Remove markdown headers
163
- trimmedMessage = trimmedMessage?.replace(/^#+\s*(.*)$/gm, "$1");
164
- // Remove inline code formatting
165
- trimmedMessage = trimmedMessage?.replace(/`([^`]+)`/g, "$1");
166
- // Remove single backticks at the start or end of the message
167
- trimmedMessage = trimmedMessage?.replace(/^`|`$/g, "");
168
- // Remove leading and trailing whitespace
169
- trimmedMessage = trimmedMessage?.replace(/^\s+|\s+$/g, "");
170
- // Remove any remaining leading or trailing whitespace
171
- trimmedMessage = trimmedMessage.trim();
172
-
173
- return trimmedMessage;
174
- }
@@ -1,151 +0,0 @@
1
- import type { Client } from "@xmtp/xmtp-js";
2
- import { isAddress } from "viem";
3
- import type { HandlerContext } from "@xmtp/message-kit";
4
-
5
- export const converseEndpointURL =
6
- "https://converse-website-git-endpoit-ephemerahq.vercel.app";
7
- //export const converseEndpointURL = "http://localhost:3000";
8
-
9
- export type InfoCache = Map<string, UserInfo>;
10
- export type ConverseProfile = {
11
- address: string | null;
12
- onXmtp: boolean;
13
- avatar: string | null;
14
- formattedName: string | null;
15
- name: string | null;
16
- };
17
- export type UserInfo = {
18
- ensDomain?: string | undefined;
19
- address?: string | undefined;
20
- preferredName: string | undefined;
21
- converseUsername?: string | undefined;
22
- ensInfo?: EnsData | undefined;
23
- avatar?: string | undefined;
24
- };
25
- export interface EnsData {
26
- address?: string;
27
- avatar?: string;
28
- avatar_small?: string;
29
- converse?: string;
30
- avatar_url?: string;
31
- contentHash?: string;
32
- description?: string;
33
- ens?: string;
34
- ens_primary?: string;
35
- github?: string;
36
- resolverAddress?: string;
37
- twitter?: string;
38
- url?: string;
39
- wallets?: {
40
- eth?: string;
41
- };
42
- }
43
-
44
- let infoCache: InfoCache = new Map();
45
-
46
- export const clearInfoCache = () => {
47
- infoCache.clear();
48
- };
49
- export const getUserInfo = async (
50
- key: string,
51
- clientAddress?: string,
52
- context?: HandlerContext,
53
- ): Promise<UserInfo | null> => {
54
- let data: UserInfo = infoCache.get(key) || {
55
- ensDomain: undefined,
56
- address: undefined,
57
- converseUsername: undefined,
58
- ensInfo: undefined,
59
- preferredName: undefined,
60
- };
61
- if (isAddress(clientAddress || "")) {
62
- data.address = clientAddress;
63
- } else if (isAddress(key || "")) {
64
- data.address = key;
65
- } else if (key?.includes(".eth")) {
66
- data.ensDomain = key;
67
- } else if (key == "@user" || key == "@me" || key == "@bot") {
68
- data.address = clientAddress;
69
- data.ensDomain = key.replace("@", "") + ".eth";
70
- data.converseUsername = key.replace("@", "");
71
- } else if (key == "@alix") {
72
- data.address = "0x3a044b218BaE80E5b9E16609443A192129A67BeA";
73
- data.converseUsername = "alix";
74
- } else if (key == "@bo") {
75
- data.address = "0xbc3246461ab5e1682baE48fa95172CDf0689201a";
76
- data.converseUsername = "bo";
77
- } else {
78
- data.converseUsername = key;
79
- }
80
- data.preferredName = data.ensDomain || data.converseUsername || "Friend";
81
- let keyToUse = data.address || data.ensDomain || data.converseUsername;
82
- let cacheData = keyToUse && infoCache.get(keyToUse);
83
- //console.log("Getting user info", { cacheData, keyToUse, data });
84
- if (cacheData) return cacheData;
85
-
86
- context?.send(
87
- "Hey there! Give me a sec while I fetch info about you first...",
88
- );
89
- if (keyToUse?.includes(".eth")) {
90
- const response = await fetch(`https://ensdata.net/${keyToUse}`);
91
- const ensData: EnsData = (await response.json()) as EnsData;
92
- //console.log("Ens data", ensData);
93
- if (ensData) {
94
- data.ensInfo = ensData;
95
- data.ensDomain = ensData?.ens;
96
- data.address = ensData?.address;
97
- }
98
- } else if (keyToUse) {
99
- keyToUse = keyToUse.replace("@", "");
100
- const response = await fetch(`${converseEndpointURL}/profile/${keyToUse}`, {
101
- method: "POST",
102
- headers: {
103
- "Content-Type": "application/json",
104
- Accept: "application/json",
105
- },
106
- body: JSON.stringify({
107
- peer: keyToUse,
108
- }),
109
- });
110
- const converseData = (await response.json()) as ConverseProfile;
111
- /// if (process.env.MSG_LOG === "true")
112
- //console.log("Converse data", keyToUse, converseData);
113
- data.converseUsername =
114
- converseData?.formattedName || converseData?.name || undefined;
115
- data.address = converseData?.address || undefined;
116
- data.avatar = converseData?.avatar || undefined;
117
- }
118
-
119
- data.preferredName = data.ensDomain || data.converseUsername || "Friend";
120
- if (data.address) infoCache.set(data.address, data);
121
- return data;
122
- };
123
- export const isOnXMTP = async (
124
- client: Client,
125
- domain: string | undefined,
126
- address: string | undefined,
127
- ) => {
128
- if (domain == "fabri.eth") return false;
129
- if (address) return (await client.canMessage([address])).length > 0;
130
- };
131
-
132
- export const PROMPT_USER_CONTENT = (userInfo: UserInfo) => {
133
- let { address, ensDomain, converseUsername, preferredName } = userInfo;
134
- let prompt = `
135
- User context:
136
- - Start by fetch their domain from or Convese username
137
- - Call the user by their name or domain, in case they have one
138
- - Ask for a name (if they don't have one) so you can suggest domains.
139
- - Users address is: ${address}`;
140
- if (preferredName) prompt += `\n- Users name is: ${preferredName}`;
141
- if (ensDomain) prompt += `\n- User ENS domain is: ${ensDomain}`;
142
- if (converseUsername)
143
- prompt += `\n- Converse username is: ${converseUsername}`;
144
-
145
- prompt = prompt.replace("{ADDRESS}", address || "");
146
- prompt = prompt.replace("{ENS_DOMAIN}", ensDomain || "");
147
- prompt = prompt.replace("{CONVERSE_USERNAME}", converseUsername || "");
148
- prompt = prompt.replace("{PREFERRED_NAME}", preferredName || "");
149
-
150
- return prompt;
151
- };
@@ -1,18 +0,0 @@
1
- import { StackClient } from "@stackso/js-core";
2
-
3
- let stack: StackClient | null = null;
4
-
5
- export function getStackClient(): StackClient | null {
6
- if (!process?.env?.STACK_API_KEY) {
7
- console.log("No STACK_API_KEY found in .env");
8
- return null;
9
- }
10
- if (!stack) {
11
- stack = new StackClient({
12
- apiKey: process.env.STACK_API_KEY as string,
13
- pointSystemId: 2893,
14
- });
15
- }
16
- return stack;
17
- }
18
- export type { StackClient };
@@ -1,42 +0,0 @@
1
- import "dotenv/config";
2
-
3
- import OpenAI from "openai";
4
- const openai = new OpenAI({
5
- apiKey: process.env.OPEN_AI_API_KEY,
6
- });
7
-
8
- export async function vision(imageData: Uint8Array, systemPrompt: string) {
9
- const base64Image = Buffer.from(imageData).toString("base64");
10
- const dataUrl = `data:image/jpeg;base64,${base64Image}`;
11
-
12
- // Create a new thread for each vision request
13
- const visionMessages = [
14
- {
15
- role: "system",
16
- content: systemPrompt,
17
- },
18
- {
19
- role: "user",
20
- content: [
21
- { type: "text", text: systemPrompt },
22
- {
23
- type: "image_url",
24
- image_url: {
25
- url: dataUrl,
26
- },
27
- },
28
- ],
29
- },
30
- ];
31
-
32
- try {
33
- const response = await openai.chat.completions.create({
34
- model: "gpt-4o",
35
- messages: visionMessages as any,
36
- });
37
- return response.choices[0].message.content;
38
- } catch (error) {
39
- console.error("Failed to interpret image with OpenAI:", error);
40
- throw error;
41
- }
42
- }