Browse Source

feat: leave group (#70)

* feat: add leave group

* feat: add ui leave group

* chore: update docs

* feat: update UI

* fix: update title
pull/75/head v4.7.0
Aldino Kemal 3 years ago
committed by GitHub
parent
commit
5a79c0a3ca
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 38
      docs/openapi.yaml
  2. 5
      readme.md
  3. 2
      src/config/settings.go
  4. 7
      src/domains/group/group.go
  5. 8
      src/go.mod
  6. 15
      src/go.sum
  7. 20
      src/internal/rest/group.go
  8. 4
      src/pkg/whatsapp/whatsapp.go
  9. 22
      src/services/group.go
  10. 32
      src/validations/group_validation.go
  11. 51
      src/views/index.html

38
docs/openapi.yaml

@ -1,7 +1,7 @@
openapi: 3.0.0
info:
title: WhatsApp API MultiDevice
version: 3.5.0
version: 3.6.0
description: This API is used for sending whatsapp via API
servers:
- url: http://localhost:3000
@ -627,6 +627,40 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorInternalServer'
/group/leave:
post:
operationId: leaveGroup
tags:
- group
summary: Leave group
requestBody:
content:
multipart/form-data:
schema:
type: object
properties:
group_id:
type: string
example: '120363024512399999@g.us'
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/GenericResponse'
'400':
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorBadRequest'
'500':
description: Internal Server Error
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorInternalServer'
components:
schemas:
@ -854,7 +888,7 @@ components:
description: 'HTTP Status Code'
message:
type: string
example: phone cannot be blank
example: field cannot be blank
description: 'Detail error message'
results:
type: object

5
readme.md

@ -112,6 +112,7 @@ API using [openapi-generator](https://openapi-generator.tech/#try)
| ✅ | Revoke Message | POST | /message/:message_id/revoke |
| ✅ | React Message | POST | /message/:message_id/react |
| ✅ | Join Group With Link | POST | /group/join-with-link |
| ✅ | Leave Group | POST | /group/leave |
```
✅ = Available
@ -120,7 +121,7 @@ API using [openapi-generator](https://openapi-generator.tech/#try)
### App User Interface
1. Homepage ![Homepage](https://i.ibb.co/Gn32yhJ/homepage.png)
1. Homepage ![Homepage](https://i.ibb.co/vPpnVdg/homepage.png)
2. Login ![Login](https://i.ibb.co/jkcB15R/login.png)
3. Send Message ![Send Message](https://i.ibb.co/rc3NXMX/send-message.png)
4. Send Image ![Send Image](https://i.ibb.co/BcFL3SD/send-image.png)
@ -133,7 +134,7 @@ API using [openapi-generator](https://openapi-generator.tech/#try)
11. User Info ![User Info](https://i.ibb.co/3zjX6Cz/user-info.png)
12. User Avatar ![User Avatar](https://i.ibb.co/ZmJZ4ZW/search-avatar.png)
13. My Privacy ![My Privacy](https://i.ibb.co/Cw1sMQz/my-privacy.png)
14. My Group ![My Group](https://i.ibb.co/B6rW8Sh/list-group.png)
14. My Group ![My Group](https://i.ibb.co/WB268Xy/list-group.png)
15. Auto Reply ![Auto Reply](https://i.ibb.co/D4rTytX/IMG-20220517-162500.jpg)
16. Basic Auth Prompt ![Basic Auth](https://i.ibb.co/PDjQ92W/Screenshot-2022-11-06-at-14-06-29.png)

2
src/config/settings.go

@ -6,7 +6,7 @@ import (
)
var (
AppVersion = "v4.6.2"
AppVersion = "v4.7.0"
AppPort = "3000"
AppDebug = false
AppOs = fmt.Sprintf("AldinoKemal")

7
src/domains/group/group.go

@ -3,13 +3,14 @@ package group
import "context"
type IGroupService interface {
JoinGroupWithLink(ctx context.Context, request JoinGroupWithLinkRequest) (response JoinGroupWithLinkResponse, err error)
JoinGroupWithLink(ctx context.Context, request JoinGroupWithLinkRequest) (groupID string, err error)
LeaveGroup(ctx context.Context, request LeaveGroupRequest) (err error)
}
type JoinGroupWithLinkRequest struct {
Link string `json:"link" form:"link"`
}
type JoinGroupWithLinkResponse struct {
JID string `json:"jid"`
type LeaveGroupRequest struct {
GroupID string `json:"group_id" form:"group_id"`
}

8
src/go.mod

@ -16,9 +16,9 @@ require (
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/spf13/cobra v1.7.0
github.com/stretchr/testify v1.8.2
github.com/valyala/fasthttp v1.46.0
github.com/valyala/fasthttp v1.47.0
go.mau.fi/libsignal v0.1.0
go.mau.fi/whatsmeow v0.0.0-20230421200254-eb71a6b59083
go.mau.fi/whatsmeow v0.0.0-20230427180258-7f679583b39b
google.golang.org/protobuf v1.30.0
)
@ -27,7 +27,7 @@ require (
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fasthttp/websocket v1.5.2 // indirect
github.com/fasthttp/websocket v1.5.3 // indirect
github.com/gobuffalo/here v0.6.7 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
@ -45,7 +45,7 @@ require (
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
golang.org/x/crypto v0.8.0 // indirect
golang.org/x/sys v0.7.0 // indirect
golang.org/x/sys v0.8.0 // indirect
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

15
src/go.sum

@ -120,8 +120,8 @@ github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.
github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws=
github.com/fasthttp/websocket v1.5.2 h1:KdCb0EpLpdJpfE3IPA5YLK/aYBO3dhZcvwxz6tXe2LQ=
github.com/fasthttp/websocket v1.5.2/go.mod h1:S0KC1VBlx1SaXGXq7yi1wKz4jMub58qEnHQG9oHuqBw=
github.com/fasthttp/websocket v1.5.3 h1:TPpQuLwJYfd4LJPXvHDYPMFWbLjsT91n3GpWtCQtdek=
github.com/fasthttp/websocket v1.5.3/go.mod h1:46gg/UBmTU1kUaTcwQXpUxtRwG2PvIZYeA8oL6vF3Fs=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
@ -407,8 +407,8 @@ github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqri
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.45.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA=
github.com/valyala/fasthttp v1.46.0 h1:6ZRhrFg8zBXTRYY6vdzbFhqsBd7FVv123pV2m9V87U4=
github.com/valyala/fasthttp v1.46.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA=
github.com/valyala/fasthttp v1.47.0 h1:y7moDoxYzMooFpT5aHgNgVOQDrS3qlkfiP9mDtGGK9c=
github.com/valyala/fasthttp v1.47.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA=
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
github.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=
@ -424,8 +424,8 @@ go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3
go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs=
go.mau.fi/libsignal v0.1.0 h1:vAKI/nJ5tMhdzke4cTK1fb0idJzz1JuEIpmjprueC+c=
go.mau.fi/libsignal v0.1.0/go.mod h1:R8ovrTezxtUNzCQE5PH30StOQWWeBskBsWE55vMfY9I=
go.mau.fi/whatsmeow v0.0.0-20230421200254-eb71a6b59083 h1:9m92f4MviAPv/PQgLZ7AO0E15goh/4B8uzvS3kyDpeg=
go.mau.fi/whatsmeow v0.0.0-20230421200254-eb71a6b59083/go.mod h1:+ObGpFE6cbbY4hKc1FmQH9MVfqaemmlXGXSnwDvCOyE=
go.mau.fi/whatsmeow v0.0.0-20230427180258-7f679583b39b h1:VSSc37LfKMt7HYeu9NibbSRwELFN5wc/hreGyY+z+o4=
go.mau.fi/whatsmeow v0.0.0-20230427180258-7f679583b39b/go.mod h1:+ObGpFE6cbbY4hKc1FmQH9MVfqaemmlXGXSnwDvCOyE=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@ -641,8 +641,9 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=

20
src/internal/rest/group.go

@ -13,6 +13,7 @@ type Group struct {
func InitRestGroup(app *fiber.App, service domainGroup.IGroupService) Group {
rest := Group{Service: service}
app.Post("/group/join-with-link", rest.JoinGroupWithLink)
app.Post("/group/leave", rest.LeaveGroup)
return rest
}
@ -28,6 +29,23 @@ func (controller *Group) JoinGroupWithLink(c *fiber.Ctx) error {
Status: 200,
Code: "SUCCESS",
Message: "Success joined group",
Results: response,
Results: map[string]string{
"group_id": response,
},
})
}
func (controller *Group) LeaveGroup(c *fiber.Ctx) error {
var request domainGroup.LeaveGroupRequest
err := c.BodyParser(&request)
utils.PanicIfNeeded(err)
err = controller.Service.LeaveGroup(c.UserContext(), request)
utils.PanicIfNeeded(err)
return c.JSON(utils.ResponseData{
Status: 200,
Code: "SUCCESS",
Message: "Success leave group",
})
}

4
src/pkg/whatsapp/whatsapp.go

@ -98,9 +98,9 @@ func ParseJID(arg string) (types.JID, error) {
}
}
func ValidateJidWithLogin(waCli *whatsmeow.Client, phone string) (types.JID, error) {
func ValidateJidWithLogin(waCli *whatsmeow.Client, jid string) (types.JID, error) {
MustLogin(waCli)
return ParseJID(phone)
return ParseJID(jid)
}
func InitWaDB() *sqlstore.Container {

22
src/services/group.go

@ -4,6 +4,7 @@ import (
"context"
domainGroup "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/group"
"github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/whatsapp"
"github.com/aldinokemal/go-whatsapp-web-multidevice/validations"
"go.mau.fi/whatsmeow"
)
@ -17,13 +18,28 @@ func NewGroupService(waCli *whatsmeow.Client) domainGroup.IGroupService {
}
}
func (service groupService) JoinGroupWithLink(_ context.Context, request domainGroup.JoinGroupWithLinkRequest) (response domainGroup.JoinGroupWithLinkResponse, err error) {
func (service groupService) JoinGroupWithLink(ctx context.Context, request domainGroup.JoinGroupWithLinkRequest) (groupID string, err error) {
if err = validations.ValidateJoinGroupWithLink(ctx, request); err != nil {
return groupID, err
}
whatsapp.MustLogin(service.WaCli)
jid, err := service.WaCli.JoinGroupWithLink(request.Link)
if err != nil {
return
}
response.JID = jid.String()
return response, nil
return jid.String(), nil
}
func (service groupService) LeaveGroup(ctx context.Context, request domainGroup.LeaveGroupRequest) (err error) {
if err = validations.ValidateLeaveGroup(ctx, request); err != nil {
return err
}
JID, err := whatsapp.ValidateJidWithLogin(service.WaCli, request.GroupID)
if err != nil {
return err
}
return service.WaCli.LeaveGroup(JID)
}

32
src/validations/group_validation.go

@ -0,0 +1,32 @@
package validations
import (
"context"
domainGroup "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/group"
pkgError "github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/error"
validation "github.com/go-ozzo/ozzo-validation/v4"
)
func ValidateJoinGroupWithLink(ctx context.Context, request domainGroup.JoinGroupWithLinkRequest) error {
err := validation.ValidateStructWithContext(ctx, &request,
validation.Field(&request.Link, validation.Required),
)
if err != nil {
return pkgError.ValidationError(err.Error())
}
return nil
}
func ValidateLeaveGroup(ctx context.Context, request domainGroup.LeaveGroupRequest) error {
err := validation.ValidateStructWithContext(ctx, &request,
validation.Field(&request.GroupID, validation.Required),
)
if err != nil {
return pkgError.ValidationError(err.Error())
}
return nil
}

51
src/views/index.html

@ -15,7 +15,7 @@
<script src="https://unpkg.com/vue@3"></script>
<script src="https://unpkg.com/axios@1.1.2/dist/axios.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment.min.js"></script>
<title>Whatsapp Web Multi</title>
<title>Whatsapp API Multi Device {{ .AppVersion }}</title>
<style>
.container {
@ -605,6 +605,7 @@
<th>Name</th>
<th>Participants</th>
<th>Created At</th>
<th>Action</th>
</tr>
</thead>
<tbody v-if="data_groups != null">
@ -613,6 +614,9 @@
<td>[[ g.Name ]]</td>
<td>[[ g.Participants.length ]]</td>
<td>[[ formatDate(g.GroupCreated) ]]</td>
<td>
<button class="ui red tiny button" @click="handleLeaveGroup(g.JID)">Leave</button>
</td>
</tr>
</tbody>
</table>
@ -1372,17 +1376,54 @@
methods: {
async groupModal() {
try {
this.dtClear()
await this.groupApi();
$('#modalUserGroup').modal('show');
$('#user_groups_table').DataTable().destroy();
$('#user_groups_table').DataTable({
"pageLength": 100,
});
this.dtRebuild()
showSuccessInfo("Groups fetched")
} catch (err) {
showErrorInfo(err)
}
},
dtClear() {
$('#user_groups_table').DataTable().destroy();
},
dtRebuild() {
$('#user_groups_table').DataTable({
"pageLength": 100,
"reloadData": true,
}).draw();
},
async handleLeaveGroup(group_id) {
try {
const ok = confirm("Are you sure to leave this group?");
if (!ok) return;
await this.leaveGroupApi(group_id);
this.dtClear()
await this.groupApi();
this.dtRebuild()
showSuccessInfo("Group left")
} catch (err) {
showErrorInfo(err)
}
},
leaveGroupApi(group_id) {
return new Promise(async (resolve, reject) => {
try {
let payload = new FormData();
payload.append("group_id", group_id)
await http.post(`/group/leave`, payload)
resolve()
} catch (error) {
if (error.response) {
reject(error.response.data.message)
} else {
reject(error.message)
}
}
})
},
groupApi() {
return new Promise(async (resolve, reject) => {
try {

Loading…
Cancel
Save