Browse Source

feat: add customable params (#13)

* feat: set chrome as default platform

feat: change port from setting

* feat: add custom port & debugging

* feat: add custom autoreply message

* chore: update docs

* chore: update docs

* fix: typo
pull/14/head v3.2.0
Aldino Kemal 4 years ago
committed by GitHub
parent
commit
bd59f642db
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 17
      readme.md
  2. 97
      src/cmd/root.go
  3. 17
      src/config/settings.go
  4. 3
      src/go.mod
  5. 4
      src/go.sum
  6. 65
      src/main.go
  7. 4
      src/services/app_service_impl.go
  8. 54
      src/structs/send_struct.go
  9. 48
      src/structs/user_struct.go
  10. 14
      src/utils/whatsapp.go

17
readme.md

@ -1,5 +1,14 @@
## Go Whatsapp API Multi Device Version
### Feature
- Send whatsapp via http API, [docs/openapi.yml](./docs/openapi.yaml) for more details
- Compress image before send
- Customizable port and debug mode
- `--port 8000`
- `--debug true`
- Auto reply message
- `--autoreply="Don't reply this message"`
### Required (without docker)
- Mac OS:
@ -21,6 +30,7 @@
3. run `cd src`
4. run `go run main.go`
5. open `http://localhost:3000`
6. run `go run main.go --help` for more detail flags
#### Docker (you don't need to install in required)
@ -39,12 +49,17 @@
2. Windows (CMD, not PowerShell): `pkger.exe && go build -o whatsapp.exe`
6. run
1. Linux & MacOS: `./whatsapp`
1. run `./whatsapp --help` for more detail flags
2. Windows: `.\whatsapp.exe` or you can double-click it
1. run `.\whatsapp.exe --help` for more detail flags
7. open `http://localhost:3000` in browser
## Production Mode (without config)
### Production Mode (docker)
- `docker run --publish 3000:3000 --restart=always aldinokemal2104/go-whatsapp-web-multidevice`
### Production Mode (binary)
- download binary from [release](https://github.com/aldinokemal/go-whatsapp-web-multidevice/releases)
You can fork or edit this source code !
### Current API

97
src/cmd/root.go

@ -0,0 +1,97 @@
package cmd
import (
"fmt"
"github.com/aldinokemal/go-whatsapp-web-multidevice/config"
"github.com/aldinokemal/go-whatsapp-web-multidevice/controllers"
"github.com/aldinokemal/go-whatsapp-web-multidevice/middleware"
"github.com/aldinokemal/go-whatsapp-web-multidevice/services"
"github.com/aldinokemal/go-whatsapp-web-multidevice/utils"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
"github.com/gofiber/fiber/v2/middleware/logger"
"github.com/gofiber/template/html"
"github.com/markbates/pkger"
_ "github.com/mattn/go-sqlite3"
"log"
"os"
"github.com/spf13/cobra"
)
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Short: "Send free whatsapp API",
Long: `This application is from clone https://github.com/aldinokemal/go-whatsapp-web-multidevice,
you can send whatsapp over http api but your whatsapp account have to be multi device version`,
Run: runRest,
}
func init() {
rootCmd.CompletionOptions.DisableDefaultCmd = true
rootCmd.PersistentFlags().StringVarP(&config.AppPort, "port", "p", config.AppPort, "change port number with --port <number> | example: --port=8080")
rootCmd.PersistentFlags().BoolVarP(&config.AppDebug, "debug", "d", config.AppDebug, "hide or displaying log with --debug <true/false> | example: --debug=true")
rootCmd.PersistentFlags().StringVarP(&config.WhatsappAutoReplyMessage, "autoreply", "", config.WhatsappAutoReplyMessage, `auto reply when received message --autoreply <string> | example: --autoreply="Don't reply this message"`)
}
func runRest(cmd *cobra.Command, args []string) {
if config.AppDebug {
config.WhatsappLogLevel = "DEBUG"
}
// TODO: Init Rest App
//preparing folder if not exist
err := utils.CreateFolder(config.PathQrCode, config.PathSendItems)
if err != nil {
log.Fatalln(err)
}
engine := html.NewFileSystem(pkger.Dir("/views"), ".html")
app := fiber.New(fiber.Config{
Views: engine,
BodyLimit: 10 * 1024 * 1024,
})
app.Static("/statics", "./statics")
app.Use(middleware.Recovery())
if config.AppDebug {
app.Use(logger.New())
}
app.Use(cors.New(cors.Config{
AllowOrigins: "*",
AllowHeaders: "Origin, Content-Type, Accept",
}))
db := utils.InitWaDB()
cli := utils.InitWaCLI(db)
// Service
appService := services.NewAppService(cli)
sendService := services.NewSendService(cli)
userService := services.NewUserService(cli)
// Controller
appController := controllers.NewAppController(appService)
sendController := controllers.NewSendController(sendService)
userController := controllers.NewUserController(userService)
appController.Route(app)
sendController.Route(app)
userController.Route(app)
app.Get("/", func(ctx *fiber.Ctx) error {
return ctx.Render("index", fiber.Map{"AppHost": fmt.Sprintf("%s://%s", ctx.Protocol(), ctx.Hostname())})
})
err = app.Listen(":" + config.AppPort)
if err != nil {
log.Fatalln("Failed to start: ", err.Error())
}
}
// Execute adds all child commands to the root command and sets flags appropriately.
func Execute() {
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
}
}

