Skip to content

Commit

Permalink
feat: add group participants (#132)
Browse files Browse the repository at this point in the history
* feat: add group participant api

* fix: sanitize leave group

* chore: update openapi

* feat: ui add participant

* chore: update docs

* chore: update docs

* chore: update docs
  • Loading branch information
aldinokemal authored Apr 19, 2024
1 parent 21a451c commit e78767d
Show file tree
Hide file tree
Showing 10 changed files with 348 additions and 49 deletions.
90 changes: 87 additions & 3 deletions docs/openapi.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
openapi: 3.0.0
info:
title: WhatsApp API MultiDevice
version: 3.10.1
version: 3.11.0
description: This API is used for sending whatsapp via API
servers:
- url: http://localhost:3000
Expand Down Expand Up @@ -773,7 +773,53 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/SendResponse'
$ref: '#/components/schemas/CreateGroupResponse'
'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'
/group/participants:
post:
operationId: addParticipantToGroup
tags:
- group
summary: Adding more participants to group
requestBody:
content:
application/json:
schema:
type: object
properties:
group_id:
type: string
example: '120363228882361111'
participants:
type: array
items:
type: string
example:
- '6819241294719274'
- '6829241294719274'
- '6839241294719274'
example:
- '6819241294719274'
- '6829241294719274'
- '6839241294719274'
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/AddParticipantToGroupResponse'
'400':
description: Bad Request
content:
Expand Down Expand Up @@ -807,7 +853,7 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/SendResponse'
$ref: '#/components/schemas/GenericResponse'
'400':
description: Bad Request
content:
Expand Down Expand Up @@ -857,6 +903,44 @@ paths:

components:
schemas:
CreateGroupResponse:
type: object
properties:
code:
type: string
example: SUCCESS
message:
type: string
example: Success get list groups
results:
type: object
properties:
group_id:
type: string
example: [email protected]
AddParticipantToGroupResponse:
type: object
properties:
code:
type: string
example: SUCCESS
message:
type: string
example: Success get list groups
results:
type: array
items:
properties:
participant:
type: string
example: '[email protected]'
status:
type: string
example: success
message:
type: string
example: Participant added

UserGroupResponse:
type: object
properties:
Expand Down
61 changes: 31 additions & 30 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,36 +93,37 @@ You can fork or edit this source code !

### Current API

You can check [docs/openapi.yml](./docs/openapi.yaml) for detail API, furthermore you can generate HTTP Client from this
API using [openapi-generator](https://openapi-generator.tech/#try)

| Feature | Menu | Method | URL |
|---------|--------------------------------|--------|-----------------------------|
|| Login | GET | /app/login |
|| Logout | GET | /app/logout |
|| Reconnect | GET | /app/reconnect |
|| User Info | GET | /user/info |
|| User Avatar | GET | /user/avatar |
|| User My Group List | GET | /user/my/groups |
|| User My Privacy Setting | GET | /user/my/privacy |
|| Send Message | POST | /send/message |
|| Send Image | POST | /send/image |
|| Send Audio | POST | /send/audio |
|| Send File | POST | /send/file |
|| Send Video | POST | /send/video |
|| Send Contact | POST | /send/contact |
|| Send Link | POST | /send/link |
|| Send Location | POST | /send/location |
|| Send Poll / Vote | POST | /send/poll |
|| Revoke Message | POST | /message/:message_id/revoke |
|| React Message | POST | /message/:message_id/react |
|| Edit Message | POST | /message/:message_id/update |
|| Join Group With Link | POST | /group/join-with-link |
|| Leave Group | POST | /group/leave |
|| Create Group | POST | /group |
|| Add More Participants in Group | POST | |
|| Remove Participant in Group | POST | |
|| Promote Participant in Group | POST | |
- You can check [docs/openapi.yml](./docs/openapi.yaml) for detail API or paste to [SwaggerEditor](https://editor.swagger.io).
- Furthermore you can generate HTTP Client from this API using [openapi-generator](https://openapi-generator.tech/#try)

| Feature | Menu | Method | URL |
|---------|------------------------------|--------|-----------------------------|
|| Login | GET | /app/login |
|| Logout | GET | /app/logout |
|| Reconnect | GET | /app/reconnect |
|| User Info | GET | /user/info |
|| User Avatar | GET | /user/avatar |
|| User My Group List | GET | /user/my/groups |
|| User My Privacy Setting | GET | /user/my/privacy |
|| Send Message | POST | /send/message |
|| Send Image | POST | /send/image |
|| Send Audio | POST | /send/audio |
|| Send File | POST | /send/file |
|| Send Video | POST | /send/video |
|| Send Contact | POST | /send/contact |
|| Send Link | POST | /send/link |
|| Send Location | POST | /send/location |
|| Send Poll / Vote | POST | /send/poll |
|| Revoke Message | POST | /message/:message_id/revoke |
|| React Message | POST | /message/:message_id/react |
|| Edit Message | POST | /message/:message_id/update |
|| Join Group With Link | POST | /group/join-with-link |
|| Leave Group | POST | /group/leave |
|| Create Group | POST | /group |
|| Add Participants in Group | POST | /group/participants |
|| Remove Participant in Group | DELETE | /group/participants |
|| Promote Participant in Group | POST | /group/participants/promote |
|| Demote Participant in Group | POST | /group/participants/demote |

```
✅ = Available
Expand Down
2 changes: 1 addition & 1 deletion src/config/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
)

var (
AppVersion = "v4.12.0"
AppVersion = "v4.13.0"
AppPort = "3000"
AppDebug = false
AppOs = "AldinoKemal"
Expand Down
12 changes: 12 additions & 0 deletions src/domains/group/group.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ type IGroupService interface {
JoinGroupWithLink(ctx context.Context, request JoinGroupWithLinkRequest) (groupID string, err error)
LeaveGroup(ctx context.Context, request LeaveGroupRequest) (err error)
CreateGroup(ctx context.Context, request CreateGroupRequest) (groupID string, err error)
AddParticipant(ctx context.Context, request ParticipantRequest) (result []ParticipantStatus, err error)
}

type JoinGroupWithLinkRequest struct {
Expand All @@ -20,3 +21,14 @@ type CreateGroupRequest struct {
Title string `json:"title" form:"title"`
Participants []string `json:"participants" form:"participants"`
}

type ParticipantRequest struct {
GroupID string `json:"group_id" form:"group_id"`
Participants []string `json:"participants" form:"participants"`
}

type ParticipantStatus struct {
Participant string `json:"participant"`
Status string `json:"status"`
Message string `json:"message"`
}
22 changes: 22 additions & 0 deletions src/internal/rest/group.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
domainGroup "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/group"
"github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/utils"
"github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/whatsapp"
"github.com/gofiber/fiber/v2"
)

Expand All @@ -16,6 +17,7 @@ func InitRestGroup(app *fiber.App, service domainGroup.IGroupService) Group {
app.Post("/group", rest.CreateGroup)
app.Post("/group/join-with-link", rest.JoinGroupWithLink)
app.Post("/group/leave", rest.LeaveGroup)
app.Post("/group/participants", rest.AddParticipants)
return rest
}

Expand All @@ -42,6 +44,8 @@ func (controller *Group) LeaveGroup(c *fiber.Ctx) error {
err := c.BodyParser(&request)
utils.PanicIfNeeded(err)

whatsapp.SanitizePhone(&request.GroupID)

err = controller.Service.LeaveGroup(c.UserContext(), request)
utils.PanicIfNeeded(err)

Expand Down Expand Up @@ -69,3 +73,21 @@ func (controller *Group) CreateGroup(c *fiber.Ctx) error {
},
})
}

func (controller *Group) AddParticipants(c *fiber.Ctx) error {
var request domainGroup.ParticipantRequest
err := c.BodyParser(&request)
utils.PanicIfNeeded(err)

whatsapp.SanitizePhone(&request.GroupID)

result, err := controller.Service.AddParticipant(c.UserContext(), request)
utils.PanicIfNeeded(err)

return c.JSON(utils.ResponseData{
Status: 200,
Code: "SUCCESS",
Message: "Success add participants",
Results: result,
})
}
3 changes: 0 additions & 3 deletions src/pkg/whatsapp/whatsapp.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,9 +233,6 @@ func handler(rawEvt interface{}) {
if evt.IsViewOnce {
metaParts = append(metaParts, "view once")
}
if evt.IsViewOnce {
metaParts = append(metaParts, "ephemeral")
}

log.Infof("Received message %s from %s (%s): %+v", evt.Info.ID, evt.Info.SourceString(), strings.Join(metaParts, ", "), evt.Message)

Expand Down
70 changes: 59 additions & 11 deletions src/services/group.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,9 @@ func (service groupService) CreateGroup(ctx context.Context, request domainGroup
}
whatsapp.MustLogin(service.WaCli)

var participantsJID []types.JID
for _, participant := range request.Participants {
formattedParticipant := participant + config.WhatsappTypeUser

if !whatsapp.IsOnWhatsapp(service.WaCli, formattedParticipant) {
return "", pkgError.ErrUserNotRegistered
}

if participantJID, err := types.ParseJID(formattedParticipant); err == nil {
participantsJID = append(participantsJID, participantJID)
}
participantsJID, err := service.participantToJID(request.Participants)
if err != nil {
return
}

groupConfig := whatsmeow.ReqCreateGroup{
Expand All @@ -80,3 +72,59 @@ func (service groupService) CreateGroup(ctx context.Context, request domainGroup

return groupInfo.JID.String(), nil
}

func (service groupService) AddParticipant(ctx context.Context, request domainGroup.ParticipantRequest) (result []domainGroup.ParticipantStatus, err error) {
if err = validations.ValidateParticipant(ctx, request); err != nil {
return result, err
}
whatsapp.MustLogin(service.WaCli)

groupJID, err := whatsapp.ValidateJidWithLogin(service.WaCli, request.GroupID)
if err != nil {
return result, err
}

participantsJID, err := service.participantToJID(request.Participants)
if err != nil {
return result, err
}

participants, err := service.WaCli.UpdateGroupParticipants(groupJID, participantsJID, whatsmeow.ParticipantChangeAdd)
if err != nil {
return result, err
}

for _, participant := range participants {
if participant.Error == 403 && participant.AddRequest != nil {
result = append(result, domainGroup.ParticipantStatus{
Participant: participant.JID.String(),
Status: "error",
Message: "Failed to add participant",
})
} else {
result = append(result, domainGroup.ParticipantStatus{
Participant: participant.JID.String(),
Status: "success",
Message: "Participant added",
})
}
}

return result, nil
}

func (service groupService) participantToJID(participants []string) ([]types.JID, error) {
var participantsJID []types.JID
for _, participant := range participants {
formattedParticipant := participant + config.WhatsappTypeUser

if !whatsapp.IsOnWhatsapp(service.WaCli, formattedParticipant) {
return nil, pkgError.ErrUserNotRegistered
}

if participantJID, err := types.ParseJID(formattedParticipant); err == nil {
participantsJID = append(participantsJID, participantJID)
}
}
return participantsJID, nil
}
14 changes: 14 additions & 0 deletions src/validations/group_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,17 @@ func ValidateCreateGroup(ctx context.Context, request domainGroup.CreateGroupReq

return nil
}

func ValidateParticipant(ctx context.Context, request domainGroup.ParticipantRequest) error {
err := validation.ValidateStructWithContext(ctx, &request,
validation.Field(&request.GroupID, validation.Required),
validation.Field(&request.Participants, validation.Required),
validation.Field(&request.Participants, validation.Each(validation.Required)),
)

if err != nil {
return pkgError.ValidationError(err.Error())
}

return nil
}
Loading

0 comments on commit e78767d

Please sign in to comment.