Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions cmd/testserver/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package main

import (
"bytes"
"encoding/json"
"image"
"image/color"
"image/draw"
"image/jpeg"
"net/http"
"strconv"
)

var (
testTextURL1 = "/text/1"
testTextURL2 = "/text/2"
testTextURL3 = "/text/3"
testImageURL1 = "/image/1"
)

// sets up required value here
func main() {
http.HandleFunc(testTextURL1, func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write(json.RawMessage(`{"message": "1"}`))
})
http.HandleFunc(testTextURL2, func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write(json.RawMessage(`{"massage": "2"}`))
})
http.HandleFunc(testImageURL1, func(w http.ResponseWriter, r *http.Request) {
m := image.NewRGBA(image.Rect(0, 0, 240, 240))
blue := color.RGBA{0, 0, 255, 255}
draw.Draw(m, m.Bounds(), &image.Uniform{blue}, image.ZP, draw.Src)
buffer := new(bytes.Buffer)
err := jpeg.Encode(buffer, m, nil)
if err != nil {
return
}
w.Header().Set("Content-Type", "image/jpeg")
w.Header().Set("Content-Length", strconv.Itoa(len(buffer.Bytes())))
w.Write(buffer.Bytes())
})

http.ListenAndServe(":6969", nil)
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/Rhymen/go-whatsapp v0.1.0
github.com/aws/aws-sdk-go v1.34.25
github.com/goccy/go-yaml v1.8.2
github.com/gorilla/mux v1.8.0
github.com/imroc/req v0.3.0
github.com/joho/godotenv v1.3.0
github.com/robfig/cron/v3 v3.0.1
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ github.com/goccy/go-yaml v1.8.2/go.mod h1:wS4gNoLalDSJxo/SpngzPQ2BN4uuZVLCmbM4S3
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.0 h1:kbxbvI4Un1LUWKxufD+BiE6AEExYYgkQLQmLFqA1LFk=
github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/imroc/req v0.3.0 h1:3EioagmlSG+z+KySToa+Ylo3pTFZs+jh3Brl7ngU12U=
Expand Down
56 changes: 37 additions & 19 deletions main.go
Original file line number Diff line number Diff line change
@@ -1,28 +1,27 @@
package main

import (
"bytes"
"encoding/gob"
"math/rand"
"os/signal"

"fmt"
"log"
"math/rand"
"os"
"time"
"os/signal"
"strings"
"syscall"
"time"

whatsapp "github.com/Rhymen/go-whatsapp"
cron "github.com/robfig/cron/v3"
godotenv "github.com/joho/godotenv"
cron "github.com/robfig/cron/v3"
// "github.com/davecgh/go-spew/spew"
)

type waHandler struct {
c *whatsapp.Conn
startTime uint64
chats map[string]struct{}

chats map[string]struct{}
}

var Schedules []Schedule
Expand All @@ -34,9 +33,9 @@ var BuildCommands []BuildCommand
var BuildGreetings []BuildGreeting

func init() {
if err := godotenv.Load(); err != nil {
log.Print("No .env file found")
}
if err := godotenv.Load(); err != nil {
log.Print("No .env file found")
}
}

