diff --git a/README.md b/README.md new file mode 100644 index 0000000..b3dd98f --- /dev/null +++ b/README.md @@ -0,0 +1,12 @@ +# twilio_sender +twilio_sender is a cli program to send text messages. + +## Install twilio +``` +go build +``` + +## Using +``` +./twilio_sender config_path.json numbers_path.json message_path.json +``` diff --git a/README.txt b/README.txt deleted file mode 100644 index 3d2628c..0000000 --- a/README.txt +++ /dev/null @@ -1 +0,0 @@ -# Install twilio diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..127740f --- /dev/null +++ b/config/config.go @@ -0,0 +1,31 @@ +package config + +import "encoding/json" +import "fmt" +import "os" + +import ( + "git.kundeng.us/phoenix/twilio_sender/models" +) + +func ParseConfig(filepath string) (config models.TwiloDetails, success bool) { + content, err := os.ReadFile(filepath) + + fmt.Println("Reading config file...") + fmt.Printf("%s\n", filepath) + + if err != nil { + fmt.Println("An error reading the file") + + return + } + + err = json.Unmarshal(content, &config) + + if err != nil { + fmt.Println("An error occurred") + return config, false + } + + return config, true +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..9050783 --- /dev/null +++ b/go.mod @@ -0,0 +1,9 @@ +module git.kundeng.us/phoenix/twilio_sender + +go 1.21.6 + +require ( + github.com/golang/mock v1.6.0 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/twilio/twilio-go v1.16.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..3ffc798 --- /dev/null +++ b/go.sum @@ -0,0 +1,45 @@ +github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/localtunnel/go-localtunnel v0.0.0-20170326223115-8a804488f275/go.mod h1:zt6UU74K6Z6oMOYJbJzYpYucqdcQwSMPBEdSvGiaUMw= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/twilio/twilio-go v1.16.1 h1:tzoAOtQiWWnKeR5EZpSlCyElRLtvrMjLeKfohtnraik= +github.com/twilio/twilio-go v1.16.1/go.mod h1:tdnfQ5TjbewoAu4lf9bMsGvfuJ/QU9gYuv9yx3TSIXU= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go new file mode 100644 index 0000000..4d2d784 --- /dev/null +++ b/main.go @@ -0,0 +1,57 @@ +package main + +import "fmt" +import "os" + +import ( + "git.kundeng.us/phoenix/twilio_sender/config" + "git.kundeng.us/phoenix/twilio_sender/models" + "git.kundeng.us/phoenix/twilio_sender/sender" + "git.kundeng.us/phoenix/twilio_sender/util" +) + +func main() { + fmt.Println("twilio_sender") + myArgs := os.Args + argCount := len(myArgs) + + fmt.Printf("Argument count: %v\n", argCount) + + if argCount < 4 { + fmt.Println("Provide argument to config file and number file") + os.Exit(-1) + } + + configPath, numberPath, messagePath := util.GetPaths(myArgs) + + fmt.Printf("Config file path: %s\n", configPath) + + cfg, _ := config.ParseConfig(configPath) + + cfg.PrintConfig() + + fmt.Println("Numbers file path:", numberPath) + + numbers := util.ParseNumbers(numberPath) + + if len(numbers) == 0 { + fmt.Println("No numbers to send") + os.Exit(-1) + } + + models.PrintNumbers(numbers) + fmt.Println("") + + fmt.Println("Message path:", messagePath) + + message := util.ParseMessage(messagePath) + + if message.IsEmpty() { + fmt.Println("No message to send") + os.Exit(-1) + } + + message.Print() + + sender.SendMessages(cfg, numbers, message, 5) +} diff --git a/models/twilio_details.go b/models/twilio_details.go new file mode 100644 index 0000000..42345db --- /dev/null +++ b/models/twilio_details.go @@ -0,0 +1,65 @@ +package models + +import ( + "fmt" +) + +type Number struct { + PhoneNumber string `json:"number"` +} + +func (nbr Number) IsEmpty() bool { + if nbr.PhoneNumber == "" { + return true + } else { + return false + } +} + +func PrintNumbers(numbers [](Number)) { + for _, element := range numbers { + fmt.Println("Phone number:", element.PhoneNumber) + } +} + +type Message struct { + Text string `json:"text"` +} + +func (msg Message) IsEmpty() bool { + if msg.Text == "" { + return true + } else { + return false + } +} + +func (msg Message) Print() { + fmt.Println("Message:", msg.Text) +} + +type TwiloDetails struct { + AccountSID string `json:"account_sid"` + ServiceSID string `json:"service_sid"` + URI string `json:"uri"` + AuthToken string `json:"auth_token"` + Number string `json:"number"` +} + +func (config TwiloDetails) PrintConfig() { + fmt.Printf("Account SID: %s\n", config.AccountSID) + fmt.Printf("Service SID: %s\n", config.ServiceSID) + fmt.Println("URI:", config.URI) + fmt.Printf("Auth Token: %s\n", config.AuthToken) + fmt.Printf("Number: %s\n", config.Number) +} + +func Init() TwiloDetails { + return TwiloDetails{ + "", + "", + "", + "", + "", + } +} diff --git a/main.py b/old/main.py similarity index 100% rename from main.py rename to old/main.py diff --git a/requirements.txt b/old/requirements.txt similarity index 100% rename from requirements.txt rename to old/requirements.txt diff --git a/old/send_messages.sh b/old/send_messages.sh new file mode 100755 index 0000000..905a962 --- /dev/null +++ b/old/send_messages.sh @@ -0,0 +1,31 @@ +#!/usr/bin/bash + + +root_dir="/home/phoenix/programming/python/experiments/twilo_demo" + +echo "Root directory: $root_dir" + +if [[ ! -d "$root_dir" ]]; then + echo "Root directory does not exist. Exiting" + exit +fi + +config_path="twilio_details.json" +numbers_part="60" +numbers_path="numbers/numbers-$numbers_part.json" +# numbers_path="numbers/test_number.json" +type_message="json" +messages_path="messages/message-$numbers_part.json" +send_message="True" + +echo "Changing directory to $root_dir" +cd $root_dir + +# source env/bin/activate +# echo "Activated virtual environment" + +# Old +# python main.py "$numbers_path" "$type_message" "$messages_path" "$send_message" +./twilio_sender "$config_path" "$numbers_path" "$messages_path" + +echo "Done" diff --git a/send_messages.sh b/send_messages.sh deleted file mode 100755 index d747bae..0000000 --- a/send_messages.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/bash - - -root_dir="/home/phoenix/programming/python/experiments/twilo_demo" - -numbers_part="60" -numbers_path="numbers/numbers-$numbers_part.json" -# numbers_path="numbers/test_number.json" -type_message="json" -messages_path="messages/message-$numbers_part.json" -send_message="True" - -echo "Changing directory to $root_dir" -cd $root_dir - -source env/bin/activate -echo "Activated virtual environment" - -python main.py "$numbers_path" "$type_message" "$messages_path" "$send_message" - -echo "Done" diff --git a/sender/send_message.go b/sender/send_message.go new file mode 100644 index 0000000..0329881 --- /dev/null +++ b/sender/send_message.go @@ -0,0 +1,61 @@ +package sender + +import ( + "encoding/json" + "fmt" + "time" + + "git.kundeng.us/phoenix/twilio_sender/models" + "github.com/twilio/twilio-go" + twilioApi "github.com/twilio/twilio-go/rest/api/v2010" +) + +func SendMessages(config models.TwiloDetails, numbers [](models.Number), message models.Message, secondInterval int) { + fmt.Println("Sending message") + + longSleepLimit := 5 + count := 0 + + client := initClient(config) + + for _, number := range numbers { + numberToSend := number + + if numberToSend.IsEmpty() { + fmt.Println("Empty number") + continue + } + + fmt.Println(numberToSend) + + params := &twilioApi.CreateMessageParams{} + params.SetTo(numberToSend.PhoneNumber) + params.SetFrom(config.Number) + params.SetBody(message.Text) + + fmt.Println("Sending to: " + numberToSend.PhoneNumber) + + resp, err := client.Api.CreateMessage(params) + if err != nil { + fmt.Println("Error sending SMS message: " + err.Error()) + } else { + response, _ := json.Marshal(*resp) + fmt.Println("Response: " + string(response)) + } + + if count%longSleepLimit == 0 { + // sleep for X seconds + tme := 5 * time.Second + time.Sleep(tme) + } else { + time.Sleep(time.Second) + } + } +} + +func initClient(config models.TwiloDetails) *twilio.RestClient { + return twilio.NewRestClientWithParams(twilio.ClientParams{ + Username: config.AccountSID, + Password: config.AuthToken, + }) +} diff --git a/twilio_details.json b/twilio_details.json index 98d98d5..f787f20 100644 --- a/twilio_details.json +++ b/twilio_details.json @@ -1,8 +1,6 @@ -[ - { - "account_sid": "account_sid", - "service_sid": "service_sid", - "auth_token": "auth_token", - "number": "0123456789" - } -] +{ + "account_sid": "account_sid", + "service_sid": "service_sid", + "auth_token": "auth_token", + "number": "0123456789" +} diff --git a/util/util.go b/util/util.go new file mode 100644 index 0000000..b7a062a --- /dev/null +++ b/util/util.go @@ -0,0 +1,66 @@ +package util + +import "encoding/json" +import "fmt" +import "os" +import "strings" + +import ( + "git.kundeng.us/phoenix/twilio_sender/models" +) + +func GetPaths(args []string) (configPath string, numberPath string, messagePath string) { + configPath = args[1] + numberPath = args[2] + messagePath = args[3] + + return +} + +func ParseNumbers(filepath string) (numbers [](models.Number)) { + nmbrs := [](string){} + + content, err := os.ReadFile(filepath) + + if err != nil { + fmt.Println("An error occurred") + return numbers + } + + err = json.Unmarshal(content, &nmbrs) + + if err != nil { + fmt.Println("An error occurred") + return numbers + } + + for _, nbr := range nmbrs { + var number models.Number + number.PhoneNumber = nbr + numbers = append(numbers, number) + } + + return numbers +} + +func ParseMessage(filepath string) (message models.Message) { + msg := [](string){""} + + content, err := os.ReadFile(filepath) + + if err != nil { + fmt.Println("An error occurred") + return + } + + err = json.Unmarshal(content, &msg) + + if err != nil { + fmt.Println("An error occurred") + return + } + + message.Text = strings.Join(msg, "") + + return +}