From 1cb3af70822aca2e3c160191c0ddedaf77f5874d Mon Sep 17 00:00:00 2001 From: Juan Carlos Claridad Date: Sun, 5 Jan 2025 02:18:13 +0800 Subject: [PATCH] added plugin-twilio this can send message using sms or whatsapp --- packages/plugin-twilio/.npmignore | 6 + packages/plugin-twilio/ReadMe.txt | 78 ++++++++ packages/plugin-twilio/eslint.config.mjs | 3 + packages/plugin-twilio/package.json | 20 ++ packages/plugin-twilio/src/actions/index.ts | 3 + packages/plugin-twilio/src/actions/sendSms.ts | 173 ++++++++++++++++++ .../src/actions/sendWhatsAppMessage.ts | 140 ++++++++++++++ packages/plugin-twilio/src/index.ts | 12 ++ packages/plugin-twilio/tsconfig.json | 13 ++ packages/plugin-twilio/tsup.config.ts | 20 ++ 10 files changed, 468 insertions(+) create mode 100644 packages/plugin-twilio/.npmignore create mode 100644 packages/plugin-twilio/ReadMe.txt create mode 100644 packages/plugin-twilio/eslint.config.mjs create mode 100644 packages/plugin-twilio/package.json create mode 100644 packages/plugin-twilio/src/actions/index.ts create mode 100644 packages/plugin-twilio/src/actions/sendSms.ts create mode 100644 packages/plugin-twilio/src/actions/sendWhatsAppMessage.ts create mode 100644 packages/plugin-twilio/src/index.ts create mode 100644 packages/plugin-twilio/tsconfig.json create mode 100644 packages/plugin-twilio/tsup.config.ts diff --git a/packages/plugin-twilio/.npmignore b/packages/plugin-twilio/.npmignore new file mode 100644 index 00000000000..078562eceab --- /dev/null +++ b/packages/plugin-twilio/.npmignore @@ -0,0 +1,6 @@ +* + +!dist/** +!package.json +!readme.md +!tsup.config.ts \ No newline at end of file diff --git a/packages/plugin-twilio/ReadMe.txt b/packages/plugin-twilio/ReadMe.txt new file mode 100644 index 00000000000..c32b9723e69 --- /dev/null +++ b/packages/plugin-twilio/ReadMe.txt @@ -0,0 +1,78 @@ +#The ENV file should contain this information + +# Cache Configs +CACHE_STORE=database + +# Discord Configuration +DISCORD_APPLICATION_ID= +DISCORD_API_TOKEN= + +# AI Model API Keys +OPENAI_API_KEY= + +# Twitter/X Configuration +TWITTER_USERNAME= +TWITTER_PASSWORD= +TWITTER_EMAIL= +TWITTER_POLL_INTERVAL=120 # How often (in seconds) the bot should check for interactions +TWITTER_SEARCH_ENABLE=FALSE # Enable timeline search, WARNING this greatly increases your chance of getting banned +#TWITTER_TARGET_USERS= + +# Twilio Part +TWILIO_ACCOUNT_SID= +TWILIO_AUTH_TOKEN= +TWILIO_PHONE_NUMBER= +TWILIO_WHATSAPP_PHONE_NUMBER= + +# Server Configuration +SERVER_PORT=3000 + +# How to use +1. create your .env file , if you don't have it yet and then populate it with the information above, make sure to fill in all information +2. Add this project into your eliza os project under packages +3. using terminal go inside plugin-twilio then type pnpm install twilio +4. go inside your agent folder update the package.json add this "@elizaos/plugin-twilio": "workspace:*" +5. add this inside your Agent index.ts import { twilioPlugin } from "@elizaos/plugin-twilio"; +6. Add twilioPlugin in Agent Runtime still inside Agent index.ts +7. pnpm install +8. pnpm build +9. pmpn start --character="characters/nameofyouragentcharacterfile.character.json" + +#Note: Make sure you have a twilio developer account and it is verified with verified phone number and have enough credits but they provide free small credits when your account is new +visit twilio: https://www.twilio.com +twilio quick start guides: https://www.twilio.com/docs/messaging/quickstart +twilio documentation: https://www.twilio.com/docs/messaging +Free Trial Account: https://www.twilio.com/docs/messaging/guides/how-to-use-your-free-trial-account + +# For WhatsApp guides follow the link below you need to have a separate twilio whatsapp enabled phone number +https://www.twilio.com/docs/whatsapp +https://www.twilio.com/docs/whatsapp/quickstart/node +https://www.twilio.com/docs/whatsapp/getting-started#registering-a-whatsapp-sender +https://www.twilio.com/docs/verify/whatsapp + + +#Some Other Whats App Info that you might need +https://www.twilio.com/docs/whatsapp + +#Twilio Phone Number guidelines +https://www.twilio.com/en-us/guidelines + + +# Clarification this project is intended to be use/place inside elizaos project packages, this can't work alone + +# Sample implementation can be found here +https://github.com/juanc07/AgentSoulSpark + +# Usage Sample + +The message that you want to send must be inside a Single Quote or double quotes + +Example 1: +Please send sms to [phone number], and my message is '[your message here]' +Please send whats app message to [phone number], and my message is '[your message here]' + +Example 2: +Please send sms to [phone number], and my message is "[your message here]" +Please send whats app message to [phone number], and my message is "[your message here]" + +#Note I haven't tested any other complex string or sentence yet, this could be improve ofcourse but for now it works that way \ No newline at end of file diff --git a/packages/plugin-twilio/eslint.config.mjs b/packages/plugin-twilio/eslint.config.mjs new file mode 100644 index 00000000000..92fe5bbebef --- /dev/null +++ b/packages/plugin-twilio/eslint.config.mjs @@ -0,0 +1,3 @@ +import eslintGlobalConfig from "../../eslint.config.mjs"; + +export default [...eslintGlobalConfig]; diff --git a/packages/plugin-twilio/package.json b/packages/plugin-twilio/package.json new file mode 100644 index 00000000000..db5033dea2e --- /dev/null +++ b/packages/plugin-twilio/package.json @@ -0,0 +1,20 @@ +{ + "name": "@elizaos/plugin-twilio", + "version": "0.1.7-alpha.2", + "main": "dist/index.js", + "type": "module", + "types": "dist/index.d.ts", + "dependencies": { + "@elizaos/core": "workspace:*", + "tsup": "8.3.5", + "twilio": "^5.4.0" + }, + "scripts": { + "build": "tsup --format esm --dts", + "dev": "tsup --format esm --dts --watch", + "lint": "eslint --fix --cache ." + }, + "peerDependencies": { + "whatwg-url": "7.1.0" + } +} diff --git a/packages/plugin-twilio/src/actions/index.ts b/packages/plugin-twilio/src/actions/index.ts new file mode 100644 index 00000000000..25a79e9a3a6 --- /dev/null +++ b/packages/plugin-twilio/src/actions/index.ts @@ -0,0 +1,3 @@ +export * from "./helloWorld.ts"; +export * from "./sendSms.ts"; +export * from "./sendWhatsAppMessage.ts"; diff --git a/packages/plugin-twilio/src/actions/sendSms.ts b/packages/plugin-twilio/src/actions/sendSms.ts new file mode 100644 index 00000000000..c3b8c6311ff --- /dev/null +++ b/packages/plugin-twilio/src/actions/sendSms.ts @@ -0,0 +1,173 @@ +import { + ActionExample, + generateText, + HandlerCallback, + IAgentRuntime, + Memory, + ModelClass, + State, + type Action, +} from "@elizaos/core"; +import twilio from 'twilio'; + +export const sendSmsAction: Action = { + name: "SendSms", + similes: [ + "SendSms" + ], + validate: async (_runtime: IAgentRuntime, _message: Memory) => { + return true; + }, + description: + "Send SMS to the mobile number provided by the user", + handler: async ( + _runtime: IAgentRuntime, + _message: Memory, + _state: State, + _options:{[key:string]: unknown}, + _callback: HandlerCallback, + ): Promise => { + // Check if environment variables are set + const accountSid = process.env.TWILIO_ACCOUNT_SID; + const authToken = process.env.TWILIO_AUTH_TOKEN; + + console.log("CHECK _message: ",_message.content.text); + + if (!accountSid || !authToken) { + console.error('TWILIO_ACCOUNT_SID or TWILIO_AUTH_TOKEN is not set'); + return false; + } + + // Extract the mobile number from the message + const mobileNumberRegex = /(?:\+|00)(\d{1,3})\s?(\d{3,5})\s?(\d{4,10})/; // This regex matches numbers like +1 123 4567890 or 001 123 4567890 + const text = (_message.content as { text?: string })?.text || ''; + const matches = text.match(mobileNumberRegex); + + const messageRegex = /(['"])(.*?)\1/; + const messageMatch = text.match(messageRegex); + + let mobileNumberProvidedByUser = null; + let messageToSendFromUser = null; + + if(messageMatch){ + messageToSendFromUser = messageMatch[2]; + } + if (matches) { + // Combine the parts of the number into a single string, removing spaces and plus signs + mobileNumberProvidedByUser = `+${matches[1]}${matches[2]}${matches[3]}`; + }else{ + const alternativeMobileNumberRegex = /\b(\d{3})[-.]?(\d{3})[-.]?(\d{4})\b/; // For formats like 123-456-7890 or 123.456.7890 + if (!mobileNumberProvidedByUser) { + const alternativeMatches = text.match(alternativeMobileNumberRegex); + if (alternativeMatches) { + mobileNumberProvidedByUser = `${alternativeMatches[1]}${alternativeMatches[2]}${alternativeMatches[3]}`; + } + } + } + + const twilioNumber = process.env.TWILIO_PHONE_NUMBER; // Your Twilio phone number + + console.log('check target mobile number: ', mobileNumberProvidedByUser); + console.log('check messageToSendFromUser: ', messageToSendFromUser); + console.log('check twilioNumber: ', twilioNumber); + + if (!twilioNumber) { + console.error('Twilio phone number is missing'); + + _callback({ + text: `Sorry there was an issue send sms, please try again later`, + }); + return false; + } + + const recentMessages = `Extract the phone number from the user recent messages ${_state.recentMessages}`; + + if (!mobileNumberProvidedByUser) { + console.error('Mobile number is missing will try to get the phone number or mobile number from recent messages'); + + mobileNumberProvidedByUser = await generateText( + { + runtime: _runtime, + context: recentMessages, + modelClass: ModelClass.SMALL, + stop: ["\n"], + customSystemPrompt: "only extract the message that the user intend to send and only get the last one" + } + ); + } + + if (!mobileNumberProvidedByUser) { + console.error('Mobile number is missing'); + + _callback({ + text: `Sorry, there was an issue send sms, please try again later`, + }); + return false; + } + + const recentUserMessages = `Extract the message intended for SMS or text: ${_state.recentMessages}`; + + if (!messageToSendFromUser) { + console.error('messageToSendFromUser is missing will try to get the user message from recent messages'); + + messageToSendFromUser = await generateText( + { + runtime: _runtime, + context: recentUserMessages, + modelClass: ModelClass.SMALL, + stop: ["\n"] + } + ); + } + + if(messageToSendFromUser==null){ + console.error('messageToSendFromUser is empty or null'); + + _callback({ + text: `Sorry there was an issue sending the WhatsApp message, please try again later`, + }); + return false; + } + + try { + // Initialize Twilio client + const client = twilio(accountSid, authToken); + + // Send the SMS + const message= await client.messages.create({ + body: messageToSendFromUser, // The message body + to: mobileNumberProvidedByUser, // The recipient's phone number + from: twilioNumber, // Your Twilio phone number + }); + + console.log("message body: ", message); + + const messageFromAgent = `SMS sent successfully to ${mobileNumberProvidedByUser}`; + + // Call the callback to notify the user + _callback({ + text: messageFromAgent, + }); + + return true; + } catch (error) { + console.error('Failed to send SMS:', error); + _callback({ + text: `Failed to send SMS to ${mobileNumberProvidedByUser}`, + }); + return false; + } + }, + examples: [ + [ + { + user: "{{user1}}", + content: { text: "please send my message via sms to target mobile number" }, + }, + { + user: "{{user2}}", + content: { text: "", action: "SEND_SMS" }, + }, + ], + ] as ActionExample[][], +} as Action; \ No newline at end of file diff --git a/packages/plugin-twilio/src/actions/sendWhatsAppMessage.ts b/packages/plugin-twilio/src/actions/sendWhatsAppMessage.ts new file mode 100644 index 00000000000..f9ec7744411 --- /dev/null +++ b/packages/plugin-twilio/src/actions/sendWhatsAppMessage.ts @@ -0,0 +1,140 @@ +import { + ActionExample, + HandlerCallback, + IAgentRuntime, + Memory, + State, + type Action, +} from "@elizaos/core"; +import twilio from 'twilio'; + +export const sendWhatsAppMessageAction: Action = { + name: "SendWhatsAppMessage", + similes: [ + "SendWhatsAppMessage" + ], + validate: async (_runtime: IAgentRuntime, _message: Memory) => { + return true; + }, + description: + "Send a WhatsApp message to the mobile number provided by the user", + handler: async ( + _runtime: IAgentRuntime, + _message: Memory, + _state: State, + _options:{[key:string]: unknown}, + _callback: HandlerCallback, + ): Promise => { + // Check if environment variables are set + const accountSid = process.env.TWILIO_ACCOUNT_SID; + const authToken = process.env.TWILIO_AUTH_TOKEN; + + console.log("CHECK _message: ",_message.content.text); + + if (!accountSid || !authToken) { + console.error('TWILIO_ACCOUNT_SID or TWILIO_AUTH_TOKEN is not set'); + return false; + } + + // Extract the mobile number from the message + const mobileNumberRegex = /(?:\+|00)(\d{1,3})\s?(\d{3,5})\s?(\d{4,10})/; // This regex matches numbers like +1 123 4567890 or 001 123 4567890 + const text = (_message.content as { text?: string })?.text || ''; + const matches = text.match(mobileNumberRegex); + + const messageRegex = /(['"])(.*?)\1/; + const messageMatch = text.match(messageRegex); + + let mobileNumberProvidedByUser = null; + let messageToSendFromUser = null; + + if(messageMatch){ + messageToSendFromUser = messageMatch[2]; + } + if (matches) { + // Combine the parts of the number into a single string, removing spaces and plus signs + mobileNumberProvidedByUser = `+${matches[1]}${matches[2]}${matches[3]}`; + } else { + const alternativeMobileNumberRegex = /\b(\d{3})[-.]?(\d{3})[-.]?(\d{4})\b/; // For formats like 123-456-7890 or 123.456.7890 + if (!mobileNumberProvidedByUser) { + const alternativeMatches = text.match(alternativeMobileNumberRegex); + if (alternativeMatches) { + mobileNumberProvidedByUser = `+${alternativeMatches[1]}${alternativeMatches[2]}${alternativeMatches[3]}`; + } + } + } + + const twilioNumber = process.env.TWILIO_WHATSAPP_PHONE_NUMBER; // Your Twilio WhatsApp number + + console.log('check target mobile number: ', mobileNumberProvidedByUser); + console.log('check messageToSendFromUser: ', messageToSendFromUser); + console.log('check twilioNumber: ', twilioNumber); + + if (!mobileNumberProvidedByUser) { + console.error('Mobile number is missing'); + + _callback({ + text: `Sorry there was an issue sending the WhatsApp message, please try again later`, + }); + return false; + } + + if (!twilioNumber) { + console.error('Twilio WhatsApp number is missing'); + + _callback({ + text: `Sorry there was an issue sending the WhatsApp message, please try again later`, + }); + return false; + } + + if(messageToSendFromUser==null){ + console.error('messageToSendFromUser is empty or null'); + + _callback({ + text: `Sorry there was an issue sending the WhatsApp message, please try again later`, + }); + return false; + } + + try { + // Initialize Twilio client + const client = twilio(accountSid, authToken); + + // Send the WhatsApp message + const message = await client.messages.create({ + body: messageToSendFromUser, // The message body + to: `whatsapp:${mobileNumberProvidedByUser}`, // The recipient's WhatsApp number + from: `whatsapp:${twilioNumber}`, // Your Twilio WhatsApp number + }); + + console.log("message body: ", message); + + const messageFromAgent = `WhatsApp message sent successfully to ${mobileNumberProvidedByUser}`; + + // Call the callback to notify the user + _callback({ + text: messageFromAgent, + }); + + return true; + } catch (error) { + console.error('Failed to send WhatsApp message:', error); + _callback({ + text: `Failed to send WhatsApp message to ${mobileNumberProvidedByUser}`, + }); + return false; + } + }, + examples: [ + [ + { + user: "{{user1}}", + content: { text: "please send my message via WhatsApp to target mobile number" }, + }, + { + user: "{{user2}}", + content: { text: "", action: "SEND_WHATSAPP_MESSAGE" }, + }, + ], + ] as ActionExample[][], +} as Action; \ No newline at end of file diff --git a/packages/plugin-twilio/src/index.ts b/packages/plugin-twilio/src/index.ts new file mode 100644 index 00000000000..a10a047d517 --- /dev/null +++ b/packages/plugin-twilio/src/index.ts @@ -0,0 +1,12 @@ +import { Plugin } from "@elizaos/core"; +import { sendWhatsAppMessageAction,sendSmsAction } from "./actions"; +export * as actions from "./actions"; + +export const twilioPlugin: Plugin = { + name: "twilio", + description: "twilio basic send sms action implementation", + actions: [ + sendSmsAction, + sendWhatsAppMessageAction, + ] +}; diff --git a/packages/plugin-twilio/tsconfig.json b/packages/plugin-twilio/tsconfig.json new file mode 100644 index 00000000000..834c4dce269 --- /dev/null +++ b/packages/plugin-twilio/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../core/tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "types": [ + "node" + ] + }, + "include": [ + "src/**/*.ts" + ] +} \ No newline at end of file diff --git a/packages/plugin-twilio/tsup.config.ts b/packages/plugin-twilio/tsup.config.ts new file mode 100644 index 00000000000..e42bf4efeae --- /dev/null +++ b/packages/plugin-twilio/tsup.config.ts @@ -0,0 +1,20 @@ +import { defineConfig } from "tsup"; + +export default defineConfig({ + entry: ["src/index.ts"], + outDir: "dist", + sourcemap: true, + clean: true, + format: ["esm"], // Ensure you're targeting CommonJS + external: [ + "dotenv", // Externalize dotenv to prevent bundling + "fs", // Externalize fs to use Node.js built-in module + "path", // Externalize other built-ins if necessary + "@reflink/reflink", + "@node-llama-cpp", + "https", + "http", + "agentkeepalive", + // Add other modules you want to externalize + ], +});