Skip to content

Commit

Permalink
feat: support Twilio (#51)
Browse files Browse the repository at this point in the history
  • Loading branch information
wenfengwang committed May 10, 2024
1 parent 1110b60 commit 7cb4bfb
Show file tree
Hide file tree
Showing 20 changed files with 954 additions and 105 deletions.
12 changes: 12 additions & 0 deletions cli/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func appCommand() *cobra.Command {
discordCommand(),
twitterCommand(),
webBrowserCommand(),
twilioCommand(),
)
return cmd
}
Expand Down Expand Up @@ -101,6 +102,17 @@ func webBrowserCommand() *cobra.Command {
return cmd
}

func twilioCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "twilio",
Short: "chat with Twilio, support SMS and WhatsApp",
Run: func(cmd *cobra.Command, args []string) {
doRequest(api.AppType_TWILIO, args[0])
},
}
return cmd
}

func doRequest(app api.AppType, instruction string) {
var opts []grpc.DialOption
opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
Expand Down
40 changes: 40 additions & 0 deletions cli/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ func authCommand() *cobra.Command {
cmd.AddCommand(authGitHubCommand())
cmd.AddCommand(authDiscordCommand())
cmd.AddCommand(authTwitterCommand())
cmd.AddCommand(authTwilioCommand())
return cmd
}

Expand Down Expand Up @@ -202,3 +203,42 @@ func authTwitterCommand() *cobra.Command {

return cmd
}

var (
twilioAccount = ""
twilioToken = ""
twilioFrom = ""
)

func authTwilioCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "twilio",
Short: "authorize Twilio",
Run: func(cmd *cobra.Command, args []string) {
if twilioAccount == "" || twilioToken == "" || twilioFrom == "" {
color.Yellow("account/token/from required")
_ = cmd.Help()
return
}
response, err := httpClient.R().SetBody(map[string]string{
"from_phone_number": twilioFrom,
"account_sid": twilioAccount,
"auth_token": twilioToken,
}).Post("/auth/twilio")
if err != nil {
color.Red("failed to authorize Twilio: %v", err)
os.Exit(-1)
}
if response.StatusCode() != 200 {
color.Red("failed to authorize Twilio: %s", string(response.Body()))
os.Exit(-1)
}
color.Green("authorization success")
},
}
cmd.Flags().StringVar(&twilioAccount, "account", "", "The Twilio Account ID")
cmd.Flags().StringVar(&twilioToken, "token", "", "The Twilio Auth Token")
cmd.Flags().StringVar(&twilioFrom, "from", "", "Twilio phone number to send from")

return cmd
}
3 changes: 3 additions & 0 deletions npi/app/twilio/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .app import Twilio

__all__ = ['Twilio']
39 changes: 39 additions & 0 deletions npi/app/twilio/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from twilio.rest import Client

from npi.core.app import App, npi_tool
from npi.config import config

from npi.app.twilio.schema import *


class Twilio(App):
def __init__(self):
super().__init__(
name='twilio',
description='Send messages using Twilio, e.g., twilio("send a message to +16507055795")',
system_role='You are a Twilio Agent can send messages via WhatsApp or SMS',
)

cred = config.get_twilio_credentials()
self.client = Client(cred.account_sid, cred.auth_token)
self.from_number = cred.from_phone_number

@npi_tool(description='Send a sms message to a phone number')
async def send_sms_message(self, params: MessageParameters):
message = self.client.messages.create(
from_=f'{self.from_number}',
body=params.message,
to=f"{params.to}",
)

return f'Message has been successfully sent, sid: {message.sid}'

@npi_tool(description='Send a whatsapp message to a phone number')
async def send_whatsapp_message(self, params: MessageParameters):
message = self.client.messages.create(
from_=f'whatsapp:{self.from_number}',
body=params.message,
to=f"whatsapp:{params.to}",
)

return f'Message has been successfully sent, sid: {message.sid}'
8 changes: 8 additions & 0 deletions npi/app/twilio/schema.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from pydantic import Field
from npi.types import Parameters


class MessageParameters(Parameters):
to: str = Field(default=None, description='the phone number you want to send the message to')
message: str = Field(default=100, description='the message body you want to send')

16 changes: 16 additions & 0 deletions npi/config/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ class TwitterCredentials:
password: str


@dataclass(frozen=True)
class TwilioCredentials:
from_phone_number: str
account_sid: str
auth_token: str


def get_project_root() -> str:
return CONFIG.get('npi_root', None) or os.environ.get('NPI_ROOT', None) or '/npiai'

Expand Down Expand Up @@ -65,6 +72,10 @@ def get_github_credentials() -> GithubCredentials | None:
return CONFIG.get('github_credentials', None)


def get_twilio_credentials() -> TwilioCredentials | None:
return CONFIG.get('twilio_credentials', None)


def set_github_credentials(access_token: str):
CONFIG['github_credentials'] = GithubCredentials(access_token=access_token)

Expand All @@ -91,3 +102,8 @@ def get_twitter_credentials() -> TwitterCredentials | None:

def set_twitter_credentials(username: str, password: str):
CONFIG['twitter_credentials'] = TwitterCredentials(username=username, password=password)


def set_twilio_credentials(account_sid: str, auth_token: str, from_phone_number: str):
CONFIG['twilio_credentials'] = TwilioCredentials(from_phone_number=from_phone_number,
account_sid=account_sid, auth_token=auth_token)
4 changes: 3 additions & 1 deletion npi/server/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from npiai_proto import api_pb2_grpc, api_pb2
from npi.core.thread import ThreadManager, Thread
from npi.app import google, discord, github
from npi.app import google, discord, github, twilio
from npi.browser_app import twitter, general_browser_agent as browser
from npi.utils import logger
from npi.error.auth import UnauthorizedError
Expand All @@ -35,6 +35,8 @@ def get_app(app_type: api_pb2.AppType):
return github.GitHub()
case api_pb2.WEB_BROWSER:
return browser.GeneralBrowserAgent()
case api_pb2.TWILIO:
return twilio.Twilio()
case _:
raise Exception("unsupported application")

Expand Down
13 changes: 13 additions & 0 deletions npi/server/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ class TwitterAuthRequest(BaseModel):
password: str


class TwilioAuthRequest(BaseModel):
from_phone_number: str
account_sid: str
auth_token: str


STATE = {}


Expand Down Expand Up @@ -104,6 +110,13 @@ async def auth_twitter(req: TwitterAuthRequest):
return responses.Response(status_code=200)


@app.post('/auth/twilio')
async def auth_twilio(req: TwilioAuthRequest):
config.set_twilio_credentials(account_sid=req.account_sid, auth_token=req.auth_token,
from_phone_number=req.from_phone_number)
return responses.Response(status_code=200)


@app.get('/ping')
async def ping():
return responses.Response(status_code=200)
61 changes: 47 additions & 14 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 7cb4bfb

Please sign in to comment.