miaw-core 1.7.1 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +65 -0
- package/dist/cli/commands/business.d.ts +20 -0
- package/dist/cli/commands/business.d.ts.map +1 -0
- package/dist/cli/commands/business.js +76 -0
- package/dist/cli/commands/business.js.map +1 -0
- package/dist/cli/commands/commands-index.d.ts +3 -0
- package/dist/cli/commands/commands-index.d.ts.map +1 -1
- package/dist/cli/commands/commands-index.js +3 -0
- package/dist/cli/commands/commands-index.js.map +1 -1
- package/dist/cli/commands/community.d.ts +78 -0
- package/dist/cli/commands/community.d.ts.map +1 -0
- package/dist/cli/commands/community.js +201 -0
- package/dist/cli/commands/community.js.map +1 -0
- package/dist/cli/commands/index.d.ts.map +1 -1
- package/dist/cli/commands/index.js +204 -0
- package/dist/cli/commands/index.js.map +1 -1
- package/dist/cli/commands/status.d.ts +24 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/status.js +58 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/repl.d.ts +7 -0
- package/dist/cli/repl.d.ts.map +1 -1
- package/dist/cli/repl.js +44 -4
- package/dist/cli/repl.js.map +1 -1
- package/dist/client/MiawClient.d.ts +106 -1
- package/dist/client/MiawClient.d.ts.map +1 -1
- package/dist/client/MiawClient.js +573 -0
- package/dist/client/MiawClient.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/types/index.d.ts +147 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/docs/BAILEYS_VS_MIAW_COMPARISON.md +20 -20
- package/docs/USAGE.md +74 -0
- package/package.json +1 -1
|
@@ -1750,6 +1750,78 @@ export class MiawClient extends EventEmitter {
|
|
|
1750
1750
|
}
|
|
1751
1751
|
this.emit("poll_vote", vote);
|
|
1752
1752
|
}
|
|
1753
|
+
// ============================================
|
|
1754
|
+
// Status / Stories (v1.8.0) — via status@broadcast
|
|
1755
|
+
// ============================================
|
|
1756
|
+
/**
|
|
1757
|
+
* Resolve the audience (statusJidList) for a status post. When recipients are
|
|
1758
|
+
* given they are used (formatted to JIDs); otherwise it defaults to every
|
|
1759
|
+
* individual contact in the store (like the WhatsApp app's "all contacts").
|
|
1760
|
+
*/
|
|
1761
|
+
getStatusAudience(recipients) {
|
|
1762
|
+
if (recipients && recipients.length > 0) {
|
|
1763
|
+
return recipients.map((r) => MessageHandler.formatPhoneToJid(r));
|
|
1764
|
+
}
|
|
1765
|
+
const jids = new Set();
|
|
1766
|
+
for (const contact of this.contactsStore.values()) {
|
|
1767
|
+
if (contact.jid?.endsWith("@s.whatsapp.net")) {
|
|
1768
|
+
jids.add(contact.jid);
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
return [...jids];
|
|
1772
|
+
}
|
|
1773
|
+
/**
|
|
1774
|
+
* Post a text status / story.
|
|
1775
|
+
* @param text - The status text
|
|
1776
|
+
* @param recipients - Audience (phone numbers or JIDs). Omit to send to all contacts.
|
|
1777
|
+
* @param options - backgroundColor / font for the text status
|
|
1778
|
+
*/
|
|
1779
|
+
async postTextStatus(text, recipients, options) {
|
|
1780
|
+
return this.postStatus({ text }, recipients, options);
|
|
1781
|
+
}
|
|
1782
|
+
/**
|
|
1783
|
+
* Post an image status / story.
|
|
1784
|
+
* @param image - Image source (file path, URL, or Buffer)
|
|
1785
|
+
* @param recipients - Audience (phone numbers or JIDs). Omit to send to all contacts.
|
|
1786
|
+
* @param options - caption for the image
|
|
1787
|
+
*/
|
|
1788
|
+
async postImageStatus(image, recipients, options) {
|
|
1789
|
+
return this.postStatus({ image: Buffer.isBuffer(image) ? image : { url: image }, caption: options?.caption }, recipients, options);
|
|
1790
|
+
}
|
|
1791
|
+
/**
|
|
1792
|
+
* Post a video status / story.
|
|
1793
|
+
* @param video - Video source (file path, URL, or Buffer)
|
|
1794
|
+
* @param recipients - Audience (phone numbers or JIDs). Omit to send to all contacts.
|
|
1795
|
+
* @param options - caption for the video
|
|
1796
|
+
*/
|
|
1797
|
+
async postVideoStatus(video, recipients, options) {
|
|
1798
|
+
return this.postStatus({ video: Buffer.isBuffer(video) ? video : { url: video }, caption: options?.caption }, recipients, options);
|
|
1799
|
+
}
|
|
1800
|
+
/** Shared status sender: builds statusJidList and posts to status@broadcast. */
|
|
1801
|
+
async postStatus(content, recipients, options) {
|
|
1802
|
+
try {
|
|
1803
|
+
if (!this.socket) {
|
|
1804
|
+
throw new Error("Not connected. Call connect() first.");
|
|
1805
|
+
}
|
|
1806
|
+
if (this.connectionState !== "connected") {
|
|
1807
|
+
throw new Error(`Cannot post status. Connection state: ${this.connectionState}`);
|
|
1808
|
+
}
|
|
1809
|
+
const statusJidList = this.getStatusAudience(recipients);
|
|
1810
|
+
if (statusJidList.length === 0 && this.options.debug) {
|
|
1811
|
+
this.logger.debug("[status] No audience (no recipients and empty contacts store); status will be visible only to you.");
|
|
1812
|
+
}
|
|
1813
|
+
const result = await this.socket.sendMessage("status@broadcast", content, {
|
|
1814
|
+
statusJidList,
|
|
1815
|
+
backgroundColor: options?.backgroundColor,
|
|
1816
|
+
font: options?.font,
|
|
1817
|
+
});
|
|
1818
|
+
return { success: true, messageId: result?.key?.id || undefined };
|
|
1819
|
+
}
|
|
1820
|
+
catch (error) {
|
|
1821
|
+
this.logger.error("Failed to post status:", error);
|
|
1822
|
+
return { success: false, error: error.message };
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1753
1825
|
/**
|
|
1754
1826
|
* Download media from a received message
|
|
1755
1827
|
* @param message - The MiawMessage containing media (must have raw field with original Baileys message)
|
|
@@ -2005,6 +2077,145 @@ export class MiawClient extends EventEmitter {
|
|
|
2005
2077
|
return null;
|
|
2006
2078
|
}
|
|
2007
2079
|
}
|
|
2080
|
+
// ============================================
|
|
2081
|
+
// Business extras (v1.8.0) — WhatsApp Business only
|
|
2082
|
+
// ============================================
|
|
2083
|
+
/**
|
|
2084
|
+
* Update your own business profile (address, websites, email, description,
|
|
2085
|
+
* hours). Category is read-only and cannot be changed here.
|
|
2086
|
+
*/
|
|
2087
|
+
async updateBusinessProfile(updates) {
|
|
2088
|
+
try {
|
|
2089
|
+
if (!this.socket) {
|
|
2090
|
+
throw new Error("Not connected. Call connect() first.");
|
|
2091
|
+
}
|
|
2092
|
+
if (this.connectionState !== "connected") {
|
|
2093
|
+
throw new Error(`Cannot update business profile. Connection state: ${this.connectionState}`);
|
|
2094
|
+
}
|
|
2095
|
+
const socket = this.socket;
|
|
2096
|
+
await socket.updateBussinesProfile(updates);
|
|
2097
|
+
return { success: true };
|
|
2098
|
+
}
|
|
2099
|
+
catch (error) {
|
|
2100
|
+
this.logger.error("Failed to update business profile:", error);
|
|
2101
|
+
return { success: false, error: error.message };
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2104
|
+
/**
|
|
2105
|
+
* Set the business cover photo.
|
|
2106
|
+
* @param image - Image source (file path, URL, or Buffer)
|
|
2107
|
+
* @returns The uploaded cover photo id (pass to removeCoverPhoto)
|
|
2108
|
+
*/
|
|
2109
|
+
async updateCoverPhoto(image) {
|
|
2110
|
+
try {
|
|
2111
|
+
if (!this.socket) {
|
|
2112
|
+
throw new Error("Not connected. Call connect() first.");
|
|
2113
|
+
}
|
|
2114
|
+
if (this.connectionState !== "connected") {
|
|
2115
|
+
throw new Error(`Cannot update cover photo. Connection state: ${this.connectionState}`);
|
|
2116
|
+
}
|
|
2117
|
+
const photo = (Buffer.isBuffer(image) ? image : { url: image });
|
|
2118
|
+
const id = await this.socket.updateCoverPhoto(photo);
|
|
2119
|
+
return { success: true, coverPhotoId: id != null ? String(id) : undefined };
|
|
2120
|
+
}
|
|
2121
|
+
catch (error) {
|
|
2122
|
+
this.logger.error("Failed to update cover photo:", error);
|
|
2123
|
+
return { success: false, error: error.message };
|
|
2124
|
+
}
|
|
2125
|
+
}
|
|
2126
|
+
/** Remove a business cover photo by its id. */
|
|
2127
|
+
async removeCoverPhoto(coverPhotoId) {
|
|
2128
|
+
try {
|
|
2129
|
+
if (!this.socket) {
|
|
2130
|
+
throw new Error("Not connected. Call connect() first.");
|
|
2131
|
+
}
|
|
2132
|
+
if (this.connectionState !== "connected") {
|
|
2133
|
+
throw new Error(`Cannot remove cover photo. Connection state: ${this.connectionState}`);
|
|
2134
|
+
}
|
|
2135
|
+
await this.socket.removeCoverPhoto(coverPhotoId);
|
|
2136
|
+
return { success: true };
|
|
2137
|
+
}
|
|
2138
|
+
catch (error) {
|
|
2139
|
+
this.logger.error("Failed to remove cover photo:", error);
|
|
2140
|
+
return { success: false, error: error.message };
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2143
|
+
/**
|
|
2144
|
+
* Fetch details for a business order. The `tokenBase64` comes from a received
|
|
2145
|
+
* order message (`orderMessage.token`) — this is only usable after a customer
|
|
2146
|
+
* sends an order.
|
|
2147
|
+
*/
|
|
2148
|
+
async getOrderDetails(orderId, tokenBase64) {
|
|
2149
|
+
try {
|
|
2150
|
+
if (!this.socket) {
|
|
2151
|
+
throw new Error("Not connected. Call connect() first.");
|
|
2152
|
+
}
|
|
2153
|
+
if (this.connectionState !== "connected") {
|
|
2154
|
+
throw new Error(`Cannot get order details. Connection state: ${this.connectionState}`);
|
|
2155
|
+
}
|
|
2156
|
+
const details = await this.socket.getOrderDetails(orderId, tokenBase64);
|
|
2157
|
+
if (!details) {
|
|
2158
|
+
return null;
|
|
2159
|
+
}
|
|
2160
|
+
const price = (details.price ?? {});
|
|
2161
|
+
return {
|
|
2162
|
+
currency: price.currency,
|
|
2163
|
+
total: price.total,
|
|
2164
|
+
products: (details.products ?? []).map((p) => ({
|
|
2165
|
+
id: p.id,
|
|
2166
|
+
name: p.name,
|
|
2167
|
+
imageUrl: p.imageUrl,
|
|
2168
|
+
quantity: p.quantity,
|
|
2169
|
+
currency: p.currency,
|
|
2170
|
+
price: p.price,
|
|
2171
|
+
})),
|
|
2172
|
+
};
|
|
2173
|
+
}
|
|
2174
|
+
catch (error) {
|
|
2175
|
+
this.logger.error("Failed to get order details:", error);
|
|
2176
|
+
return null;
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
/** Add or edit a business quick reply. */
|
|
2180
|
+
async addQuickReply(quickReply) {
|
|
2181
|
+
try {
|
|
2182
|
+
if (!this.socket) {
|
|
2183
|
+
throw new Error("Not connected. Call connect() first.");
|
|
2184
|
+
}
|
|
2185
|
+
if (this.connectionState !== "connected") {
|
|
2186
|
+
throw new Error(`Cannot add quick reply. Connection state: ${this.connectionState}`);
|
|
2187
|
+
}
|
|
2188
|
+
await this.socket.addOrEditQuickReply({
|
|
2189
|
+
shortcut: quickReply.shortcut,
|
|
2190
|
+
message: quickReply.message,
|
|
2191
|
+
keywords: quickReply.keywords ?? [],
|
|
2192
|
+
count: 0,
|
|
2193
|
+
deleted: false,
|
|
2194
|
+
});
|
|
2195
|
+
return { success: true };
|
|
2196
|
+
}
|
|
2197
|
+
catch (error) {
|
|
2198
|
+
this.logger.error("Failed to add quick reply:", error);
|
|
2199
|
+
return { success: false, error: error.message };
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
/** Remove a business quick reply by its timestamp. */
|
|
2203
|
+
async removeQuickReply(timestamp) {
|
|
2204
|
+
try {
|
|
2205
|
+
if (!this.socket) {
|
|
2206
|
+
throw new Error("Not connected. Call connect() first.");
|
|
2207
|
+
}
|
|
2208
|
+
if (this.connectionState !== "connected") {
|
|
2209
|
+
throw new Error(`Cannot remove quick reply. Connection state: ${this.connectionState}`);
|
|
2210
|
+
}
|
|
2211
|
+
await this.socket.removeQuickReply(timestamp);
|
|
2212
|
+
return { success: true };
|
|
2213
|
+
}
|
|
2214
|
+
catch (error) {
|
|
2215
|
+
this.logger.error("Failed to remove quick reply:", error);
|
|
2216
|
+
return { success: false, error: error.message };
|
|
2217
|
+
}
|
|
2218
|
+
}
|
|
2008
2219
|
/**
|
|
2009
2220
|
* Get profile picture URL for a contact or group
|
|
2010
2221
|
* @param jidOrPhone - Contact's JID, phone number, or group JID
|
|
@@ -3713,6 +3924,368 @@ export class MiawClient extends EventEmitter {
|
|
|
3713
3924
|
}
|
|
3714
3925
|
}
|
|
3715
3926
|
// ============================================
|
|
3927
|
+
// Community Methods (v1.9.0)
|
|
3928
|
+
// ============================================
|
|
3929
|
+
/**
|
|
3930
|
+
* Map Baileys GroupMetadata (communities share the shape) to CommunityInfo.
|
|
3931
|
+
*/
|
|
3932
|
+
mapToCommunityInfo(metadata) {
|
|
3933
|
+
const participants = (metadata.participants || []).map((p) => ({
|
|
3934
|
+
jid: this.resolveLidToJid(p.id),
|
|
3935
|
+
role: p.admin === "superadmin"
|
|
3936
|
+
? "superadmin"
|
|
3937
|
+
: p.admin === "admin"
|
|
3938
|
+
? "admin"
|
|
3939
|
+
: "member",
|
|
3940
|
+
}));
|
|
3941
|
+
return {
|
|
3942
|
+
jid: metadata.id,
|
|
3943
|
+
name: metadata.subject,
|
|
3944
|
+
description: metadata.desc || undefined,
|
|
3945
|
+
owner: metadata.owner ? this.resolveLidToJid(metadata.owner) : undefined,
|
|
3946
|
+
createdAt: metadata.creation,
|
|
3947
|
+
participantCount: (metadata.participants || []).length,
|
|
3948
|
+
participants,
|
|
3949
|
+
announce: metadata.announce,
|
|
3950
|
+
restrict: metadata.restrict,
|
|
3951
|
+
};
|
|
3952
|
+
}
|
|
3953
|
+
/**
|
|
3954
|
+
* Create a new community.
|
|
3955
|
+
* @param name - Community name/subject
|
|
3956
|
+
* @param description - Optional community description
|
|
3957
|
+
*/
|
|
3958
|
+
async createCommunity(name, description) {
|
|
3959
|
+
try {
|
|
3960
|
+
if (!this.socket) {
|
|
3961
|
+
throw new Error("Not connected. Call connect() first.");
|
|
3962
|
+
}
|
|
3963
|
+
if (this.connectionState !== "connected") {
|
|
3964
|
+
throw new Error(`Cannot create community. Connection state: ${this.connectionState}`);
|
|
3965
|
+
}
|
|
3966
|
+
const nameValidation = validateGroupName(name);
|
|
3967
|
+
if (!nameValidation.valid) {
|
|
3968
|
+
return { success: false, error: nameValidation.error };
|
|
3969
|
+
}
|
|
3970
|
+
const metadata = await this.socket.communityCreate(name, description || "");
|
|
3971
|
+
if (!metadata) {
|
|
3972
|
+
return { success: true };
|
|
3973
|
+
}
|
|
3974
|
+
const info = this.mapToCommunityInfo(metadata);
|
|
3975
|
+
return { success: true, communityJid: info.jid, communityInfo: info };
|
|
3976
|
+
}
|
|
3977
|
+
catch (error) {
|
|
3978
|
+
this.logger.error("Failed to create community:", error);
|
|
3979
|
+
return { success: false, error: error.message };
|
|
3980
|
+
}
|
|
3981
|
+
}
|
|
3982
|
+
/** Get community metadata/information. */
|
|
3983
|
+
async getCommunityInfo(communityJid) {
|
|
3984
|
+
try {
|
|
3985
|
+
if (!this.socket) {
|
|
3986
|
+
throw new Error("Not connected. Call connect() first.");
|
|
3987
|
+
}
|
|
3988
|
+
if (this.connectionState !== "connected") {
|
|
3989
|
+
throw new Error(`Cannot get community info. Connection state: ${this.connectionState}`);
|
|
3990
|
+
}
|
|
3991
|
+
const metadata = await this.socket.communityMetadata(communityJid);
|
|
3992
|
+
return metadata ? this.mapToCommunityInfo(metadata) : null;
|
|
3993
|
+
}
|
|
3994
|
+
catch (error) {
|
|
3995
|
+
this.logger.error("Failed to get community info:", error);
|
|
3996
|
+
return null;
|
|
3997
|
+
}
|
|
3998
|
+
}
|
|
3999
|
+
/** Get a community's participants. */
|
|
4000
|
+
async getCommunityParticipants(communityJid) {
|
|
4001
|
+
const info = await this.getCommunityInfo(communityJid);
|
|
4002
|
+
return info?.participants || null;
|
|
4003
|
+
}
|
|
4004
|
+
/** Fetch all communities the account participates in. */
|
|
4005
|
+
async getAllCommunities() {
|
|
4006
|
+
try {
|
|
4007
|
+
if (!this.socket) {
|
|
4008
|
+
throw new Error("Not connected. Call connect() first.");
|
|
4009
|
+
}
|
|
4010
|
+
if (this.connectionState !== "connected") {
|
|
4011
|
+
throw new Error(`Cannot fetch communities. Connection state: ${this.connectionState}`);
|
|
4012
|
+
}
|
|
4013
|
+
const all = await this.socket.communityFetchAllParticipating();
|
|
4014
|
+
return Object.values(all || {}).map((m) => this.mapToCommunityInfo(m));
|
|
4015
|
+
}
|
|
4016
|
+
catch (error) {
|
|
4017
|
+
this.logger.error("Failed to fetch communities:", error);
|
|
4018
|
+
return [];
|
|
4019
|
+
}
|
|
4020
|
+
}
|
|
4021
|
+
/** Update a community's name/subject. */
|
|
4022
|
+
async updateCommunityName(communityJid, name) {
|
|
4023
|
+
try {
|
|
4024
|
+
if (!this.socket) {
|
|
4025
|
+
throw new Error("Not connected. Call connect() first.");
|
|
4026
|
+
}
|
|
4027
|
+
if (this.connectionState !== "connected") {
|
|
4028
|
+
throw new Error(`Cannot update community. Connection state: ${this.connectionState}`);
|
|
4029
|
+
}
|
|
4030
|
+
await this.socket.communityUpdateSubject(communityJid, name);
|
|
4031
|
+
return { success: true };
|
|
4032
|
+
}
|
|
4033
|
+
catch (error) {
|
|
4034
|
+
this.logger.error("Failed to update community name:", error);
|
|
4035
|
+
return { success: false, error: error.message };
|
|
4036
|
+
}
|
|
4037
|
+
}
|
|
4038
|
+
/** Update a community's description. */
|
|
4039
|
+
async updateCommunityDescription(communityJid, description) {
|
|
4040
|
+
try {
|
|
4041
|
+
if (!this.socket) {
|
|
4042
|
+
throw new Error("Not connected. Call connect() first.");
|
|
4043
|
+
}
|
|
4044
|
+
if (this.connectionState !== "connected") {
|
|
4045
|
+
throw new Error(`Cannot update community. Connection state: ${this.connectionState}`);
|
|
4046
|
+
}
|
|
4047
|
+
await this.socket.communityUpdateDescription(communityJid, description);
|
|
4048
|
+
return { success: true };
|
|
4049
|
+
}
|
|
4050
|
+
catch (error) {
|
|
4051
|
+
this.logger.error("Failed to update community description:", error);
|
|
4052
|
+
return { success: false, error: error.message };
|
|
4053
|
+
}
|
|
4054
|
+
}
|
|
4055
|
+
/** Leave a community. */
|
|
4056
|
+
async leaveCommunity(communityJid) {
|
|
4057
|
+
try {
|
|
4058
|
+
if (!this.socket) {
|
|
4059
|
+
throw new Error("Not connected. Call connect() first.");
|
|
4060
|
+
}
|
|
4061
|
+
if (this.connectionState !== "connected") {
|
|
4062
|
+
throw new Error(`Cannot leave community. Connection state: ${this.connectionState}`);
|
|
4063
|
+
}
|
|
4064
|
+
await this.socket.communityLeave(communityJid);
|
|
4065
|
+
return { success: true };
|
|
4066
|
+
}
|
|
4067
|
+
catch (error) {
|
|
4068
|
+
this.logger.error("Failed to leave community:", error);
|
|
4069
|
+
return { success: false, error: error.message };
|
|
4070
|
+
}
|
|
4071
|
+
}
|
|
4072
|
+
/**
|
|
4073
|
+
* Create a new group inside a community.
|
|
4074
|
+
* @param communityJid - Parent community JID
|
|
4075
|
+
* @param name - Group name/subject
|
|
4076
|
+
* @param participants - Initial members (phone numbers or JIDs)
|
|
4077
|
+
*/
|
|
4078
|
+
async createCommunityGroup(communityJid, name, participants = []) {
|
|
4079
|
+
try {
|
|
4080
|
+
if (!this.socket) {
|
|
4081
|
+
throw new Error("Not connected. Call connect() first.");
|
|
4082
|
+
}
|
|
4083
|
+
if (this.connectionState !== "connected") {
|
|
4084
|
+
throw new Error(`Cannot create community group. Connection state: ${this.connectionState}`);
|
|
4085
|
+
}
|
|
4086
|
+
const nameValidation = validateGroupName(name);
|
|
4087
|
+
if (!nameValidation.valid) {
|
|
4088
|
+
return { success: false, error: nameValidation.error };
|
|
4089
|
+
}
|
|
4090
|
+
const formatted = participants.map((p) => MessageHandler.formatPhoneToJid(p));
|
|
4091
|
+
const metadata = await this.socket.communityCreateGroup(name, formatted, communityJid);
|
|
4092
|
+
if (!metadata) {
|
|
4093
|
+
return { success: true };
|
|
4094
|
+
}
|
|
4095
|
+
const info = this.mapToCommunityInfo(metadata);
|
|
4096
|
+
return {
|
|
4097
|
+
success: true,
|
|
4098
|
+
groupJid: info.jid,
|
|
4099
|
+
groupInfo: {
|
|
4100
|
+
jid: info.jid,
|
|
4101
|
+
name: info.name,
|
|
4102
|
+
description: info.description,
|
|
4103
|
+
owner: info.owner,
|
|
4104
|
+
createdAt: info.createdAt,
|
|
4105
|
+
participantCount: info.participantCount,
|
|
4106
|
+
participants: info.participants,
|
|
4107
|
+
announce: info.announce,
|
|
4108
|
+
restrict: info.restrict,
|
|
4109
|
+
},
|
|
4110
|
+
};
|
|
4111
|
+
}
|
|
4112
|
+
catch (error) {
|
|
4113
|
+
this.logger.error("Failed to create community group:", error);
|
|
4114
|
+
return { success: false, error: error.message };
|
|
4115
|
+
}
|
|
4116
|
+
}
|
|
4117
|
+
/** Link an existing group to a community. */
|
|
4118
|
+
async linkGroupToCommunity(groupJid, communityJid) {
|
|
4119
|
+
try {
|
|
4120
|
+
if (!this.socket) {
|
|
4121
|
+
throw new Error("Not connected. Call connect() first.");
|
|
4122
|
+
}
|
|
4123
|
+
if (this.connectionState !== "connected") {
|
|
4124
|
+
throw new Error(`Cannot link group. Connection state: ${this.connectionState}`);
|
|
4125
|
+
}
|
|
4126
|
+
await this.socket.communityLinkGroup(groupJid, communityJid);
|
|
4127
|
+
return { success: true };
|
|
4128
|
+
}
|
|
4129
|
+
catch (error) {
|
|
4130
|
+
this.logger.error("Failed to link group to community:", error);
|
|
4131
|
+
return { success: false, error: error.message };
|
|
4132
|
+
}
|
|
4133
|
+
}
|
|
4134
|
+
/** Unlink a group from a community. */
|
|
4135
|
+
async unlinkGroupFromCommunity(groupJid, communityJid) {
|
|
4136
|
+
try {
|
|
4137
|
+
if (!this.socket) {
|
|
4138
|
+
throw new Error("Not connected. Call connect() first.");
|
|
4139
|
+
}
|
|
4140
|
+
if (this.connectionState !== "connected") {
|
|
4141
|
+
throw new Error(`Cannot unlink group. Connection state: ${this.connectionState}`);
|
|
4142
|
+
}
|
|
4143
|
+
await this.socket.communityUnlinkGroup(groupJid, communityJid);
|
|
4144
|
+
return { success: true };
|
|
4145
|
+
}
|
|
4146
|
+
catch (error) {
|
|
4147
|
+
this.logger.error("Failed to unlink group from community:", error);
|
|
4148
|
+
return { success: false, error: error.message };
|
|
4149
|
+
}
|
|
4150
|
+
}
|
|
4151
|
+
/** List the groups linked inside a community. */
|
|
4152
|
+
async getLinkedGroups(communityJid) {
|
|
4153
|
+
try {
|
|
4154
|
+
if (!this.socket) {
|
|
4155
|
+
throw new Error("Not connected. Call connect() first.");
|
|
4156
|
+
}
|
|
4157
|
+
if (this.connectionState !== "connected") {
|
|
4158
|
+
throw new Error(`Cannot fetch linked groups. Connection state: ${this.connectionState}`);
|
|
4159
|
+
}
|
|
4160
|
+
const result = await this.socket.communityFetchLinkedGroups(communityJid);
|
|
4161
|
+
return (result?.linkedGroups || []).map((g) => ({
|
|
4162
|
+
id: g.id,
|
|
4163
|
+
subject: g.subject,
|
|
4164
|
+
creation: g.creation,
|
|
4165
|
+
owner: g.owner ? this.resolveLidToJid(g.owner) : undefined,
|
|
4166
|
+
size: g.size,
|
|
4167
|
+
}));
|
|
4168
|
+
}
|
|
4169
|
+
catch (error) {
|
|
4170
|
+
this.logger.error("Failed to fetch linked groups:", error);
|
|
4171
|
+
return [];
|
|
4172
|
+
}
|
|
4173
|
+
}
|
|
4174
|
+
/** Shared helper for community participant operations. */
|
|
4175
|
+
async communityParticipantsOperation(communityJid, participants, action) {
|
|
4176
|
+
if (!this.socket) {
|
|
4177
|
+
throw new Error("Not connected. Call connect() first.");
|
|
4178
|
+
}
|
|
4179
|
+
if (this.connectionState !== "connected") {
|
|
4180
|
+
throw new Error(`Cannot perform community operation. Connection state: ${this.connectionState}`);
|
|
4181
|
+
}
|
|
4182
|
+
const formatted = participants.map((p) => MessageHandler.formatPhoneToJid(p));
|
|
4183
|
+
const results = await this.socket.communityParticipantsUpdate(communityJid, formatted, action);
|
|
4184
|
+
return results.map((r) => ({
|
|
4185
|
+
jid: r.jid || "",
|
|
4186
|
+
status: r.status,
|
|
4187
|
+
success: r.status === "200",
|
|
4188
|
+
}));
|
|
4189
|
+
}
|
|
4190
|
+
/** Add members to a community. */
|
|
4191
|
+
async addCommunityMembers(communityJid, participants) {
|
|
4192
|
+
return this.communityParticipantsOperation(communityJid, participants, "add");
|
|
4193
|
+
}
|
|
4194
|
+
/** Remove members from a community. */
|
|
4195
|
+
async removeCommunityMembers(communityJid, participants) {
|
|
4196
|
+
return this.communityParticipantsOperation(communityJid, participants, "remove");
|
|
4197
|
+
}
|
|
4198
|
+
/** Promote members to community admin. */
|
|
4199
|
+
async promoteCommunityMembers(communityJid, participants) {
|
|
4200
|
+
return this.communityParticipantsOperation(communityJid, participants, "promote");
|
|
4201
|
+
}
|
|
4202
|
+
/** Demote community admins to members. */
|
|
4203
|
+
async demoteCommunityMembers(communityJid, participants) {
|
|
4204
|
+
return this.communityParticipantsOperation(communityJid, participants, "demote");
|
|
4205
|
+
}
|
|
4206
|
+
/** Get the community invite link. */
|
|
4207
|
+
async getCommunityInviteLink(communityJid) {
|
|
4208
|
+
try {
|
|
4209
|
+
if (!this.socket) {
|
|
4210
|
+
throw new Error("Not connected. Call connect() first.");
|
|
4211
|
+
}
|
|
4212
|
+
if (this.connectionState !== "connected") {
|
|
4213
|
+
throw new Error(`Cannot get invite link. Connection state: ${this.connectionState}`);
|
|
4214
|
+
}
|
|
4215
|
+
const code = await this.socket.communityInviteCode(communityJid);
|
|
4216
|
+
return code ? `https://chat.whatsapp.com/${code}` : null;
|
|
4217
|
+
}
|
|
4218
|
+
catch (error) {
|
|
4219
|
+
this.logger.error("Failed to get community invite link:", error);
|
|
4220
|
+
return null;
|
|
4221
|
+
}
|
|
4222
|
+
}
|
|
4223
|
+
/** Revoke the community invite link and get a new one. */
|
|
4224
|
+
async revokeCommunityInvite(communityJid) {
|
|
4225
|
+
try {
|
|
4226
|
+
if (!this.socket) {
|
|
4227
|
+
throw new Error("Not connected. Call connect() first.");
|
|
4228
|
+
}
|
|
4229
|
+
if (this.connectionState !== "connected") {
|
|
4230
|
+
throw new Error(`Cannot revoke invite. Connection state: ${this.connectionState}`);
|
|
4231
|
+
}
|
|
4232
|
+
const code = await this.socket.communityRevokeInvite(communityJid);
|
|
4233
|
+
return code ? `https://chat.whatsapp.com/${code}` : null;
|
|
4234
|
+
}
|
|
4235
|
+
catch (error) {
|
|
4236
|
+
this.logger.error("Failed to revoke community invite:", error);
|
|
4237
|
+
return null;
|
|
4238
|
+
}
|
|
4239
|
+
}
|
|
4240
|
+
/** Accept a community invite and join. */
|
|
4241
|
+
async acceptCommunityInvite(inviteCode) {
|
|
4242
|
+
try {
|
|
4243
|
+
if (!this.socket) {
|
|
4244
|
+
throw new Error("Not connected. Call connect() first.");
|
|
4245
|
+
}
|
|
4246
|
+
if (this.connectionState !== "connected") {
|
|
4247
|
+
throw new Error(`Cannot accept invite. Connection state: ${this.connectionState}`);
|
|
4248
|
+
}
|
|
4249
|
+
let code = inviteCode;
|
|
4250
|
+
if (inviteCode.includes("chat.whatsapp.com/")) {
|
|
4251
|
+
code = inviteCode.split("chat.whatsapp.com/")[1];
|
|
4252
|
+
}
|
|
4253
|
+
const jid = await this.socket.communityAcceptInvite(code);
|
|
4254
|
+
return jid || null;
|
|
4255
|
+
}
|
|
4256
|
+
catch (error) {
|
|
4257
|
+
this.logger.error("Failed to accept community invite:", error);
|
|
4258
|
+
return null;
|
|
4259
|
+
}
|
|
4260
|
+
}
|
|
4261
|
+
/** Preview a community from an invite code without joining. */
|
|
4262
|
+
async getCommunityInviteInfo(inviteCode) {
|
|
4263
|
+
try {
|
|
4264
|
+
if (!this.socket) {
|
|
4265
|
+
throw new Error("Not connected. Call connect() first.");
|
|
4266
|
+
}
|
|
4267
|
+
if (this.connectionState !== "connected") {
|
|
4268
|
+
throw new Error(`Cannot get invite info. Connection state: ${this.connectionState}`);
|
|
4269
|
+
}
|
|
4270
|
+
let code = inviteCode;
|
|
4271
|
+
if (inviteCode.includes("chat.whatsapp.com/")) {
|
|
4272
|
+
code = inviteCode.split("chat.whatsapp.com/")[1];
|
|
4273
|
+
}
|
|
4274
|
+
const metadata = await this.socket.communityGetInviteInfo(code);
|
|
4275
|
+
return {
|
|
4276
|
+
jid: metadata.id,
|
|
4277
|
+
name: metadata.subject,
|
|
4278
|
+
description: metadata.desc || undefined,
|
|
4279
|
+
participantCount: metadata.size || metadata.participants?.length || 0,
|
|
4280
|
+
createdAt: metadata.creation,
|
|
4281
|
+
};
|
|
4282
|
+
}
|
|
4283
|
+
catch (error) {
|
|
4284
|
+
this.logger.error("Failed to get community invite info:", error);
|
|
4285
|
+
return null;
|
|
4286
|
+
}
|
|
4287
|
+
}
|
|
4288
|
+
// ============================================
|
|
3716
4289
|
// Profile Management Methods (v0.8.0)
|
|
3717
4290
|
// ============================================
|
|
3718
4291
|
/**
|