func main() {
Expand Down Expand Up @@ -79,20 +78,20 @@ func main() {
if !pong || err != nil {
log.Fatalf("error pinging in: %v\n", err)
}

schedule := readScheduleFiles()
if !schedule {
log.Println("Can't read Schedule Files")
return
}

for index := range(Schedules) {
for index := range Schedules {
phone_numbers := Schedules[index].ExpectedUsers
process_name := Schedules[index].ProcessName
log.Println("Read the schedule to run with cron formula " + Schedules[index].Rule)
jadwal.AddFunc(Schedules[index].Rule, func(){
jadwal.AddFunc(Schedules[index].Rule, func() {
log.Println("Run Schedule " + process_name)
for pIndex := range(phone_numbers) {
for pIndex := range phone_numbers {
go sendMessage(wac, Schedules[index].Message, phone_numbers[pIndex])
}
})
Expand All @@ -118,7 +117,6 @@ func main() {
}

func sendMessage(wac *whatsapp.Conn, message string, RJID string) {

msg := whatsapp.TextMessage{
Info: whatsapp.MessageInfo{
RemoteJid: RJID,
Expand All @@ -135,13 +133,13 @@ func sendMessage(wac *whatsapp.Conn, message string, RJID string) {
// if min is 2 then 2 + (50/2) = 27
// if max is 4 then 4 + (50/2) = 29
// if 29 > 5 then max is 5
min = min + (len(kata)/2)
max = max + (len(kata)/2)
min = min + (len(kata) / 2)
max = max + (len(kata) / 2)
if max > limit_max {
min = limit_max - 2
max = limit_max
}
waitSec := rand.Intn(max-min)+min
waitSec := rand.Intn(max-min) + min
log.Printf("Randomly paused %d for throtling", waitSec)
wac.Presence(RJID, whatsapp.PresenceComposing)
time.Sleep(time.Duration(waitSec) * time.Second)
Expand All @@ -154,6 +152,26 @@ func sendMessage(wac *whatsapp.Conn, message string, RJID string) {
}
}

func sendImageMessage(wac *whatsapp.Conn, img ImageMessage, RJID string) {
msg := whatsapp.ImageMessage{
Info: whatsapp.MessageInfo{
RemoteJid: RJID,
},
Caption: img.Caption,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ronaudinho this will be much better if the caption from the text message
like example if the coral have commands message

...
     message: "Let's start how cool you are {{https://miro.medium.com/max/356/1*EnF9uIN_u2_X7ey24lB7Tg.png}} man"
...

so this will returned "Let's start how cool you are man"

Thumbnail: img.Thumbnail,
Type: img.Type,
Content: bytes.NewReader(img.Content),
}

wac.Presence(RJID, whatsapp.PresenceComposing)
msgId, err := wac.Send(msg)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ronaudinho I have this kind of bug from another bot that I develop by sending image the wac.send(msg) always returned

error sending message: message sending responded with %!d(float64=400)

I'm using this image https://miro.medium.com/max/356/1*EnF9uIN_u2_X7ey24lB7Tg.png
weirldly return "blank" like this
image

if err != nil {
fmt.Fprintf(os.Stderr, "error sending message: %v", err)
} else {
fmt.Println("Message Sent -> ID : " + msgId)
}
}

func login(wac *whatsapp.Conn, phone_number string) error {
session, err := readSession(phone_number)
if err == nil {
Expand Down Expand Up @@ -236,4 +254,4 @@ func (h *waHandler) HandleError(err error) {
} else {
log.Printf("error occoured: %v\n", err)
}
}
}
85 changes: 67 additions & 18 deletions parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ package main
import (
"encoding/json"
"fmt"
req "github.com/imroc/req"
"io/ioutil"
"os"
"strings"

wa "github.com/Rhymen/go-whatsapp"
"github.com/imroc/req"
)

var (
Expand All @@ -23,38 +25,85 @@ var (
errRespNotSupported = func(dst string) string { return fmt.Sprintf("[resp not supported error: %s]", dst) }
)

type ImageMessage struct {
Caption string
Thumbnail []byte // set to nil, add on your own as I have no way to test this
Type string
Content []byte
}

// nemoParser parses pesan for URL in {{url}} format
// if no URL is found in pesan, pesan is returned as is
// if URL is found, try POST request to url
// if response contains supported JSON, pesan will be replaced with response
// currently only supports JSON response with message key
func nemoParser(pesan string, Sessions Session) (string, error) {
// if response contains image(s), image with caption will be sent instead of text
// if there are multiple URL that return images, it will send all images, only captioning the last image
// currently if duplicate URLs exist, the request will be repeated
func nemoParser(pesan string, Sessions Session) (*wa.TextMessage, map[int]ImageMessage, error) {
var countImg int
txt := &wa.TextMessage{}
mapImgs := make(map[int]ImageMessage)
urlCount := strings.Count(pesan, "{{")
if urlCount == 0 {
return pesan, nil
txt.Text = pesan
return txt, nil, nil
}
for i := 0; i < urlCount; i++ {
url := between(pesan, "{{", "}}")
r, err := req.Post(url, req.BodyJSON(Sessions))
resp, err := req.Post(url, req.BodyJSON(Sessions))
if err != nil {
pesan = strings.Replace(pesan, fmt.Sprintf("{{%s}}", url), errReqErr(url), -1)
pesan = strings.Replace(pesan, fmt.Sprintf("{{%s}}", url), errReqErr(url), 1)
}

var m map[string]interface{}
r.ToJSON(&m)
// check if json/image/else
switch ct := resp.Response().Header.Get("Content-Type"); ct {
case "application/json":
var m map[string]interface{}
resp.ToJSON(&m)

// TODO maybe calls this in main to setup
sk := supportKey(os.Getenv(defSupportedRespKeysConfig))
k := lookupKey(m, sk)
if k == "" {
pesan = strings.Replace(pesan, fmt.Sprintf("{{%s}}", url), errRespNotSupported(url), -1)
continue
}
if m[k] != "" {
pesan = strings.Replace(pesan, fmt.Sprintf("{{%s}}", url), fmt.Sprintf("%v", m[k]), -1)
// TODO maybe calls this in main to setup
sk := supportKey(os.Getenv(defSupportedRespKeysConfig))
k := lookupKey(m, sk)
if k == "" {
pesan = strings.Replace(pesan, fmt.Sprintf("{{%s}}", url), errRespNotSupported(url), 1)
txt.Text = pesan
continue
}
if m[k] != "" {
pesan = strings.Replace(pesan, fmt.Sprintf("{{%s}}", url), fmt.Sprintf("%v", m[k]), 1)
}
// if there is image, simply remove URL
if len(mapImgs) > 0 {
continue
}
txt.Text = pesan
default:
if strings.Contains(ct, "image") {
b, err := resp.ToBytes()
if err != nil {
pesan = strings.Replace(pesan, fmt.Sprintf("{{%s}}", url), errReqErr(url), 1)
continue
}
pesan = strings.Replace(pesan, fmt.Sprintf("{{%s}}", url), "", 1)
mapImgs[countImg] = ImageMessage{
Content: b,
Type: ct,
}
countImg++
continue
}
pesan = strings.Replace(pesan, fmt.Sprintf("{{%s}}", url), errRespNotSupported(url), 1)
}
}
if len(mapImgs) > 0 {
lastImg := mapImgs[countImg-1]
lastImg.Caption = pesan
mapImgs[countImg-1] = lastImg
return nil, mapImgs, nil
}

return pesan, nil
return txt, nil, nil
}

// lookupKey looks up if key exists in a map based on priority
Expand All @@ -68,7 +117,7 @@ func nemoParser(pesan string, Sessions Session) (string, error) {
func lookupKey(m map[string]interface{}, sup map[string]int) string {
var key string
var prio int
for k, _ := range m {
for k := range m {
for v, p := range sup {
if k != v {
continue
Expand Down
Loading