|  |  |  | import _ from 'lodash'; | 
					
						
							|  |  |  | import { storeOnNode } from '../snode_api/SNodeAPI'; | 
					
						
							|  |  |  | import { getSwarmFor } from '../snode_api/snodePool'; | 
					
						
							|  |  |  | import { firstTrue } from '../utils/Promise'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const DEFAULT_CONNECTIONS = 3; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Refactor note: We should really clean this up ... it's very messy | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * We need to split it into 2 sends: | 
					
						
							|  |  |  |  *  - Snodes | 
					
						
							|  |  |  |  *  - Open Groups | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Mikunj: | 
					
						
							|  |  |  |  *  Temporarily i've made it so `MessageSender` handles open group sends and calls this function for regular sends. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export async function sendMessage( | 
					
						
							|  |  |  |   pubKey: string, | 
					
						
							|  |  |  |   data: Uint8Array, | 
					
						
							|  |  |  |   messageTimeStamp: number, | 
					
						
							|  |  |  |   ttl: number, | 
					
						
							|  |  |  |   options: { | 
					
						
							|  |  |  |     isPublic?: boolean; | 
					
						
							|  |  |  |   } = {} | 
					
						
							|  |  |  | ): Promise<void> { | 
					
						
							|  |  |  |   const { isPublic = false } = options; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (isPublic) { | 
					
						
							|  |  |  |     window?.log?.warn('this sendMessage() should not be called anymore with an open group message'); | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const data64 = window.dcodeIO.ByteBuffer.wrap(data).toString('base64'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Using timestamp as a unique identifier
 | 
					
						
							|  |  |  |   const swarm = await getSwarmFor(pubKey); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // send parameters
 | 
					
						
							|  |  |  |   const params = { | 
					
						
							|  |  |  |     pubKey, | 
					
						
							|  |  |  |     ttl: `${ttl}`, | 
					
						
							|  |  |  |     timestamp: `${messageTimeStamp}`, | 
					
						
							|  |  |  |     data: data64, | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const usedNodes = _.slice(swarm, 0, DEFAULT_CONNECTIONS); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const promises = usedNodes.map(async usedNode => { | 
					
						
							|  |  |  |     // TODO: Revert back to using snode address instead of IP
 | 
					
						
							|  |  |  |     // No pRetry here as if this is a bad path it will be handled and retried in lokiOnionFetch.
 | 
					
						
							|  |  |  |     // the only case we could care about a retry would be when the usedNode is not correct,
 | 
					
						
							|  |  |  |     // but considering we trigger this request with a few snode in //, this should be fine.
 | 
					
						
							|  |  |  |     const successfulSend = await storeOnNode(usedNode, params); | 
					
						
							|  |  |  |     if (successfulSend) { | 
					
						
							|  |  |  |       return usedNode; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     // should we mark snode as bad if it can't store our message?
 | 
					
						
							|  |  |  |     return undefined; | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   let snode; | 
					
						
							|  |  |  |   try { | 
					
						
							|  |  |  |     snode = await firstTrue(promises); | 
					
						
							|  |  |  |   } catch (e) { | 
					
						
							|  |  |  |     const snodeStr = snode ? `${snode.ip}:${snode.port}` : 'null'; | 
					
						
							|  |  |  |     window?.log?.warn( | 
					
						
							|  |  |  |       `loki_message:::sendMessage - ${e.code} ${e.message} to ${pubKey} via snode:${snodeStr}` | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     throw e; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (!snode) { | 
					
						
							|  |  |  |     throw new window.textsecure.EmptySwarmError(pubKey, 'Ran out of swarm nodes to query'); | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     window?.log?.info( | 
					
						
							|  |  |  |       `loki_message:::sendMessage - Successfully stored message to ${pubKey} via ${snode.ip}:${snode.port}` | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } |