From 001236e76f8053f3f96c379ebfdd2ab0ea451ed5 Mon Sep 17 00:00:00 2001 From: Ruan Date: Thu, 23 Jan 2025 15:07:01 -0300 Subject: [PATCH] feat: Adding new route for check if user is in whatsapp --- .gitignore | 4 +- readme.md | 10 ++ src/domains/user/account.go | 19 +++ src/domains/user/user.go | 1 + src/internal/rest/user.go | 20 +++ src/services/user.go | 35 ++++++ src/validations/user_validation.go | 11 ++ src/views/components/AccountUserCheck.js | 117 ++++++++++++++++++ .../generic/FormCheckUserRecipient.js | 50 ++++++++ src/views/index.html | 4 +- 10 files changed, 269 insertions(+), 2 deletions(-) create mode 100644 src/views/components/AccountUserCheck.js create mode 100644 src/views/components/generic/FormCheckUserRecipient.js diff --git a/.gitignore b/.gitignore index 5c7040f..0d20448 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,6 @@ main main.exe *.jpe src/pkged.go -storages \ No newline at end of file +storages +.tool-versions + diff --git a/readme.md b/readme.md index 4052b5c..4c40c1d 100644 --- a/readme.md +++ b/readme.md @@ -103,6 +103,14 @@ You can fork or edit this source code ! to [SwaggerEditor](https://editor.swagger.io). - Furthermore you can generate HTTP Client from this API using [openapi-generator](https://openapi-generator.tech/#try) +## CURL for the api + +#### You need to encode to Base64 your user:pass [basic-auth] and pass in header + +- curl -X 'GET' 'http://127.0.0.1:3000/user/check?phone=YOUR_PHONE' -H 'accept: application/json' \ +-H 'Authorization: Basic qwertyASDFzxc=' +- curl -X 'GET' 'http://127.0.0.1:3000/user/check?phone=YOUR_PHONE' -H 'accept: application/json' + | Feature | Menu | Method | URL | |---------|------------------------------|--------|-------------------------------| | ✅ | Login with Scan QR | GET | /app/login | @@ -111,6 +119,7 @@ You can fork or edit this source code ! | ✅ | Reconnect | GET | /app/reconnect | | ✅ | Devices | GET | /app/devices | | ✅ | User Info | GET | /user/info | +| ✅ | Check User is on whatsapp | GET | /user/check | | ✅ | User Avatar | GET | /user/avatar | | ✅ | User My Groups | GET | /user/my/groups | | ✅ | User My Newsletter | GET | /user/my/newsletters | @@ -164,6 +173,7 @@ You can fork or edit this source code ! | Reaction Message | ![Reaction Message](https://i.ibb.co.com/BfHgSHG/react-message.png) | | Edit Message | ![Edit Message](https://i.ibb.co.com/kXfpqJw/update-message.png) | | User Info | ![User Info](https://i.ibb.co.com/3zjX6Cz/user-info.png?v=1) | +| Check User | ![Check User ](https://i.ibb.co/92gVZrx/Check-User.png?v=1) | | User Avatar | ![User Avatar](https://i.ibb.co.com/ZmJZ4ZW/search-avatar.png?v=1) | | My Privacy | ![My Privacy](https://i.ibb.co.com/Cw1sMQz/my-privacy.png) | | My Group | ![My Group](https://i.ibb.co.com/WB268Xy/list-group.png) | diff --git a/src/domains/user/account.go b/src/domains/user/account.go index d424755..45e9959 100644 --- a/src/domains/user/account.go +++ b/src/domains/user/account.go @@ -6,6 +6,10 @@ type InfoRequest struct { Phone string `json:"phone" query:"phone"` } +type CheckRequest struct { + Phone string `json:"phone" query:"phone"` +} + type InfoResponseDataDevice struct { User string Agent uint8 @@ -21,10 +25,25 @@ type InfoResponseData struct { Devices []InfoResponseDataDevice `json:"devices"` } +type CheckResponseData struct { + Query string `json:"query"` + IsInWhatsapp bool `json:"IsInWhatsapp"` + VerifiedName string `json:"verified_name"` + JID string `json:"jid"` +} + type InfoResponse struct { Data []InfoResponseData `json:"data"` } +type CheckResponse struct { + Data []CheckResponseData `json:"data"` +} + +type UserCollection struct { + Users []CheckResponseData +} + type AvatarRequest struct { Phone string `json:"phone" query:"phone"` IsPreview bool `json:"is_preview" query:"is_preview"` diff --git a/src/domains/user/user.go b/src/domains/user/user.go index feb3b78..453f6da 100644 --- a/src/domains/user/user.go +++ b/src/domains/user/user.go @@ -6,6 +6,7 @@ import ( type IUserService interface { Info(ctx context.Context, request InfoRequest) (response InfoResponse, err error) + Check(ctx context.Context, request CheckRequest) (response CheckResponse, err error) Avatar(ctx context.Context, request AvatarRequest) (response AvatarResponse, err error) MyListGroups(ctx context.Context) (response MyListGroupsResponse, err error) MyListNewsletter(ctx context.Context) (response MyListNewsletterResponse, err error) diff --git a/src/internal/rest/user.go b/src/internal/rest/user.go index 6621bb2..93d40b4 100644 --- a/src/internal/rest/user.go +++ b/src/internal/rest/user.go @@ -14,6 +14,7 @@ type User struct { func InitRestUser(app *fiber.App, service domainUser.IUserService) User { rest := User{Service: service} app.Get("/user/info", rest.UserInfo) + app.Get("/user/check", rest.UserCheck) app.Get("/user/avatar", rest.UserAvatar) app.Get("/user/my/privacy", rest.UserMyPrivacySetting) app.Get("/user/my/groups", rest.UserMyListGroups) @@ -40,6 +41,25 @@ func (controller *User) UserInfo(c *fiber.Ctx) error { }) } +func (controller *User) UserCheck(c *fiber.Ctx) error { + var request domainUser.CheckRequest + err := c.QueryParser(&request) + utils.PanicIfNeeded(err) + + whatsapp.SanitizePhone(&request.Phone) + + response, err := controller.Service.Check(c.UserContext(), request) + utils.PanicIfNeeded(err) + + return c.JSON(utils.ResponseData{ + Status: 200, + Code: "SUCCESS", + Message: "Success get user info", + Results: response.Data[0], + }) +} + + func (controller *User) UserAvatar(c *fiber.Ctx) error { var request domainUser.AvatarRequest err := c.QueryParser(&request) diff --git a/src/services/user.go b/src/services/user.go index 19d0652..3361b47 100644 --- a/src/services/user.go +++ b/src/services/user.go @@ -66,6 +66,41 @@ func (service userService) Info(ctx context.Context, request domainUser.InfoRequ return response, nil } +func (service userService) Check(ctx context.Context, request domainUser.CheckRequest) (response domainUser.CheckResponse, err error) { + err = validations.ValidateUserCheck(ctx, request) + if err != nil { + return response, err + } + + var jids []types.JID + dataWaRecipient, err := whatsapp.ValidateJidWithLogin(service.WaCli, request.Phone) + if err != nil { + return response, err + } + + jids = append(jids, dataWaRecipient) + resp, err := service.WaCli.IsOnWhatsApp([]string{request.Phone}) + if err != nil { + return response, err + } + + uc := new(domainUser.UserCollection) + for _, item := range resp { + if item.VerifiedName != nil { + var msg = domainUser.CheckResponseData{Query: item.Query, IsInWhatsapp: item.IsIn, JID: fmt.Sprintf("%s", item.JID), VerifiedName: item.VerifiedName.Details.GetVerifiedName()} + uc.Users = append(uc.Users, msg) + } else { + var msg = domainUser.CheckResponseData{Query: item.Query, IsInWhatsapp: item.IsIn, JID: fmt.Sprintf("%s", item.JID), VerifiedName: ""} + uc.Users = append(uc.Users, msg) + } + } + + response.Data = append(response.Data, uc.Users[0]) + // response := string(responseJson) + + return response, err +} + func (service userService) Avatar(ctx context.Context, request domainUser.AvatarRequest) (response domainUser.AvatarResponse, err error) { chanResp := make(chan domainUser.AvatarResponse) diff --git a/src/validations/user_validation.go b/src/validations/user_validation.go index 5f6e359..7691d27 100644 --- a/src/validations/user_validation.go +++ b/src/validations/user_validation.go @@ -18,6 +18,17 @@ func ValidateUserInfo(ctx context.Context, request domainUser.InfoRequest) error return nil } +func ValidateUserCheck(ctx context.Context, request domainUser.CheckRequest) error { + err := validation.ValidateStructWithContext(ctx, &request, + validation.Field(&request.Phone, validation.Required), + ) + + if err != nil { + return pkgError.ValidationError(err.Error()) + } + + return nil +} func ValidateUserAvatar(ctx context.Context, request domainUser.AvatarRequest) error { err := validation.ValidateStructWithContext(ctx, &request, validation.Field(&request.Phone, validation.Required), diff --git a/src/views/components/AccountUserCheck.js b/src/views/components/AccountUserCheck.js new file mode 100644 index 0000000..5c3f7b8 --- /dev/null +++ b/src/views/components/AccountUserCheck.js @@ -0,0 +1,117 @@ +import FormCheckUserRecipient from "./generic/FormCheckUserRecipient.js"; + +export default { + name: 'AccountUserCheck', + components: { + FormCheckUserRecipient + }, + data() { + return { + type: window.TYPEUSER, + phone: '', + // + is_in_whatsapp: null, + jid: null, + verified_name: null, + query: null, + code: null, + // + loading: false, + } + }, + + computed: { + phone_id() { + return this.phone + this.type; + } + }, + methods: { + async openModal() { + this.handleReset(); + $('#modalUserCheck').modal('show'); + }, + async handleSubmit() { + try { + await this.submitApi(); + showSuccessInfo("Info fetched") + } catch (err) { + showErrorInfo(err) + } + }, + async submitApi() { + this.loading = true; + try { + let response = await window.http.get(`/user/check?phone=${this.phone_id}`) + this.is_in_whatsapp = response.data.results.IsInWhatsapp; + this.jid = response.data.results.jid; + this.verified_name = response.data.results.verified_name; + this.query = response.data.results.query; + this.code = response.code; + } catch (error) { + if (error.response) { + this.verified_name = null; + this.jid = null; + throw new Error(error.response.data.message); + } + this.verified_name = null; + this.jid = null; + throw new Error(error.message); + } finally { + this.loading = false; + } + }, + handleReset() { + this.phone = ''; + this.is_in_whatsapp = null; + this.jid = null; + this.verified_name = null; + this.query = null; + this.code = null; + this.type = window.TYPEUSER; + } + }, + template: ` +
+
+
User Check
+
+ You can check if the user exists on whatapp +
+
+
+ + + + + ` +} \ No newline at end of file diff --git a/src/views/components/generic/FormCheckUserRecipient.js b/src/views/components/generic/FormCheckUserRecipient.js new file mode 100644 index 0000000..23587f6 --- /dev/null +++ b/src/views/components/generic/FormCheckUserRecipient.js @@ -0,0 +1,50 @@ +export default { + name: 'FormCheckUserRecipient', + props: { + type: { + type: String, + required: true + }, + phone: { + type: String, + required: true + }, + }, + data() { + return { + recipientTypes: [] + }; + }, + computed: { + phone_id() { + return this.phone + this.type; + } + }, + mounted() { + this.recipientTypes = [ + { value: window.TYPEUSER, text: 'Private Message' }, + ]; + }, + methods: { + updateType(event) { + this.$emit('update:type', event.target.value); + }, + updatePhone(event) { + this.$emit('update:phone', event.target.value); + } + }, + template: ` +
+ + +
+ +
+ + + +
+ ` +} \ No newline at end of file diff --git a/src/views/index.html b/src/views/index.html index e9708ab..bdcb961 100644 --- a/src/views/index.html +++ b/src/views/index.html @@ -101,6 +101,7 @@
+
@@ -158,6 +159,7 @@ import GroupAddParticipants from "./components/GroupManageParticipants.js"; import AccountAvatar from "./components/AccountAvatar.js"; import AccountUserInfo from "./components/AccountUserInfo.js"; + import AccountUserCheck from "./components/AccountUserCheck.js"; import AccountPrivacy from "./components/AccountPrivacy.js"; import NewsletterList from "./components/NewsletterList.js"; @@ -198,7 +200,7 @@ MessageDelete, MessageUpdate, MessageReact, MessageRevoke, GroupList, GroupCreate, GroupJoinWithLink, GroupAddParticipants, NewsletterList, - AccountAvatar, AccountUserInfo, AccountPrivacy + AccountAvatar, AccountUserInfo, AccountUserCheck, AccountPrivacy }, delimiters: ['[[', ']]'], data() {