Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7436beaab3 | |||
| 5926da9493 | |||
| a9e926b907 | |||
| 2b2c3f9155 | |||
| 71fe69fd21 | |||
| eecf6df2a2 | |||
| d04a3e1af8 | |||
| 21462c26d5 | |||
| fc2122b11a | |||
| 11eed3aedc | |||
| dbc6dd6aed | |||
| e152aee740 | |||
| 6060293793 | |||
| afce764ce9 |
+14
-1
@@ -1,7 +1,7 @@
|
|||||||
syntax = "proto3";
|
syntax = "proto3";
|
||||||
package proto;
|
package proto;
|
||||||
|
|
||||||
/// WhatsApp Version: 2.3000.1035216863
|
/// WhatsApp Version: 2.3000.1035484955
|
||||||
|
|
||||||
message ADVDeviceIdentity {
|
message ADVDeviceIdentity {
|
||||||
optional uint32 rawId = 1;
|
optional uint32 rawId = 1;
|
||||||
@@ -417,6 +417,7 @@ message BotCapabilityMetadata {
|
|||||||
JSON_PATCH_STREAMING = 58;
|
JSON_PATCH_STREAMING = 58;
|
||||||
AI_TAB_FORCE_CLIPPY = 59;
|
AI_TAB_FORCE_CLIPPY = 59;
|
||||||
UNIFIED_RESPONSE_EMBEDDED_SCREENS = 60;
|
UNIFIED_RESPONSE_EMBEDDED_SCREENS = 60;
|
||||||
|
AI_SUBSCRIPTION_ENABLED = 61;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1933,8 +1934,20 @@ message HandshakeMessage {
|
|||||||
optional bytes paddedBytes = 6;
|
optional bytes paddedBytes = 6;
|
||||||
optional bool sendServerHelloPaddedBytes = 7;
|
optional bool sendServerHelloPaddedBytes = 7;
|
||||||
optional bool simulateXxkemFs = 8;
|
optional bool simulateXxkemFs = 8;
|
||||||
|
optional HandshakeMessage.HandshakePqMode pqMode = 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum HandshakePqMode {
|
||||||
|
HANDSHAKE_PQ_MODE_UNKNOWN = 0;
|
||||||
|
XXKEM = 1;
|
||||||
|
XXKEM_FS = 2;
|
||||||
|
WA_CLASSICAL = 3;
|
||||||
|
WA_PQ = 4;
|
||||||
|
IKKEM = 5;
|
||||||
|
IKKEM_FS = 6;
|
||||||
|
XXKEM_2 = 7;
|
||||||
|
IKKEM_2 = 8;
|
||||||
|
}
|
||||||
message ServerHello {
|
message ServerHello {
|
||||||
optional bytes ephemeral = 1;
|
optional bytes ephemeral = 1;
|
||||||
optional bytes static = 2;
|
optional bytes static = 2;
|
||||||
|
|||||||
Vendored
+16
-1
@@ -1092,7 +1092,8 @@ export namespace proto {
|
|||||||
RICH_RESPONSE_UR_IMAGINE_VIDEO = 57,
|
RICH_RESPONSE_UR_IMAGINE_VIDEO = 57,
|
||||||
JSON_PATCH_STREAMING = 58,
|
JSON_PATCH_STREAMING = 58,
|
||||||
AI_TAB_FORCE_CLIPPY = 59,
|
AI_TAB_FORCE_CLIPPY = 59,
|
||||||
UNIFIED_RESPONSE_EMBEDDED_SCREENS = 60
|
UNIFIED_RESPONSE_EMBEDDED_SCREENS = 60,
|
||||||
|
AI_SUBSCRIPTION_ENABLED = 61
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4759,6 +4760,7 @@ export namespace proto {
|
|||||||
paddedBytes?: (Uint8Array|null);
|
paddedBytes?: (Uint8Array|null);
|
||||||
sendServerHelloPaddedBytes?: (boolean|null);
|
sendServerHelloPaddedBytes?: (boolean|null);
|
||||||
simulateXxkemFs?: (boolean|null);
|
simulateXxkemFs?: (boolean|null);
|
||||||
|
pqMode?: (proto.HandshakeMessage.HandshakePqMode|null);
|
||||||
}
|
}
|
||||||
|
|
||||||
class ClientHello implements IClientHello {
|
class ClientHello implements IClientHello {
|
||||||
@@ -4771,6 +4773,7 @@ export namespace proto {
|
|||||||
public paddedBytes?: (Uint8Array|null);
|
public paddedBytes?: (Uint8Array|null);
|
||||||
public sendServerHelloPaddedBytes?: (boolean|null);
|
public sendServerHelloPaddedBytes?: (boolean|null);
|
||||||
public simulateXxkemFs?: (boolean|null);
|
public simulateXxkemFs?: (boolean|null);
|
||||||
|
public pqMode?: (proto.HandshakeMessage.HandshakePqMode|null);
|
||||||
public static create(properties?: proto.HandshakeMessage.IClientHello): proto.HandshakeMessage.ClientHello;
|
public static create(properties?: proto.HandshakeMessage.IClientHello): proto.HandshakeMessage.ClientHello;
|
||||||
public static encode(m: proto.HandshakeMessage.IClientHello, w?: $protobuf.Writer): $protobuf.Writer;
|
public static encode(m: proto.HandshakeMessage.IClientHello, w?: $protobuf.Writer): $protobuf.Writer;
|
||||||
public static decode(r: ($protobuf.Reader|Uint8Array), l?: number): proto.HandshakeMessage.ClientHello;
|
public static decode(r: ($protobuf.Reader|Uint8Array), l?: number): proto.HandshakeMessage.ClientHello;
|
||||||
@@ -4780,6 +4783,18 @@ export namespace proto {
|
|||||||
public static getTypeUrl(typeUrlPrefix?: string): string;
|
public static getTypeUrl(typeUrlPrefix?: string): string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum HandshakePqMode {
|
||||||
|
HANDSHAKE_PQ_MODE_UNKNOWN = 0,
|
||||||
|
XXKEM = 1,
|
||||||
|
XXKEM_FS = 2,
|
||||||
|
WA_CLASSICAL = 3,
|
||||||
|
WA_PQ = 4,
|
||||||
|
IKKEM = 5,
|
||||||
|
IKKEM_FS = 6,
|
||||||
|
XXKEM_2 = 7,
|
||||||
|
IKKEM_2 = 8
|
||||||
|
}
|
||||||
|
|
||||||
interface IServerHello {
|
interface IServerHello {
|
||||||
ephemeral?: (Uint8Array|null);
|
ephemeral?: (Uint8Array|null);
|
||||||
"static"?: (Uint8Array|null);
|
"static"?: (Uint8Array|null);
|
||||||
|
|||||||
@@ -6897,6 +6897,10 @@ export const proto = $root.proto = (() => {
|
|||||||
case 60:
|
case 60:
|
||||||
m.capabilities[i] = 60;
|
m.capabilities[i] = 60;
|
||||||
break;
|
break;
|
||||||
|
case "AI_SUBSCRIPTION_ENABLED":
|
||||||
|
case 61:
|
||||||
|
m.capabilities[i] = 61;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6993,6 +6997,7 @@ export const proto = $root.proto = (() => {
|
|||||||
values[valuesById[58] = "JSON_PATCH_STREAMING"] = 58;
|
values[valuesById[58] = "JSON_PATCH_STREAMING"] = 58;
|
||||||
values[valuesById[59] = "AI_TAB_FORCE_CLIPPY"] = 59;
|
values[valuesById[59] = "AI_TAB_FORCE_CLIPPY"] = 59;
|
||||||
values[valuesById[60] = "UNIFIED_RESPONSE_EMBEDDED_SCREENS"] = 60;
|
values[valuesById[60] = "UNIFIED_RESPONSE_EMBEDDED_SCREENS"] = 60;
|
||||||
|
values[valuesById[61] = "AI_SUBSCRIPTION_ENABLED"] = 61;
|
||||||
return values;
|
return values;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
@@ -32585,6 +32590,7 @@ export const proto = $root.proto = (() => {
|
|||||||
ClientHello.prototype.paddedBytes = null;
|
ClientHello.prototype.paddedBytes = null;
|
||||||
ClientHello.prototype.sendServerHelloPaddedBytes = null;
|
ClientHello.prototype.sendServerHelloPaddedBytes = null;
|
||||||
ClientHello.prototype.simulateXxkemFs = null;
|
ClientHello.prototype.simulateXxkemFs = null;
|
||||||
|
ClientHello.prototype.pqMode = null;
|
||||||
|
|
||||||
let $oneOfFields;
|
let $oneOfFields;
|
||||||
|
|
||||||
@@ -32636,6 +32642,12 @@ export const proto = $root.proto = (() => {
|
|||||||
set: $util.oneOfSetter($oneOfFields)
|
set: $util.oneOfSetter($oneOfFields)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Virtual OneOf for proto3 optional field
|
||||||
|
Object.defineProperty(ClientHello.prototype, "_pqMode", {
|
||||||
|
get: $util.oneOfGetter($oneOfFields = ["pqMode"]),
|
||||||
|
set: $util.oneOfSetter($oneOfFields)
|
||||||
|
});
|
||||||
|
|
||||||
ClientHello.create = function create(properties) {
|
ClientHello.create = function create(properties) {
|
||||||
return new ClientHello(properties);
|
return new ClientHello(properties);
|
||||||
};
|
};
|
||||||
@@ -32659,6 +32671,8 @@ export const proto = $root.proto = (() => {
|
|||||||
w.uint32(56).bool(m.sendServerHelloPaddedBytes);
|
w.uint32(56).bool(m.sendServerHelloPaddedBytes);
|
||||||
if (m.simulateXxkemFs != null && Object.hasOwnProperty.call(m, "simulateXxkemFs"))
|
if (m.simulateXxkemFs != null && Object.hasOwnProperty.call(m, "simulateXxkemFs"))
|
||||||
w.uint32(64).bool(m.simulateXxkemFs);
|
w.uint32(64).bool(m.simulateXxkemFs);
|
||||||
|
if (m.pqMode != null && Object.hasOwnProperty.call(m, "pqMode"))
|
||||||
|
w.uint32(72).int32(m.pqMode);
|
||||||
return w;
|
return w;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -32703,6 +32717,10 @@ export const proto = $root.proto = (() => {
|
|||||||
m.simulateXxkemFs = r.bool();
|
m.simulateXxkemFs = r.bool();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 9: {
|
||||||
|
m.pqMode = r.int32();
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
r.skipType(t & 7);
|
r.skipType(t & 7);
|
||||||
break;
|
break;
|
||||||
@@ -32754,6 +32772,50 @@ export const proto = $root.proto = (() => {
|
|||||||
if (d.simulateXxkemFs != null) {
|
if (d.simulateXxkemFs != null) {
|
||||||
m.simulateXxkemFs = Boolean(d.simulateXxkemFs);
|
m.simulateXxkemFs = Boolean(d.simulateXxkemFs);
|
||||||
}
|
}
|
||||||
|
switch (d.pqMode) {
|
||||||
|
default:
|
||||||
|
if (typeof d.pqMode === "number") {
|
||||||
|
m.pqMode = d.pqMode;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "HANDSHAKE_PQ_MODE_UNKNOWN":
|
||||||
|
case 0:
|
||||||
|
m.pqMode = 0;
|
||||||
|
break;
|
||||||
|
case "XXKEM":
|
||||||
|
case 1:
|
||||||
|
m.pqMode = 1;
|
||||||
|
break;
|
||||||
|
case "XXKEM_FS":
|
||||||
|
case 2:
|
||||||
|
m.pqMode = 2;
|
||||||
|
break;
|
||||||
|
case "WA_CLASSICAL":
|
||||||
|
case 3:
|
||||||
|
m.pqMode = 3;
|
||||||
|
break;
|
||||||
|
case "WA_PQ":
|
||||||
|
case 4:
|
||||||
|
m.pqMode = 4;
|
||||||
|
break;
|
||||||
|
case "IKKEM":
|
||||||
|
case 5:
|
||||||
|
m.pqMode = 5;
|
||||||
|
break;
|
||||||
|
case "IKKEM_FS":
|
||||||
|
case 6:
|
||||||
|
m.pqMode = 6;
|
||||||
|
break;
|
||||||
|
case "XXKEM_2":
|
||||||
|
case 7:
|
||||||
|
m.pqMode = 7;
|
||||||
|
break;
|
||||||
|
case "IKKEM_2":
|
||||||
|
case 8:
|
||||||
|
m.pqMode = 8;
|
||||||
|
break;
|
||||||
|
}
|
||||||
return m;
|
return m;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -32801,6 +32863,11 @@ export const proto = $root.proto = (() => {
|
|||||||
if (o.oneofs)
|
if (o.oneofs)
|
||||||
d._simulateXxkemFs = "simulateXxkemFs";
|
d._simulateXxkemFs = "simulateXxkemFs";
|
||||||
}
|
}
|
||||||
|
if (m.pqMode != null && m.hasOwnProperty("pqMode")) {
|
||||||
|
d.pqMode = o.enums === String ? $root.proto.HandshakeMessage.HandshakePqMode[m.pqMode] === undefined ? m.pqMode : $root.proto.HandshakeMessage.HandshakePqMode[m.pqMode] : m.pqMode;
|
||||||
|
if (o.oneofs)
|
||||||
|
d._pqMode = "pqMode";
|
||||||
|
}
|
||||||
return d;
|
return d;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -32818,6 +32885,20 @@ export const proto = $root.proto = (() => {
|
|||||||
return ClientHello;
|
return ClientHello;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
HandshakeMessage.HandshakePqMode = (function() {
|
||||||
|
const valuesById = {}, values = Object.create(valuesById);
|
||||||
|
values[valuesById[0] = "HANDSHAKE_PQ_MODE_UNKNOWN"] = 0;
|
||||||
|
values[valuesById[1] = "XXKEM"] = 1;
|
||||||
|
values[valuesById[2] = "XXKEM_FS"] = 2;
|
||||||
|
values[valuesById[3] = "WA_CLASSICAL"] = 3;
|
||||||
|
values[valuesById[4] = "WA_PQ"] = 4;
|
||||||
|
values[valuesById[5] = "IKKEM"] = 5;
|
||||||
|
values[valuesById[6] = "IKKEM_FS"] = 6;
|
||||||
|
values[valuesById[7] = "XXKEM_2"] = 7;
|
||||||
|
values[valuesById[8] = "IKKEM_2"] = 8;
|
||||||
|
return values;
|
||||||
|
})();
|
||||||
|
|
||||||
HandshakeMessage.ServerHello = (function() {
|
HandshakeMessage.ServerHello = (function() {
|
||||||
|
|
||||||
function ServerHello(p) {
|
function ServerHello(p) {
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{"version":[2,3000,1035223526]}
|
{"version":[2,3000,1035504971]}
|
||||||
|
|||||||
+1088
-209
File diff suppressed because it is too large
Load Diff
@@ -123,11 +123,11 @@ export const makeNewsletterSocket = (config: SocketConfig) => {
|
|||||||
},
|
},
|
||||||
|
|
||||||
newsletterMute: (jid: string) => {
|
newsletterMute: (jid: string) => {
|
||||||
return executeWMexQuery({ newsletter_id: jid }, QueryIds.MUTE, XWAPaths.xwa2_newsletter_mute_v2)
|
return executeWMexQuery({ input: { newsletter_id: jid, type: 'MUTE_ADMIN_ACTIVITY', value: 'OFF' } }, QueryIds.MUTE, XWAPaths.xwa2_newsletter_mute_v2)
|
||||||
},
|
},
|
||||||
|
|
||||||
newsletterUnmute: (jid: string) => {
|
newsletterUnmute: (jid: string) => {
|
||||||
return executeWMexQuery({ newsletter_id: jid }, QueryIds.UNMUTE, XWAPaths.xwa2_newsletter_unmute_v2)
|
return executeWMexQuery({ input: { newsletter_id: jid, type: 'MUTE_ADMIN_ACTIVITY', value: 'ON' } }, QueryIds.UNMUTE, XWAPaths.xwa2_newsletter_unmute_v2)
|
||||||
},
|
},
|
||||||
|
|
||||||
newsletterUpdateName: async (jid: string, name: string) => {
|
newsletterUpdateName: async (jid: string, name: string) => {
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ export type BaileysEventMap = {
|
|||||||
chats: Chat[]
|
chats: Chat[]
|
||||||
contacts: Contact[]
|
contacts: Contact[]
|
||||||
messages: WAMessage[]
|
messages: WAMessage[]
|
||||||
|
pastParticipants?: proto.IPastParticipants[] | null
|
||||||
isLatest?: boolean
|
isLatest?: boolean
|
||||||
progress?: number | null
|
progress?: number | null
|
||||||
syncType?: proto.HistorySync.HistorySyncType | null
|
syncType?: proto.HistorySync.HistorySyncType | null
|
||||||
@@ -185,6 +186,7 @@ export type BufferedEventData = {
|
|||||||
chats: { [jid: string]: Chat }
|
chats: { [jid: string]: Chat }
|
||||||
contacts: { [jid: string]: Contact }
|
contacts: { [jid: string]: Contact }
|
||||||
messages: { [uqId: string]: WAMessage }
|
messages: { [uqId: string]: WAMessage }
|
||||||
|
pastParticipants?: proto.IPastParticipants[]
|
||||||
empty: boolean
|
empty: boolean
|
||||||
isLatest: boolean
|
isLatest: boolean
|
||||||
progress?: number | null
|
progress?: number | null
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ export enum XWAPaths {
|
|||||||
xwa2_newsletter_view = 'xwa2_newsletter_view',
|
xwa2_newsletter_view = 'xwa2_newsletter_view',
|
||||||
xwa2_newsletter_metadata = 'xwa2_newsletter',
|
xwa2_newsletter_metadata = 'xwa2_newsletter',
|
||||||
xwa2_newsletter_admin_count = 'xwa2_newsletter_admin',
|
xwa2_newsletter_admin_count = 'xwa2_newsletter_admin',
|
||||||
xwa2_newsletter_mute_v2 = 'xwa2_newsletter_mute_v2',
|
xwa2_newsletter_mute_v2 = 'xwa2_newsletter_update_user_setting',
|
||||||
xwa2_newsletter_unmute_v2 = 'xwa2_newsletter_unmute_v2',
|
xwa2_newsletter_unmute_v2 = 'xwa2_newsletter_update_user_setting',
|
||||||
xwa2_newsletter_follow = 'xwa2_newsletter_follow',
|
xwa2_newsletter_follow = 'xwa2_newsletter_join_v2',
|
||||||
xwa2_newsletter_unfollow = 'xwa2_newsletter_unfollow',
|
xwa2_newsletter_unfollow = 'xwa2_newsletter_leave_v2',
|
||||||
xwa2_newsletter_change_owner = 'xwa2_newsletter_change_owner',
|
xwa2_newsletter_change_owner = 'xwa2_newsletter_change_owner',
|
||||||
xwa2_newsletter_demote = 'xwa2_newsletter_demote',
|
xwa2_newsletter_demote = 'xwa2_newsletter_demote',
|
||||||
xwa2_newsletter_delete_v2 = 'xwa2_newsletter_delete_v2'
|
xwa2_newsletter_delete_v2 = 'xwa2_newsletter_delete_v2'
|
||||||
@@ -17,10 +17,10 @@ export enum QueryIds {
|
|||||||
UPDATE_METADATA = '24250201037901610',
|
UPDATE_METADATA = '24250201037901610',
|
||||||
METADATA = '6563316087068696',
|
METADATA = '6563316087068696',
|
||||||
SUBSCRIBERS = '9783111038412085',
|
SUBSCRIBERS = '9783111038412085',
|
||||||
FOLLOW = '7871414976211147',
|
FOLLOW = '24404358912487870',
|
||||||
UNFOLLOW = '7238632346214362',
|
UNFOLLOW = '9767147403369991',
|
||||||
MUTE = '29766401636284406',
|
MUTE = '31938993655691868',
|
||||||
UNMUTE = '9864994326891137',
|
UNMUTE = '31938993655691868',
|
||||||
ADMIN_COUNT = '7130823597031706',
|
ADMIN_COUNT = '7130823597031706',
|
||||||
CHANGE_OWNER = '7341777602580933',
|
CHANGE_OWNER = '7341777602580933',
|
||||||
DEMOTE = '6551828931592903',
|
DEMOTE = '6551828931592903',
|
||||||
|
|||||||
@@ -967,6 +967,10 @@ function append<E extends BufferableEvent>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data.historySets.pastParticipants = [
|
||||||
|
...(data.historySets.pastParticipants || []),
|
||||||
|
...(eventData.pastParticipants || [])
|
||||||
|
]
|
||||||
data.historySets.empty = false
|
data.historySets.empty = false
|
||||||
data.historySets.syncType = eventData.syncType
|
data.historySets.syncType = eventData.syncType
|
||||||
data.historySets.progress = eventData.progress
|
data.historySets.progress = eventData.progress
|
||||||
@@ -1292,6 +1296,7 @@ function consolidateEvents(data: BufferedEventData) {
|
|||||||
chats: Object.values(data.historySets.chats),
|
chats: Object.values(data.historySets.chats),
|
||||||
messages: Object.values(data.historySets.messages),
|
messages: Object.values(data.historySets.messages),
|
||||||
contacts: Object.values(data.historySets.contacts),
|
contacts: Object.values(data.historySets.contacts),
|
||||||
|
pastParticipants: data.historySets.pastParticipants,
|
||||||
syncType: data.historySets.syncType,
|
syncType: data.historySets.syncType,
|
||||||
progress: data.historySets.progress,
|
progress: data.historySets.progress,
|
||||||
isLatest: data.historySets.isLatest,
|
isLatest: data.historySets.isLatest,
|
||||||
|
|||||||
@@ -378,6 +378,7 @@ export const processHistoryMessage = (item: proto.IHistorySync, logger?: ILogger
|
|||||||
chats,
|
chats,
|
||||||
contacts,
|
contacts,
|
||||||
messages,
|
messages,
|
||||||
|
pastParticipants: item.pastParticipants,
|
||||||
lidPnMappings,
|
lidPnMappings,
|
||||||
syncType: item.syncType,
|
syncType: item.syncType,
|
||||||
progress: item.progress
|
progress: item.progress
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ export const extractImageThumb = async (bufferOrFilePath: Readable | Buffer | st
|
|||||||
height: dimensions.height
|
height: dimensions.height
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if ('jimp' in lib && (typeof lib.jimp?.Jimp === 'object' || typeof lib.jimp?.Jimp === 'function')) {
|
} else if ('jimp' in lib && typeof lib.jimp?.Jimp === 'function') {
|
||||||
const jimp = await (lib.jimp.Jimp as any).read(bufferOrFilePath)
|
const jimp = await (lib.jimp.Jimp as any).read(bufferOrFilePath)
|
||||||
const dimensions = {
|
const dimensions = {
|
||||||
width: jimp.width,
|
width: jimp.width,
|
||||||
|
|||||||
+70
-94
@@ -45,8 +45,8 @@ import {
|
|||||||
generateThumbnail,
|
generateThumbnail,
|
||||||
getAudioDuration,
|
getAudioDuration,
|
||||||
getAudioWaveform,
|
getAudioWaveform,
|
||||||
getRawMediaUploadData,
|
|
||||||
getStream,
|
getStream,
|
||||||
|
getRawMediaUploadData,
|
||||||
type MediaDownloadOptions
|
type MediaDownloadOptions
|
||||||
} from './messages-media'
|
} from './messages-media'
|
||||||
import { shouldIncludeReportingToken } from './reporting-utils'
|
import { shouldIncludeReportingToken } from './reporting-utils'
|
||||||
@@ -475,48 +475,6 @@ export const formatNativeFlowButton = (button: NativeButton): NativeFlowButton =
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const recoverCarouselImageMetadata = async (
|
|
||||||
card: CarouselMessageOptions['cards'][number],
|
|
||||||
imageMessage: proto.Message.IImageMessage,
|
|
||||||
mediaOptions: MessageContentGenerationOptions
|
|
||||||
) => {
|
|
||||||
if (imageMessage.jpegThumbnail && imageMessage.height && imageMessage.width) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { stream } = await getStream(card.image!, mediaOptions.options)
|
|
||||||
const thumb = await extractImageThumb(stream)
|
|
||||||
|
|
||||||
if (!imageMessage.jpegThumbnail) {
|
|
||||||
imageMessage.jpegThumbnail = thumb.buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!imageMessage.width && thumb.original.width) {
|
|
||||||
imageMessage.width = thumb.original.width
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!imageMessage.height && thumb.original.height) {
|
|
||||||
imageMessage.height = thumb.original.height
|
|
||||||
}
|
|
||||||
|
|
||||||
mediaOptions.logger?.info(
|
|
||||||
{
|
|
||||||
cardTitle: card.title,
|
|
||||||
hasJpegThumbnail: !!imageMessage.jpegThumbnail,
|
|
||||||
width: imageMessage.width,
|
|
||||||
height: imageMessage.height
|
|
||||||
},
|
|
||||||
'[CAROUSEL] Recovered image thumbnail/dimensions from source media'
|
|
||||||
)
|
|
||||||
} catch (error) {
|
|
||||||
mediaOptions.logger?.warn(
|
|
||||||
{ cardTitle: card.title, error },
|
|
||||||
'[CAROUSEL] Failed to recover image thumbnail/dimensions from source media'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a button message using Native Flow format wrapped in viewOnceMessage
|
* Generates a button message using Native Flow format wrapped in viewOnceMessage
|
||||||
* This is the modern approach for button messages that works on iOS and Android
|
* This is the modern approach for button messages that works on iOS and Android
|
||||||
@@ -677,6 +635,59 @@ export const generateCarouselMessage = async (
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const recoverCarouselImageMetadata = async (
|
||||||
|
imageMessage: proto.Message.IImageMessage | null | undefined,
|
||||||
|
cardImage: WAMediaUpload,
|
||||||
|
cardTitle: string | undefined,
|
||||||
|
uploadOptions: MessageContentGenerationOptions
|
||||||
|
) => {
|
||||||
|
if (!imageMessage) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const missingThumbnail = !imageMessage.jpegThumbnail
|
||||||
|
const missingWidth = !imageMessage.width
|
||||||
|
const missingHeight = !imageMessage.height
|
||||||
|
if (!missingThumbnail && !missingWidth && !missingHeight) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { stream } = await getStream(cardImage, uploadOptions.options)
|
||||||
|
const { buffer, original } = await extractImageThumb(stream)
|
||||||
|
|
||||||
|
if (missingThumbnail) {
|
||||||
|
imageMessage.jpegThumbnail = buffer.toString('base64')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (missingWidth && original.width) {
|
||||||
|
imageMessage.width = original.width
|
||||||
|
}
|
||||||
|
|
||||||
|
if (missingHeight && original.height) {
|
||||||
|
imageMessage.height = original.height
|
||||||
|
}
|
||||||
|
|
||||||
|
uploadOptions.logger?.info(
|
||||||
|
{
|
||||||
|
cardTitle,
|
||||||
|
recoveredThumbnail: !!imageMessage.jpegThumbnail,
|
||||||
|
width: imageMessage.width,
|
||||||
|
height: imageMessage.height
|
||||||
|
},
|
||||||
|
'[CAROUSEL] Recovered image metadata from source media'
|
||||||
|
)
|
||||||
|
} catch (error) {
|
||||||
|
uploadOptions.logger?.warn(
|
||||||
|
{
|
||||||
|
cardTitle,
|
||||||
|
trace: error instanceof Error ? error.stack : String(error)
|
||||||
|
},
|
||||||
|
'[CAROUSEL] Failed source-media thumbnail fallback'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Map cards to the carousel format (processing media)
|
// Map cards to the carousel format (processing media)
|
||||||
const carouselCards = await Promise.all(
|
const carouselCards = await Promise.all(
|
||||||
cards.map(async card => {
|
cards.map(async card => {
|
||||||
@@ -692,10 +703,12 @@ export const generateCarouselMessage = async (
|
|||||||
if (hasMedia && mediaOptions) {
|
if (hasMedia && mediaOptions) {
|
||||||
if (card.image) {
|
if (card.image) {
|
||||||
const { imageMessage } = await prepareWAMessageMedia({ image: card.image }, mediaOptions)
|
const { imageMessage } = await prepareWAMessageMedia({ image: card.image }, mediaOptions)
|
||||||
if (imageMessage) {
|
|
||||||
await recoverCarouselImageMetadata(card, imageMessage, mediaOptions)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Mirror the working Pastorini-style result: every carousel image card should
|
||||||
|
// carry a jpegThumbnail and dimensions before it reaches the Web live renderer.
|
||||||
|
await recoverCarouselImageMetadata(imageMessage, card.image, card.title, mediaOptions)
|
||||||
|
|
||||||
|
// Validate image fields needed for WhatsApp rendering
|
||||||
if (imageMessage && !imageMessage.jpegThumbnail) {
|
if (imageMessage && !imageMessage.jpegThumbnail) {
|
||||||
mediaOptions.logger?.warn(
|
mediaOptions.logger?.warn(
|
||||||
{ cardTitle: card.title },
|
{ cardTitle: card.title },
|
||||||
@@ -1275,57 +1288,20 @@ export const generateWAMessageContent = async (
|
|||||||
options.logger?.info('Sending CTA buttons as nativeFlowMessage with viewOnceMessage wrapper')
|
options.logger?.info('Sending CTA buttons as nativeFlowMessage with viewOnceMessage wrapper')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Check for nativeCarousel — inline handler (validated on Android, iOS, Web)
|
// Check for nativeCarousel
|
||||||
// Direct interactiveMessage at root (field 45), NO viewOnceMessage wrapper,
|
|
||||||
// NO messageContextInfo, NO biz/bot stanza nodes needed
|
|
||||||
else if (hasNonNullishProperty(message, 'nativeCarousel')) {
|
else if (hasNonNullishProperty(message, 'nativeCarousel')) {
|
||||||
const carouselMsg = message as any
|
const carouselMsg = message as any
|
||||||
const cards = carouselMsg.nativeCarousel.cards || []
|
const carouselOptions: CarouselMessageOptions = {
|
||||||
const title = carouselMsg.nativeCarousel.title || carouselMsg.title
|
cards: carouselMsg.nativeCarousel.cards,
|
||||||
const text = carouselMsg.text
|
title: carouselMsg.nativeCarousel.title || carouselMsg.title,
|
||||||
const footer = carouselMsg.footer
|
text: carouselMsg.text,
|
||||||
|
footer: carouselMsg.footer
|
||||||
const carouselCards = await Promise.all(
|
|
||||||
cards.map(async (card: any) => {
|
|
||||||
const hasMedia = !!(card.image || card.video)
|
|
||||||
const header: any = {
|
|
||||||
title: card.title || '',
|
|
||||||
subtitle: card.footer || '',
|
|
||||||
hasMediaAttachment: hasMedia
|
|
||||||
}
|
|
||||||
if (hasMedia && card.image) {
|
|
||||||
const { imageMessage } = await prepareWAMessageMedia({ image: card.image }, options)
|
|
||||||
if (imageMessage && !imageMessage.height) imageMessage.height = 500
|
|
||||||
if (imageMessage && !imageMessage.width) imageMessage.width = 500
|
|
||||||
header.imageMessage = imageMessage
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
header,
|
|
||||||
body: { text: card.body || '' },
|
|
||||||
footer: card.footer ? { text: card.footer } : undefined,
|
|
||||||
nativeFlowMessage: {
|
|
||||||
buttons: (card.buttons || []).map((btn: any) => {
|
|
||||||
switch (btn.type) {
|
|
||||||
case 'url': return { name: 'cta_url', buttonParamsJson: JSON.stringify({ display_text: btn.text, url: btn.url, merchant_url: btn.url }) }
|
|
||||||
case 'copy': return { name: 'cta_copy', buttonParamsJson: JSON.stringify({ display_text: btn.text, copy_code: btn.copyText }) }
|
|
||||||
case 'call': return { name: 'cta_call', buttonParamsJson: JSON.stringify({ display_text: btn.text, phone_number: btn.phoneNumber }) }
|
|
||||||
default: return { name: 'quick_reply', buttonParamsJson: JSON.stringify({ display_text: btn.text, id: btn.id }) }
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
m.interactiveMessage = {
|
|
||||||
header: { title: title || ' ', hasMediaAttachment: false },
|
|
||||||
body: { text: text || '' },
|
|
||||||
footer: footer ? { text: footer } : undefined,
|
|
||||||
carouselMessage: {
|
|
||||||
cards: carouselCards,
|
|
||||||
messageVersion: 1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// Pass options for media processing if cards have images/videos
|
||||||
|
const generated = await generateCarouselMessage(carouselOptions, options)
|
||||||
|
// Frida capture shows interactiveMessage DIRECT (field 45) in DSM — no viewOnceMessage wrapper
|
||||||
|
// Testing without wrapper: biz node + quality_control already match Pastorini CDP stanza
|
||||||
|
m.interactiveMessage = generated.interactiveMessage
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
// Check for nativeList
|
// Check for nativeList
|
||||||
|
|||||||
@@ -158,11 +158,13 @@ export async function storeTcTokensFromIqResult({
|
|||||||
...existingEntry,
|
...existingEntry,
|
||||||
token: Buffer.from(tokenNode.content),
|
token: Buffer.from(tokenNode.content),
|
||||||
timestamp: tokenNode.attrs.t,
|
timestamp: tokenNode.attrs.t,
|
||||||
// Resets real_issue_timestamp to null when storing a new token
|
// WABA Android: resets real_issue_timestamp to null when storing a new token
|
||||||
|
// (UPDATE wa_trusted_contacts_send SET real_issue_timestamp=null)
|
||||||
realIssueTimestamp: null
|
realIssueTimestamp: null
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store under resolved storageJid AND under fallbackJid (PN) for reliable lookup
|
// Store under resolved storageJid AND under fallbackJid (PN) for reliable lookup
|
||||||
|
// The read path may resolve to a different LID than the store path
|
||||||
const normalizedFallback = jidNormalizedUser(fallbackJid)
|
const normalizedFallback = jidNormalizedUser(fallbackJid)
|
||||||
const keysToStore: Record<string, typeof tokenEntry | null> = {
|
const keysToStore: Record<string, typeof tokenEntry | null> = {
|
||||||
[storageJid]: tokenEntry
|
[storageJid]: tokenEntry
|
||||||
|
|||||||
Reference in New Issue
Block a user