17
src/config/settings.go

@ -1,5 +1,16 @@
package config
const PathQrCode = "statics/images/qrcode"
const PathSendItems = "statics/images/senditems"
const DBName = "hydrogenWaCli.db"
type Browser string
var (
AppPort string = "3000"
AppDebug bool = false
PathQrCode string = "statics/images/qrcode"
PathSendItems string = "statics/images/senditems"
DBName string = "hydrogenWaCli.db"
WhatsappLogLevel string = "ERROR"
WhatsappAutoReplyMessage string
)

3
src/go.mod

@ -10,6 +10,7 @@ require (
github.com/markbates/pkger v0.17.1
github.com/mattn/go-sqlite3 v1.14.11
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/spf13/cobra v1.4.0
github.com/stretchr/testify v1.7.0
go.mau.fi/whatsmeow v0.0.0-20220514092657-a05359d4385a
google.golang.org/protobuf v1.28.0
@ -22,8 +23,10 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/gobuffalo/here v0.6.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/klauspost/compress v1.14.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.33.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect

4
src/go.sum

@ -253,6 +253,7 @@ github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpT
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
@ -349,7 +350,10 @@ github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY52
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4=
github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q=
github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=

65
src/main.go

@ -1,64 +1,11 @@
/*
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
*/
package main
import (
"fmt"
"github.com/aldinokemal/go-whatsapp-web-multidevice/config"
"github.com/aldinokemal/go-whatsapp-web-multidevice/controllers"
"github.com/aldinokemal/go-whatsapp-web-multidevice/middleware"
"github.com/aldinokemal/go-whatsapp-web-multidevice/services"
"github.com/aldinokemal/go-whatsapp-web-multidevice/utils"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
"github.com/gofiber/fiber/v2/middleware/logger"
"github.com/gofiber/template/html"
"github.com/markbates/pkger"
_ "github.com/mattn/go-sqlite3"
"log"
)
import "github.com/aldinokemal/go-whatsapp-web-multidevice/cmd"
func main() {
// preparing folder if not exist
err := utils.CreateFolder(config.PathQrCode, config.PathSendItems)
if err != nil {
log.Fatalln(err)
}
engine := html.NewFileSystem(pkger.Dir("/views"), ".html")
app := fiber.New(fiber.Config{
Views: engine,
BodyLimit: 10 * 1024 * 1024,
})
app.Static("/statics", "./statics")
app.Use(middleware.Recovery())
app.Use(logger.New())
app.Use(cors.New(cors.Config{
AllowOrigins: "*",
AllowHeaders: "Origin, Content-Type, Accept",
}))
db := utils.InitWaDB()
cli := utils.InitWaCLI(db)
// Service
appService := services.NewAppService(cli)
sendService := services.NewSendService(cli)
userService := services.NewUserService(cli)
// Controller
appController := controllers.NewAppController(appService)
sendController := controllers.NewSendController(sendService)
userController := controllers.NewUserController(userService)
appController.Route(app)
sendController.Route(app)
userController.Route(app)
app.Get("/", func(ctx *fiber.Ctx) error {
return ctx.Render("index", fiber.Map{"AppHost": fmt.Sprintf("%s://%s", ctx.Protocol(), ctx.Hostname())})
})
err = app.Listen(":3000")
if err != nil {
log.Fatalln("Failed to start: ", err.Error())
}
cmd.Execute()
}

4
src/services/app_service_impl.go

@ -88,7 +88,7 @@ func (service AppServiceImpl) Logout(c *fiber.Ctx) (err error) {
if err != nil {
panic(err)
}
fmt.Println(files)
for _, f := range files {
err = os.Remove(f)
if err != nil {
@ -100,7 +100,7 @@ func (service AppServiceImpl) Logout(c *fiber.Ctx) (err error) {
if err != nil {
panic(err)
}
fmt.Println(qrImages)
for _, f := range qrImages {
err = os.Remove(f)
if err != nil {

54
src/structs/send_struct.go

@ -1,61 +1,9 @@
package structs
import (
"go.mau.fi/whatsmeow/types"
"mime/multipart"
)
// ============================== USER ==============================
type UserInfoRequest struct {
Phone string `json:"phone" query:"phone"`
}
type UserInfoResponseDataDevice struct {
User string
Agent uint8
Device string
Server string
AD bool
}
type UserInfoResponseData struct {
VerifiedName string `json:"verified_name"`
Status string `json:"status"`
PictureID string `json:"picture_id"`
Devices []UserInfoResponseDataDevice `json:"devices"`
}
type UserInfoResponse struct {
Data []UserInfoResponseData `json:"data"`
}
type UserAvatarRequest struct {
Phone string `json:"phone" query:"phone"`
}
type UserAvatarResponse struct {
URL string `json:"url"`
ID string `json:"id"`
Type string `json:"type"`
}
type UserMyPrivacySettingResponse struct {
GroupAdd string `json:"group_add"`
LastSeen string `json:"last_seen"`
Status string `json:"status"`
Profile string `json:"profile"`
ReadReceipts string `json:"read_receipts"`
}
type UserMyListGroupsResponse struct {
Data []types.GroupInfo `json:"data"`
}
// ============================== END USER ==============================
// ============================== SEND ==============================
type SendType string
const TypeUser SendType = "user"
@ -92,5 +40,3 @@ type SendFileRequest struct {
type SendFileResponse struct {
Status string `json:"status"`
}
// ============================== END SEND ==============================

48
src/structs/user_struct.go

@ -0,0 +1,48 @@
package structs
import "go.mau.fi/whatsmeow/types"
type UserInfoRequest struct {
Phone string `json:"phone" query:"phone"`
}
type UserInfoResponseDataDevice struct {
User string
Agent uint8
Device string
Server string
AD bool
}
type UserInfoResponseData struct {
VerifiedName string `json:"verified_name"`
Status string `json:"status"`
PictureID string `json:"picture_id"`
Devices []UserInfoResponseDataDevice `json:"devices"`
}
type UserInfoResponse struct {
Data []UserInfoResponseData `json:"data"`
}
type UserAvatarRequest struct {
Phone string `json:"phone" query:"phone"`
}
type UserAvatarResponse struct {
URL string `json:"url"`
ID string `json:"id"`
Type string `json:"type"`
}
type UserMyPrivacySettingResponse struct {
GroupAdd string `json:"group_add"`
LastSeen string `json:"last_seen"`
Status string `json:"status"`
Profile string `json:"profile"`
ReadReceipts string `json:"read_receipts"`
}
type UserMyListGroupsResponse struct {
Data []types.GroupInfo `json:"data"`
}

14
src/utils/whatsapp.go

@ -27,8 +27,6 @@ var (
startupTime = time.Now().Unix()
)
const logLevel = "DEBUG"
func GetPlatformName(deviceID int) string {
switch deviceID {
case 2:
@ -79,8 +77,8 @@ func ParseJID(arg string) (types.JID, bool) {
func InitWaDB() *sqlstore.Container {
// Running Whatsapp
log = waLog.Stdout("Main", logLevel, true)
dbLog := waLog.Stdout("Database", logLevel, true)
log = waLog.Stdout("Main", config.WhatsappLogLevel, true)
dbLog := waLog.Stdout("Database", config.WhatsappLogLevel, true)
storeContainer, err := sqlstore.New("sqlite3", fmt.Sprintf("file:%s?_foreign_keys=off", config.DBName), dbLog)
if err != nil {
log.Errorf("Failed to connect to database: %v", err)
@ -97,9 +95,9 @@ func InitWaCLI(storeContainer *sqlstore.Container) *whatsmeow.Client {
panic(err)
}
store.CompanionProps.PlatformType = waProto.CompanionProps_UNKNOWN.Enum()
store.CompanionProps.PlatformType = waProto.CompanionProps_CHROME.Enum()
store.CompanionProps.Os = proto.String("AldinoKemal")
cli = whatsmeow.NewClient(device, waLog.Stdout("Client", logLevel, true))
cli = whatsmeow.NewClient(device, waLog.Stdout("Client", config.WhatsappLogLevel, true))
cli.AddEventHandler(handler)
return cli
@ -171,6 +169,10 @@ func handler(rawEvt interface{}) {
}
log.Infof("Saved image in message to %s", path)
}
if config.WhatsappAutoReplyMessage != "" {
_, _ = cli.SendMessage(evt.Info.Sender, "", &waProto.Message{Conversation: proto.String(config.WhatsappAutoReplyMessage)})
}
case *events.Receipt:
if evt.Type == events.ReceiptTypeRead || evt.Type == events.ReceiptTypeReadSelf {
log.Infof("%v was read by %s at %s", evt.MessageIDs, evt.SourceString(), evt.Timestamp)

Loading…
Cancel
Save