create-message-kit 1.1.5 → 1.1.7-beta.10
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.
- package/README.md +4 -0
 - package/index.js +1 -6
 - package/package.json +1 -1
 - package/templates/agent/src/handler/ens.ts +12 -13
 - package/templates/agent/src/index.ts +4 -4
 - package/templates/agent/src/lib/gpt.ts +161 -0
 - package/templates/agent/src/lib/openai.ts +3 -3
 - package/templates/agent/src/lib/resolver.ts +22 -7
 - package/templates/agent/src/prompt.ts +8 -3
 - package/templates/agent/src/skills.ts +13 -0
 - package/templates/group/src/handler/agent.ts +11 -5
 - package/templates/group/src/handler/game.ts +2 -3
 - package/templates/group/src/handler/group.ts +24 -0
 - package/templates/group/src/handler/helpers.ts +23 -0
 - package/templates/group/src/handler/loyalty.ts +6 -3
 - package/templates/group/src/handler/splitpayment.ts +3 -3
 - package/templates/group/src/handler/tipping.ts +29 -23
 - package/templates/group/src/handler/transaction.ts +28 -9
 - package/templates/group/src/index.ts +9 -54
 - package/templates/group/src/lib/gpt.ts +161 -0
 - package/templates/group/src/lib/openai.ts +3 -3
 - package/templates/group/src/lib/resolver.ts +22 -7
 - package/templates/group/src/lib/vision.ts +1 -2
 - package/templates/group/src/skills.ts +11 -2
 
    
        package/README.md
    CHANGED
    
    
    
        package/index.js
    CHANGED
    
    | 
         @@ -14,15 +14,11 @@ const packageJson = JSON.parse( 
     | 
|
| 
       14 
14 
     | 
    
         
             
              fs.readFileSync(resolve(__dirname, "package.json"), "utf8"),
         
     | 
| 
       15 
15 
     | 
    
         
             
            );
         
     | 
| 
       16 
16 
     | 
    
         
             
            const version = packageJson.version;
         
     | 
| 
       17 
     | 
    
         
            -
            const pckMessageKitLib = JSON.parse(
         
     | 
| 
       18 
     | 
    
         
            -
              fs.readFileSync(resolve(__dirname, "../message-kit/package.json"), "utf8"),
         
     | 
| 
       19 
     | 
    
         
            -
            );
         
     | 
| 
       20 
     | 
    
         
            -
            const versionMessageKitLib = pckMessageKitLib.version;
         
     | 
| 
       21 
17 
     | 
    
         
             
            program
         
     | 
| 
       22 
18 
     | 
    
         
             
              .name("byob")
         
     | 
| 
       23 
19 
     | 
    
         
             
              .description("CLI to initialize projects")
         
     | 
| 
       24 
