39 changed files with 940 additions and 280 deletions
-
4.gitignore
-
347docs/openapi.yaml
-
8readme.md
-
2src/cmd/root.go
-
2src/config/settings.go
-
6src/domains/message/message.go
-
11src/domains/newsletter/newsletter.go
-
4src/domains/user/account.go
-
1src/domains/user/user.go
-
22src/go.mod
-
25src/go.sum
-
20src/internal/rest/message.go
-
32src/internal/rest/newsletter.go
-
13src/internal/rest/user.go
-
32src/pkg/whatsapp/whatsapp.go
-
27src/services/message.go
-
32src/services/newsletter.go
-
20src/services/send.go
-
14src/services/user.go
-
14src/validations/message_validation.go
-
20src/validations/newsletter_validation.go
-
26src/views/components/AccountAvatar.js
-
24src/views/components/AccountUserInfo.js
-
2src/views/components/GroupManageParticipants.js
-
27src/views/components/MessageDelete.js
-
27src/views/components/MessageReact.js
-
27src/views/components/MessageRevoke.js
-
27src/views/components/MessageUpdate.js
-
117src/views/components/NewsletterList.js
-
26src/views/components/SendAudio.js
-
27src/views/components/SendContact.js
-
27src/views/components/SendFile.js
-
27src/views/components/SendImage.js
-
27src/views/components/SendLocation.js
-
26src/views/components/SendMessage.js
-
29src/views/components/SendPoll.js
-
27src/views/components/SendVideo.js
-
52src/views/components/generic/FormRecipient.js
-
19src/views/index.html
@ -0,0 +1,11 @@ |
|||||
|
package newsletter |
||||
|
|
||||
|
import "context" |
||||
|
|
||||
|
type INewsletterService interface { |
||||
|
Unfollow(ctx context.Context, request UnfollowRequest) (err error) |
||||
|
} |
||||
|
|
||||
|
type UnfollowRequest struct { |
||||
|
NewsletterID string `json:"newsletter_id" form:"newsletter_id"` |
||||
|
} |
||||
@ -0,0 +1,32 @@ |
|||||
|
package rest |
||||
|
|
||||
|
import ( |
||||
|
domainNewsletter "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/newsletter" |
||||
|
"github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/utils" |
||||
|
"github.com/gofiber/fiber/v2" |
||||
|
) |
||||
|
|
||||
|
type Newsletter struct { |
||||
|
Service domainNewsletter.INewsletterService |
||||
|
} |
||||
|
|
||||
|
func InitRestNewsletter(app *fiber.App, service domainNewsletter.INewsletterService) Newsletter { |
||||
|
rest := Newsletter{Service: service} |
||||
|
app.Post("/newsletter/unfollow", rest.Unfollow) |
||||
|
return rest |
||||
|
} |
||||
|
|
||||
|
func (controller *Newsletter) Unfollow(c *fiber.Ctx) error { |
||||
|
var request domainNewsletter.UnfollowRequest |
||||
|
err := c.BodyParser(&request) |
||||
|
utils.PanicIfNeeded(err) |
||||
|
|
||||
|
err = controller.Service.Unfollow(c.UserContext(), request) |
||||
|
utils.PanicIfNeeded(err) |
||||
|
|
||||
|
return c.JSON(utils.ResponseData{ |
||||
|
Status: 200, |
||||
|
Code: "SUCCESS", |
||||
|
Message: "Success unfollow newsletter", |
||||
|
}) |
||||
|
} |
||||
@ -0,0 +1,32 @@ |
|||||
|
package services |
||||
|
|
||||
|
import ( |
||||
|
"context" |
||||
|
domainNewsletter "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/newsletter" |
||||
|
"github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/whatsapp" |
||||
|
"github.com/aldinokemal/go-whatsapp-web-multidevice/validations" |
||||
|
"go.mau.fi/whatsmeow" |
||||
|
) |
||||
|
|
||||
|
type newsletterService struct { |
||||
|
WaCli *whatsmeow.Client |
||||
|
} |
||||
|
|
||||
|
func NewNewsletterService(waCli *whatsmeow.Client) domainNewsletter.INewsletterService { |
||||
|
return &newsletterService{ |
||||
|
WaCli: waCli, |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (service newsletterService) Unfollow(ctx context.Context, request domainNewsletter.UnfollowRequest) (err error) { |
||||
|
if err = validations.ValidateUnfollowNewsletter(ctx, request); err != nil { |
||||
|
return err |
||||
|
} |
||||
|
|
||||
|
JID, err := whatsapp.ValidateJidWithLogin(service.WaCli, request.NewsletterID) |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
|
||||
|
return service.WaCli.UnfollowNewsletter(JID) |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
package validations |
||||
|
|
||||
|
import ( |
||||
|
"context" |
||||
|
domainNewsletter "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/newsletter" |
||||
|
pkgError "github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/error" |
||||
|
validation "github.com/go-ozzo/ozzo-validation/v4" |
||||
|
) |
||||
|
|
||||
|
func ValidateUnfollowNewsletter(ctx context.Context, request domainNewsletter.UnfollowRequest) error { |
||||
|
err := validation.ValidateStructWithContext(ctx, &request, |
||||
|
validation.Field(&request.NewsletterID, validation.Required), |
||||
|
) |
||||
|
|
||||
|
if err != nil { |
||||
|
return pkgError.ValidationError(err.Error()) |
||||
|
} |
||||
|
|
||||
|
return nil |
||||
|
} |
||||
@ -0,0 +1,117 @@ |
|||||
|
export default { |
||||
|
name: 'ListNewsletter', |
||||
|
data() { |
||||
|
return { |
||||
|
newsletters: [] |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
async openModal() { |
||||
|
try { |
||||
|
this.dtClear() |
||||
|
await this.submitApi(); |
||||
|
$('#modalNewsletterList').modal('show'); |
||||
|
this.dtRebuild() |
||||
|
showSuccessInfo("Newsletters fetched") |
||||
|
} catch (err) { |
||||
|
showErrorInfo(err) |
||||
|
} |
||||
|
}, |
||||
|
dtClear() { |
||||
|
$('#account_newsletters_table').DataTable().destroy(); |
||||
|
}, |
||||
|
dtRebuild() { |
||||
|
$('#account_newsletters_table').DataTable({ |
||||
|
"pageLength": 100, |
||||
|
"reloadData": true, |
||||
|
}).draw(); |
||||
|
}, |
||||
|
async handleUnfollowNewsletter(newsletter_id) { |
||||
|
try { |
||||
|
const ok = confirm("Are you sure to leave this newsletter?"); |
||||
|
if (!ok) return; |
||||
|
|
||||
|
await this.unfollowNewsletterApi(newsletter_id); |
||||
|
this.dtClear() |
||||
|
await this.submitApi(); |
||||
|
this.dtRebuild() |
||||
|
showSuccessInfo("Success unfollow newsletter") |
||||
|
} catch (err) { |
||||
|
showErrorInfo(err) |
||||
|
} |
||||
|
}, |
||||
|
async unfollowNewsletterApi(newsletter_id) { |
||||
|
try { |
||||
|
let payload = { |
||||
|
newsletter_id: newsletter_id |
||||
|
}; |
||||
|
await window.http.post(`/newsletter/unfollow`, payload) |
||||
|
} catch (error) { |
||||
|
if (error.response) { |
||||
|
throw new Error(error.response.data.message); |
||||
|
} |
||||
|
throw new Error(error.message); |
||||
|
|
||||
|
} |
||||
|
}, |
||||
|
async submitApi() { |
||||
|
try { |
||||
|
let response = await window.http.get(`/user/my/newsletters`) |
||||
|
this.newsletters = response.data.results.data; |
||||
|
} catch (error) { |
||||
|
if (error.response) { |
||||
|
throw new Error(error.response.data.message); |
||||
|
} |
||||
|
throw new Error(error.message); |
||||
|
} |
||||
|
}, |
||||
|
formatDate: function (value) { |
||||
|
if (!value) return '' |
||||
|
if (isNaN(value)) return 'Invalid date'; |
||||
|
return moment.unix(value).format('LLL'); |
||||
|
} |
||||
|
}, |
||||
|
template: `
|
||||
|
<div class="green card" @click="openModal" style="cursor: pointer"> |
||||
|
<div class="content"> |
||||
|
<a class="ui green right ribbon label">Newsletter</a> |
||||
|
<div class="header">List Newsletters</div> |
||||
|
<div class="description"> |
||||
|
Display all your newsletters |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Modal AccountNewsletter --> |
||||
|
<div class="ui small modal" id="modalNewsletterList"> |
||||
|
<i class="close icon"></i> |
||||
|
<div class="header"> |
||||
|
My Newsletter List |
||||
|
</div> |
||||
|
<div class="content"> |
||||
|
<table class="ui celled table" id="account_newsletters_table"> |
||||
|
<thead> |
||||
|
<tr> |
||||
|
<th>Newsletter ID</th> |
||||
|
<th>Name</th> |
||||
|
<th>Role</th> |
||||
|
<th>Created At</th> |
||||
|
<th>Action</th> |
||||
|
</tr> |
||||
|
</thead> |
||||
|
<tbody v-if="newsletters != null"> |
||||
|
<tr v-for="n in newsletters"> |
||||
|
<td>{{ n.id.split('@')[0] }}</td> |
||||
|
<td>{{ n.thread_metadata?.name?.text || 'N/A' }}</td> |
||||
|
<td>{{ n.viewer_metadata?.role || 'N/A' }}</td> |
||||
|
<td>{{ formatDate(n.thread_metadata?.creation_time) }}</td> |
||||
|
<td> |
||||
|
<button class="ui red tiny button" @click="handleUnfollowNewsletter(n.id)">Unfollow</button> |
||||
|
</td> |
||||
|
</tr> |
||||
|
</tbody> |
||||
|
</table> |
||||
|
</div> |
||||
|
</div> |
||||
|
`
|
||||
|
} |
||||
@ -0,0 +1,52 @@ |
|||||
|
export default { |
||||
|
name: 'FormRecipient', |
||||
|
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' }, |
||||
|
{ value: window.TYPEGROUP, text: 'Group Message' }, |
||||
|
{ value: window.TYPENEWSLETTER, text: 'Newsletter' } |
||||
|
]; |
||||
|
}, |
||||
|
methods: { |
||||
|
updateType(event) { |
||||
|
this.$emit('update:type', event.target.value); |
||||
|
}, |
||||
|
updatePhone(event) { |
||||
|
this.$emit('update:phone', event.target.value); |
||||
|
} |
||||
|
}, |
||||
|
template: `
|
||||
|
<div class="field"> |
||||
|
<label>Type</label> |
||||
|
<select name="type" @change="updateType" class="ui dropdown"> |
||||
|
<option v-for="type in recipientTypes" :value="type.value">{{ type.text }}</option> |
||||
|
</select> |
||||
|
</div> |
||||
|
|
||||
|
<div class="field"> |
||||
|
<label>Phone / Group ID</label> |
||||
|
<input :value="phone" aria-label="wa identifier" @input="updatePhone"> |
||||
|
<input :value="phone_id" disabled aria-label="whatsapp_id"> |
||||
|
</div> |
||||
|
`
|
||||
|
} |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue