send.go 46 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400
  1. // Copyright (c) 2022 Tulir Asokan
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this
  5. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6. package whatsmeow
  7. import (
  8. "context"
  9. "crypto/sha256"
  10. "encoding/base64"
  11. "encoding/binary"
  12. "encoding/hex"
  13. "errors"
  14. "fmt"
  15. "slices"
  16. "sort"
  17. "strconv"
  18. "strings"
  19. "time"
  20. "github.com/rs/zerolog"
  21. "go.mau.fi/libsignal/groups"
  22. "go.mau.fi/libsignal/keys/prekey"
  23. "go.mau.fi/libsignal/protocol"
  24. "go.mau.fi/libsignal/session"
  25. "go.mau.fi/libsignal/signalerror"
  26. "go.mau.fi/util/random"
  27. "google.golang.org/protobuf/proto"
  28. waBinary "go.mau.fi/whatsmeow/binary"
  29. "go.mau.fi/whatsmeow/proto/waAICommon"
  30. "go.mau.fi/whatsmeow/proto/waCommon"
  31. "go.mau.fi/whatsmeow/proto/waE2E"
  32. "go.mau.fi/whatsmeow/types"
  33. "go.mau.fi/whatsmeow/types/events"
  34. )
  35. const WebMessageIDPrefix = "3EB0"
  36. // GenerateMessageID generates a random string that can be used as a message ID on WhatsApp.
  37. //
  38. // msgID := cli.GenerateMessageID()
  39. // cli.SendMessage(context.Background(), targetJID, &waE2E.Message{...}, whatsmeow.SendRequestExtra{ID: msgID})
  40. func (cli *Client) GenerateMessageID() types.MessageID {
  41. if cli != nil && cli.MessengerConfig != nil {
  42. return types.MessageID(strconv.FormatInt(GenerateFacebookMessageID(), 10))
  43. }
  44. data := make([]byte, 8, 8+20+16)
  45. binary.BigEndian.PutUint64(data, uint64(time.Now().Unix()))
  46. ownID := cli.getOwnID()
  47. if !ownID.IsEmpty() {
  48. data = append(data, []byte(ownID.User)...)
  49. data = append(data, []byte("@c.us")...)
  50. }
  51. data = append(data, random.Bytes(16)...)
  52. hash := sha256.Sum256(data)
  53. return WebMessageIDPrefix + strings.ToUpper(hex.EncodeToString(hash[:9]))
  54. }
  55. func GenerateFacebookMessageID() int64 {
  56. const randomMask = (1 << 22) - 1
  57. return (time.Now().UnixMilli() << 22) | (int64(binary.BigEndian.Uint32(random.Bytes(4))) & randomMask)
  58. }
  59. // GenerateMessageID generates a random string that can be used as a message ID on WhatsApp.
  60. //
  61. // msgID := whatsmeow.GenerateMessageID()
  62. // cli.SendMessage(context.Background(), targetJID, &waE2E.Message{...}, whatsmeow.SendRequestExtra{ID: msgID})
  63. //
  64. // Deprecated: WhatsApp web has switched to using a hash of the current timestamp, user id and random bytes. Use Client.GenerateMessageID instead.
  65. func GenerateMessageID() types.MessageID {
  66. return WebMessageIDPrefix + strings.ToUpper(hex.EncodeToString(random.Bytes(8)))
  67. }
  68. type MessageDebugTimings struct {
  69. LIDFetch time.Duration
  70. Queue time.Duration
  71. Marshal time.Duration
  72. GetParticipants time.Duration
  73. GetDevices time.Duration
  74. GroupEncrypt time.Duration
  75. PeerEncrypt time.Duration
  76. Send time.Duration
  77. Resp time.Duration
  78. Retry time.Duration
  79. }
  80. func (mdt MessageDebugTimings) MarshalZerologObject(evt *zerolog.Event) {
  81. if mdt.LIDFetch != 0 {
  82. evt.Dur("lid_fetch", mdt.LIDFetch)
  83. }
  84. evt.Dur("queue", mdt.Queue)
  85. evt.Dur("marshal", mdt.Marshal)
  86. if mdt.GetParticipants != 0 {
  87. evt.Dur("get_participants", mdt.GetParticipants)
  88. }
  89. evt.Dur("get_devices", mdt.GetDevices)
  90. if mdt.GroupEncrypt != 0 {
  91. evt.Dur("group_encrypt", mdt.GroupEncrypt)
  92. }
  93. evt.Dur("peer_encrypt", mdt.PeerEncrypt)
  94. evt.Dur("send", mdt.Send)
  95. evt.Dur("resp", mdt.Resp)
  96. if mdt.Retry != 0 {
  97. evt.Dur("retry", mdt.Retry)
  98. }
  99. }
  100. type SendResponse struct {
  101. // The message timestamp returned by the server
  102. Timestamp time.Time
  103. // The ID of the sent message
  104. ID types.MessageID
  105. // The server-specified ID of the sent message. Only present for newsletter messages.
  106. ServerID types.MessageServerID
  107. // Message handling duration, used for debugging
  108. DebugTimings MessageDebugTimings
  109. // The identity the message was sent with (LID or PN)
  110. // This is currently not reliable in all cases.
  111. Sender types.JID
  112. }
  113. // SendRequestExtra contains the optional parameters for SendMessage.
  114. //
  115. // By default, optional parameters don't have to be provided at all, e.g.
  116. //
  117. // cli.SendMessage(ctx, to, message)
  118. //
  119. // When providing optional parameters, add a single instance of this struct as the last parameter:
  120. //
  121. // cli.SendMessage(ctx, to, message, whatsmeow.SendRequestExtra{...})
  122. //
  123. // Trying to add multiple extra parameters will return an error.
  124. type SendRequestExtra struct {
  125. // The message ID to use when sending. If this is not provided, a random message ID will be generated
  126. ID types.MessageID
  127. // JID of the bot to be invoked (optional)
  128. InlineBotJID types.JID
  129. // Should the message be sent as a peer message (protocol messages to your own devices, e.g. app state key requests)
  130. Peer bool
  131. // A timeout for the send request. Unlike timeouts using the context parameter, this only applies
  132. // to the actual response waiting and not preparing/encrypting the message.
  133. // Defaults to 75 seconds. The timeout can be disabled by using a negative value.
  134. Timeout time.Duration
  135. // When sending media to newsletters, the Handle field returned by the file upload.
  136. MediaHandle string
  137. Meta *types.MsgMetaInfo
  138. // use this only if you know what you are doing
  139. AdditionalNodes *[]waBinary.Node
  140. }
  141. // SendMessage sends the given message.
  142. //
  143. // This method will wait for the server to acknowledge the message before returning.
  144. // The return value is the timestamp of the message from the server.
  145. //
  146. // Optional parameters like the message ID can be specified with the SendRequestExtra struct.
  147. // Only one extra parameter is allowed, put all necessary parameters in the same struct.
  148. //
  149. // The message itself can contain anything you want (within the protobuf schema).
  150. // e.g. for a simple text message, use the Conversation field:
  151. //
  152. // cli.SendMessage(context.Background(), targetJID, &waE2E.Message{
  153. // Conversation: proto.String("Hello, World!"),
  154. // })
  155. //
  156. // Things like replies, mentioning users and the "forwarded" flag are stored in ContextInfo,
  157. // which can be put in ExtendedTextMessage and any of the media message types.
  158. //
  159. // For uploading and sending media/attachments, see the Upload method.
  160. //
  161. // For other message types, you'll have to figure it out yourself. Looking at the protobuf schema
  162. // in binary/proto/def.proto may be useful to find out all the allowed fields. Printing the RawMessage
  163. // field in incoming message events to figure out what it contains is also a good way to learn how to
  164. // send the same kind of message.
  165. func (cli *Client) SendMessage(ctx context.Context, to types.JID, message *waE2E.Message, extra ...SendRequestExtra) (resp SendResponse, err error) {
  166. if cli == nil {
  167. err = ErrClientIsNil
  168. return
  169. }
  170. var req SendRequestExtra
  171. if len(extra) > 1 {
  172. err = errors.New("only one extra parameter may be provided to SendMessage")
  173. return
  174. } else if len(extra) == 1 {
  175. req = extra[0]
  176. }
  177. if to.Device > 0 && !req.Peer {
  178. err = ErrRecipientADJID
  179. return
  180. }
  181. ownID := cli.getOwnID()
  182. if ownID.IsEmpty() {
  183. err = ErrNotLoggedIn
  184. return
  185. }
  186. if req.Timeout == 0 {
  187. req.Timeout = defaultRequestTimeout
  188. }
  189. if len(req.ID) == 0 {
  190. req.ID = cli.GenerateMessageID()
  191. }
  192. if to.Server == types.NewsletterServer {
  193. // TODO somehow deduplicate this with the code in sendNewsletter?
  194. if message.EditedMessage != nil {
  195. req.ID = types.MessageID(message.GetEditedMessage().GetMessage().GetProtocolMessage().GetKey().GetID())
  196. } else if message.ProtocolMessage != nil && message.ProtocolMessage.GetType() == waE2E.ProtocolMessage_REVOKE {
  197. req.ID = types.MessageID(message.GetProtocolMessage().GetKey().GetID())
  198. }
  199. }
  200. resp.ID = req.ID
  201. isInlineBotMode := false
  202. if !req.InlineBotJID.IsEmpty() {
  203. if !req.InlineBotJID.IsBot() {
  204. err = ErrInvalidInlineBotID
  205. return
  206. }
  207. isInlineBotMode = true
  208. }
  209. isBotMode := isInlineBotMode || to.IsBot()
  210. needsMessageSecret := isBotMode || cli.shouldIncludeReportingToken(message)
  211. var extraParams nodeExtraParams
  212. if needsMessageSecret {
  213. if message.MessageContextInfo == nil {
  214. message.MessageContextInfo = &waE2E.MessageContextInfo{}
  215. }
  216. if message.MessageContextInfo.MessageSecret == nil {
  217. message.MessageContextInfo.MessageSecret = random.Bytes(32)
  218. }
  219. }
  220. if isBotMode {
  221. if message.MessageContextInfo.BotMetadata == nil {
  222. message.MessageContextInfo.BotMetadata = &waAICommon.BotMetadata{
  223. PersonaID: proto.String("867051314767696$760019659443059"),
  224. }
  225. }
  226. if isInlineBotMode {
  227. // inline mode specific code
  228. messageSecret := message.GetMessageContextInfo().GetMessageSecret()
  229. message = &waE2E.Message{
  230. BotInvokeMessage: &waE2E.FutureProofMessage{
  231. Message: &waE2E.Message{
  232. ExtendedTextMessage: message.ExtendedTextMessage,
  233. MessageContextInfo: &waE2E.MessageContextInfo{
  234. BotMetadata: message.MessageContextInfo.BotMetadata,
  235. },
  236. },
  237. },
  238. MessageContextInfo: message.MessageContextInfo,
  239. }
  240. botMessage := &waE2E.Message{
  241. BotInvokeMessage: message.BotInvokeMessage,
  242. MessageContextInfo: &waE2E.MessageContextInfo{
  243. BotMetadata: message.MessageContextInfo.BotMetadata,
  244. BotMessageSecret: applyBotMessageHKDF(messageSecret),
  245. },
  246. }
  247. messagePlaintext, _, marshalErr := marshalMessage(req.InlineBotJID, botMessage)
  248. if marshalErr != nil {
  249. err = marshalErr
  250. return
  251. }
  252. var participantNodes []waBinary.Node
  253. participantNodes, _, err = cli.encryptMessageForDevices(ctx, []types.JID{req.InlineBotJID}, resp.ID, messagePlaintext, nil, waBinary.Attrs{})
  254. if err != nil {
  255. return
  256. }
  257. extraParams.botNode = &waBinary.Node{
  258. Tag: "bot",
  259. Attrs: nil,
  260. Content: participantNodes,
  261. }
  262. }
  263. }
  264. var groupParticipants []types.JID
  265. if to.Server == types.GroupServer || to.Server == types.BroadcastServer {
  266. start := time.Now()
  267. if to.Server == types.GroupServer {
  268. var cachedData *groupMetaCache
  269. cachedData, err = cli.getCachedGroupData(ctx, to)
  270. if err != nil {
  271. err = fmt.Errorf("failed to get group members: %w", err)
  272. return
  273. }
  274. groupParticipants = cachedData.Members
  275. // TODO this is fairly hacky, is there a proper way to determine which identity the message is sent with?
  276. if cachedData.AddressingMode == types.AddressingModeLID {
  277. ownID = cli.getOwnLID()
  278. extraParams.addressingMode = types.AddressingModeLID
  279. } else if cachedData.CommunityAnnouncementGroup && req.Meta != nil {
  280. ownID = cli.getOwnLID()
  281. // Why is this set to PN?
  282. extraParams.addressingMode = types.AddressingModePN
  283. }
  284. } else {
  285. groupParticipants, err = cli.getBroadcastListParticipants(ctx, to)
  286. if err != nil {
  287. err = fmt.Errorf("failed to get broadcast list members: %w", err)
  288. return
  289. }
  290. }
  291. resp.DebugTimings.GetParticipants = time.Since(start)
  292. } else if to.Server == types.HiddenUserServer {
  293. ownID = cli.getOwnLID()
  294. } else if to.Server == types.DefaultUserServer && cli.Store.LIDMigrationTimestamp > 0 {
  295. start := time.Now()
  296. var toLID types.JID
  297. toLID, err = cli.Store.LIDs.GetLIDForPN(ctx, to)
  298. if err != nil {
  299. err = fmt.Errorf("failed to get LID for PN %s: %w", to, err)
  300. return
  301. } else if toLID.IsEmpty() {
  302. var info map[types.JID]types.UserInfo
  303. info, err = cli.GetUserInfo(ctx, []types.JID{to})
  304. if err != nil {
  305. err = fmt.Errorf("failed to get user info for %s to fill LID cache: %w", to, err)
  306. return
  307. } else if toLID = info[to].LID; toLID.IsEmpty() {
  308. err = fmt.Errorf("no LID found for %s from server", to)
  309. return
  310. }
  311. }
  312. resp.DebugTimings.LIDFetch = time.Since(start)
  313. cli.Log.Debugf("Replacing SendMessage destination with LID as migration timestamp is set %s -> %s", to, toLID)
  314. to = toLID
  315. ownID = cli.getOwnLID()
  316. }
  317. if req.Meta != nil {
  318. extraParams.metaNode = &waBinary.Node{
  319. Tag: "meta",
  320. Attrs: waBinary.Attrs{},
  321. }
  322. if req.Meta.DeprecatedLIDSession != nil {
  323. extraParams.metaNode.Attrs["deprecated_lid_session"] = *req.Meta.DeprecatedLIDSession
  324. }
  325. if req.Meta.ThreadMessageID != "" {
  326. extraParams.metaNode.Attrs["thread_msg_id"] = req.Meta.ThreadMessageID
  327. extraParams.metaNode.Attrs["thread_msg_sender_jid"] = req.Meta.ThreadMessageSenderJID
  328. }
  329. }
  330. if req.AdditionalNodes != nil {
  331. extraParams.additionalNodes = req.AdditionalNodes
  332. }
  333. resp.Sender = ownID
  334. start := time.Now()
  335. // Sending multiple messages at a time can cause weird issues and makes it harder to retry safely
  336. // This is also required for the session prefetching that makes group sends faster
  337. // (everything will explode if you send a message to the same user twice in parallel)
  338. cli.messageSendLock.Lock()
  339. resp.DebugTimings.Queue = time.Since(start)
  340. defer cli.messageSendLock.Unlock()
  341. respChan := cli.waitResponse(req.ID)
  342. // Peer message retries aren't implemented yet
  343. if !req.Peer {
  344. cli.addRecentMessage(to, req.ID, message, nil)
  345. }
  346. if message.GetMessageContextInfo().GetMessageSecret() != nil {
  347. err = cli.Store.MsgSecrets.PutMessageSecret(ctx, to, ownID, req.ID, message.GetMessageContextInfo().GetMessageSecret())
  348. if err != nil {
  349. cli.Log.Warnf("Failed to store message secret key for outgoing message %s: %v", req.ID, err)
  350. } else {
  351. cli.Log.Debugf("Stored message secret key for outgoing message %s", req.ID)
  352. }
  353. }
  354. var phash string
  355. var data []byte
  356. switch to.Server {
  357. case types.GroupServer, types.BroadcastServer:
  358. phash, data, err = cli.sendGroup(ctx, ownID, to, groupParticipants, req.ID, message, &resp.DebugTimings, extraParams)
  359. case types.DefaultUserServer, types.BotServer, types.HiddenUserServer:
  360. if req.Peer {
  361. data, err = cli.sendPeerMessage(ctx, to, req.ID, message, &resp.DebugTimings)
  362. } else {
  363. phash, data, err = cli.sendDM(ctx, ownID, to, req.ID, message, &resp.DebugTimings, extraParams)
  364. }
  365. case types.NewsletterServer:
  366. data, err = cli.sendNewsletter(ctx, to, req.ID, message, req.MediaHandle, &resp.DebugTimings)
  367. default:
  368. err = fmt.Errorf("%w %s", ErrUnknownServer, to.Server)
  369. }
  370. start = time.Now()
  371. if err != nil {
  372. cli.cancelResponse(req.ID, respChan)
  373. return
  374. }
  375. var respNode *waBinary.Node
  376. var timeoutChan <-chan time.Time
  377. if req.Timeout > 0 {
  378. timeoutChan = time.After(req.Timeout)
  379. } else {
  380. timeoutChan = make(<-chan time.Time)
  381. }
  382. select {
  383. case respNode = <-respChan:
  384. case <-timeoutChan:
  385. cli.cancelResponse(req.ID, respChan)
  386. err = ErrMessageTimedOut
  387. return
  388. case <-ctx.Done():
  389. cli.cancelResponse(req.ID, respChan)
  390. err = ctx.Err()
  391. return
  392. }
  393. resp.DebugTimings.Resp = time.Since(start)
  394. if isDisconnectNode(respNode) {
  395. start = time.Now()
  396. respNode, err = cli.retryFrame(ctx, "message send", req.ID, data, respNode, 0)
  397. resp.DebugTimings.Retry = time.Since(start)
  398. if err != nil {
  399. return
  400. }
  401. }
  402. ag := respNode.AttrGetter()
  403. resp.ServerID = types.MessageServerID(ag.OptionalInt("server_id"))
  404. resp.Timestamp = ag.UnixTime("t")
  405. if errorCode := ag.Int("error"); errorCode != 0 {
  406. err = fmt.Errorf("%w %d", ErrServerReturnedError, errorCode)
  407. }
  408. expectedPHash := ag.OptionalString("phash")
  409. if len(expectedPHash) > 0 && phash != expectedPHash {
  410. cli.Log.Warnf("Server returned different participant list hash (%s != %s) when sending to %s. Some devices may not have received the message.", phash, expectedPHash, to)
  411. switch to.Server {
  412. case types.GroupServer:
  413. // TODO also invalidate device list caches
  414. cli.groupCacheLock.Lock()
  415. delete(cli.groupCache, to)
  416. cli.groupCacheLock.Unlock()
  417. case types.BroadcastServer:
  418. // TODO do something
  419. case types.DefaultUserServer, types.HiddenUserServer, types.BotServer, types.HostedServer, types.HostedLIDServer:
  420. cli.userDevicesCacheLock.Lock()
  421. delete(cli.userDevicesCache, to)
  422. cli.userDevicesCacheLock.Unlock()
  423. }
  424. }
  425. return
  426. }
  427. // RevokeMessage deletes the given message from everyone in the chat.
  428. //
  429. // This method will wait for the server to acknowledge the revocation message before returning.
  430. // The return value is the timestamp of the message from the server.
  431. //
  432. // Deprecated: This method is deprecated in favor of BuildRevoke
  433. func (cli *Client) RevokeMessage(ctx context.Context, chat types.JID, id types.MessageID) (SendResponse, error) {
  434. return cli.SendMessage(ctx, chat, cli.BuildRevoke(chat, types.EmptyJID, id))
  435. }
  436. // BuildMessageKey builds a MessageKey object, which is used to refer to previous messages
  437. // for things such as replies, revocations and reactions.
  438. func (cli *Client) BuildMessageKey(chat, sender types.JID, id types.MessageID) *waCommon.MessageKey {
  439. key := &waCommon.MessageKey{
  440. FromMe: proto.Bool(true),
  441. ID: proto.String(id),
  442. RemoteJID: proto.String(chat.String()),
  443. }
  444. if !sender.IsEmpty() && sender.User != cli.getOwnID().User && sender.User != cli.getOwnLID().User {
  445. key.FromMe = proto.Bool(false)
  446. if chat.Server != types.DefaultUserServer && chat.Server != types.HiddenUserServer && chat.Server != types.MessengerServer {
  447. key.Participant = proto.String(sender.ToNonAD().String())
  448. }
  449. }
  450. return key
  451. }
  452. // BuildRevoke builds a message revocation message using the given variables.
  453. // The built message can be sent normally using Client.SendMessage.
  454. //
  455. // To revoke your own messages, pass your JID or an empty JID as the second parameter (sender).
  456. //
  457. // resp, err := cli.SendMessage(context.Background(), chat, cli.BuildRevoke(chat, types.EmptyJID, originalMessageID)
  458. //
  459. // To revoke someone else's messages when you are group admin, pass the message sender's JID as the second parameter.
  460. //
  461. // resp, err := cli.SendMessage(context.Background(), chat, cli.BuildRevoke(chat, senderJID, originalMessageID)
  462. func (cli *Client) BuildRevoke(chat, sender types.JID, id types.MessageID) *waE2E.Message {
  463. return &waE2E.Message{
  464. ProtocolMessage: &waE2E.ProtocolMessage{
  465. Type: waE2E.ProtocolMessage_REVOKE.Enum(),
  466. Key: cli.BuildMessageKey(chat, sender, id),
  467. },
  468. }
  469. }
  470. // BuildReaction builds a message reaction message using the given variables.
  471. // The built message can be sent normally using Client.SendMessage.
  472. //
  473. // resp, err := cli.SendMessage(context.Background(), chat, cli.BuildReaction(chat, senderJID, targetMessageID, "🐈️")
  474. //
  475. // Note that for newsletter messages, you need to use NewsletterSendReaction instead of BuildReaction + SendMessage.
  476. func (cli *Client) BuildReaction(chat, sender types.JID, id types.MessageID, reaction string) *waE2E.Message {
  477. return &waE2E.Message{
  478. ReactionMessage: &waE2E.ReactionMessage{
  479. Key: cli.BuildMessageKey(chat, sender, id),
  480. Text: proto.String(reaction),
  481. SenderTimestampMS: proto.Int64(time.Now().UnixMilli()),
  482. },
  483. }
  484. }
  485. // BuildUnavailableMessageRequest builds a message to request the user's primary device to send
  486. // the copy of a message that this client was unable to decrypt.
  487. //
  488. // The built message can be sent using Client.SendMessage, but you must pass whatsmeow.SendRequestExtra{Peer: true} as the last parameter.
  489. // The full response will come as a ProtocolMessage with type `PEER_DATA_OPERATION_REQUEST_RESPONSE_MESSAGE`.
  490. // The response events will also be dispatched as normal *events.Message's with UnavailableRequestID set to the request message ID.
  491. func (cli *Client) BuildUnavailableMessageRequest(chat, sender types.JID, id string) *waE2E.Message {
  492. return &waE2E.Message{
  493. ProtocolMessage: &waE2E.ProtocolMessage{
  494. Type: waE2E.ProtocolMessage_PEER_DATA_OPERATION_REQUEST_MESSAGE.Enum(),
  495. PeerDataOperationRequestMessage: &waE2E.PeerDataOperationRequestMessage{
  496. PeerDataOperationRequestType: waE2E.PeerDataOperationRequestType_PLACEHOLDER_MESSAGE_RESEND.Enum(),
  497. PlaceholderMessageResendRequest: []*waE2E.PeerDataOperationRequestMessage_PlaceholderMessageResendRequest{{
  498. MessageKey: cli.BuildMessageKey(chat, sender, id),
  499. }},
  500. },
  501. },
  502. }
  503. }
  504. // BuildHistorySyncRequest builds a message to request additional history from the user's primary device.
  505. //
  506. // The built message can be sent using Client.SendMessage, but you must pass whatsmeow.SendRequestExtra{Peer: true} as the last parameter.
  507. // The response will come as an *events.HistorySync with type `ON_DEMAND`.
  508. //
  509. // The response will contain to `count` messages immediately before the given message.
  510. // The recommended number of messages to request at a time is 50.
  511. func (cli *Client) BuildHistorySyncRequest(lastKnownMessageInfo *types.MessageInfo, count int) *waE2E.Message {
  512. return &waE2E.Message{
  513. ProtocolMessage: &waE2E.ProtocolMessage{
  514. Type: waE2E.ProtocolMessage_PEER_DATA_OPERATION_REQUEST_MESSAGE.Enum(),
  515. PeerDataOperationRequestMessage: &waE2E.PeerDataOperationRequestMessage{
  516. PeerDataOperationRequestType: waE2E.PeerDataOperationRequestType_HISTORY_SYNC_ON_DEMAND.Enum(),
  517. HistorySyncOnDemandRequest: &waE2E.PeerDataOperationRequestMessage_HistorySyncOnDemandRequest{
  518. ChatJID: proto.String(lastKnownMessageInfo.Chat.String()),
  519. OldestMsgID: proto.String(lastKnownMessageInfo.ID),
  520. OldestMsgFromMe: proto.Bool(lastKnownMessageInfo.IsFromMe),
  521. OnDemandMsgCount: proto.Int32(int32(count)),
  522. OldestMsgTimestampMS: proto.Int64(lastKnownMessageInfo.Timestamp.UnixMilli()),
  523. },
  524. },
  525. },
  526. }
  527. }
  528. // EditWindow specifies how long a message can be edited for after it was sent.
  529. const EditWindow = 20 * time.Minute
  530. // BuildEdit builds a message edit message using the given variables.
  531. // The built message can be sent normally using Client.SendMessage.
  532. //
  533. // resp, err := cli.SendMessage(context.Background(), chat, cli.BuildEdit(chat, originalMessageID, &waE2E.Message{
  534. // Conversation: proto.String("edited message"),
  535. // })
  536. func (cli *Client) BuildEdit(chat types.JID, id types.MessageID, newContent *waE2E.Message) *waE2E.Message {
  537. return &waE2E.Message{
  538. EditedMessage: &waE2E.FutureProofMessage{
  539. Message: &waE2E.Message{
  540. ProtocolMessage: &waE2E.ProtocolMessage{
  541. Key: &waCommon.MessageKey{
  542. FromMe: proto.Bool(true),
  543. ID: proto.String(id),
  544. RemoteJID: proto.String(chat.String()),
  545. },
  546. Type: waE2E.ProtocolMessage_MESSAGE_EDIT.Enum(),
  547. EditedMessage: newContent,
  548. TimestampMS: proto.Int64(time.Now().UnixMilli()),
  549. },
  550. },
  551. },
  552. }
  553. }
  554. const (
  555. DisappearingTimerOff = time.Duration(0)
  556. DisappearingTimer24Hours = 24 * time.Hour
  557. DisappearingTimer7Days = 7 * 24 * time.Hour
  558. DisappearingTimer90Days = 90 * 24 * time.Hour
  559. )
  560. // ParseDisappearingTimerString parses common human-readable disappearing message timer strings into Duration values.
  561. // If the string doesn't look like one of the allowed values (0, 24h, 7d, 90d), the second return value is false.
  562. func ParseDisappearingTimerString(val string) (time.Duration, bool) {
  563. switch strings.ReplaceAll(strings.ToLower(val), " ", "") {
  564. case "0d", "0h", "0s", "0", "off":
  565. return DisappearingTimerOff, true
  566. case "1day", "day", "1d", "1", "24h", "24", "86400s", "86400":
  567. return DisappearingTimer24Hours, true
  568. case "1week", "week", "7d", "7", "168h", "168", "604800s", "604800":
  569. return DisappearingTimer7Days, true
  570. case "3months", "3m", "3mo", "90d", "90", "2160h", "2160", "7776000s", "7776000":
  571. return DisappearingTimer90Days, true
  572. default:
  573. return 0, false
  574. }
  575. }
  576. // SetDisappearingTimer sets the disappearing timer in a chat. Both private chats and groups are supported, but they're
  577. // set with different methods.
  578. //
  579. // Note that while this function allows passing non-standard durations, official WhatsApp apps will ignore those,
  580. // and in groups the server will just reject the change. You can use the DisappearingTimer<Duration> constants for convenience.
  581. //
  582. // In groups, the server will echo the change as a notification, so it'll show up as a *events.GroupInfo update.
  583. func (cli *Client) SetDisappearingTimer(ctx context.Context, chat types.JID, timer time.Duration, settingTS time.Time) (err error) {
  584. switch chat.Server {
  585. case types.DefaultUserServer, types.HiddenUserServer:
  586. if settingTS.IsZero() {
  587. settingTS = time.Now()
  588. }
  589. _, err = cli.SendMessage(ctx, chat, &waE2E.Message{
  590. ProtocolMessage: &waE2E.ProtocolMessage{
  591. Type: waE2E.ProtocolMessage_EPHEMERAL_SETTING.Enum(),
  592. EphemeralExpiration: proto.Uint32(uint32(timer.Seconds())),
  593. EphemeralSettingTimestamp: proto.Int64(settingTS.Unix()),
  594. },
  595. })
  596. case types.GroupServer:
  597. if timer == 0 {
  598. _, err = cli.sendGroupIQ(ctx, iqSet, chat, waBinary.Node{Tag: "not_ephemeral"})
  599. } else {
  600. _, err = cli.sendGroupIQ(ctx, iqSet, chat, waBinary.Node{
  601. Tag: "ephemeral",
  602. Attrs: waBinary.Attrs{
  603. "expiration": strconv.Itoa(int(timer.Seconds())),
  604. },
  605. })
  606. if errors.Is(err, ErrIQBadRequest) {
  607. err = wrapIQError(ErrInvalidDisappearingTimer, err)
  608. }
  609. }
  610. default:
  611. err = fmt.Errorf("can't set disappearing time in a %s chat", chat.Server)
  612. }
  613. return
  614. }
  615. func participantListHashV2(participants []types.JID) string {
  616. participantsStrings := make([]string, len(participants))
  617. for i, part := range participants {
  618. participantsStrings[i] = part.ADString()
  619. }
  620. sort.Strings(participantsStrings)
  621. hash := sha256.Sum256([]byte(strings.Join(participantsStrings, "")))
  622. return fmt.Sprintf("2:%s", base64.RawStdEncoding.EncodeToString(hash[:6]))
  623. }
  624. func (cli *Client) sendNewsletter(
  625. ctx context.Context,
  626. to types.JID,
  627. id types.MessageID,
  628. message *waE2E.Message,
  629. mediaID string,
  630. timings *MessageDebugTimings,
  631. ) ([]byte, error) {
  632. attrs := waBinary.Attrs{
  633. "to": to,
  634. "id": id,
  635. "type": getTypeFromMessage(message),
  636. }
  637. if mediaID != "" {
  638. attrs["media_id"] = mediaID
  639. }
  640. if message.EditedMessage != nil {
  641. attrs["edit"] = string(types.EditAttributeAdminEdit)
  642. message = message.GetEditedMessage().GetMessage().GetProtocolMessage().GetEditedMessage()
  643. } else if message.ProtocolMessage != nil && message.ProtocolMessage.GetType() == waE2E.ProtocolMessage_REVOKE {
  644. attrs["edit"] = string(types.EditAttributeAdminRevoke)
  645. message = nil
  646. }
  647. start := time.Now()
  648. plaintext, _, err := marshalMessage(to, message)
  649. timings.Marshal = time.Since(start)
  650. if err != nil {
  651. return nil, err
  652. }
  653. plaintextNode := waBinary.Node{
  654. Tag: "plaintext",
  655. Content: plaintext,
  656. Attrs: waBinary.Attrs{},
  657. }
  658. if message != nil {
  659. if mediaType := getMediaTypeFromMessage(message); mediaType != "" {
  660. plaintextNode.Attrs["mediatype"] = mediaType
  661. }
  662. }
  663. node := waBinary.Node{
  664. Tag: "message",
  665. Attrs: attrs,
  666. Content: []waBinary.Node{plaintextNode},
  667. }
  668. start = time.Now()
  669. data, err := cli.sendNodeAndGetData(ctx, node)
  670. timings.Send = time.Since(start)
  671. if err != nil {
  672. return nil, fmt.Errorf("failed to send message node: %w", err)
  673. }
  674. return data, nil
  675. }
  676. type nodeExtraParams struct {
  677. botNode *waBinary.Node
  678. metaNode *waBinary.Node
  679. additionalNodes *[]waBinary.Node
  680. addressingMode types.AddressingMode
  681. }
  682. func (cli *Client) sendGroup(
  683. ctx context.Context,
  684. ownID,
  685. to types.JID,
  686. participants []types.JID,
  687. id types.MessageID,
  688. message *waE2E.Message,
  689. timings *MessageDebugTimings,
  690. extraParams nodeExtraParams,
  691. ) (string, []byte, error) {
  692. start := time.Now()
  693. plaintext, _, err := marshalMessage(to, message)
  694. timings.Marshal = time.Since(start)
  695. if err != nil {
  696. return "", nil, err
  697. }
  698. start = time.Now()
  699. builder := groups.NewGroupSessionBuilder(cli.Store, pbSerializer)
  700. senderKeyName := protocol.NewSenderKeyName(to.String(), cli.getOwnLID().SignalAddress())
  701. signalSKDMessage, err := builder.Create(ctx, senderKeyName)
  702. if err != nil {
  703. return "", nil, fmt.Errorf("failed to create sender key distribution message to send %s to %s: %w", id, to, err)
  704. }
  705. skdMessage := &waE2E.Message{
  706. SenderKeyDistributionMessage: &waE2E.SenderKeyDistributionMessage{
  707. GroupID: proto.String(to.String()),
  708. AxolotlSenderKeyDistributionMessage: signalSKDMessage.Serialize(),
  709. },
  710. }
  711. skdPlaintext, err := proto.Marshal(skdMessage)
  712. if err != nil {
  713. return "", nil, fmt.Errorf("failed to marshal sender key distribution message to send %s to %s: %w", id, to, err)
  714. }
  715. cipher := groups.NewGroupCipher(builder, senderKeyName, cli.Store)
  716. encrypted, err := cipher.Encrypt(ctx, padMessage(plaintext))
  717. if err != nil {
  718. return "", nil, fmt.Errorf("failed to encrypt group message to send %s to %s: %w", id, to, err)
  719. }
  720. ciphertext := encrypted.SignedSerialize()
  721. timings.GroupEncrypt = time.Since(start)
  722. node, allDevices, err := cli.prepareMessageNode(
  723. ctx, to, id, message, participants, skdPlaintext, nil, timings, extraParams,
  724. )
  725. if err != nil {
  726. return "", nil, err
  727. }
  728. phash := participantListHashV2(allDevices)
  729. node.Attrs["phash"] = phash
  730. skMsg := waBinary.Node{
  731. Tag: "enc",
  732. Content: ciphertext,
  733. Attrs: waBinary.Attrs{"v": "2", "type": "skmsg"},
  734. }
  735. if mediaType := getMediaTypeFromMessage(message); mediaType != "" {
  736. skMsg.Attrs["mediatype"] = mediaType
  737. }
  738. node.Content = append(node.GetChildren(), skMsg)
  739. if cli.shouldIncludeReportingToken(message) && message.GetMessageContextInfo().GetMessageSecret() != nil {
  740. node.Content = append(node.GetChildren(), cli.getMessageReportingToken(plaintext, message, ownID, to, id))
  741. }
  742. start = time.Now()
  743. data, err := cli.sendNodeAndGetData(ctx, *node)
  744. timings.Send = time.Since(start)
  745. if err != nil {
  746. return "", nil, fmt.Errorf("failed to send message node: %w", err)
  747. }
  748. return phash, data, nil
  749. }
  750. func (cli *Client) sendPeerMessage(
  751. ctx context.Context,
  752. to types.JID,
  753. id types.MessageID,
  754. message *waE2E.Message,
  755. timings *MessageDebugTimings,
  756. ) ([]byte, error) {
  757. node, err := cli.preparePeerMessageNode(ctx, to, id, message, timings)
  758. if err != nil {
  759. return nil, err
  760. }
  761. start := time.Now()
  762. data, err := cli.sendNodeAndGetData(ctx, *node)
  763. timings.Send = time.Since(start)
  764. if err != nil {
  765. return nil, fmt.Errorf("failed to send message node: %w", err)
  766. }
  767. return data, nil
  768. }
  769. func (cli *Client) sendDM(
  770. ctx context.Context,
  771. ownID,
  772. to types.JID,
  773. id types.MessageID,
  774. message *waE2E.Message,
  775. timings *MessageDebugTimings,
  776. extraParams nodeExtraParams,
  777. ) (string, []byte, error) {
  778. start := time.Now()
  779. messagePlaintext, deviceSentMessagePlaintext, err := marshalMessage(to, message)
  780. timings.Marshal = time.Since(start)
  781. if err != nil {
  782. return "", nil, err
  783. }
  784. node, allDevices, err := cli.prepareMessageNode(
  785. ctx, to, id, message, []types.JID{to, ownID.ToNonAD()},
  786. messagePlaintext, deviceSentMessagePlaintext, timings, extraParams,
  787. )
  788. if err != nil {
  789. return "", nil, err
  790. }
  791. phash := participantListHashV2(allDevices)
  792. if cli.shouldIncludeReportingToken(message) && message.GetMessageContextInfo().GetMessageSecret() != nil {
  793. node.Content = append(node.GetChildren(), cli.getMessageReportingToken(messagePlaintext, message, ownID, to, id))
  794. }
  795. if tcToken, err := cli.Store.PrivacyTokens.GetPrivacyToken(ctx, to); err != nil {
  796. cli.Log.Warnf("Failed to get privacy token for %s: %v", to, err)
  797. } else if tcToken != nil {
  798. node.Content = append(node.GetChildren(), waBinary.Node{
  799. Tag: "tctoken",
  800. Content: tcToken.Token,
  801. })
  802. }
  803. start = time.Now()
  804. data, err := cli.sendNodeAndGetData(ctx, *node)
  805. timings.Send = time.Since(start)
  806. if err != nil {
  807. return "", nil, fmt.Errorf("failed to send message node: %w", err)
  808. }
  809. return phash, data, nil
  810. }
  811. func getTypeFromMessage(msg *waE2E.Message) string {
  812. switch {
  813. case msg.ViewOnceMessage != nil:
  814. return getTypeFromMessage(msg.ViewOnceMessage.Message)
  815. case msg.ViewOnceMessageV2 != nil:
  816. return getTypeFromMessage(msg.ViewOnceMessageV2.Message)
  817. case msg.ViewOnceMessageV2Extension != nil:
  818. return getTypeFromMessage(msg.ViewOnceMessageV2Extension.Message)
  819. case msg.LottieStickerMessage != nil:
  820. return getTypeFromMessage(msg.LottieStickerMessage.Message)
  821. case msg.EphemeralMessage != nil:
  822. return getTypeFromMessage(msg.EphemeralMessage.Message)
  823. case msg.DocumentWithCaptionMessage != nil:
  824. return getTypeFromMessage(msg.DocumentWithCaptionMessage.Message)
  825. case msg.ReactionMessage != nil, msg.EncReactionMessage != nil:
  826. return "reaction"
  827. case msg.PollCreationMessage != nil, msg.PollUpdateMessage != nil:
  828. return "poll"
  829. case getMediaTypeFromMessage(msg) != "":
  830. return "media"
  831. case msg.Conversation != nil, msg.ExtendedTextMessage != nil, msg.ProtocolMessage != nil:
  832. return "text"
  833. default:
  834. return "text"
  835. }
  836. }
  837. func getMediaTypeFromMessage(msg *waE2E.Message) string {
  838. switch {
  839. case msg.ViewOnceMessage != nil:
  840. return getMediaTypeFromMessage(msg.ViewOnceMessage.Message)
  841. case msg.ViewOnceMessageV2 != nil:
  842. return getMediaTypeFromMessage(msg.ViewOnceMessageV2.Message)
  843. case msg.ViewOnceMessageV2Extension != nil:
  844. return getMediaTypeFromMessage(msg.ViewOnceMessageV2Extension.Message)
  845. case msg.LottieStickerMessage != nil:
  846. return getMediaTypeFromMessage(msg.LottieStickerMessage.Message)
  847. case msg.EphemeralMessage != nil:
  848. return getMediaTypeFromMessage(msg.EphemeralMessage.Message)
  849. case msg.DocumentWithCaptionMessage != nil:
  850. return getMediaTypeFromMessage(msg.DocumentWithCaptionMessage.Message)
  851. case msg.ExtendedTextMessage != nil && msg.ExtendedTextMessage.Title != nil:
  852. return "url"
  853. case msg.ImageMessage != nil:
  854. return "image"
  855. case msg.StickerMessage != nil:
  856. return "sticker"
  857. case msg.DocumentMessage != nil:
  858. return "document"
  859. case msg.AudioMessage != nil:
  860. if msg.AudioMessage.GetPTT() {
  861. return "ptt"
  862. } else {
  863. return "audio"
  864. }
  865. case msg.VideoMessage != nil:
  866. if msg.VideoMessage.GetGifPlayback() {
  867. return "gif"
  868. } else {
  869. return "video"
  870. }
  871. case msg.ContactMessage != nil:
  872. return "vcard"
  873. case msg.ContactsArrayMessage != nil:
  874. return "contact_array"
  875. case msg.ListMessage != nil:
  876. return "list"
  877. case msg.ListResponseMessage != nil:
  878. return "list_response"
  879. case msg.ButtonsResponseMessage != nil:
  880. return "buttons_response"
  881. case msg.OrderMessage != nil:
  882. return "order"
  883. case msg.ProductMessage != nil:
  884. return "product"
  885. case msg.InteractiveResponseMessage != nil:
  886. return "native_flow_response"
  887. default:
  888. return ""
  889. }
  890. }
  891. func getButtonTypeFromMessage(msg *waE2E.Message) string {
  892. switch {
  893. case msg.ViewOnceMessage != nil:
  894. return getButtonTypeFromMessage(msg.ViewOnceMessage.Message)
  895. case msg.ViewOnceMessageV2 != nil:
  896. return getButtonTypeFromMessage(msg.ViewOnceMessageV2.Message)
  897. case msg.EphemeralMessage != nil:
  898. return getButtonTypeFromMessage(msg.EphemeralMessage.Message)
  899. case msg.ButtonsMessage != nil:
  900. return "buttons"
  901. case msg.ButtonsResponseMessage != nil:
  902. return "buttons_response"
  903. case msg.ListMessage != nil:
  904. return "list"
  905. case msg.ListResponseMessage != nil:
  906. return "list_response"
  907. case msg.InteractiveResponseMessage != nil:
  908. return "interactive_response"
  909. default:
  910. return ""
  911. }
  912. }
  913. func getButtonAttributes(msg *waE2E.Message) waBinary.Attrs {
  914. switch {
  915. case msg.ViewOnceMessage != nil:
  916. return getButtonAttributes(msg.ViewOnceMessage.Message)
  917. case msg.ViewOnceMessageV2 != nil:
  918. return getButtonAttributes(msg.ViewOnceMessageV2.Message)
  919. case msg.EphemeralMessage != nil:
  920. return getButtonAttributes(msg.EphemeralMessage.Message)
  921. case msg.TemplateMessage != nil:
  922. return waBinary.Attrs{}
  923. case msg.ListMessage != nil:
  924. return waBinary.Attrs{
  925. "v": "2",
  926. "type": strings.ToLower(waE2E.ListMessage_ListType_name[int32(msg.ListMessage.GetListType())]),
  927. }
  928. default:
  929. return waBinary.Attrs{}
  930. }
  931. }
  932. const RemoveReactionText = ""
  933. func getEditAttribute(msg *waE2E.Message) types.EditAttribute {
  934. switch {
  935. case msg.EditedMessage != nil && msg.EditedMessage.Message != nil:
  936. return getEditAttribute(msg.EditedMessage.Message)
  937. case msg.ProtocolMessage != nil && msg.ProtocolMessage.GetKey() != nil:
  938. switch msg.ProtocolMessage.GetType() {
  939. case waE2E.ProtocolMessage_REVOKE:
  940. if msg.ProtocolMessage.GetKey().GetFromMe() {
  941. return types.EditAttributeSenderRevoke
  942. } else {
  943. return types.EditAttributeAdminRevoke
  944. }
  945. case waE2E.ProtocolMessage_MESSAGE_EDIT:
  946. if msg.ProtocolMessage.EditedMessage != nil {
  947. return types.EditAttributeMessageEdit
  948. }
  949. }
  950. case msg.ReactionMessage != nil && msg.ReactionMessage.GetText() == RemoveReactionText:
  951. return types.EditAttributeSenderRevoke
  952. case msg.KeepInChatMessage != nil && msg.KeepInChatMessage.GetKey().GetFromMe() && msg.KeepInChatMessage.GetKeepType() == waE2E.KeepType_UNDO_KEEP_FOR_ALL:
  953. return types.EditAttributeSenderRevoke
  954. }
  955. return types.EditAttributeEmpty
  956. }
  957. func (cli *Client) preparePeerMessageNode(
  958. ctx context.Context,
  959. to types.JID,
  960. id types.MessageID,
  961. message *waE2E.Message,
  962. timings *MessageDebugTimings,
  963. ) (*waBinary.Node, error) {
  964. attrs := waBinary.Attrs{
  965. "id": id,
  966. "type": "text",
  967. "category": "peer",
  968. "to": to,
  969. }
  970. if message.GetProtocolMessage().GetType() == waE2E.ProtocolMessage_APP_STATE_SYNC_KEY_REQUEST {
  971. attrs["push_priority"] = "high"
  972. }
  973. start := time.Now()
  974. plaintext, err := proto.Marshal(message)
  975. timings.Marshal = time.Since(start)
  976. if err != nil {
  977. err = fmt.Errorf("failed to marshal message: %w", err)
  978. return nil, err
  979. }
  980. encryptionIdentity := to
  981. if to.Server == types.DefaultUserServer {
  982. encryptionIdentity, err = cli.Store.LIDs.GetLIDForPN(ctx, to)
  983. if err != nil {
  984. return nil, fmt.Errorf("failed to get LID for PN %s: %w", to, err)
  985. }
  986. }
  987. start = time.Now()
  988. encrypted, isPreKey, err := cli.encryptMessageForDevice(ctx, plaintext, encryptionIdentity, nil, nil, nil)
  989. timings.PeerEncrypt = time.Since(start)
  990. if err != nil {
  991. return nil, fmt.Errorf("failed to encrypt peer message for %s: %v", to, err)
  992. }
  993. content := []waBinary.Node{{
  994. Tag: "meta",
  995. Attrs: waBinary.Attrs{
  996. "appdata": "default",
  997. },
  998. }, *encrypted}
  999. if isPreKey && cli.MessengerConfig == nil {
  1000. content = append(content, cli.makeDeviceIdentityNode())
  1001. }
  1002. return &waBinary.Node{
  1003. Tag: "message",
  1004. Attrs: attrs,
  1005. Content: content,
  1006. }, nil
  1007. }
  1008. func (cli *Client) getMessageContent(
  1009. baseNode waBinary.Node,
  1010. message *waE2E.Message,
  1011. msgAttrs waBinary.Attrs,
  1012. includeIdentity bool,
  1013. extraParams nodeExtraParams,
  1014. ) []waBinary.Node {
  1015. content := []waBinary.Node{baseNode}
  1016. if includeIdentity {
  1017. content = append(content, cli.makeDeviceIdentityNode())
  1018. }
  1019. if msgAttrs["type"] == "poll" {
  1020. pollType := "creation"
  1021. if message.PollUpdateMessage != nil {
  1022. pollType = "vote"
  1023. }
  1024. content = append(content, waBinary.Node{
  1025. Tag: "meta",
  1026. Attrs: waBinary.Attrs{
  1027. "polltype": pollType,
  1028. },
  1029. })
  1030. }
  1031. if extraParams.botNode != nil {
  1032. content = append(content, *extraParams.botNode)
  1033. }
  1034. if extraParams.metaNode != nil {
  1035. content = append(content, *extraParams.metaNode)
  1036. }
  1037. if extraParams.additionalNodes != nil {
  1038. content = append(content, *extraParams.additionalNodes...)
  1039. }
  1040. if buttonType := getButtonTypeFromMessage(message); buttonType != "" {
  1041. content = append(content, waBinary.Node{
  1042. Tag: "biz",
  1043. Content: []waBinary.Node{{
  1044. Tag: buttonType,
  1045. Attrs: getButtonAttributes(message),
  1046. }},
  1047. })
  1048. }
  1049. return content
  1050. }
  1051. func (cli *Client) prepareMessageNode(
  1052. ctx context.Context,
  1053. to types.JID,
  1054. id types.MessageID,
  1055. message *waE2E.Message,
  1056. participants []types.JID,
  1057. plaintext, dsmPlaintext []byte,
  1058. timings *MessageDebugTimings,
  1059. extraParams nodeExtraParams,
  1060. ) (*waBinary.Node, []types.JID, error) {
  1061. start := time.Now()
  1062. allDevices, err := cli.GetUserDevices(ctx, participants)
  1063. timings.GetDevices = time.Since(start)
  1064. if err != nil {
  1065. return nil, nil, fmt.Errorf("failed to get device list: %w", err)
  1066. }
  1067. if to.Server == types.GroupServer {
  1068. allDevices = slices.DeleteFunc(allDevices, func(jid types.JID) bool {
  1069. return jid.Server == types.HostedServer || jid.Server == types.HostedLIDServer
  1070. })
  1071. }
  1072. msgType := getTypeFromMessage(message)
  1073. encAttrs := waBinary.Attrs{}
  1074. // Only include encMediaType for 1:1 messages (groups don't have a device-sent message plaintext)
  1075. if encMediaType := getMediaTypeFromMessage(message); dsmPlaintext != nil && encMediaType != "" {
  1076. encAttrs["mediatype"] = encMediaType
  1077. }
  1078. attrs := waBinary.Attrs{
  1079. "id": id,
  1080. "type": msgType,
  1081. "to": to,
  1082. }
  1083. // TODO this is a very hacky hack for announcement group messages, why is it pn anyway?
  1084. if extraParams.addressingMode != "" {
  1085. attrs["addressing_mode"] = string(extraParams.addressingMode)
  1086. }
  1087. if editAttr := getEditAttribute(message); editAttr != "" {
  1088. attrs["edit"] = string(editAttr)
  1089. encAttrs["decrypt-fail"] = string(events.DecryptFailHide)
  1090. }
  1091. if msgType == "reaction" || message.GetPollUpdateMessage() != nil {
  1092. encAttrs["decrypt-fail"] = string(events.DecryptFailHide)
  1093. }
  1094. start = time.Now()
  1095. participantNodes, includeIdentity, err := cli.encryptMessageForDevices(
  1096. ctx, allDevices, id, plaintext, dsmPlaintext, encAttrs,
  1097. )
  1098. timings.PeerEncrypt = time.Since(start)
  1099. if err != nil {
  1100. return nil, nil, err
  1101. }
  1102. participantNode := waBinary.Node{
  1103. Tag: "participants",
  1104. Content: participantNodes,
  1105. }
  1106. return &waBinary.Node{
  1107. Tag: "message",
  1108. Attrs: attrs,
  1109. Content: cli.getMessageContent(
  1110. participantNode, message, attrs, includeIdentity, extraParams,
  1111. ),
  1112. }, allDevices, nil
  1113. }
  1114. func marshalMessage(to types.JID, message *waE2E.Message) (plaintext, dsmPlaintext []byte, err error) {
  1115. if message == nil && to.Server == types.NewsletterServer {
  1116. return
  1117. }
  1118. plaintext, err = proto.Marshal(message)
  1119. if err != nil {
  1120. err = fmt.Errorf("failed to marshal message: %w", err)
  1121. return
  1122. }
  1123. if to.Server != types.GroupServer && to.Server != types.NewsletterServer {
  1124. dsmPlaintext, err = proto.Marshal(&waE2E.Message{
  1125. DeviceSentMessage: &waE2E.DeviceSentMessage{
  1126. DestinationJID: proto.String(to.String()),
  1127. Message: message,
  1128. },
  1129. MessageContextInfo: message.MessageContextInfo,
  1130. })
  1131. if err != nil {
  1132. err = fmt.Errorf("failed to marshal message (for own devices): %w", err)
  1133. return
  1134. }
  1135. }
  1136. return
  1137. }
  1138. func (cli *Client) makeDeviceIdentityNode() waBinary.Node {
  1139. deviceIdentity, err := proto.Marshal(cli.Store.Account)
  1140. if err != nil {
  1141. panic(fmt.Errorf("failed to marshal device identity: %w", err))
  1142. }
  1143. return waBinary.Node{
  1144. Tag: "device-identity",
  1145. Content: deviceIdentity,
  1146. }
  1147. }
  1148. func (cli *Client) encryptMessageForDevices(
  1149. ctx context.Context,
  1150. allDevices []types.JID,
  1151. id string,
  1152. msgPlaintext, dsmPlaintext []byte,
  1153. encAttrs waBinary.Attrs,
  1154. ) ([]waBinary.Node, bool, error) {
  1155. ownJID := cli.getOwnID()
  1156. ownLID := cli.getOwnLID()
  1157. includeIdentity := false
  1158. participantNodes := make([]waBinary.Node, 0, len(allDevices))
  1159. var pnDevices []types.JID
  1160. for _, jid := range allDevices {
  1161. if jid.Server == types.DefaultUserServer {
  1162. pnDevices = append(pnDevices, jid)
  1163. }
  1164. }
  1165. lidMappings, err := cli.Store.LIDs.GetManyLIDsForPNs(ctx, pnDevices)
  1166. if err != nil {
  1167. return nil, false, fmt.Errorf("failed to fetch LID mappings: %w", err)
  1168. }
  1169. encryptionIdentities := make(map[types.JID]types.JID, len(allDevices))
  1170. sessionAddressToJID := make(map[string]types.JID, len(allDevices))
  1171. sessionAddresses := make([]string, 0, len(allDevices))
  1172. for _, jid := range allDevices {
  1173. encryptionIdentity := jid
  1174. if jid.Server == types.DefaultUserServer {
  1175. // TODO query LID from server for missing entries
  1176. if lidForPN, ok := lidMappings[jid]; ok && !lidForPN.IsEmpty() {
  1177. cli.migrateSessionStore(ctx, jid, lidForPN)
  1178. encryptionIdentity = lidForPN
  1179. }
  1180. }
  1181. encryptionIdentities[jid] = encryptionIdentity
  1182. addr := encryptionIdentity.SignalAddress().String()
  1183. sessionAddresses = append(sessionAddresses, addr)
  1184. sessionAddressToJID[addr] = jid
  1185. }
  1186. existingSessions, ctx, err := cli.Store.WithCachedSessions(ctx, sessionAddresses)
  1187. if err != nil {
  1188. return nil, false, fmt.Errorf("failed to prefetch sessions: %w", err)
  1189. }
  1190. var retryDevices []types.JID
  1191. for addr, exists := range existingSessions {
  1192. if !exists {
  1193. retryDevices = append(retryDevices, sessionAddressToJID[addr])
  1194. }
  1195. }
  1196. bundles := cli.fetchPreKeysNoError(ctx, retryDevices)
  1197. for _, jid := range allDevices {
  1198. plaintext := msgPlaintext
  1199. if (jid.User == ownJID.User || jid.User == ownLID.User) && dsmPlaintext != nil {
  1200. if jid == ownJID || jid == ownLID {
  1201. continue
  1202. }
  1203. plaintext = dsmPlaintext
  1204. }
  1205. encrypted, isPreKey, err := cli.encryptMessageForDeviceAndWrap(
  1206. ctx, plaintext, jid, encryptionIdentities[jid], bundles[jid], encAttrs, existingSessions,
  1207. )
  1208. if err != nil {
  1209. // TODO return these errors if it's a fatal one (like context cancellation or database)
  1210. cli.Log.Warnf("Failed to encrypt %s for %s: %v", id, jid, err)
  1211. if ctx.Err() != nil {
  1212. return nil, false, err
  1213. }
  1214. continue
  1215. }
  1216. participantNodes = append(participantNodes, *encrypted)
  1217. if isPreKey {
  1218. includeIdentity = true
  1219. }
  1220. }
  1221. err = cli.Store.PutCachedSessions(ctx)
  1222. if err != nil {
  1223. return nil, false, fmt.Errorf("failed to save cached sessions: %w", err)
  1224. }
  1225. return participantNodes, includeIdentity, nil
  1226. }
  1227. func (cli *Client) encryptMessageForDeviceAndWrap(
  1228. ctx context.Context,
  1229. plaintext []byte,
  1230. wireIdentity,
  1231. encryptionIdentity types.JID,
  1232. bundle *prekey.Bundle,
  1233. encAttrs waBinary.Attrs,
  1234. existingSessions map[string]bool,
  1235. ) (*waBinary.Node, bool, error) {
  1236. node, includeDeviceIdentity, err := cli.encryptMessageForDevice(
  1237. ctx, plaintext, encryptionIdentity, bundle, encAttrs, existingSessions,
  1238. )
  1239. if err != nil {
  1240. return nil, false, err
  1241. }
  1242. return &waBinary.Node{
  1243. Tag: "to",
  1244. Attrs: waBinary.Attrs{"jid": wireIdentity},
  1245. Content: []waBinary.Node{*node},
  1246. }, includeDeviceIdentity, nil
  1247. }
  1248. func copyAttrs(from, to waBinary.Attrs) {
  1249. for k, v := range from {
  1250. to[k] = v
  1251. }
  1252. }
  1253. func (cli *Client) encryptMessageForDevice(
  1254. ctx context.Context,
  1255. plaintext []byte,
  1256. to types.JID,
  1257. bundle *prekey.Bundle,
  1258. extraAttrs waBinary.Attrs,
  1259. existingSessions map[string]bool,
  1260. ) (*waBinary.Node, bool, error) {
  1261. builder := session.NewBuilderFromSignal(cli.Store, to.SignalAddress(), pbSerializer)
  1262. if bundle != nil {
  1263. cli.Log.Debugf("Processing prekey bundle for %s", to)
  1264. err := builder.ProcessBundle(ctx, bundle)
  1265. if cli.AutoTrustIdentity && errors.Is(err, signalerror.ErrUntrustedIdentity) {
  1266. cli.Log.Warnf("Got %v error while trying to process prekey bundle for %s, clearing stored identity and retrying", err, to)
  1267. err = cli.clearUntrustedIdentity(ctx, to)
  1268. if err != nil {
  1269. return nil, false, fmt.Errorf("failed to clear untrusted identity: %w", err)
  1270. }
  1271. err = builder.ProcessBundle(ctx, bundle)
  1272. }
  1273. if err != nil {
  1274. return nil, false, fmt.Errorf("failed to process prekey bundle: %w", err)
  1275. }
  1276. } else {
  1277. sessionExists, checked := existingSessions[to.SignalAddress().String()]
  1278. if !checked {
  1279. var err error
  1280. sessionExists, err = cli.Store.ContainsSession(ctx, to.SignalAddress())
  1281. if err != nil {
  1282. return nil, false, err
  1283. }
  1284. }
  1285. if !sessionExists {
  1286. return nil, false, fmt.Errorf("%w with %s", ErrNoSession, to.SignalAddress().String())
  1287. }
  1288. }
  1289. cipher := session.NewCipher(builder, to.SignalAddress())
  1290. ciphertext, err := cipher.Encrypt(ctx, padMessage(plaintext))
  1291. if err != nil {
  1292. return nil, false, fmt.Errorf("cipher encryption failed: %w", err)
  1293. }
  1294. encAttrs := waBinary.Attrs{
  1295. "v": "2",
  1296. "type": "msg",
  1297. }
  1298. if ciphertext.Type() == protocol.PREKEY_TYPE {
  1299. encAttrs["type"] = "pkmsg"
  1300. }
  1301. copyAttrs(extraAttrs, encAttrs)
  1302. includeDeviceIdentity := encAttrs["type"] == "pkmsg" && cli.MessengerConfig == nil
  1303. return &waBinary.Node{
  1304. Tag: "enc",
  1305. Attrs: encAttrs,
  1306. Content: ciphertext.Serialize(),
  1307. }, includeDeviceIdentity, nil
  1308. }