20 
     | 
    
         
             
              .action(async () => {
         
     | 
| 
       25 
     | 
    
         
            -
                log.info(pc.cyan(`Welcome to MessageKit v${ 
     | 
| 
      
 21 
     | 
    
         
            +
                log.info(pc.cyan(`Welcome to MessageKit CLI v${version}!`));
         
     | 
| 
       26 
22 
     | 
    
         
             
                const coolLogo = `
         
     | 
| 
       27 
23 
     | 
    
         
             
            ███╗   ███╗███████╗███████╗███████╗ █████╗  ██████╗ ███████╗██╗  ██╗██╗████████╗
         
     | 
| 
       28 
24 
     | 
    
         
             
            ████╗ ████║██╔════╝██╔════╝██╔════╝██╔══██╗██╔════╝ ██╔════╝██║ ██╔╝██║╚══██╔══╝
         
     | 
| 
         @@ -183,7 +179,6 @@ function createGitignore(destDir) { 
     | 
|
| 
       183 
179 
     | 
    
         
             
            # Main
         
     | 
| 
       184 
180 
     | 
    
         
             
            .env
         
     | 
| 
       185 
181 
     | 
    
         
             
            node_modules/
         
     | 
| 
       186 
     | 
    
         
            -
            .gitignore
         
     | 
| 
       187 
182 
     | 
    
         
             
            .data/
         
     | 
| 
       188 
183 
     | 
    
         
             
            dist/
         
     | 
| 
       189 
184 
     | 
    
         
             
            .DS_Store
         
     | 
    
        package/package.json
    CHANGED
    
    
| 
         @@ -1,16 +1,19 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            import { HandlerContext } from "@xmtp/message-kit";
         
     | 
| 
       2 
     | 
    
         
            -
            import { getUserInfo, clearInfoCache, isOnXMTP } from " 
     | 
| 
      
 1 
     | 
    
         
            +
            import { HandlerContext, SkillResponse } from "@xmtp/message-kit";
         
     | 
| 
      
 2 
     | 
    
         
            +
            import { getUserInfo, clearInfoCache, isOnXMTP } from "@xmtp/message-kit";
         
     | 
| 
       3 
3 
     | 
    
         
             
            import { isAddress } from "viem";
         
     | 
| 
       4 
     | 
    
         
            -
            import { clearMemory } from " 
     | 
| 
      
 4 
     | 
    
         
            +
            import { clearMemory } from "@xmtp/message-kit";
         
     | 
| 
       5 
5 
     | 
    
         | 
| 
       6 
6 
     | 
    
         
             
            export const frameUrl = "https://ens.steer.fun/";
         
     | 
| 
       7 
7 
     | 
    
         
             
            export const ensUrl = "https://app.ens.domains/";
         
     | 
| 
       8 
8 
     | 
    
         
             
            export const baseTxUrl = "https://base-tx-frame.vercel.app";
         
     | 
| 
       9 
9 
     | 
    
         | 
| 
       10 
     | 
    
         
            -
            export async function handleEns( 
     | 
| 
      
 10 
     | 
    
         
            +
            export async function handleEns(
         
     | 
| 
      
 11 
     | 
    
         
            +
              context: HandlerContext,
         
     | 
| 
      
 12 
     | 
    
         
            +
            ): Promise<SkillResponse | undefined> {
         
     | 
| 
       11 
13 
     | 
    
         
             
              const {
         
     | 
| 
       12 
14 
     | 
    
         
             
                message: {
         
     | 
| 
       13 
     | 
    
         
            -
                   
     | 
| 
      
 15 
     | 
    
         
            +
                  sender,
         
     | 
| 
      
 16 
     | 
    
         
            +
                  content: { command, params },
         
     | 
| 
       14 
17 
     | 
    
         
             
                },
         
     | 
| 
       15 
18 
     | 
    
         
             
              } = context;
         
     | 
| 
       16 
19 
     | 
    
         
             
              if (command == "reset") {
         
     | 
| 
         @@ -84,13 +87,7 @@ export async function handleEns(context: HandlerContext) { 
     | 
|
| 
       84 
87 
     | 
    
         
             
                }
         
     | 
| 
       85 
88 
     | 
    
         
             
                message += `\n\nWould you like to tip the domain owner for getting there first 🤣?`;
         
     | 
| 
       86 
89 
     | 
    
         
             
                message = message.trim();
         
     | 
| 
       87 
     | 
    
         
            -
                if (
         
     | 
| 
       88 
     | 
    
         
            -
                  await isOnXMTP(
         
     | 
| 
       89 
     | 
    
         
            -
                    context.v2client,
         
     | 
| 
       90 
     | 
    
         
            -
                    data?.ensInfo?.ens,
         
     | 
| 
       91 
     | 
    
         
            -
                    data?.ensInfo?.address,
         
     | 
| 
       92 
     | 
    
         
            -
                  )
         
     | 
| 
       93 
     | 
    
         
            -
                ) {
         
     | 
| 
      
 90 
     | 
    
         
            +
                if (await isOnXMTP(context.client, context.v2client, sender?.address)) {
         
     | 
| 
       94 
91 
     | 
    
         
             
                  await context.send(
         
     | 
| 
       95 
92 
     | 
    
         
             
                    `Ah, this domains is in XMTP, you can message it directly: https://converse.xyz/dm/${domain}`,
         
     | 
| 
       96 
93 
     | 
    
         
             
                  );
         
     | 
| 
         @@ -115,7 +112,7 @@ export async function handleEns(context: HandlerContext) { 
     | 
|
| 
       115 
112 
     | 
    
         
             
                  };
         
     | 
| 
       116 
113 
     | 
    
         
             
                } else {
         
     | 
| 
       117 
114 
     | 
    
         
             
                  let message = `Looks like ${domain} is already registered!`;
         
     | 
| 
       118 
     | 
    
         
            -
                  await context. 
     | 
| 
      
 115 
     | 
    
         
            +
                  await context.executeSkill("/cool " + domain);
         
     | 
| 
       119 
116 
     | 
    
         
             
                  return {
         
     | 
| 
       120 
117 
     | 
    
         
             
                    code: 404,
         
     | 
| 
       121 
118 
     | 
    
         
             
                    message,
         
     | 
| 
         @@ -145,6 +142,8 @@ export async function handleEns(context: HandlerContext) { 
     | 
|
| 
       145 
142 
     | 
    
         
             
                  code: 200,
         
     | 
| 
       146 
143 
     | 
    
         
             
                  message: `${generateCoolAlternatives(domain)}`,
         
     | 
| 
       147 
144 
     | 
    
         
             
                };
         
     | 
| 
      
 145 
     | 
    
         
            +
              } else {
         
     | 
| 
      
 146 
     | 
    
         
            +
                return { code: 400, message: "Command not found." };
         
     | 
| 
       148 
147 
     | 
    
         
             
              }
         
     | 
| 
       149 
148 
     | 
    
         
             
            }
         
     | 
| 
       150 
149 
     | 
    
         | 
| 
         @@ -1,10 +1,10 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            import { run, HandlerContext } from "@xmtp/message-kit";
         
     | 
| 
       2 
     | 
    
         
            -
            import { textGeneration,  
     | 
| 
      
 2 
     | 
    
         
            +
            import { textGeneration, processMultilineResponse } from "@xmtp/message-kit";
         
     | 
| 
       3 
3 
     | 
    
         
             
            import { agent_prompt } from "./prompt.js";
         
     | 
| 
       4 
     | 
    
         
            -
            import { getUserInfo } from " 
     | 
| 
      
 4 
     | 
    
         
            +
            import { getUserInfo } from "@xmtp/message-kit";
         
     | 
| 
       5 
5 
     | 
    
         | 
| 
       6 
6 
     | 
    
         
             
            run(async (context: HandlerContext) => {
         
     | 
| 
       7 
     | 
    
         
            -
              /*All the  
     | 
| 
      
 7 
     | 
    
         
            +
              /*All the skills are handled through the skills file*/
         
     | 
| 
       8 
8 
     | 
    
         
             
              /* If its just text, it will be handled by the ensAgent*/
         
     | 
| 
       9 
9 
     | 
    
         
             
              /* If its a group message, it will be handled by the groupAgent*/
         
     | 
| 
       10 
10 
     | 
    
         
             
              if (!process?.env?.OPEN_AI_API_KEY) {
         
     | 
| 
         @@ -31,7 +31,7 @@ run(async (context: HandlerContext) => { 
     | 
|
| 
       31 
31 
     | 
    
         
             
                  userPrompt,
         
     | 
| 
       32 
32 
     | 
    
         
             
                  await agent_prompt(userInfo),
         
     | 
| 
       33 
33 
     | 
    
         
             
                );
         
     | 
| 
       34 
     | 
    
         
            -
                await  
     | 
| 
      
 34 
     | 
    
         
            +
                await processMultilineResponse(sender.address, reply, context);
         
     | 
| 
       35 
35 
     | 
    
         
             
              } catch (error) {
         
     | 
| 
       36 
36 
     | 
    
         
             
                console.error("Error during OpenAI call:", error);
         
     | 
| 
       37 
37 
     | 
    
         
             
                await context.send("An error occurred while processing your request.");
         
     | 
| 
         @@ -0,0 +1,161 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            import "dotenv/config";
         
     | 
| 
      
 2 
     | 
    
         
            +
            import type { SkillGroup } from "@xmtp/message-kit";
         
     | 
| 
      
 3 
     | 
    
         
            +
            import OpenAI from "openai";
         
     | 
| 
      
 4 
     | 
    
         
            +
            const openai = new OpenAI({
         
     | 
| 
      
 5 
     | 
    
         
            +
              apiKey: process.env.OPEN_AI_API_KEY,
         
     | 
| 
      
 6 
     | 
    
         
            +
            });
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            type ChatHistoryEntry = { role: string; content: string };
         
     | 
| 
      
 9 
     | 
    
         
            +
            type ChatHistories = Record<string, ChatHistoryEntry[]>;
         
     | 
| 
      
 10 
     | 
    
         
            +
            // New ChatMemory class
         
     | 
| 
      
 11 
     | 
    
         
            +
            class ChatMemory {
         
     | 
| 
      
 12 
     | 
    
         
            +
              private histories: ChatHistories = {};
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
              getHistory(address: string): ChatHistoryEntry[] {
         
     | 
| 
      
 15 
     | 
    
         
            +
                return this.histories[address] || [];
         
     | 
| 
      
 16 
     | 
    
         
            +
              }
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
              addEntry(address: string, entry: ChatHistoryEntry) {
         
     | 
| 
      
 19 
     | 
    
         
            +
                if (!this.histories[address]) {
         
     | 
| 
      
 20 
     | 
    
         
            +
                  this.histories[address] = [];
         
     | 
| 
      
 21 
     | 
    
         
            +
                }
         
     | 
| 
      
 22 
     | 
    
         
            +
                this.histories[address].push(entry);
         
     | 
| 
      
 23 
     | 
    
         
            +
              }
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
              initializeWithSystem(address: string, systemPrompt: string) {
         
     | 
| 
      
 26 
     | 
    
         
            +
                if (this.getHistory(address).length === 0) {
         
     | 
| 
      
 27 
     | 
    
         
            +
                  this.addEntry(address, {
         
     | 
| 
      
 28 
     | 
    
         
            +
                    role: "system",
         
     | 
| 
      
 29 
     | 
    
         
            +
                    content: systemPrompt,
         
     | 
| 
      
 30 
     | 
    
         
            +
                  });
         
     | 
| 
      
 31 
     | 
    
         
            +
                }
         
     | 
| 
      
 32 
     | 
    
         
            +
              }
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
              clear() {
         
     | 
| 
      
 35 
     | 
    
         
            +
                this.histories = {};
         
     | 
| 
      
 36 
     | 
    
         
            +
              }
         
     | 
| 
      
 37 
     | 
    
         
            +
            }
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
            // Create singleton instance
         
     | 
| 
      
 40 
     | 
    
         
            +
            export const chatMemory = new ChatMemory();
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
            export const clearMemory = () => {
         
     | 
| 
      
 43 
     | 
    
         
            +
              chatMemory.clear();
         
     | 
| 
      
 44 
     | 
    
         
            +
            };
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
            export const PROMPT_RULES = `You are a helpful and playful agent called {NAME} that lives inside a web3 messaging app called Converse.
         
     | 
| 
      
 47 
     | 
    
         
            +
            - You can respond with multiple messages if needed. Each message should be separated by a newline character.
         
     | 
| 
      
 48 
     | 
    
         
            +
            - You can trigger skills by only sending the command in a newline message.
         
     | 
| 
      
 49 
     | 
    
         
            +
            - Never announce actions without using a command separated by a newline character.
         
     | 
| 
      
 50 
     | 
    
         
            +
            - Dont answer in markdown format, just answer in plaintext.
         
     | 
| 
      
 51 
     | 
    
         
            +
            - Do not make guesses or assumptions
         
     | 
| 
      
 52 
     | 
    
         
            +
            - Only answer if the verified information is in the prompt.
         
     | 
| 
      
 53 
     | 
    
         
            +
            - Check that you are not missing a command
         
     | 
| 
      
 54 
     | 
    
         
            +
            - Focus only on helping users with operations detailed below.
         
     | 
| 
      
 55 
     | 
    
         
            +
            `;
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
            export function PROMPT_SKILLS_AND_EXAMPLES(skills: SkillGroup[], tag: string) {
         
     | 
| 
      
 58 
     | 
    
         
            +
              let foundSkills = skills.filter(
         
     | 
| 
      
 59 
     | 
    
         
            +
                (skill) => skill.tag == `@${tag.toLowerCase()}`,
         
     | 
| 
      
 60 
     | 
    
         
            +
              );
         
     | 
| 
      
 61 
     | 
    
         
            +
              if (!foundSkills.length || !foundSkills[0] || !foundSkills[0].skills)
         
     | 
| 
      
 62 
     | 
    
         
            +
                return "";
         
     | 
| 
      
 63 
     | 
    
         
            +
              let returnPrompt = `\nCommands:\n${foundSkills[0].skills
         
     | 
| 
      
 64 
     | 
    
         
            +
                .map((skill) => skill.command)
         
     | 
| 
      
 65 
     | 
    
         
            +
                .join("\n")}\n\nExamples:\n${foundSkills[0].skills
         
     | 
| 
      
 66 
     | 
    
         
            +
                .map((skill) => skill.examples)
         
     | 
| 
      
 67 
     | 
    
         
            +
                .join("\n")}`;
         
     | 
| 
      
 68 
     | 
    
         
            +
              return returnPrompt;
         
     | 
| 
      
 69 
     | 
    
         
            +
            }
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
            export async function textGeneration(
         
     | 
| 
      
 72 
     | 
    
         
            +
              memoryKey: string,
         
     | 
| 
      
 73 
     | 
    
         
            +
              userPrompt: string,
         
     | 
| 
      
 74 
     | 
    
         
            +
              systemPrompt: string,
         
     | 
| 
      
 75 
     | 
    
         
            +
            ) {
         
     | 
| 
      
 76 
     | 
    
         
            +
              if (!memoryKey) {
         
     | 
| 
      
 77 
     | 
    
         
            +
                clearMemory();
         
     | 
| 
      
 78 
     | 
    
         
            +
              }
         
     | 
| 
      
 79 
     | 
    
         
            +
              let messages = chatMemory.getHistory(memoryKey);
         
     | 
| 
      
 80 
     | 
    
         
            +
              chatMemory.initializeWithSystem(memoryKey, systemPrompt);
         
     | 
| 
      
 81 
     | 
    
         
            +
              if (messages.length === 0) {
         
     | 
| 
      
 82 
     | 
    
         
            +
                messages.push({
         
     | 
| 
      
 83 
     | 
    
         
            +
                  role: "system",
         
     | 
| 
      
 84 
     | 
    
         
            +
                  content: systemPrompt,
         
     | 
| 
      
 85 
     | 
    
         
            +
                });
         
     | 
| 
      
 86 
     | 
    
         
            +
              }
         
     | 
| 
      
 87 
     | 
    
         
            +
              messages.push({
         
     | 
| 
      
 88 
     | 
    
         
            +
                role: "user",
         
     | 
| 
      
 89 
     | 
    
         
            +
                content: userPrompt,
         
     | 
| 
      
 90 
     | 
    
         
            +
              });
         
     | 
| 
      
 91 
     | 
    
         
            +
              try {
         
     | 
| 
      
 92 
     | 
    
         
            +
                const response = await openai.chat.completions.create({
         
     | 
| 
      
 93 
     | 
    
         
            +
                  model: "gpt-4o",
         
     | 
| 
      
 94 
     | 
    
         
            +
                  messages: messages as any,
         
     | 
| 
      
 95 
     | 
    
         
            +
                });
         
     | 
| 
      
 96 
     | 
    
         
            +
                const reply = response.choices[0].message.content;
         
     | 
| 
      
 97 
     | 
    
         
            +
                messages.push({
         
     | 
| 
      
 98 
     | 
    
         
            +
                  role: "assistant",
         
     | 
| 
      
 99 
     | 
    
         
            +
                  content: reply || "No response from OpenAI.",
         
     | 
| 
      
 100 
     | 
    
         
            +
                });
         
     | 
| 
      
 101 
     | 
    
         
            +
                const cleanedReply = parseMarkdown(reply as string);
         
     | 
| 
      
 102 
     | 
    
         
            +
                chatMemory.addEntry(memoryKey, {
         
     | 
| 
      
 103 
     | 
    
         
            +
                  role: "assistant",
         
     | 
| 
      
 104 
     | 
    
         
            +
                  content: cleanedReply,
         
     | 
| 
      
 105 
     | 
    
         
            +
                });
         
     | 
| 
      
 106 
     | 
    
         
            +
                return { reply: cleanedReply, history: messages };
         
     | 
| 
      
 107 
     | 
    
         
            +
              } catch (error) {
         
     | 
| 
      
 108 
     | 
    
         
            +
                console.error("Failed to fetch from OpenAI:", error);
         
     | 
| 
      
 109 
     | 
    
         
            +
                throw error;
         
     | 
| 
      
 110 
     | 
    
         
            +
              }
         
     | 
| 
      
 111 
     | 
    
         
            +
            }
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
            export async function processMultilineResponse(
         
     | 
| 
      
 114 
     | 
    
         
            +
              memoryKey: string,
         
     | 
| 
      
 115 
     | 
    
         
            +
              reply: string,
         
     | 
| 
      
 116 
     | 
    
         
            +
              context: any,
         
     | 
| 
      
 117 
     | 
    
         
            +
            ) {
         
     | 
| 
      
 118 
     | 
    
         
            +
              if (!memoryKey) {
         
     | 
| 
      
 119 
     | 
    
         
            +
                clearMemory();
         
     | 
| 
      
 120 
     | 
    
         
            +
              }
         
     | 
| 
      
 121 
     | 
    
         
            +
              let messages = reply
         
     | 
| 
      
 122 
     | 
    
         
            +
                .split("\n")
         
     | 
| 
      
 123 
     | 
    
         
            +
                .map((message: string) => parseMarkdown(message))
         
     | 
| 
      
 124 
     | 
    
         
            +
                .filter((message): message is string => message.length > 0);
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
              console.log(messages);
         
     | 
| 
      
 127 
     | 
    
         
            +
              for (const message of messages) {
         
     | 
| 
      
 128 
     | 
    
         
            +
                if (message.startsWith("/")) {
         
     | 
| 
      
 129 
     | 
    
         
            +
                  const response = await context.executeSkill(message);
         
     | 
| 
      
 130 
     | 
    
         
            +
                  if (response && typeof response.message === "string") {
         
     | 
| 
      
 131 
     | 
    
         
            +
                    let msg = parseMarkdown(response.message);
         
     | 
| 
      
 132 
     | 
    
         
            +
                    chatMemory.addEntry(memoryKey, {
         
     | 
| 
      
 133 
     | 
    
         
            +
                      role: "system",
         
     | 
| 
      
 134 
     | 
    
         
            +
                      content: msg,
         
     | 
| 
      
 135 
     | 
    
         
            +
                    });
         
     | 
| 
      
 136 
     | 
    
         
            +
                    await context.send(response.message);
         
     | 
| 
      
 137 
     | 
    
         
            +
                  }
         
     | 
| 
      
 138 
     | 
    
         
            +
                } else {
         
     | 
| 
      
 139 
     | 
    
         
            +
                  await context.send(message);
         
     | 
| 
      
 140 
     | 
    
         
            +
                }
         
     | 
| 
      
 141 
     | 
    
         
            +
              }
         
     | 
| 
      
 142 
     | 
    
         
            +
            }
         
     | 
| 
      
 143 
     | 
    
         
            +
            export function parseMarkdown(message: string) {
         
     | 
| 
      
 144 
     | 
    
         
            +
              let trimmedMessage = message;
         
     | 
| 
      
 145 
     | 
    
         
            +
              // Remove bold and underline markdown
         
     | 
| 
      
 146 
     | 
    
         
            +
              trimmedMessage = trimmedMessage?.replace(/(\*\*|__)(.*?)\1/g, "$2");
         
     | 
| 
      
 147 
     | 
    
         
            +
              // Remove markdown links, keeping only the URL
         
     | 
| 
      
 148 
     | 
    
         
            +
              trimmedMessage = trimmedMessage?.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "$2");
         
     | 
| 
      
 149 
     | 
    
         
            +
              // Remove markdown headers
         
     | 
| 
      
 150 
     | 
    
         
            +
              trimmedMessage = trimmedMessage?.replace(/^#+\s*(.*)$/gm, "$1");
         
     | 
| 
      
 151 
     | 
    
         
            +
              // Remove inline code formatting
         
     | 
| 
      
 152 
     | 
    
         
            +
              trimmedMessage = trimmedMessage?.replace(/`([^`]+)`/g, "$1");
         
     | 
| 
      
 153 
     | 
    
         
            +
              // Remove single backticks at the start or end of the message
         
     | 
| 
      
 154 
     | 
    
         
            +
              trimmedMessage = trimmedMessage?.replace(/^`|`$/g, "");
         
     | 
| 
      
 155 
     | 
    
         
            +
              // Remove leading and trailing whitespace
         
     | 
| 
      
 156 
     | 
    
         
            +
              trimmedMessage = trimmedMessage?.replace(/^\s+|\s+$/g, "");
         
     | 
| 
      
 157 
     | 
    
         
            +
              // Remove any remaining leading or trailing whitespace
         
     | 
| 
      
 158 
     | 
    
         
            +
              trimmedMessage = trimmedMessage.trim();
         
     | 
| 
      
 159 
     | 
    
         
            +
             
     | 
| 
      
 160 
     | 
    
         
            +
              return trimmedMessage;
         
     | 
| 
      
 161 
     | 
    
         
            +
            }
         
     | 
| 
         @@ -48,7 +48,7 @@ export const chatMemory = new ChatMemory(); 
     | 
|
| 
       48 
48 
     | 
    
         
             
            let chatHistories: ChatHistories = {};
         
     | 
| 
       49 
49 
     | 
    
         
             
            export const PROMPT_RULES = `You are a helpful and playful agent called {NAME} that lives inside a web3 messaging app called Converse.
         
     | 
| 
       50 
50 
     | 
    
         
             
            - You can respond with multiple messages if needed. Each message should be separated by a newline character.
         
     | 
| 
       51 
     | 
    
         
            -
            - You can trigger  
     | 
| 
      
 51 
     | 
    
         
            +
            - You can trigger skills by only sending the command in a newline message.
         
     | 
| 
       52 
52 
     | 
    
         
             
            - Never announce actions without using a command separated by a newline character.
         
     | 
| 
       53 
53 
     | 
    
         
             
            - Dont answer in markdown format, just answer in plaintext.
         
     | 
| 
       54 
54 
     | 
    
         
             
            - Do not make guesses or assumptions
         
     | 
| 
         @@ -81,7 +81,7 @@ export async function agentResponse( 
     | 
|
| 
       81 
81 
     | 
    
         
             
                  userPrompt,
         
     | 
| 
       82 
82 
     | 
    
         
             
                  systemPrompt,
         
     | 
| 
       83 
83 
     | 
    
         
             
                );
         
     | 
| 
       84 
     | 
    
         
            -
                await  
     | 
| 
      
 84 
     | 
    
         
            +
                await processMultilineResponse(sender.address, reply, context);
         
     | 
| 
       85 
85 
     | 
    
         
             
              } catch (error) {
         
     | 
| 
       86 
86 
     | 
    
         
             
                console.error("Error during OpenAI call:", error);
         
     | 
| 
       87 
87 
     | 
    
         
             
                await context.reply("An error occurred while processing your request.");
         
     | 
| 
         @@ -126,7 +126,7 @@ export async function textGeneration( 
     | 
|
| 
       126 
126 
     | 
    
         
             
              }
         
     | 
| 
       127 
127 
     | 
    
         
             
            }
         
     | 
| 
       128 
128 
     | 
    
         | 
| 
       129 
     | 
    
         
            -
            export async function  
     | 
| 
      
 129 
     | 
    
         
            +
            export async function processMultilineResponse(
         
     | 
| 
       130 
130 
     | 
    
         
             
              address: string,
         
     | 
| 
       131 
131 
     | 
    
         
             
              reply: string,
         
     | 
| 
       132 
132 
     | 
    
         
             
              context: any,
         
     | 
| 
         @@ -1,5 +1,6 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            import { Client } from "@xmtp/xmtp-js";
         
     | 
| 
      
 1 
     | 
    
         
            +
            import type { Client } from "@xmtp/xmtp-js";
         
     | 
| 
       2 
2 
     | 
    
         
             
            import { isAddress } from "viem";
         
     | 
| 
      
 3 
     | 
    
         
            +
            import type { HandlerContext } from "@xmtp/message-kit";
         
     | 
| 
       3 
4 
     | 
    
         | 
| 
       4 
5 
     | 
    
         
             
            export const converseEndpointURL =
         
     | 
| 
       5 
6 
     | 
    
         
             
              "https://converse-website-git-endpoit-ephemerahq.vercel.app";
         
     | 
| 
         @@ -16,8 +17,8 @@ export type ConverseProfile = { 
     | 
|
| 
       16 
17 
     | 
    
         
             
            export type UserInfo = {
         
     | 
| 
       17 
18 
     | 
    
         
             
              ensDomain?: string | undefined;
         
     | 
| 
       18 
19 
     | 
    
         
             
              address?: string | undefined;
         
     | 
| 
      
 20 
     | 
    
         
            +
              preferredName: string | undefined;
         
     | 
| 
       19 
21 
     | 
    
         
             
              converseUsername?: string | undefined;
         
     | 
| 
       20 
     | 
    
         
            -
              preferredName?: string | undefined;
         
     | 
| 
       21 
22 
     | 
    
         
             
              ensInfo?: EnsData | undefined;
         
     | 
| 
       22 
23 
     | 
    
         
             
              avatar?: string | undefined;
         
     | 
| 
       23 
24 
     | 
    
         
             
            };
         
     | 
| 
         @@ -48,12 +49,14 @@ export const clearInfoCache = () => { 
     | 
|
| 
       48 
49 
     | 
    
         
             
            export const getUserInfo = async (
         
     | 
| 
       49 
50 
     | 
    
         
             
              key: string,
         
     | 
| 
       50 
51 
     | 
    
         
             
              clientAddress?: string,
         
     | 
| 
      
 52 
     | 
    
         
            +
              context?: HandlerContext,
         
     | 
| 
       51 
53 
     | 
    
         
             
            ): Promise<UserInfo | null> => {
         
     | 
| 
       52 
54 
     | 
    
         
             
              let data: UserInfo = infoCache.get(key) || {
         
     | 
| 
       53 
55 
     | 
    
         
             
                ensDomain: undefined,
         
     | 
| 
       54 
56 
     | 
    
         
             
                address: undefined,
         
     | 
| 
       55 
57 
     | 
    
         
             
                converseUsername: undefined,
         
     | 
| 
       56 
58 
     | 
    
         
             
                ensInfo: undefined,
         
     | 
| 
      
 59 
     | 
    
         
            +
                preferredName: undefined,
         
     | 
| 
       57 
60 
     | 
    
         
             
              };
         
     | 
| 
       58 
61 
     | 
    
         
             
              if (isAddress(clientAddress || "")) {
         
     | 
| 
       59 
62 
     | 
    
         
             
                data.address = clientAddress;
         
     | 
| 
         @@ -74,12 +77,15 @@ export const getUserInfo = async ( 
     | 
|
| 
       74 
77 
     | 
    
         
             
              } else {
         
     | 
| 
       75 
78 
     | 
    
         
             
                data.converseUsername = key;
         
     | 
| 
       76 
79 
     | 
    
         
             
              }
         
     | 
| 
       77 
     | 
    
         
            -
             
     | 
| 
      
 80 
     | 
    
         
            +
              data.preferredName = data.ensDomain || data.converseUsername || "Friend";
         
     | 
| 
       78 
81 
     | 
    
         
             
              let keyToUse = data.address || data.ensDomain || data.converseUsername;
         
     | 
| 
       79 
82 
     | 
    
         
             
              let cacheData = keyToUse && infoCache.get(keyToUse);
         
     | 
| 
       80 
     | 
    
         
            -
              console.log("Getting user info", { cacheData, keyToUse, data });
         
     | 
| 
      
 83 
     | 
    
         
            +
              //console.log("Getting user info", { cacheData, keyToUse, data });
         
     | 
| 
       81 
84 
     | 
    
         
             
              if (cacheData) return cacheData;
         
     | 
| 
       82 
85 
     | 
    
         | 
| 
      
 86 
     | 
    
         
            +
              context?.send(
         
     | 
| 
      
 87 
     | 
    
         
            +
                "Hey there! Give me a sec while I fetch info about you first...",
         
     | 
| 
      
 88 
     | 
    
         
            +
              );
         
     | 
| 
       83 
89 
     | 
    
         
             
              if (keyToUse?.includes(".eth")) {
         
     | 
| 
       84 
90 
     | 
    
         
             
                const response = await fetch(`https://ensdata.net/${keyToUse}`);
         
     | 
| 
       85 
91 
     | 
    
         
             
                const ensData: EnsData = (await response.json()) as EnsData;
         
     | 
| 
         @@ -102,13 +108,15 @@ export const getUserInfo = async ( 
     | 
|
| 
       102 
108 
     | 
    
         
             
                  }),
         
     | 
| 
       103 
109 
     | 
    
         
             
                });
         
     | 
| 
       104 
110 
     | 
    
         
             
                const converseData = (await response.json()) as ConverseProfile;
         
     | 
| 
       105 
     | 
    
         
            -
                if (process.env.MSG_LOG)
         
     | 
| 
       106 
     | 
    
         
            -
             
     | 
| 
      
 111 
     | 
    
         
            +
                //if (process.env.MSG_LOG === "true")
         
     | 
| 
      
 112 
     | 
    
         
            +
                //console.log("Converse data", keyToUse, converseData);
         
     | 
| 
       107 
113 
     | 
    
         
             
                data.converseUsername =
         
     | 
| 
       108 
114 
     | 
    
         
             
                  converseData?.formattedName || converseData?.name || undefined;
         
     | 
| 
       109 
115 
     | 
    
         
             
                data.address = converseData?.address || undefined;
         
     | 
| 
       110 
116 
     | 
    
         
             
                data.avatar = converseData?.avatar || undefined;
         
     | 
| 
       111 
117 
     | 
    
         
             
              }
         
     | 
| 
      
 118 
     | 
    
         
            +
             
     | 
| 
      
 119 
     | 
    
         
            +
              data.preferredName = data.ensDomain || data.converseUsername || "Friend";
         
     | 
| 
       112 
120 
     | 
    
         
             
              if (data.address) infoCache.set(data.address, data);
         
     | 
| 
       113 
121 
     | 
    
         
             
              return data;
         
     | 
| 
       114 
122 
     | 
    
         
             
            };
         
     | 
| 
         @@ -123,7 +131,8 @@ export const isOnXMTP = async ( 
     | 
|
| 
       123 
131 
     | 
    
         | 
| 
       124 
132 
     | 
    
         
             
            export const PROMPT_USER_CONTENT = (userInfo: UserInfo) => {
         
     | 
| 
       125 
133 
     | 
    
         
             
              let { address, ensDomain, converseUsername, preferredName } = userInfo;
         
     | 
| 
       126 
     | 
    
         
            -
              let prompt = ` 
     | 
| 
      
 134 
     | 
    
         
            +
              let prompt = `
         
     | 
| 
      
 135 
     | 
    
         
            +
            User context: 
         
     | 
| 
       127 
136 
     | 
    
         
             
            - Start by fetch their domain from or Convese username
         
     | 
| 
       128 
137 
     | 
    
         
             
            - Call the user by their name or domain, in case they have one
         
     | 
| 
       129 
138 
     | 
    
         
             
            - Ask for a name (if they don't have one) so you can suggest domains.
         
     | 
| 
         @@ -132,5 +141,11 @@ export const PROMPT_USER_CONTENT = (userInfo: UserInfo) => { 
     | 
|
| 
       132 
141 
     | 
    
         
             
              if (ensDomain) prompt += `\n- User ENS domain is: ${ensDomain}`;
         
     | 
| 
       133 
142 
     | 
    
         
             
              if (converseUsername)
         
     | 
| 
       134 
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 
     | 
    
         
            +
             
     | 
| 
       135 
150 
     | 
    
         
             
              return prompt;
         
     | 
| 
       136 
151 
     | 
    
         
             
            };
         
     | 
| 
         @@ -1,6 +1,11 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            import { skills } from "./skills.js";
         
     | 
| 
       2 
     | 
    
         
            -
            import { 
     | 
| 
       3 
     | 
    
         
            -
             
     | 
| 
      
 2 
     | 
    
         
            +
            import {
         
     | 
| 
      
 3 
     | 
    
         
            +
              getUserInfo,
         
     | 
| 
      
 4 
     | 
    
         
            +
              UserInfo,
         
     | 
| 
      
 5 
     | 
    
         
            +
              PROMPT_USER_CONTENT,
         
     | 
| 
      
 6 
     | 
    
         
            +
              PROMPT_RULES,
         
     | 
| 
      
 7 
     | 
    
         
            +
              PROMPT_SKILLS_AND_EXAMPLES,
         
     | 
| 
      
 8 
     | 
    
         
            +
            } from "@xmtp/message-kit";
         
     | 
| 
       4 
9 
     | 
    
         | 
| 
       5 
10 
     | 
    
         
             
            export async function agent_prompt(userInfo: UserInfo) {
         
     | 
| 
       6 
11 
     | 
    
         
             
              let { address, ensDomain, converseUsername, preferredName } = userInfo;
         
     | 
| 
         @@ -12,7 +17,7 @@ export async function agent_prompt(userInfo: UserInfo) { 
     | 
|
| 
       12 
17 
     | 
    
         
             
              systemPrompt += PROMPT_USER_CONTENT(userInfo);
         
     | 
| 
       13 
18 
     | 
    
         | 
| 
       14 
19 
     | 
    
         
             
              //Add skills and examples to the prompt
         
     | 
| 
       15 
     | 
    
         
            -
              systemPrompt += PROMPT_SKILLS_AND_EXAMPLES(skills);
         
     | 
| 
      
 20 
     | 
    
         
            +
              systemPrompt += PROMPT_SKILLS_AND_EXAMPLES(skills, "@ens");
         
     | 
| 
       16 
21 
     | 
    
         | 
| 
       17 
22 
     | 
    
         
             
              systemPrompt += `
         
     | 
| 
       18 
23 
     | 
    
         | 
| 
         @@ -20,6 +20,19 @@ export const skills: SkillGroup[] = [ 
     | 
|
| 
       20 
20 
     | 
    
         
             
                      },
         
     | 
| 
       21 
21 
     | 
    
         
             
                    },
         
     | 
| 
       22 
22 
     | 
    
         
             
                  },
         
     | 
| 
      
 23 
     | 
    
         
            +
                  {
         
     | 
| 
      
 24 
     | 
    
         
            +
                    command: "/exists",
         
     | 
| 
      
 25 
     | 
    
         
            +
                    adminOnly: true,
         
     | 
| 
      
 26 
     | 
    
         
            +
                    examples: ["/exists"],
         
     | 
| 
      
 27 
     | 
    
         
            +
                    handler: handleEns,
         
     | 
| 
      
 28 
     | 
    
         
            +
                    triggers: ["/exists"],
         
     | 
| 
      
 29 
     | 
    
         
            +
                    description: "Check if an address is onboarded.",
         
     | 
| 
      
 30 
     | 
    
         
            +
                    params: {
         
     | 
| 
      
 31 
     | 
    
         
            +
                      address: {
         
     | 
| 
      
 32 
     | 
    
         
            +
                        type: "address",
         
     | 
| 
      
 33 
     | 
    
         
            +
                      },
         
     | 
| 
      
 34 
     | 
    
         
            +
                    },
         
     | 
| 
      
 35 
     | 
    
         
            +
                  },
         
     | 
| 
       23 
36 
     | 
    
         
             
                  {
         
     | 
| 
       24 
37 
     | 
    
         
             
                    command: "/info [domain]",
         
     | 
| 
       25 
38 
     | 
    
         
             
                    triggers: ["/info"],
         
     | 
| 
         @@ -1,5 +1,5 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            import { HandlerContext, AbstractedMember } from "@xmtp/message-kit";
         
     | 
| 
       2 
     | 
    
         
            -
            import { textGeneration } from " 
     | 
| 
      
 2 
     | 
    
         
            +
            import { textGeneration } from "@xmtp/message-kit";
         
     | 
| 
       3 
3 
     | 
    
         | 
| 
       4 
4 
     | 
    
         
             
            export async function handler(context: HandlerContext) {
         
     | 
| 
       5 
5 
     | 
    
         
             
              if (!process?.env?.OPEN_AI_API_KEY) {
         
     | 
| 
         @@ -10,20 +10,26 @@ export async function handler(context: HandlerContext) { 
     | 
|
| 
       10 
10 
     | 
    
         
             
              const {
         
     | 
| 
       11 
11 
     | 
    
         
             
                message: {
         
     | 
| 
       12 
12 
     | 
    
         
             
                  sender,
         
     | 
| 
       13 
     | 
    
         
            -
                  content: {  
     | 
| 
      
 13 
     | 
    
         
            +
                  content: { params, text },
         
     | 
| 
       14 
14 
     | 
    
         
             
                },
         
     | 
| 
       15 
15 
     | 
    
         
             
              } = context;
         
     | 
| 
       16 
16 
     | 
    
         | 
| 
       17 
17 
     | 
    
         
             
              const systemPrompt = generateSystemPrompt(context);
         
     | 
| 
       18 
18 
     | 
    
         
             
              try {
         
     | 
| 
       19 
     | 
    
         
            -
                let userPrompt = params?.prompt ??  
     | 
| 
      
 19 
     | 
    
         
            +
                let userPrompt = params?.prompt ?? text;
         
     | 
| 
       20 
20 
     | 
    
         | 
| 
       21 
21 
     | 
    
         
             
                const { reply } = await textGeneration(
         
     | 
| 
       22 
22 
     | 
    
         
             
                  sender.address,
         
     | 
| 
       23 
23 
     | 
    
         
             
                  userPrompt,
         
     | 
| 
       24 
24 
     | 
    
         
             
                  systemPrompt,
         
     | 
| 
       25 
25 
     | 
    
         
             
                );
         
     | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                try {
         
     | 
| 
      
 28 
     | 
    
         
            +
                  await context.executeSkill(reply);
         
     | 
| 
      
 29 
     | 
    
         
            +
                } catch (error) {
         
     | 
| 
      
 30 
     | 
    
         
            +
                  console.error("Error executing skill:", error);
         
     | 
| 
      
 31 
     | 
    
         
            +
                  await context.reply("Failed to execute the requested action.");
         
     | 
| 
      
 32 
     | 
    
         
            +
                }
         
     | 
| 
       27 
33 
     | 
    
         
             
              } catch (error) {
         
     | 
| 
       28 
34 
     | 
    
         
             
                console.error("Error during OpenAI call:", error);
         
     | 
| 
       29 
35 
     | 
    
         
             
                await context.reply("An error occurred while processing your request.");
         
     | 
| 
         @@ -59,7 +65,7 @@ function generateSystemPrompt(context: HandlerContext) { 
     | 
|
| 
       59 
65 
     | 
    
         
             
              Important:
         
     | 
| 
       60 
66 
     | 
    
         
             
              - If a user asks jokes, make jokes about web3 devs\n
         
     | 
| 
       61 
67 
     | 
    
         
             
              - If the user asks about performing an action and you can think of a command that would help, answer directly with the command and nothing else. 
         
     | 
| 
       62 
     | 
    
         
            -
              - Populate the command with the correct or random values. Always return  
     | 
| 
      
 68 
     | 
    
         
            +
              - Populate the command with the correct or random values. Always return skills with real values only, using usernames with @ and excluding addresses.\n
         
     | 
| 
       63 
69 
     | 
    
         
             
              - If the user asks a question or makes a statement that does not clearly map to a command, respond with helpful information or a clarification question.\n
         
     | 
| 
       64 
70 
     | 
    
         
             
              - If the user is grateful, respond asking for a tip in a playful manner.
         
     | 
| 
       65 
71 
     | 
    
         
             
              `;
         
     | 
| 
         @@ -4,11 +4,10 @@ import { HandlerContext } from "@xmtp/message-kit"; 
     | 
|
| 
       4 
4 
     | 
    
         
             
            export async function handler(context: HandlerContext) {
         
     | 
| 
       5 
5 
     | 
    
         
             
              const {
         
     | 
| 
       6 
6 
     | 
    
         
             
                message: {
         
     | 
| 
       7 
     | 
    
         
            -
                  content: { command, params },
         
     | 
| 
      
 7 
     | 
    
         
            +
                  content: { command, params, text },
         
     | 
| 
       8 
8 
     | 
    
         
             
                },
         
     | 
| 
       9 
9 
     | 
    
         
             
              } = context;
         
     | 
| 
       10 
10 
     | 
    
         
             
              if (!command) {
         
     | 
| 
       11 
     | 
    
         
            -
                const { content: text } = context?.message?.content;
         
     | 
| 
       12 
11 
     | 
    
         
             
                if (text === "🔎" || text === "🔍") {
         
     | 
| 
       13 
12 
     | 
    
         
             
                  // Send the URL for the requested game
         
     | 
| 
       14 
13 
     | 
    
         
             
                  context.reply("https://framedl.xyz/");
         
     | 
| 
         @@ -34,7 +33,7 @@ export async function handler(context: HandlerContext) { 
     | 
|
| 
       34 
33 
     | 
    
         
             
                  context.send("Available games: \n/game wordle\n/game slot");
         
     | 
| 
       35 
34 
     | 
    
         
             
                  break;
         
     | 
| 
       36 
35 
     | 
    
         
             
                default:
         
     | 
| 
       37 
     | 
    
         
            -
                  // Inform the user about unrecognized  
     | 
| 
      
 36 
     | 
    
         
            +
                  // Inform the user about unrecognized skills and provide available options
         
     | 
| 
       38 
37 
     | 
    
         
             
                  context.send(
         
     | 
| 
       39 
38 
     | 
    
         
             
                    "Command not recognized. Available games: wordle, slot, or help.",
         
     | 
| 
       40 
39 
     | 
    
         
             
                  );
         
     | 
| 
         @@ -0,0 +1,24 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            import { HandlerContext } from "@xmtp/message-kit";
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            export async function handler(context: HandlerContext) {
         
     | 
| 
      
 4 
     | 
    
         
            +
              const {
         
     | 
| 
      
 5 
     | 
    
         
            +
                skills,
         
     | 
| 
      
 6 
     | 
    
         
            +
                group,
         
     | 
| 
      
 7 
     | 
    
         
            +
                message: {
         
     | 
| 
      
 8 
     | 
    
         
            +
                  content: { command },
         
     | 
| 
      
 9 
     | 
    
         
            +
                },
         
     | 
| 
      
 10 
     | 
    
         
            +
              } = context;
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
              if (command == "help") {
         
     | 
| 
      
 13 
     | 
    
         
            +
                const intro =
         
     | 
| 
      
 14 
     | 
    
         
            +
                  "Available experiences:\n" +
         
     | 
| 
      
 15 
     | 
    
         
            +
                  skills
         
     | 
| 
      
 16 
     | 
    
         
            +
                    ?.flatMap((app) => app.skills)
         
     | 
| 
      
 17 
     | 
    
         
            +
                    .map((skill) => `${skill.command} - ${skill.description}`)
         
     | 
| 
      
 18 
     | 
    
         
            +
                    .join("\n") +
         
     | 
| 
      
 19 
     | 
    
         
            +
                  "\nUse these skills to interact with specific apps.";
         
     | 
| 
      
 20 
     | 
    
         
            +
                context.send(intro);
         
     | 
| 
      
 21 
     | 
    
         
            +
              } else if (command == "id") {
         
     | 
| 
      
 22 
     | 
    
         
            +
                context.send(context.group?.id);
         
     | 
| 
      
 23 
     | 
    
         
            +
              }
         
     | 
| 
      
 24 
     | 
    
         
            +
            }
         
     | 
| 
         @@ -0,0 +1,23 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            import { HandlerContext } from "@xmtp/message-kit";
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            export async function handler(context: HandlerContext) {
         
     | 
| 
      
 4 
     | 
    
         
            +
              const {
         
     | 
| 
      
 5 
     | 
    
         
            +
                skills,
         
     | 
| 
      
 6 
     | 
    
         
            +
                message: {
         
     | 
| 
      
 7 
     | 
    
         
            +
                  content: { command },
         
     | 
| 
      
 8 
     | 
    
         
            +
                },
         
     | 
| 
      
 9 
     | 
    
         
            +
              } = context;
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
              if (command == "help") {
         
     | 
| 
      
 12 
     | 
    
         
            +
                const intro =
         
     | 
| 
      
 13 
     | 
    
         
            +
                  "Available experiences:\n" +
         
     | 
| 
      
 14 
     | 
    
         
            +
                  skills
         
     | 
| 
      
 15 
     | 
    
         
            +
                    ?.flatMap((app) => app.skills)
         
     | 
| 
      
 16 
     | 
    
         
            +
                    .map((skill) => `${skill.command} - ${skill.description}`)
         
     | 
| 
      
 17 
     | 
    
         
            +
                    .join("\n") +
         
     | 
| 
      
 18 
     | 
    
         
            +
                  "\nUse these skills to interact with specific apps.";
         
     | 
| 
      
 19 
     | 
    
         
            +
                context.send(intro);
         
     | 
| 
      
 20 
     | 
    
         
            +
              } else if (command == "id") {
         
     | 
| 
      
 21 
     | 
    
         
            +
                context.send(context.group?.id);
         
     | 
| 
      
 22 
     | 
    
         
            +
              }
         
     | 
| 
      
 23 
     | 
    
         
            +
            }
         
     | 
| 
         @@ -6,10 +6,13 @@ export async function handler(context: HandlerContext, fake?: boolean) { 
     | 
|
| 
       6 
6 
     | 
    
         
             
              const {
         
     | 
| 
       7 
7 
     | 
    
         
             
                members,
         
     | 
| 
       8 
8 
     | 
    
         
             
                group,
         
     | 
| 
       9 
     | 
    
         
            -
                message: { 
     | 
| 
      
 9 
     | 
    
         
            +
                message: {
         
     | 
| 
      
 10 
     | 
    
         
            +
                  sender,
         
     | 
| 
      
 11 
     | 
    
         
            +
                  typeId,
         
     | 
| 
      
 12 
     | 
    
         
            +
                  content: { command, params, text },
         
     | 
| 
      
 13 
     | 
    
         
            +
                },
         
     | 
| 
       10 
14 
     | 
    
         
             
              } = context;
         
     | 
| 
       11 
15 
     | 
    
         
             
              if (typeId === "text" && group) {
         
     | 
| 
       12 
     | 
    
         
            -
                const { command } = content;
         
     | 
| 
       13 
16 
     | 
    
         
             
                if (command === "points") {
         
     | 
| 
       14 
17 
     | 
    
         
             
                  const points = await stack?.getPoints(sender.address);
         
     | 
| 
       15 
18 
     | 
    
         
             
                  context.reply(`You have ${points} points`);
         
     | 
| 
         @@ -31,7 +34,7 @@ export async function handler(context: HandlerContext, fake?: boolean) { 
     | 
|
| 
       31 
34 
     | 
    
         
             
                  return;
         
     | 
| 
       32 
35 
     | 
    
         
             
                }
         
     | 
| 
       33 
36 
     | 
    
         
             
              } else if (typeId === "group_updated" && group) {
         
     | 
| 
       34 
     | 
    
         
            -
                const { initiatedByInboxId, addedInboxes } =  
     | 
| 
      
 37 
     | 
    
         
            +
                const { initiatedByInboxId, addedInboxes } = params;
         
     | 
| 
       35 
38 
     | 
    
         
             
                const adminAddress = members?.find(
         
     | 
| 
       36 
39 
     | 
    
         
             
                  (member: AbstractedMember) => member.inboxId === initiatedByInboxId,
         
     | 
| 
       37 
40 
     | 
    
         
             
                );
         
     | 
| 
         @@ -1,5 +1,5 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            import { HandlerContext } from "@xmtp/message-kit";
         
     | 
| 
       2 
     | 
    
         
            -
            import { textGeneration } from "../lib/ 
     | 
| 
      
 2 
     | 
    
         
            +
            import { textGeneration } from "../lib/gpt.js";
         
     | 
| 
       3 
3 
     | 
    
         
             
            import { vision } from "../lib/vision.js";
         
     | 
| 
       4 
4 
     | 
    
         
             
            import { getUserInfo } from "../lib/resolver.js";
         
     | 
| 
       5 
5 
     | 
    
         | 
| 
         @@ -10,7 +10,7 @@ export async function handler(context: HandlerContext) { 
     | 
|
| 
       10 
10 
     | 
    
         
             
              }
         
     | 
| 
       11 
11 
     | 
    
         
             
              const {
         
     | 
| 
       12 
12 
     | 
    
         
             
                members,
         
     | 
| 
       13 
     | 
    
         
            -
                 
     | 
| 
      
 13 
     | 
    
         
            +
                skill,
         
     | 
| 
       14 
14 
     | 
    
         
             
                message: {
         
     | 
| 
       15 
15 
     | 
    
         
             
                  typeId,
         
     | 
| 
       16 
16 
     | 
    
         
             
                  content: { attachment },
         
     | 
| 
         @@ -57,7 +57,7 @@ export async function handler(context: HandlerContext) { 
     | 
|
| 
       57 
57 
     | 
    
         
             
                  let splitMessages = JSON.parse(reply);
         
     | 
| 
       58 
58 
     | 
    
         
             
                  for (const message of splitMessages) {
         
     | 
| 
       59 
59 
     | 
    
         
             
                    let msg = message as string;
         
     | 
| 
       60 
     | 
    
         
            -
                    if (msg.startsWith("/")) await  
     | 
| 
      
 60 
     | 
    
         
            +
                    if (msg.startsWith("/")) await skill(msg);
         
     | 
| 
       61 
61 
     | 
    
         
             
                    else await context.send(msg);
         
     | 
| 
       62 
62 
     | 
    
         
             
                  }
         
     | 
| 
       63 
63 
     | 
    
         
             
                }
         
     | 
| 
         @@ -1,14 +1,21 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            import { 
     | 
| 
       2 
     | 
    
         
            -
             
     | 
| 
      
 1 
     | 
    
         
            +
            import {
         
     | 
| 
      
 2 
     | 
    
         
            +
              HandlerContext,
         
     | 
| 
      
 3 
     | 
    
         
            +
              AbstractedMember,
         
     | 
| 
      
 4 
     | 
    
         
            +
              SkillResponse,
         
     | 
| 
      
 5 
     | 
    
         
            +
            } from "@xmtp/message-kit";
         
     | 
| 
      
 6 
     | 
    
         
            +
            import { getUserInfo } from "@xmtp/message-kit";
         
     | 
| 
       3 
7 
     | 
    
         | 
| 
       4 
     | 
    
         
            -
            export async function handler(context: HandlerContext) {
         
     | 
| 
      
 8 
     | 
    
         
            +
            export async function handler(context: HandlerContext): Promise<SkillResponse> {
         
     | 
| 
       5 
9 
     | 
    
         
             
              const {
         
     | 
| 
       6 
10 
     | 
    
         
             
                members,
         
     | 
| 
       7 
11 
     | 
    
         
             
                getMessageById,
         
     | 
| 
       8 
     | 
    
         
            -
                message: { 
     | 
| 
      
 12 
     | 
    
         
            +
                message: {
         
     | 
| 
      
 13 
     | 
    
         
            +
                  content: { reference, reply, text, params },
         
     | 
| 
      
 14 
     | 
    
         
            +
                  sender,
         
     | 
| 
      
 15 
     | 
    
         
            +
                  typeId,
         
     | 
| 
      
 16 
     | 
    
         
            +
                },
         
     | 
| 
       9 
17 
     | 
    
         
             
              } = context;
         
     | 
| 
       10 
     | 
    
         
            -
               
     | 
| 
       11 
     | 
    
         
            -
              const msg = await getMessageById(content.reference);
         
     | 
| 
      
 18 
     | 
    
         
            +
              const msg = reference ? await getMessageById(reference) : undefined;
         
     | 
| 
       12 
19 
     | 
    
         
             
              const replyReceiver = members?.find(
         
     | 
| 
       13 
20 
     | 
    
         
             
                (member) => member.inboxId === msg?.senderInboxId,
         
     | 
| 
       14 
21 
     | 
    
         
             
              );
         
     | 
| 
         @@ -16,34 +23,29 @@ export async function handler(context: HandlerContext) { 
     | 
|
| 
       16 
23 
     | 
    
         
             
                receivers: AbstractedMember[] = [];
         
     | 
| 
       17 
24 
     | 
    
         
             
              // Handle different types of messages
         
     | 
| 
       18 
25 
     | 
    
         
             
              if (typeId === "reply" && replyReceiver) {
         
     | 
| 
       19 
     | 
    
         
            -
                 
     | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
       21 
     | 
    
         
            -
                if (reply.includes("degen")) {
         
     | 
| 
      
 26 
     | 
    
         
            +
                if (reply?.includes("degen")) {
         
     | 
| 
       22 
27 
     | 
    
         
             
                  receivers = [replyReceiver];
         
     | 
| 
       23 
28 
     | 
    
         
             
                  const match = reply.match(/(\d+)/);
         
     | 
| 
       24 
29 
     | 
    
         
             
                  if (match)
         
     | 
| 
       25 
30 
     | 
    
         
             
                    amount = parseInt(match[0]); // Extract amount from reply
         
     | 
| 
       26 
31 
     | 
    
         
             
                  else amount = 10;
         
     | 
| 
       27 
32 
     | 
    
         
             
                }
         
     | 
| 
       28 
     | 
    
         
            -
              } else if (typeId === "text") {
         
     | 
| 
       29 
     | 
    
         
            -
                 
     | 
| 
       30 
     | 
    
         
            -
                 
     | 
| 
       31 
     | 
    
         
            -
             
     | 
| 
       32 
     | 
    
         
            -
                  const {
         
     | 
| 
       33 
     | 
    
         
            -
                    params: { amount: extractedAmount, username },
         
     | 
| 
       34 
     | 
    
         
            -
                  } = content;
         
     | 
| 
       35 
     | 
    
         
            -
                  amount = extractedAmount || 10; // Default amount if not specified
         
     | 
| 
      
 33 
     | 
    
         
            +
              } else if (typeId === "text" && text?.startsWith("/tip") && params) {
         
     | 
| 
      
 34 
     | 
    
         
            +
                // Process text skills starting with "/tip"
         
     | 
| 
      
 35 
     | 
    
         
            +
                const { amount: extractedAmount, username } = params;
         
     | 
| 
      
 36 
     | 
    
         
            +
                amount = extractedAmount || 10; // Default amount if not specified
         
     | 
| 
       36 
37 
     | 
    
         | 
| 
       37 
     | 
    
         
            -
             
     | 
| 
       38 
     | 
    
         
            -
             
     | 
| 
       39 
     | 
    
         
            -
             
     | 
| 
       40 
     | 
    
         
            -
                }
         
     | 
| 
      
 38 
     | 
    
         
            +
                receivers = await Promise.all(
         
     | 
| 
      
 39 
     | 
    
         
            +
                  username.map((username: string) => getUserInfo(username)),
         
     | 
| 
      
 40 
     | 
    
         
            +
                );
         
     | 
| 
       41 
41 
     | 
    
         
             
              }
         
     | 
| 
       42 
42 
     | 
    
         
             
              if (!sender || receivers.length === 0 || amount === 0) {
         
     | 
| 
       43 
43 
     | 
    
         
             
                context.reply("Sender or receiver or amount not found.");
         
     | 
| 
       44 
     | 
    
         
            -
                return 
     | 
| 
      
 44 
     | 
    
         
            +
                return {
         
     | 
| 
      
 45 
     | 
    
         
            +
                  code: 400,
         
     | 
| 
      
 46 
     | 
    
         
            +
                  message: "Sender or receiver or amount not found.",
         
     | 
| 
      
 47 
     | 
    
         
            +
                };
         
     | 
| 
       45 
48 
     | 
    
         
             
              }
         
     | 
| 
       46 
     | 
    
         
            -
              console.log(receivers);
         
     | 
| 
       47 
49 
     | 
    
         
             
              const receiverAddresses = receivers.map((receiver) => receiver.address);
         
     | 
| 
       48 
50 
     | 
    
         
             
              // Process sending tokens to each receiver
         
     | 
| 
       49 
51 
     | 
    
         | 
| 
         @@ -57,4 +59,8 @@ export async function handler(context: HandlerContext) { 
     | 
|
| 
       57 
59 
     | 
    
         
             
                `You sent ${amount * receiverAddresses.length} tokens in total.`,
         
     | 
| 
       58 
60 
     | 
    
         
             
                [sender.address],
         
     | 
| 
       59 
61 
     | 
    
         
             
              );
         
     | 
| 
      
 62 
     | 
    
         
            +
              return {
         
     | 
| 
      
 63 
     | 
    
         
            +
                code: 200,
         
     | 
| 
      
 64 
     | 
    
         
            +
                message: "Success",
         
     | 
| 
      
 65 
     | 
    
         
            +
              };
         
     | 
| 
       60 
66 
     | 
    
         
             
            }
         
     | 
| 
         @@ -1,8 +1,7 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            import { HandlerContext } from "@xmtp/message-kit";
         
     | 
| 
       2 
     | 
    
         
            -
            import { getUserInfo } from "../lib/resolver.js";
         
     | 
| 
      
 1 
     | 
    
         
            +
            import { getUserInfo, HandlerContext, SkillResponse } from "@xmtp/message-kit";
         
     | 
| 
       3 
2 
     | 
    
         | 
| 
       4 
3 
     | 
    
         
             
            // Main handler function for processing commands
         
     | 
| 
       5 
     | 
    
         
            -
            export async function handler(context: HandlerContext) {
         
     | 
| 
      
 4 
     | 
    
         
            +
            export async function handler(context: HandlerContext): Promise<SkillResponse> {
         
     | 
| 
       6 
5 
     | 
    
         
             
              const {
         
     | 
| 
       7 
6 
     | 
    
         
             
                message: {
         
     | 
| 
       8 
7 
     | 
    
         
             
                  content: { command, params },
         
     | 
| 
         @@ -19,13 +18,19 @@ export async function handler(context: HandlerContext) { 
     | 
|
| 
       19 
18 
     | 
    
         
             
                    context.reply(
         
     | 
| 
       20 
19 
     | 
    
         
             
                      "Missing required parameters. Please provide amount, token, and username.",
         
     | 
| 
       21 
20 
     | 
    
         
             
                    );
         
     | 
| 
       22 
     | 
    
         
            -
                    return 
     | 
| 
      
 21 
     | 
    
         
            +
                    return {
         
     | 
| 
      
 22 
     | 
    
         
            +
                      code: 400,
         
     | 
| 
      
 23 
     | 
    
         
            +
                      message:
         
     | 
| 
      
 24 
     | 
    
         
            +
                        "Missing required parameters. Please provide amount, token, and username.",
         
     | 
| 
      
 25 
     | 
    
         
            +
                    };
         
     | 
| 
       23 
26 
     | 
    
         
             
                  }
         
     | 
| 
       24 
     | 
    
         
            -
                  let name = senderInfo.converseUsername || senderInfo.address;
         
     | 
| 
       25 
27 
     | 
    
         | 
| 
       26 
28 
     | 
    
         
             
                  let sendUrl = `${baseUrl}/?transaction_type=send&amount=${amountSend}&token=${tokenSend}&receiver=${senderInfo.address}`;
         
     | 
| 
       27 
29 
     | 
    
         
             
                  context.send(`${sendUrl}`);
         
     | 
| 
       28 
     | 
    
         
            -
                   
     | 
| 
      
 30 
     | 
    
         
            +
                  return {
         
     | 
| 
      
 31 
     | 
    
         
            +
                    code: 200,
         
     | 
| 
      
 32 
     | 
    
         
            +
                    message: `${sendUrl}`,
         
     | 
| 
      
 33 
     | 
    
         
            +
                  };
         
     | 
| 
       29 
34 
     | 
    
         
             
                case "swap":
         
     | 
| 
       30 
35 
     | 
    
         
             
                  // Destructure and validate parameters for the swap command
         
     | 
| 
       31 
36 
     | 
    
         
             
                  const { amount, token_from, token_to } = params; // [!code hl] // [!code focus]
         
     | 
| 
         @@ -34,18 +39,32 @@ export async function handler(context: HandlerContext) { 
     | 
|
| 
       34 
39 
     | 
    
         
             
                    context.reply(
         
     | 
| 
       35 
40 
     | 
    
         
             
                      "Missing required parameters. Please provide amount, token_from, and token_to.",
         
     | 
| 
       36 
41 
     | 
    
         
             
                    );
         
     | 
| 
       37 
     | 
    
         
            -
                    return 
     | 
| 
      
 42 
     | 
    
         
            +
                    return {
         
     | 
| 
      
 43 
     | 
    
         
            +
                      code: 400,
         
     | 
| 
      
 44 
     | 
    
         
            +
                      message:
         
     | 
| 
      
 45 
     | 
    
         
            +
                        "Missing required parameters. Please provide amount, token_from, and token_to.",
         
     | 
| 
      
 46 
     | 
    
         
            +
                    };
         
     | 
| 
       38 
47 
     | 
    
         
             
                  }
         
     | 
| 
       39 
48 
     | 
    
         | 
| 
       40 
49 
     | 
    
         
             
                  let swapUrl = `${baseUrl}/?transaction_type=swap&token_from=${token_from}&token_to=${token_to}&amount=${amount}`;
         
     | 
| 
       41 
50 
     | 
    
         
             
                  context.send(`${swapUrl}`);
         
     | 
| 
       42 
     | 
    
         
            -
                   
     | 
| 
      
 51 
     | 
    
         
            +
                  return {
         
     | 
| 
      
 52 
     | 
    
         
            +
                    code: 200,
         
     | 
| 
      
 53 
     | 
    
         
            +
                    message: `${swapUrl}`,
         
     | 
| 
      
 54 
     | 
    
         
            +
                  };
         
     | 
| 
       43 
55 
     | 
    
         
             
                case "show": // [!code hl] // [!code focus]
         
     | 
| 
       44 
56 
     | 
    
         
             
                  // Show the base URL without the transaction path
         
     | 
| 
       45 
57 
     | 
    
         
             
                  context.reply(`${baseUrl.replace("/transaction", "")}`);
         
     | 
| 
       46 
     | 
    
         
            -
                   
     | 
| 
      
 58 
     | 
    
         
            +
                  return {
         
     | 
| 
      
 59 
     | 
    
         
            +
                    code: 200,
         
     | 
| 
      
 60 
     | 
    
         
            +
                    message: `${baseUrl.replace("/transaction", "")}`,
         
     | 
| 
      
 61 
     | 
    
         
            +
                  };
         
     | 
| 
       47 
62 
     | 
    
         
             
                default:
         
     | 
| 
       48 
63 
     | 
    
         
             
                  // Handle unknown commands
         
     | 
| 
       49 
64 
     | 
    
         
             
                  context.reply("Unknown command. Use help to see all available commands.");
         
     | 
| 
      
 65 
     | 
    
         
            +
                  return {
         
     | 
| 
      
 66 
     | 
    
         
            +
                    code: 400,
         
     | 
| 
      
 67 
     | 
    
         
            +
                    message: "Unknown command. Use help to see all available commands.",
         
     | 
| 
      
 68 
     | 
    
         
            +
                  };
         
     | 
| 
       50 
69 
     | 
    
         
             
              }
         
     | 
| 
       51 
70 
     | 
    
         
             
            }
         
     | 
| 
         @@ -1,57 +1,12 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            import { run, HandlerContext } from "@xmtp/message-kit";
         
     | 
| 
       2 
     | 
    
         
            -
            import { handler as splitpayment } from "./handler/splitpayment.js";
         
     | 
| 
       3 
2 
     | 
    
         | 
| 
       4 
3 
     | 
    
         
             
            // Main function to run the app
         
     | 
| 
       5 
     | 
    
         
            -
            run(
         
     | 
| 
       6 
     | 
    
         
            -
               
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
       9 
     | 
    
         
            -
                 
     | 
| 
       10 
     | 
    
         
            -
             
     | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
       14 
     | 
    
         
            -
                  case "remoteStaticAttachment":
         
     | 
| 
       15 
     | 
    
         
            -
                    handleAttachment(context);
         
     | 
| 
       16 
     | 
    
         
            -
                    break;
         
     | 
| 
       17 
     | 
    
         
            -
                }
         
     | 
| 
       18 
     | 
    
         
            -
                if (!context.group) {
         
     | 
| 
       19 
     | 
    
         
            -
                  context.send("This is a group bot, add this address to a group");
         
     | 
| 
       20 
     | 
    
         
            -
                }
         
     | 
| 
       21 
     | 
    
         
            -
              },
         
     | 
| 
       22 
     | 
    
         
            -
              { attachments: true },
         
     | 
| 
       23 
     | 
    
         
            -
            );
         
     | 
| 
       24 
     | 
    
         
            -
            async function handleReply(context: HandlerContext) {
         
     | 
| 
       25 
     | 
    
         
            -
              const {
         
     | 
| 
       26 
     | 
    
         
            -
                v2client,
         
     | 
| 
       27 
     | 
    
         
            -
                getReplyChain,
         
     | 
| 
       28 
     | 
    
         
            -
                version,
         
     | 
| 
       29 
     | 
    
         
            -
                message: {
         
     | 
| 
       30 
     | 
    
         
            -
                  content: { reference },
         
     | 
| 
       31 
     | 
    
         
            -
                },
         
     | 
| 
       32 
     | 
    
         
            -
              } = context;
         
     | 
| 
       33 
     | 
    
         
            -
             
     | 
| 
       34 
     | 
    
         
            -
              const { chain, isSenderInChain } = await getReplyChain(
         
     | 
| 
       35 
     | 
    
         
            -
                reference,
         
     | 
| 
       36 
     | 
    
         
            -
                version,
         
     | 
| 
       37 
     | 
    
         
            -
                v2client.address,
         
     | 
| 
       38 
     | 
    
         
            -
              );
         
     | 
| 
       39 
     | 
    
         
            -
              //await context.skill(chain);
         
     | 
| 
       40 
     | 
    
         
            -
            }
         
     | 
| 
       41 
     | 
    
         
            -
             
     | 
| 
       42 
     | 
    
         
            -
            // Handle attachment messages
         
     | 
| 
       43 
     | 
    
         
            -
            async function handleAttachment(context: HandlerContext) {
         
     | 
| 
       44 
     | 
    
         
            -
              await splitpayment(context);
         
     | 
| 
       45 
     | 
    
         
            -
            }
         
     | 
| 
       46 
     | 
    
         
            -
             
     | 
| 
       47 
     | 
    
         
            -
            export async function helpHandler(context: HandlerContext) {
         
     | 
| 
       48 
     | 
    
         
            -
              const { skills } = context;
         
     | 
| 
       49 
     | 
    
         
            -
              const intro =
         
     | 
| 
       50 
     | 
    
         
            -
                "Available experiences:\n" +
         
     | 
| 
       51 
     | 
    
         
            -
                skills
         
     | 
| 
       52 
     | 
    
         
            -
                  ?.flatMap((app) => app.skills)
         
     | 
| 
       53 
     | 
    
         
            -
                  .map((skill) => `${skill.command} - ${skill.description}`)
         
     | 
| 
       54 
     | 
    
         
            -
                  .join("\n") +
         
     | 
| 
       55 
     | 
    
         
            -
                "\nUse these commands to interact with specific apps.";
         
     | 
| 
       56 
     | 
    
         
            -
              context.send(intro);
         
     | 
| 
       57 
     | 
    
         
            -
            }
         
     | 
| 
      
 4 
     | 
    
         
            +
            run(async (context: HandlerContext) => {
         
     | 
| 
      
 5 
     | 
    
         
            +
              const { group } = context;
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              if (!group) {
         
     | 
| 
      
 8 
     | 
    
         
            +
                context.send(
         
     | 
| 
      
 9 
     | 
    
         
            +
                  "This This bot only works in group chats. Please add this bot to a group to continue",
         
     | 
| 
      
 10 
     | 
    
         
            +
                );
         
     | 
| 
      
 11 
     | 
    
         
            +
              }
         
     | 
| 
      
 12 
     | 
    
         
            +
            });
         
     | 
| 
         @@ -0,0 +1,161 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            import "dotenv/config";
         
     | 
| 
      
 2 
     | 
    
         
            +
            import type { SkillGroup } from "@xmtp/message-kit";
         
     | 
| 
      
 3 
     | 
    
         
            +
            import OpenAI from "openai";
         
     | 
| 
      
 4 
     | 
    
         
            +
            const openai = new OpenAI({
         
     | 
| 
      
 5 
     | 
    
         
            +
              apiKey: process.env.OPEN_AI_API_KEY,
         
     | 
| 
      
 6 
     | 
    
         
            +
            });
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            type ChatHistoryEntry = { role: string; content: string };
         
     | 
| 
      
 9 
     | 
    
         
            +
            type ChatHistories = Record<string, ChatHistoryEntry[]>;
         
     | 
| 
      
 10 
     | 
    
         
            +
            // New ChatMemory class
         
     | 
| 
      
 11 
     | 
    
         
            +
            class ChatMemory {
         
     | 
| 
      
 12 
     | 
    
         
            +
              private histories: ChatHistories = {};
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
              getHistory(address: string): ChatHistoryEntry[] {
         
     | 
| 
      
 15 
     | 
    
         
            +
                return this.histories[address] || [];
         
     | 
| 
      
 16 
     | 
    
         
            +
              }
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
              addEntry(address: string, entry: ChatHistoryEntry) {
         
     | 
| 
      
 19 
     | 
    
         
            +
                if (!this.histories[address]) {
         
     | 
| 
      
 20 
     | 
    
         
            +
                  this.histories[address] = [];
         
     | 
| 
      
 21 
     | 
    
         
            +
                }
         
     | 
| 
      
 22 
     | 
    
         
            +
                this.histories[address].push(entry);
         
     | 
| 
      
 23 
     | 
    
         
            +
              }
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
              initializeWithSystem(address: string, systemPrompt: string) {
         
     | 
| 
      
 26 
     | 
    
         
            +
                if (this.getHistory(address).length === 0) {
         
     | 
| 
      
 27 
     | 
    
         
            +
                  this.addEntry(address, {
         
     | 
| 
      
 28 
     | 
    
         
            +
                    role: "system",
         
     | 
| 
      
 29 
     | 
    
         
            +
                    content: systemPrompt,
         
     | 
| 
      
 30 
     | 
    
         
            +
                  });
         
     | 
| 
      
 31 
     | 
    
         
            +
                }
         
     | 
| 
      
 32 
     | 
    
         
            +
              }
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
              clear() {
         
     | 
| 
      
 35 
     | 
    
         
            +
                this.histories = {};
         
     | 
| 
      
 36 
     | 
    
         
            +
              }
         
     | 
| 
      
 37 
     | 
    
         
            +
            }
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
            // Create singleton instance
         
     | 
| 
      
 40 
     | 
    
         
            +
            export const chatMemory = new ChatMemory();
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
            export const clearMemory = () => {
         
     | 
| 
      
 43 
     | 
    
         
            +
              chatMemory.clear();
         
     | 
| 
      
 44 
     | 
    
         
            +
            };
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
            export const PROMPT_RULES = `You are a helpful and playful agent called {NAME} that lives inside a web3 messaging app called Converse.
         
     | 
| 
      
 47 
     | 
    
         
            +
            - You can respond with multiple messages if needed. Each message should be separated by a newline character.
         
     | 
| 
      
 48 
     | 
    
         
            +
            - You can trigger skills by only sending the command in a newline message.
         
     | 
| 
      
 49 
     | 
    
         
            +
            - Never announce actions without using a command separated by a newline character.
         
     | 
| 
      
 50 
     | 
    
         
            +
            - Dont answer in markdown format, just answer in plaintext.
         
     | 
| 
      
 51 
     | 
    
         
            +
            - Do not make guesses or assumptions
         
     | 
| 
      
 52 
     | 
    
         
            +
            - Only answer if the verified information is in the prompt.
         
     | 
| 
      
 53 
     | 
    
         
            +
            - Check that you are not missing a command
         
     | 
| 
      
 54 
     | 
    
         
            +
            - Focus only on helping users with operations detailed below.
         
     | 
| 
      
 55 
     | 
    
         
            +
            `;
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
            export function PROMPT_SKILLS_AND_EXAMPLES(skills: SkillGroup[], tag: string) {
         
     | 
| 
      
 58 
     | 
    
         
            +
              let foundSkills = skills.filter(
         
     | 
| 
      
 59 
     | 
    
         
            +
                (skill) => skill.tag == `@${tag.toLowerCase()}`,
         
     | 
| 
      
 60 
     | 
    
         
            +
              );
         
     | 
| 
      
 61 
     | 
    
         
            +
              if (!foundSkills.length || !foundSkills[0] || !foundSkills[0].skills)
         
     | 
| 
      
 62 
     | 
    
         
            +
                return "";
         
     | 
| 
      
 63 
     | 
    
         
            +
              let returnPrompt = `\nCommands:\n${foundSkills[0].skills
         
     | 
| 
      
 64 
     | 
    
         
            +
                .map((skill) => skill.command)
         
     | 
| 
      
 65 
     | 
    
         
            +
                .join("\n")}\n\nExamples:\n${foundSkills[0].skills
         
     | 
| 
      
 66 
     | 
    
         
            +
                .map((skill) => skill.examples)
         
     | 
| 
      
 67 
     | 
    
         
            +
                .join("\n")}`;
         
     | 
| 
      
 68 
     | 
    
         
            +
              return returnPrompt;
         
     | 
| 
      
 69 
     | 
    
         
            +
            }
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
            export async function textGeneration(
         
     | 
| 
      
 72 
     | 
    
         
            +
              memoryKey: string,
         
     | 
| 
      
 73 
     | 
    
         
            +
              userPrompt: string,
         
     | 
| 
      
 74 
     | 
    
         
            +
              systemPrompt: string,
         
     | 
| 
      
 75 
     | 
    
         
            +
            ) {
         
     | 
| 
      
 76 
     | 
    
         
            +
              if (!memoryKey) {
         
     | 
| 
      
 77 
     | 
    
         
            +
                clearMemory();
         
     | 
| 
      
 78 
     | 
    
         
            +
              }
         
     | 
| 
      
 79 
     | 
    
         
            +
              let messages = chatMemory.getHistory(memoryKey);
         
     | 
| 
      
 80 
     | 
    
         
            +
              chatMemory.initializeWithSystem(memoryKey, systemPrompt);
         
     | 
| 
      
 81 
     | 
    
         
            +
              if (messages.length === 0) {
         
     | 
| 
      
 82 
     | 
    
         
            +
                messages.push({
         
     | 
| 
      
 83 
     | 
    
         
            +
                  role: "system",
         
     | 
| 
      
 84 
     | 
    
         
            +
                  content: systemPrompt,
         
     | 
| 
      
 85 
     | 
    
         
            +
                });
         
     | 
| 
      
 86 
     | 
    
         
            +
              }
         
     | 
| 
      
 87 
     | 
    
         
            +
              messages.push({
         
     | 
| 
      
 88 
     | 
    
         
            +
                role: "user",
         
     | 
| 
      
 89 
     | 
    
         
            +
                content: userPrompt,
         
     | 
| 
      
 90 
     | 
    
         
            +
              });
         
     | 
| 
      
 91 
     | 
    
         
            +
              try {
         
     | 
| 
      
 92 
     | 
    
         
            +
                const response = await openai.chat.completions.create({
         
     | 
| 
      
 93 
     | 
    
         
            +
                  model: "gpt-4o",
         
     | 
| 
      
 94 
     | 
    
         
            +
                  messages: messages as any,
         
     | 
| 
      
 95 
     | 
    
         
            +
                });
         
     | 
| 
      
 96 
     | 
    
         
            +
                const reply = response.choices[0].message.content;
         
     | 
| 
      
 97 
     | 
    
         
            +
                messages.push({
         
     | 
| 
      
 98 
     | 
    
         
            +
                  role: "assistant",
         
     | 
| 
      
 99 
     | 
    
         
            +
                  content: reply || "No response from OpenAI.",
         
     | 
| 
      
 100 
     | 
    
         
            +
                });
         
     | 
| 
      
 101 
     | 
    
         
            +
                const cleanedReply = parseMarkdown(reply as string);
         
     | 
| 
      
 102 
     | 
    
         
            +
                chatMemory.addEntry(memoryKey, {
         
     | 
| 
      
 103 
     | 
    
         
            +
                  role: "assistant",
         
     | 
| 
      
 104 
     | 
    
         
            +
                  content: cleanedReply,
         
     | 
| 
      
 105 
     | 
    
         
            +
                });
         
     | 
| 
      
 106 
     | 
    
         
            +
                return { reply: cleanedReply, history: messages };
         
     | 
| 
      
 107 
     | 
    
         
            +
              } catch (error) {
         
     | 
| 
      
 108 
     | 
    
         
            +
                console.error("Failed to fetch from OpenAI:", error);
         
     | 
| 
      
 109 
     | 
    
         
            +
                throw error;
         
     | 
| 
      
 110 
     | 
    
         
            +
              }
         
     | 
| 
      
 111 
     | 
    
         
            +
            }
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
            export async function processMultilineResponse(
         
     | 
| 
      
 114 
     | 
    
         
            +
              memoryKey: string,
         
     | 
| 
      
 115 
     | 
    
         
            +
              reply: string,
         
     | 
| 
      
 116 
     | 
    
         
            +
              context: any,
         
     | 
| 
      
 117 
     | 
    
         
            +
            ) {
         
     | 
| 
      
 118 
     | 
    
         
            +
              if (!memoryKey) {
         
     | 
| 
      
 119 
     | 
    
         
            +
                clearMemory();
         
     | 
| 
      
 120 
     | 
    
         
            +
              }
         
     | 
| 
      
 121 
     | 
    
         
            +
              let messages = reply
         
     | 
| 
      
 122 
     | 
    
         
            +
                .split("\n")
         
     | 
| 
      
 123 
     | 
    
         
            +
                .map((message: string) => parseMarkdown(message))
         
     | 
| 
      
 124 
     | 
    
         
            +
                .filter((message): message is string => message.length > 0);
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
              console.log(messages);
         
     | 
| 
      
 127 
     | 
    
         
            +
              for (const message of messages) {
         
     | 
| 
      
 128 
     | 
    
         
            +
                if (message.startsWith("/")) {
         
     | 
| 
      
 129 
     | 
    
         
            +
                  const response = await context.executeSkill(message);
         
     | 
| 
      
 130 
     | 
    
         
            +
                  if (response && typeof response.message === "string") {
         
     | 
| 
      
 131 
     | 
    
         
            +
                    let msg = parseMarkdown(response.message);
         
     | 
| 
      
 132 
     | 
    
         
            +
                    chatMemory.addEntry(memoryKey, {
         
     | 
| 
      
 133 
     | 
    
         
            +
                      role: "system",
         
     | 
| 
      
 134 
     | 
    
         
            +
                      content: msg,
         
     | 
| 
      
 135 
     | 
    
         
            +
                    });
         
     | 
| 
      
 136 
     | 
    
         
            +
                    await context.send(response.message);
         
     | 
| 
      
 137 
     | 
    
         
            +
                  }
         
     | 
| 
      
 138 
     | 
    
         
            +
                } else {
         
     | 
| 
      
 139 
     | 
    
         
            +
                  await context.send(message);
         
     | 
| 
      
 140 
     | 
    
         
            +
                }
         
     | 
| 
      
 141 
     | 
    
         
            +
              }
         
     | 
| 
      
 142 
     | 
    
         
            +
            }
         
     | 
| 
      
 143 
     | 
    
         
            +
            export function parseMarkdown(message: string) {
         
     | 
| 
      
 144 
     | 
    
         
            +
              let trimmedMessage = message;
         
     | 
| 
      
 145 
     | 
    
         
            +
              // Remove bold and underline markdown
         
     | 
| 
      
 146 
     | 
    
         
            +
              trimmedMessage = trimmedMessage?.replace(/(\*\*|__)(.*?)\1/g, "$2");
         
     | 
| 
      
 147 
     | 
    
         
            +
              // Remove markdown links, keeping only the URL
         
     | 
| 
      
 148 
     | 
    
         
            +
              trimmedMessage = trimmedMessage?.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "$2");
         
     | 
| 
      
 149 
     | 
    
         
            +
              // Remove markdown headers
         
     | 
| 
      
 150 
     | 
    
         
            +
              trimmedMessage = trimmedMessage?.replace(/^#+\s*(.*)$/gm, "$1");
         
     | 
| 
      
 151 
     | 
    
         
            +
              // Remove inline code formatting
         
     | 
| 
      
 152 
     | 
    
         
            +
              trimmedMessage = trimmedMessage?.replace(/`([^`]+)`/g, "$1");
         
     | 
| 
      
 153 
     | 
    
         
            +
              // Remove single backticks at the start or end of the message
         
     | 
| 
      
 154 
     | 
    
         
            +
              trimmedMessage = trimmedMessage?.replace(/^`|`$/g, "");
         
     | 
| 
      
 155 
     | 
    
         
            +
              // Remove leading and trailing whitespace
         
     | 
| 
      
 156 
     | 
    
         
            +
              trimmedMessage = trimmedMessage?.replace(/^\s+|\s+$/g, "");
         
     | 
| 
      
 157 
     | 
    
         
            +
              // Remove any remaining leading or trailing whitespace
         
     | 
| 
      
 158 
     | 
    
         
            +
              trimmedMessage = trimmedMessage.trim();
         
     | 
| 
      
 159 
     | 
    
         
            +
             
     | 
| 
      
 160 
     | 
    
         
            +
              return trimmedMessage;
         
     | 
| 
      
 161 
     | 
    
         
            +
            }
         
     | 
| 
         @@ -48,7 +48,7 @@ export const chatMemory = new ChatMemory(); 
     | 
|
| 
       48 
48 
     | 
    
         
             
            let chatHistories: ChatHistories = {};
         
     | 
| 
       49 
49 
     | 
    
         
             
            export const PROMPT_RULES = `You are a helpful and playful agent called {NAME} that lives inside a web3 messaging app called Converse.
         
     | 
| 
       50 
50 
     | 
    
         
             
            - You can respond with multiple messages if needed. Each message should be separated by a newline character.
         
     | 
| 
       51 
     | 
    
         
            -
            - You can trigger  
     | 
| 
      
 51 
     | 
    
         
            +
            - You can trigger skills by only sending the command in a newline message.
         
     | 
| 
       52 
52 
     | 
    
         
             
            - Never announce actions without using a command separated by a newline character.
         
     | 
| 
       53 
53 
     | 
    
         
             
            - Dont answer in markdown format, just answer in plaintext.
         
     | 
| 
       54 
54 
     | 
    
         
             
            - Do not make guesses or assumptions
         
     | 
| 
         @@ -81,7 +81,7 @@ export async function agentResponse( 
     | 
|
| 
       81 
81 
     | 
    
         
             
                  userPrompt,
         
     | 
| 
       82 
82 
     | 
    
         
             
                  systemPrompt,
         
     | 
| 
       83 
83 
     | 
    
         
             
                );
         
     | 
| 
       84 
     | 
    
         
            -
                await  
     | 
| 
      
 84 
     | 
    
         
            +
                await processMultilineResponse(sender.address, reply, context);
         
     | 
| 
       85 
85 
     | 
    
         
             
              } catch (error) {
         
     | 
| 
       86 
86 
     | 
    
         
             
                console.error("Error during OpenAI call:", error);
         
     | 
| 
       87 
87 
     | 
    
         
             
                await context.reply("An error occurred while processing your request.");
         
     | 
| 
         @@ -126,7 +126,7 @@ export async function textGeneration( 
     | 
|
| 
       126 
126 
     | 
    
         
             
              }
         
     | 
| 
       127 
127 
     | 
    
         
             
            }
         
     | 
| 
       128 
128 
     | 
    
         | 
| 
       129 
     | 
    
         
            -
            export async function  
     | 
| 
      
 129 
     | 
    
         
            +
            export async function processMultilineResponse(
         
     | 
| 
       130 
130 
     | 
    
         
             
              address: string,
         
     | 
| 
       131 
131 
     | 
    
         
             
              reply: string,
         
     | 
| 
       132 
132 
     | 
    
         
             
              context: any,
         
     | 
| 
         @@ -1,5 +1,6 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            import { Client } from "@xmtp/xmtp-js";
         
     | 
| 
      
 1 
     | 
    
         
            +
            import type { Client } from "@xmtp/xmtp-js";
         
     | 
| 
       2 
2 
     | 
    
         
             
            import { isAddress } from "viem";
         
     | 
| 
      
 3 
     | 
    
         
            +
            import type { HandlerContext } from "@xmtp/message-kit";
         
     | 
| 
       3 
4 
     | 
    
         | 
| 
       4 
5 
     | 
    
         
             
            export const converseEndpointURL =
         
     | 
| 
       5 
6 
     | 
    
         
             
              "https://converse-website-git-endpoit-ephemerahq.vercel.app";
         
     | 
| 
         @@ -16,8 +17,8 @@ export type ConverseProfile = { 
     | 
|
| 
       16 
17 
     | 
    
         
             
            export type UserInfo = {
         
     | 
| 
       17 
18 
     | 
    
         
             
              ensDomain?: string | undefined;
         
     | 
| 
       18 
19 
     | 
    
         
             
              address?: string | undefined;
         
     | 
| 
      
 20 
     | 
    
         
            +
              preferredName: string | undefined;
         
     | 
| 
       19 
21 
     | 
    
         
             
              converseUsername?: string | undefined;
         
     | 
| 
       20 
     | 
    
         
            -
              preferredName?: string | undefined;
         
     | 
| 
       21 
22 
     | 
    
         
             
              ensInfo?: EnsData | undefined;
         
     | 
| 
       22 
23 
     | 
    
         
             
              avatar?: string | undefined;
         
     | 
| 
       23 
24 
     | 
    
         
             
            };
         
     | 
| 
         @@ -48,12 +49,14 @@ export const clearInfoCache = () => { 
     | 
|
| 
       48 
49 
     | 
    
         
             
            export const getUserInfo = async (
         
     | 
| 
       49 
50 
     | 
    
         
             
              key: string,
         
     | 
| 
       50 
51 
     | 
    
         
             
              clientAddress?: string,
         
     | 
| 
      
 52 
     | 
    
         
            +
              context?: HandlerContext,
         
     | 
| 
       51 
53 
     | 
    
         
             
            ): Promise<UserInfo | null> => {
         
     | 
| 
       52 
54 
     | 
    
         
             
              let data: UserInfo = infoCache.get(key) || {
         
     | 
| 
       53 
55 
     | 
    
         
             
                ensDomain: undefined,
         
     | 
| 
       54 
56 
     | 
    
         
             
                address: undefined,
         
     | 
| 
       55 
57 
     | 
    
         
             
                converseUsername: undefined,
         
     | 
| 
       56 
58 
     | 
    
         
             
                ensInfo: undefined,
         
     | 
| 
      
 59 
     | 
    
         
            +
                preferredName: undefined,
         
     | 
| 
       57 
60 
     | 
    
         
             
              };
         
     | 
| 
       58 
61 
     | 
    
         
             
              if (isAddress(clientAddress || "")) {
         
     | 
| 
       59 
62 
     | 
    
         
             
                data.address = clientAddress;
         
     | 
| 
         @@ -74,12 +77,15 @@ export const getUserInfo = async ( 
     | 
|
| 
       74 
77 
     | 
    
         
             
              } else {
         
     | 
| 
       75 
78 
     | 
    
         
             
                data.converseUsername = key;
         
     | 
| 
       76 
79 
     | 
    
         
             
              }
         
     | 
| 
       77 
     | 
    
         
            -
             
     | 
| 
      
 80 
     | 
    
         
            +
              data.preferredName = data.ensDomain || data.converseUsername || "Friend";
         
     | 
| 
       78 
81 
     | 
    
         
             
              let keyToUse = data.address || data.ensDomain || data.converseUsername;
         
     | 
| 
       79 
82 
     | 
    
         
             
              let cacheData = keyToUse && infoCache.get(keyToUse);
         
     | 
| 
       80 
     | 
    
         
            -
              console.log("Getting user info", { cacheData, keyToUse, data });
         
     | 
| 
      
 83 
     | 
    
         
            +
              //console.log("Getting user info", { cacheData, keyToUse, data });
         
     | 
| 
       81 
84 
     | 
    
         
             
              if (cacheData) return cacheData;
         
     | 
| 
       82 
85 
     | 
    
         | 
| 
      
 86 
     | 
    
         
            +
              context?.send(
         
     | 
| 
      
 87 
     | 
    
         
            +
                "Hey there! Give me a sec while I fetch info about you first...",
         
     | 
| 
      
 88 
     | 
    
         
            +
              );
         
     | 
| 
       83 
89 
     | 
    
         
             
              if (keyToUse?.includes(".eth")) {
         
     | 
| 
       84 
90 
     | 
    
         
             
                const response = await fetch(`https://ensdata.net/${keyToUse}`);
         
     | 
| 
       85 
91 
     | 
    
         
             
                const ensData: EnsData = (await response.json()) as EnsData;
         
     | 
| 
         @@ -102,13 +108,15 @@ export const getUserInfo = async ( 
     | 
|
| 
       102 
108 
     | 
    
         
             
                  }),
         
     | 
| 
       103 
109 
     | 
    
         
             
                });
         
     | 
| 
       104 
110 
     | 
    
         
             
                const converseData = (await response.json()) as ConverseProfile;
         
     | 
| 
       105 
     | 
    
         
            -
                if (process.env.MSG_LOG)
         
     | 
| 
       106 
     | 
    
         
            -
             
     | 
| 
      
 111 
     | 
    
         
            +
                /// if (process.env.MSG_LOG === "true")
         
     | 
| 
      
 112 
     | 
    
         
            +
                //console.log("Converse data", keyToUse, converseData);
         
     | 
| 
       107 
113 
     | 
    
         
             
                data.converseUsername =
         
     | 
| 
       108 
114 
     | 
    
         
             
                  converseData?.formattedName || converseData?.name || undefined;
         
     | 
| 
       109 
115 
     | 
    
         
             
                data.address = converseData?.address || undefined;
         
     | 
| 
       110 
116 
     | 
    
         
             
                data.avatar = converseData?.avatar || undefined;
         
     | 
| 
       111 
117 
     | 
    
         
             
              }
         
     | 
| 
      
 118 
     | 
    
         
            +
             
     | 
| 
      
 119 
     | 
    
         
            +
              data.preferredName = data.ensDomain || data.converseUsername || "Friend";
         
     | 
| 
       112 
120 
     | 
    
         
             
              if (data.address) infoCache.set(data.address, data);
         
     | 
| 
       113 
121 
     | 
    
         
             
              return data;
         
     | 
| 
       114 
122 
     | 
    
         
             
            };
         
     | 
| 
         @@ -123,7 +131,8 @@ export const isOnXMTP = async ( 
     | 
|
| 
       123 
131 
     | 
    
         | 
| 
       124 
132 
     | 
    
         
             
            export const PROMPT_USER_CONTENT = (userInfo: UserInfo) => {
         
     | 
| 
       125 
133 
     | 
    
         
             
              let { address, ensDomain, converseUsername, preferredName } = userInfo;
         
     | 
| 
       126 
     | 
    
         
            -
              let prompt = ` 
     | 
| 
      
 134 
     | 
    
         
            +
              let prompt = `
         
     | 
| 
      
 135 
     | 
    
         
            +
            User context: 
         
     | 
| 
       127 
136 
     | 
    
         
             
            - Start by fetch their domain from or Convese username
         
     | 
| 
       128 
137 
     | 
    
         
             
            - Call the user by their name or domain, in case they have one
         
     | 
| 
       129 
138 
     | 
    
         
             
            - Ask for a name (if they don't have one) so you can suggest domains.
         
     | 
| 
         @@ -132,5 +141,11 @@ export const PROMPT_USER_CONTENT = (userInfo: UserInfo) => { 
     | 
|
| 
       132 
141 
     | 
    
         
             
              if (ensDomain) prompt += `\n- User ENS domain is: ${ensDomain}`;
         
     | 
| 
       133 
142 
     | 
    
         
             
              if (converseUsername)
         
     | 
| 
       134 
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 
     | 
    
         
            +
             
     | 
| 
       135 
150 
     | 
    
         
             
              return prompt;
         
     | 
| 
       136 
151 
     | 
    
         
             
            };
         
     | 
| 
         @@ -3,7 +3,7 @@ import { handler as agent } from "./handler/agent.js"; 
     | 
|
| 
       3 
3 
     | 
    
         
             
            import { handler as transaction } from "./handler/transaction.js";
         
     | 
| 
       4 
4 
     | 
    
         
             
            import { handler as games } from "./handler/game.js";
         
     | 
| 
       5 
5 
     | 
    
         
             
            import { handler as loyalty } from "./handler/loyalty.js";
         
     | 
| 
       6 
     | 
    
         
            -
            import {  
     | 
| 
      
 6 
     | 
    
         
            +
            import { handler as groupHelp } from "./handler/helpers.js";
         
     | 
| 
       7 
7 
     | 
    
         
             
            import type { SkillGroup } from "@xmtp/message-kit";
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
       9 
9 
     | 
    
         
             
            export const skills: SkillGroup[] = [
         
     | 
| 
         @@ -31,6 +31,7 @@ export const skills: SkillGroup[] = [ 
     | 
|
| 
       31 
31 
     | 
    
         
             
                  },
         
     | 
| 
       32 
32 
     | 
    
         
             
                ],
         
     | 
| 
       33 
33 
     | 
    
         
             
              },
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
       34 
35 
     | 
    
         
             
              {
         
     | 
| 
       35 
36 
     | 
    
         
             
                name: "Transactions",
         
     | 
| 
       36 
37 
     | 
    
         
             
                description: "Multipurpose transaction frame built onbase.",
         
     | 
| 
         @@ -159,10 +160,18 @@ export const skills: SkillGroup[] = [ 
     | 
|
| 
       159 
160 
     | 
    
         
             
                    command: "/help",
         
     | 
| 
       160 
161 
     | 
    
         
             
                    triggers: ["/help"],
         
     | 
| 
       161 
162 
     | 
    
         
             
                    examples: ["/help"],
         
     | 
| 
       162 
     | 
    
         
            -
                    handler:  
     | 
| 
      
 163 
     | 
    
         
            +
                    handler: groupHelp,
         
     | 
| 
       163 
164 
     | 
    
         
             
                    description: "Get help with the bot.",
         
     | 
| 
       164 
165 
     | 
    
         
             
                    params: {},
         
     | 
| 
       165 
166 
     | 
    
         
             
                  },
         
     | 
| 
      
 167 
     | 
    
         
            +
                  {
         
     | 
| 
      
 168 
     | 
    
         
            +
                    command: "/id",
         
     | 
| 
      
 169 
     | 
    
         
            +
                    adminOnly: true,
         
     | 
| 
      
 170 
     | 
    
         
            +
                    handler: groupHelp,
         
     | 
| 
      
 171 
     | 
    
         
            +
                    triggers: ["/id"],
         
     | 
| 
      
 172 
     | 
    
         
            +
                    description: "Get the group ID.",
         
     | 
| 
      
 173 
     | 
    
         
            +
                    params: {},
         
     | 
| 
      
 174 
     | 
    
         
            +
                  },
         
     | 
| 
       166 
175 
     | 
    
         
             
                ],
         
     | 
| 
       167 
176 
     | 
    
         
             
              },
         
     | 
| 
       168 
177 
     | 
    
         
             
            ];
         
     |