feat: groups share history mode - fix #2282

This commit is contained in:
devlikepro committed 2026-10-02 13:07:08 +07:00
1 parent cc650fca3b
commit 0da9ef68cf
12 files changed
+185 -31

No files matched your search

+17 -1
View File
@@ -362,13 +362,29 @@ export class GroupsController {
@WorkingSessionParam session: WhatsappSession,
@Param('id') id: string,
@Body() request: SettingsMemberShareHistoryMode,
) {
): Promise<boolean> {
return session.setMemberShareHistoryMode(
id,
request.membersCanShareHistory,
);
}
@Get(':id/settings/security/member-share-history-mode')
@SessionApiParam
@GroupIdApiParam
@CheckPolicies(CanSession(Action.Read, FromParam('session')))
@ApiOperation({
summary: 'Get settings - members can send message history to new members',
description:
'The group settings for whether members can share message history with new members, or only admins can.',
})
getMemberShareHistoryMode(
@WorkingSessionParam session: WhatsappSession,
@Param('id') id: string,
): Promise<SettingsMemberShareHistoryMode> {
return session.getMemberShareHistoryMode(id);
}
@Put(':id/settings/security/membership-approval')
@SessionApiParam
@GroupIdApiParam
+42
View File
@@ -9,6 +9,7 @@ import {
GroupIdInput,
GroupJoinInput,
GroupMembershipApprovalInput,
GroupMemberShareHistoryInput,
GroupParticipantsInput,
GroupPictureInput,
GroupsListInput,
@@ -405,6 +406,47 @@ export class GroupTools extends McpController {
});
}
@Tool('groups-get-member-share-history', {
title: 'Get member share history setting',
description:
'Get whether all members or only admins can share message history with new members',
inputSchema: GroupIdInput,
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
},
})
async getMemberShareHistory({ session, id }: z.infer<typeof GroupIdInput>) {
return this.textRequest({
method: 'GET',
url: `/api/${session}/groups/${id}/settings/security/member-share-history-mode`,
});
}
@Tool('groups-set-member-share-history', {
title: 'Set member share history setting',
description:
'Allow all members or only admins to share message history with new members',
inputSchema: GroupMemberShareHistoryInput,
annotations: {
readOnlyHint: false,
destructiveHint: false,
idempotentHint: true,
},
})
async setMemberShareHistory({
session,
id,
...body
}: z.infer<typeof GroupMemberShareHistoryInput>) {
return this.textRequest({
method: 'PUT',
url: `/api/${session}/groups/${id}/settings/security/member-share-history-mode`,
data: body,
});
}
@Tool('groups-get-join-requests', {
title: 'Get pending requests to join the group',
description: 'Get pending requests to join the group',
+8
View File
@@ -8,6 +8,7 @@ import {
JoinGroupRequest,
ParticipantsRequest,
SettingsMembershipApproval,
SettingsMemberShareHistoryMode,
SettingsSecurityChangeInfo,
SubjectRequest,
} from '@waha/structures/groups.dto';
@@ -72,3 +73,10 @@ export const GroupMembershipApprovalInput = DtoToZod(
session: SessionField,
id: GroupIdField,
});
export const GroupMemberShareHistoryInput = DtoToZod(
SettingsMemberShareHistoryMode,
).extend({
session: SessionField,
id: GroupIdField,
});
+11 -1
View File
@@ -114,6 +114,7 @@ import {
GroupsListFields,
ParticipantsRequest,
SettingsMemberAddMode,
SettingsMemberShareHistoryMode,
SettingsMembershipApproval,
SettingsSecurityChangeInfo,
} from '../../structures/groups.dto';
@@ -1025,7 +1026,16 @@ export abstract class WhatsappSession {
throw new NotImplementedByEngineError();
}
public setMemberShareHistoryMode(id, value) {
public getMemberShareHistoryMode(
id: string,
): Promise<SettingsMemberShareHistoryMode> {
throw new NotImplementedByEngineError();
}
public setMemberShareHistoryMode(
id: string,
value: boolean,
): Promise<boolean> {
throw new NotImplementedByEngineError();
}
+13
View File
@@ -11726,6 +11726,15 @@ export namespace messages {
responseSerialize: (message: Empty) => Buffer.from(message.serialize()),
responseDeserialize: (bytes: Buffer) => Empty.deserialize(new Uint8Array(bytes))
},
SetGroupMemberShareHistoryMode: {
path: "/messages.MessageService/SetGroupMemberShareHistoryMode",
requestStream: false,
responseStream: false,
requestSerialize: (message: JidBoolRequest) => Buffer.from(message.serialize()),
requestDeserialize: (bytes: Buffer) => JidBoolRequest.deserialize(new Uint8Array(bytes)),
responseSerialize: (message: Empty) => Buffer.from(message.serialize()),
responseDeserialize: (bytes: Buffer) => Empty.deserialize(new Uint8Array(bytes))
},
UpdateGroupParticipants: {
path: "/messages.MessageService/UpdateGroupParticipants",
requestStream: false,
@@ -12144,6 +12153,7 @@ export namespace messages {
abstract SetGroupLocked(call: grpc_1.ServerUnaryCall<JidBoolRequest, Empty>, callback: grpc_1.sendUnaryData<Empty>): void;
abstract SetGroupAnnounce(call: grpc_1.ServerUnaryCall<JidBoolRequest, Empty>, callback: grpc_1.sendUnaryData<Empty>): void;
abstract SetGroupMemberAddMode(call: grpc_1.ServerUnaryCall<JidBoolRequest, Empty>, callback: grpc_1.sendUnaryData<Empty>): void;
abstract SetGroupMemberShareHistoryMode(call: grpc_1.ServerUnaryCall<JidBoolRequest, Empty>, callback: grpc_1.sendUnaryData<Empty>): void;
abstract UpdateGroupParticipants(call: grpc_1.ServerUnaryCall<UpdateParticipantsRequest, JsonList>, callback: grpc_1.sendUnaryData<JsonList>): void;
abstract SetGroupJoinApprovalMode(call: grpc_1.ServerUnaryCall<JidBoolRequest, Empty>, callback: grpc_1.sendUnaryData<Empty>): void;
abstract GetGroupRequestParticipants(call: grpc_1.ServerUnaryCall<JidRequest, JsonList>, callback: grpc_1.sendUnaryData<JsonList>): void;
@@ -12279,6 +12289,9 @@ export namespace messages {
SetGroupMemberAddMode: GrpcUnaryServiceInterface<JidBoolRequest, Empty> = (message: JidBoolRequest, metadata: grpc_1.Metadata | grpc_1.CallOptions | grpc_1.requestCallback<Empty>, options?: grpc_1.CallOptions | grpc_1.requestCallback<Empty>, callback?: grpc_1.requestCallback<Empty>): grpc_1.ClientUnaryCall => {
return super.SetGroupMemberAddMode(message, metadata, options, callback);
};
SetGroupMemberShareHistoryMode: GrpcUnaryServiceInterface<JidBoolRequest, Empty> = (message: JidBoolRequest, metadata: grpc_1.Metadata | grpc_1.CallOptions | grpc_1.requestCallback<Empty>, options?: grpc_1.CallOptions | grpc_1.requestCallback<Empty>, callback?: grpc_1.requestCallback<Empty>): grpc_1.ClientUnaryCall => {
return super.SetGroupMemberShareHistoryMode(message, metadata, options, callback);
};
UpdateGroupParticipants: GrpcUnaryServiceInterface<UpdateParticipantsRequest, JsonList> = (message: UpdateParticipantsRequest, metadata: grpc_1.Metadata | grpc_1.CallOptions | grpc_1.requestCallback<JsonList>, options?: grpc_1.CallOptions | grpc_1.requestCallback<JsonList>, callback?: grpc_1.requestCallback<JsonList>): grpc_1.ClientUnaryCall => {
return super.UpdateGroupParticipants(message, metadata, options, callback);
};
@@ -1073,6 +1073,18 @@ setGroupMemberAddMode: {
responseDeserialize: deserialize_messages_Empty,
},
// who can add members - true - all members, false - admins only
setGroupMemberShareHistoryMode: {
path: '/messages.MessageService/SetGroupMemberShareHistoryMode',
requestStream: false,
responseStream: false,
requestType: gows_pb.JidBoolRequest,
responseType: gows_pb.Empty,
requestSerialize: serialize_messages_JidBoolRequest,
requestDeserialize: deserialize_messages_JidBoolRequest,
responseSerialize: serialize_messages_Empty,
responseDeserialize: deserialize_messages_Empty,
},
// who can share message history with new members - true - all members, false - admins only
updateGroupParticipants: {
path: '/messages.MessageService/UpdateGroupParticipants',
requestStream: false,
@@ -133,6 +133,7 @@ import {
Participant,
ParticipantsRequest,
SettingsMemberAddMode,
SettingsMemberShareHistoryMode,
SettingsMembershipApproval,
SettingsSecurityChangeInfo,
} from '@waha/structures/groups.dto';
@@ -1804,6 +1805,30 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
return;
}
public async getMemberShareHistoryMode(
id: string,
): Promise<SettingsMemberShareHistoryMode> {
const group = await this.getGroup(id);
return {
membersCanShareHistory:
group.MemberShareHistoryMode === 'all_member_share',
};
}
@Activity()
public async setMemberShareHistoryMode(
id: string,
value: boolean,
): Promise<boolean> {
const req = new messages.JidBoolRequest({
session: this.session,
jid: id,
value: value,
});
await promisify(this.client.SetGroupMemberShareHistoryMode)(req);
return true;
}
public async getMembershipApprovalMode(
id: string,
): Promise<SettingsMembershipApproval> {
+1
View File
@@ -101,6 +101,7 @@ export interface GroupInfoFull {
IsAnnounce: boolean; // specifies whether only admins can send messages in the group
Participants: GOWSGroupParticipant[];
MemberAddMode: string; // all_member_add | admin_add
MemberShareHistoryMode: string; // all_member_share | admin_share
IsJoinApprovalRequired: boolean;
}
+14 -25
View File
@@ -28,7 +28,6 @@ import makeWASocket, {
import { WACallEvent } from '@adiwajshing/baileys/lib/Types/Call';
import { BaileysEventMap } from '@adiwajshing/baileys/lib/Types/Events';
import { GroupMetadata } from '@adiwajshing/baileys/lib/Types/GroupMetadata';
import { executeWMexQuery } from '@adiwajshing/baileys/lib/Socket/mex';
import {
Label as NOWEBLabel,
LabelActionBody,
@@ -298,14 +297,6 @@ export interface NowebConfig {
waVersion: WAVersion;
}
// WhatsApp Web MEX (w:mex) doc/query_id for the group property mutation that sets
// "members can send message history to new members". Captured from WhatsApp Web's
// WAWebMexUpdateGroupPropertyJobMutation on 2026-09-25 (see whatsapp-rust
// wacore/src/iq/mex_operations.rs, mod update_group_property). WhatsApp rotates these
// ids when Web is updated -- if this mutation starts failing, re-capture the query_id
// from a fresh WhatsApp Web session before assuming the endpoint itself is broken.
const MEX_QUERY_ID_UPDATE_GROUP_PROPERTY = '9418211574894172';
export class WhatsappSessionNoWebCore extends WhatsappSession {
private START_ATTEMPT_DELAY_SECONDS = 2;
private AUTO_RESTART_AFTER_SECONDS = 28 * 60;
@@ -2248,23 +2239,21 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
return await this.sock.groupMemberAddMode(id, mode);
}
public async getMemberShareHistoryMode(
id: string,
): Promise<SettingsMemberShareHistoryMode> {
const group = await this.getGroup(id);
return { membersCanShareHistory: !!group.memberShareHistoryMode };
}
@Activity()
public async setMemberShareHistoryMode(id, value) {
const mode = value ? 'ALL_MEMBER_SHARE' : 'ADMIN_SHARE';
const result = await executeWMexQuery<{ state?: string }>(
{ group_id: id, update: { member_share_group_history_mode: mode } },
MEX_QUERY_ID_UPDATE_GROUP_PROPERTY,
'xwa2_group_update_property',
this.sock.query,
this.sock.generateMessageTag,
);
if (result?.state !== 'ACTIVE') {
throw new Error(
`Failed to set member share history mode for group '${id}': ` +
`unexpected state '${result?.state}'`,
);
}
return result;
public async setMemberShareHistoryMode(
id: string,
value: boolean,
): Promise<boolean> {
const mode = value ? 'all_member_share' : 'admin_share';
await this.sock.groupMemberShareHistoryMode(id, mode);
return true;
}
public async getMembershipApprovalMode(
@@ -117,6 +117,7 @@ import {
GroupSortField,
ParticipantsRequest,
SettingsMemberAddMode,
SettingsMemberShareHistoryMode,
SettingsMembershipApproval,
SettingsSecurityChangeInfo,
} from '@waha/structures/groups.dto';
@@ -1757,6 +1758,28 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
return groupChat.setAddMembersAdminsOnly(!value);
}
public async getMemberShareHistoryMode(
id: string,
): Promise<SettingsMemberShareHistoryMode> {
const groupChat = (await this.whatsapp.getChatById(id)) as GroupChat;
return {
membersCanShareHistory:
// @ts-ignore
groupChat.groupMetadata.memberShareGroupHistoryMode ===
'all_member_share',
};
}
@Activity()
public async setMemberShareHistoryMode(
id: string,
value: boolean,
): Promise<boolean> {
const groupChat = (await this.whatsapp.getChatById(id)) as GroupChat;
// The library setter is inverted - it takes "adminsOnly"
return groupChat.setShareHistoryAdminsOnly(!value);
}
@Activity()
public async getMembershipApprovalMode(
id: string,
+15
View File
@@ -178,6 +178,7 @@ import {
NormalizeJoinRequestMethod,
ParticipantsRequest,
SettingsMemberAddMode,
SettingsMemberShareHistoryMode,
SettingsMembershipApproval,
SettingsSecurityChangeInfo,
} from '@waha/structures/groups.dto';
@@ -1624,6 +1625,20 @@ export class WhatsappSessionWPPCore extends WhatsappSession {
);
}
public async getMemberShareHistoryMode(
id: string,
): Promise<SettingsMemberShareHistoryMode> {
const groupId = await this.hooks.wid.chat.promise(
id,
'getMemberShareHistoryMode',
);
const group = await this.wpp!.getChatById(groupId);
// Undocumented property - 'all_member_share' or 'admin_share'
// @ts-ignore
const mode = group?.groupMetadata?.memberShareGroupHistoryMode;
return { membersCanShareHistory: mode === 'all_member_share' };
}
public async getMembershipApprovalMode(
id: string,
): Promise<SettingsMembershipApproval> {
+4 -4
View File
@@ -7,7 +7,7 @@ __metadata:
"@adiwajshing/baileys@github:devlikeapro/Baileys#fork-master-2026-04-28":
version: 7.0.0-rc14
resolution: "@adiwajshing/baileys@https://github.com/devlikeapro/Baileys.git#commit=93433a380977fce781d25c5e40d8f12d8ea8109e"
resolution: "@adiwajshing/baileys@https://github.com/devlikeapro/Baileys.git#commit=14ed18cd9fc524309fbecaed4949ea73eaf6761e"
dependencies:
"@cacheable/node-cache": "npm:^1.4.0"
"@hapi/boom": "npm:^9.1.3"
@@ -32,7 +32,7 @@ __metadata:
optional: true
link-preview-js:
optional: true
checksum: 10/3fdf6e7eea36d474a46c70e645872f588d3c47dcdc99898477be1665302844aa3c5da221cd9773380ea66d28deb9e9e565a1507323be5840eba23352c7707583
checksum: 10/d7c290e64065517c43094f5700daf1230dfa54254aa2965d8efca94bdefbd464904a390f16d695c0305a0e7f408acd9fda566bacb30dc493ff563fcb06892873
languageName: node
linkType: hard
@@ -14381,7 +14381,7 @@ __metadata:
"whatsapp-web.js@github:devlikeapro/whatsapp-web.js#fork-main-2026-06-26":
version: 1.34.7
resolution: "whatsapp-web.js@https://github.com/devlikeapro/whatsapp-web.js.git#commit=a95362738822bcb670f9e20f79acaf83304a6a2d"
resolution: "whatsapp-web.js@https://github.com/devlikeapro/whatsapp-web.js.git#commit=7bcd3528538372f9420a63472491513066993090"
dependencies:
archiver: "npm:7.0.1"
fluent-ffmpeg: "npm:2.1.3"
@@ -14398,7 +14398,7 @@ __metadata:
optional: true
unzipper:
optional: true
checksum: 10/ee21c2d7cf3e94eccc931fca7f386aaf6fd5ba4d8f362dd79df8f45fab22298538eaedb5fa5c4ae80a622e51a229601f20fd41c90a781428ad681ce821861de6
checksum: 10/52b776bd9cde2799fed01ca507f8a43e17cce11254fa58cc3e96fa40f206532311050a38d6c25ee895616c9c39286615e6513c952667b6bd8610d5de03068cdb
languageName: node
linkType: hard