@baileys-md/baileys
Version:
Baileys WhatsApp API
413 lines • 14.6 kB
JavaScript
//=======================================================//
import { getBinaryNodeChild, getBinaryNodeChildren, getBinaryNodeChildString, jidEncode, jidNormalizedUser } from "../WABinary/index.js";
import { generateMessageID, generateMessageIDV2, unixTimestampSeconds } from "../Utils/index.js";
import { WAMessageStubType } from "../Types/index.js";
import { makeBusinessSocket } from "./business.js";
import { proto } from "../../WAProto/index.js";
import logger from "../Utils/logger.js";
//=======================================================//
export const makeCommunitiesSocket = (config) => {
const sock = makeBusinessSocket(config);
const { authState, ev, query, upsertMessage } = sock;
const communityQuery = async (jid, type, content) => query({
tag: "iq",
attrs: {
type,
xmlns: "w:g2",
to: jid
},
content
});
const communityMetadata = async (jid) => {
const result = await communityQuery(jid, "get", [{ tag: "query", attrs: { request: "interactive" } }]);
return extractCommunityMetadata(result);
};
const communityFetchAllParticipating = async () => {
const result = await query({
tag: "iq",
attrs: {
to: "@g.us",
xmlns: "w:g2",
type: "get"
},
content: [
{
tag: "participating",
attrs: {},
content: [
{ tag: "participants", attrs: {} },
{ tag: "description", attrs: {} }
]
}
]
});
const data = {};
const communitiesChild = getBinaryNodeChild(result, "communities");
if (communitiesChild) {
const communities = getBinaryNodeChildren(communitiesChild, "community");
for (const communityNode of communities) {
const meta = extractCommunityMetadata({
tag: "result",
attrs: {},
content: [communityNode]
});
data[meta.id] = meta;
}
}
sock.ev.emit("groups.update", Object.values(data));
return data;
};
async function parseGroupResult(node) {
logger.info({ node }, "parseGroupResult");
const groupNode = getBinaryNodeChild(node, "group");
if (groupNode) {
try {
logger.info({ groupNode }, "groupNode");
const metadata = await sock.groupMetadata(`${groupNode.attrs.id}@g.us`);
return metadata ? metadata : Optional.empty();
}
catch (error) {
console.error("Error parsing group metadata:", error);
return Optional.empty();
}
}
return Optional.empty();
}
const Optional = {
empty: () => null,
of: (value) => (value !== null ? { value } : null)
};
sock.ws.on("CB:ib,,dirty", async (node) => {
const { attrs } = getBinaryNodeChild(node, "dirty");
if (attrs.type !== "communities") {
return;
}
await communityFetchAllParticipating();
await sock.cleanDirtyBits("groups");
});
return {
...sock,
communityMetadata,
communityCreate: async (subject, body) => {
const descriptionId = generateMessageID().substring(0, 12);
const result = await communityQuery("@g.us", "set", [
{
tag: "create",
attrs: { subject },
content: [
{
tag: "description",
attrs: { id: descriptionId },
content: [
{
tag: "body",
attrs: {},
content: Buffer.from(body || "", "utf-8")
}
]
},
{
tag: "parent",
attrs: { default_membership_approval_mode: "request_required" }
},
{
tag: "allow_non_admin_sub_group_creation",
attrs: {}
},
{
tag: "create_general_chat",
attrs: {}
}
]
}
]);
return await parseGroupResult(result);
},
communityCreateGroup: async (subject, participants, parentCommunityJid) => {
const key = generateMessageIDV2();
const result = await communityQuery("@g.us", "set", [
{
tag: "create",
attrs: {
subject,
key
},
content: [
...participants.map(jid => ({
tag: "participant",
attrs: { jid }
})),
{ tag: "linked_parent", attrs: { jid: parentCommunityJid } }
]
}
]);
return await parseGroupResult(result);
},
communityLeave: async (id) => {
await communityQuery("@g.us", "set", [
{
tag: "leave",
attrs: {},
content: [{ tag: "community", attrs: { id } }]
}
]);
},
communityUpdateSubject: async (jid, subject) => {
await communityQuery(jid, "set", [
{
tag: "subject",
attrs: {},
content: Buffer.from(subject, "utf-8")
}
]);
},
communityLinkGroup: async (groupJid, parentCommunityJid) => {
await communityQuery(parentCommunityJid, "set", [
{
tag: "links",
attrs: {},
content: [
{
tag: "link",
attrs: { link_type: "sub_group" },
content: [{ tag: "group", attrs: { jid: groupJid } }]
}
]
}
]);
},
communityUnlinkGroup: async (groupJid, parentCommunityJid) => {
await communityQuery(parentCommunityJid, "set", [
{
tag: "unlink",
attrs: { unlink_type: "sub_group" },
content: [{ tag: "group", attrs: { jid: groupJid } }]
}
]);
},
communityFetchLinkedGroups: async (jid) => {
let communityJid = jid;
let isCommunity = false;
const metadata = await sock.groupMetadata(jid);
if (metadata.linkedParent) {
communityJid = metadata.linkedParent;
}
else {
isCommunity = true;
}
const result = await communityQuery(communityJid, "get", [{ tag: "sub_groups", attrs: {} }]);
const linkedGroupsData = [];
const subGroupsNode = getBinaryNodeChild(result, "sub_groups");
if (subGroupsNode) {
const groupNodes = getBinaryNodeChildren(subGroupsNode, "group");
for (const groupNode of groupNodes) {
linkedGroupsData.push({
id: groupNode.attrs.id ? jidEncode(groupNode.attrs.id, "g.us") : undefined,
subject: groupNode.attrs.subject || "",
creation: groupNode.attrs.creation ? Number(groupNode.attrs.creation) : undefined,
owner: groupNode.attrs.creator ? jidNormalizedUser(groupNode.attrs.creator) : undefined,
size: groupNode.attrs.size ? Number(groupNode.attrs.size) : undefined
});
}
}
return {
communityJid,
isCommunity,
linkedGroups: linkedGroupsData
};
},
communityRequestParticipantsList: async (jid) => {
const result = await communityQuery(jid, "get", [
{
tag: "membership_approval_requests",
attrs: {}
}
]);
const node = getBinaryNodeChild(result, "membership_approval_requests");
const participants = getBinaryNodeChildren(node, "membership_approval_request");
return participants.map(v => v.attrs);
},
communityRequestParticipantsUpdate: async (jid, participants, action) => {
const result = await communityQuery(jid, "set", [
{
tag: "membership_requests_action",
attrs: {},
content: [
{
tag: action,
attrs: {},
content: participants.map(jid => ({
tag: "participant",
attrs: { jid }
}))
}
]
}
]);
const node = getBinaryNodeChild(result, "membership_requests_action");
const nodeAction = getBinaryNodeChild(node, action);
const participantsAffected = getBinaryNodeChildren(nodeAction, "participant");
return participantsAffected.map(p => {
return { status: p.attrs.error || "200", jid: p.attrs.jid };
});
},
communityParticipantsUpdate: async (jid, participants, action) => {
const result = await communityQuery(jid, "set", [
{
tag: action,
attrs: {},
content: participants.map(jid => ({
tag: "participant",
attrs: { jid }
}))
}
]);
const node = getBinaryNodeChild(result, action);
const participantsAffected = getBinaryNodeChildren(node, "participant");
return participantsAffected.map(p => {
return { status: p.attrs.error || "200", jid: p.attrs.jid, content: p };
});
},
communityUpdateDescription: async (jid, description) => {
const metadata = await communityMetadata(jid);
const prev = metadata.descId ?? null;
await communityQuery(jid, "set", [
{
tag: "description",
attrs: {
...(description ? { id: generateMessageID() } : { delete: "true" }),
...(prev ? { prev } : {})
},
content: description ? [{ tag: "body", attrs: {}, content: Buffer.from(description, "utf-8") }] : undefined
}
]);
},
communityInviteCode: async (jid) => {
const result = await communityQuery(jid, "get", [{ tag: "invite", attrs: {} }]);
const inviteNode = getBinaryNodeChild(result, "invite");
return inviteNode?.attrs.code;
},
communityRevokeInvite: async (jid) => {
const result = await communityQuery(jid, "set", [{ tag: "invite", attrs: {} }]);
const inviteNode = getBinaryNodeChild(result, "invite");
return inviteNode?.attrs.code;
},
communityAcceptInvite: async (code) => {
const results = await communityQuery("@g.us", "set", [{ tag: "invite", attrs: { code } }]);
const result = getBinaryNodeChild(results, "community");
return result?.attrs.jid;
},
communityRevokeInviteV4: async (communityJid, invitedJid) => {
const result = await communityQuery(communityJid, "set", [
{ tag: "revoke", attrs: {}, content: [{ tag: "participant", attrs: { jid: invitedJid } }] }
]);
return !!result;
},
communityAcceptInviteV4: ev.createBufferedFunction(async (key, inviteMessage) => {
key = typeof key === "string" ? { remoteJid: key } : key;
const results = await communityQuery(inviteMessage.groupJid, "set", [
{
tag: "accept",
attrs: {
code: inviteMessage.inviteCode,
expiration: inviteMessage.inviteExpiration.toString(),
admin: key.remoteJid
}
}
]);
if (key.id) {
inviteMessage = proto.Message.GroupInviteMessage.fromObject(inviteMessage);
inviteMessage.inviteExpiration = 0;
inviteMessage.inviteCode = "";
ev.emit("messages.update", [
{
key,
update: {
message: {
groupInviteMessage: inviteMessage
}
}
}
]);
}
await upsertMessage({
key: {
remoteJid: inviteMessage.groupJid,
id: generateMessageIDV2(sock.user?.id),
fromMe: false,
participant: key.remoteJid
},
messageStubType: WAMessageStubType.GROUP_PARTICIPANT_ADD,
messageStubParameters: [JSON.stringify(authState.creds.me)],
participant: key.remoteJid,
messageTimestamp: unixTimestampSeconds()
}, "notify");
return results.attrs.from;
}),
communityGetInviteInfo: async (code) => {
const results = await communityQuery("@g.us", "get", [{ tag: "invite", attrs: { code } }]);
return extractCommunityMetadata(results);
},
communityToggleEphemeral: async (jid, ephemeralExpiration) => {
const content = ephemeralExpiration
? { tag: "ephemeral", attrs: { expiration: ephemeralExpiration.toString() } }
: { tag: "not_ephemeral", attrs: {} };
await communityQuery(jid, "set", [content]);
},
communitySettingUpdate: async (jid, setting) => {
await communityQuery(jid, "set", [{ tag: setting, attrs: {} }]);
},
communityMemberAddMode: async (jid, mode) => {
await communityQuery(jid, "set", [{ tag: "member_add_mode", attrs: {}, content: mode }]);
},
communityJoinApprovalMode: async (jid, mode) => {
await communityQuery(jid, "set", [
{ tag: "membership_approval_mode", attrs: {}, content: [{ tag: "community_join", attrs: { state: mode } }] }
]);
},
communityFetchAllParticipating
};
};
export const extractCommunityMetadata = (result) => {
const community = getBinaryNodeChild(result, "community");
const descChild = getBinaryNodeChild(community, "description");
let desc;
let descId;
if (descChild) {
desc = getBinaryNodeChildString(descChild, "body");
descId = descChild.attrs.id;
}
const communityId = community.attrs.id?.includes("@")
? community.attrs.id
: jidEncode(community.attrs.id || "", "g.us");
const eph = getBinaryNodeChild(community, "ephemeral")?.attrs.expiration;
const memberAddMode = getBinaryNodeChildString(community, "member_add_mode") === "all_member_add";
const metadata = {
id: communityId,
subject: community.attrs.subject || "",
subjectOwner: community.attrs.s_o,
subjectTime: Number(community.attrs.s_t || 0),
size: getBinaryNodeChildren(community, "participant").length,
creation: Number(community.attrs.creation || 0),
owner: community.attrs.creator ? jidNormalizedUser(community.attrs.creator) : undefined,
desc,
descId,
linkedParent: getBinaryNodeChild(community, "linked_parent")?.attrs.jid || undefined,
restrict: !!getBinaryNodeChild(community, "locked"),
announce: !!getBinaryNodeChild(community, "announcement"),
isCommunity: !!getBinaryNodeChild(community, "parent"),
isCommunityAnnounce: !!getBinaryNodeChild(community, "default_sub_community"),
joinApprovalMode: !!getBinaryNodeChild(community, "membership_approval_mode"),
memberAddMode,
participants: getBinaryNodeChildren(community, "participant").map(({ attrs }) => {
return {
id: attrs.jid,
admin: (attrs.type || null)
};
}),
ephemeralDuration: eph ? +eph : undefined,
addressingMode: getBinaryNodeChildString(community, "addressing_mode")
};
return metadata;
};
//=======================================================//