Handle transaction on chat.
@param chat {list}: a chat object, which is a list of
{id: {number}, name: {string}, input: {string}} objects
773266
function handleChat(request, response, chat) {
    var resp, id;
    if (request.method === "POST") {
        // add a line to the chat log
        requestBody(request, function(form) {
            while (chat.length > CHATLINES) {
                chat.shift();
            }

            /* If data is present in the chat, then increment the last id,
             * otherwise start at 0.
             */
            if (chat.length) {
                id = chat[chat.length-1].id + 1;
            } else {
                id = 0;
            }
            chat.push({
                id: id,  // chat line id
                name: form.name,  // the user's name
                input: form.input  // the user's text input
            });
            respOk(response, '', 'text/json');
        });
    } else {
        /* Return chat data. If lastid is specified, then we only return
         * chat lines since this id.
         */
        var form = requestQuery(request);
        var lastid = +form.lastid;
        if (lastid >= 0) {
            for (var i=0; i<chat.length; i++) {
                if (chat[i].id === lastid) {
                    break;
                }
            }
            resp = JSON.stringify(chat.slice(i+1));
        } else {
            resp = JSON.stringify(chat);
        }
        respOk(response, resp, 'text/json');
    }
}
839057
def _on_read(self, sender, *args, **kwargs):
        
        data = kwargs.get('data', None)
        self.on_read(data=data)

        self._handle_message(data)
933938
def on_connect(client):
    
    print "++ Opened connection to %s" % client.addrport()
    broadcast('%s joins the conversation.\n' % client.addrport() )
    CLIENT_LIST.append(client)
    client.send("Welcome to the Chat Server, %s.\n" % client.addrport() )
1282803
def send_chat_action(self, peer: Peer, action: botapi.ChatAction, on_success: callable=None):
        
        botapi.send_chat_action(chat_id=peer.id, action=action, on_success=on_success, **self.request_args).run()
1045319
async def update_chat(self):
        
        other = await self.bot.get_chat(self.id)

        for key, value in other:
            self[key] = value
1365097
def sendChatAction(self, chat_id, action):
        
        p = _strip(locals())
        return self._api_request('sendChatAction', _rectify(p))
532944
async def respond(self, *args, **kwargs):
        
        return await self._client.send_message(
            await self.get_input_chat(), *args, **kwargs)
1081216
function sendMsg(that) {
        var msg, to, ids;

        // No msg sent.
        if (that.isDisabled()) return;

        msg = that.readTextarea();

        // Move cursor at the beginning.
        if (msg === '') {
            node.warn('Chat: message has no text, not sent.');
            return;
        }
        // Simplify things, if there is only one recipient.
        ids = that.recipientsIds;
        if (ids.length === 0) {
            node.warn('Chat: empty recipient list, message not sent.');
            return;
        }
        to = ids.length === 1 ? ids[0] : ids;
        that.writeMsg('outgoing', { msg: msg }); // to not used now.
        node.say(that.chatEvent, to, msg);
        // Make sure the cursor goes back to top.
        setTimeout(function() { that.textarea.value = ''; });
    }
357421
def on_message(self, message_id_service, contact_id_service, content):
        
        try:
            live_chat = Chat.live.get(
                Q(agent__id_service=contact_id_service) | Q(asker__id_service=contact_id_service))            
        except ObjectDoesNotExist:
            self._new_chat_processing(message_id_service, contact_id_service, content)
        else:
            live_chat.handle_message(message_id_service, contact_id_service, content, self)
92797
public Observable<ChatResult> handleParticipantsAdded(final String conversationId) {
        return persistenceController.getConversation(conversationId).flatMap(conversation -> {
            if (conversation == null) {
                return handleNoLocalConversation(conversationId);
            } else {
                return Observable.fromCallable(() -> new ChatResult(true, null));
            }
        });
    }
652865
function handleMessageFromHub(ws, json, device_pubkey, bIndirectCorrespondent, callbacks){
	var subject = json.subject;
	var body = json.body;
	if (!subject || typeof body == "undefined")
		return callbacks.ifError("no subject or body");
	//if (bIndirectCorrespondent && ["cancel_new_wallet", "my_xpubkey", "new_wallet_address"].indexOf(subject) === -1)
	//    return callbacks.ifError("you're indirect correspondent, cannot trust "+subject+" from you");
	var from_address = objectHash.getDeviceAddress(device_pubkey);
	
	switch (subject){
		case "pairing":
			device.handlePairingMessage(json, device_pubkey, callbacks);
			break;
		
		case "text":
			message_counter++;
			if (!ValidationUtils.isNonemptyString(body))
				return callbacks.ifError("text body must be string");
			// the wallet should have an event handler that displays the text to the user
			eventBus.emit("text", from_address, body, message_counter);
			callbacks.ifOk();
			break;

		case "removed_paired_device":
			if(conf.bIgnoreUnpairRequests) {
				// unpairing is ignored
				callbacks.ifError("removed_paired_device ignored: "+from_address);
			} else {
				determineIfDeviceCanBeRemoved(from_address, function(bRemovable){
					if (!bRemovable)
						return callbacks.ifError("device "+from_address+" is not removable");
					device.removeCorrespondentDevice(from_address, function(){
						eventBus.emit("removed_paired_device", from_address);
						callbacks.ifOk();
					});
				});
			}
			break;

		case "chat_recording_pref":
			message_counter++;
			eventBus.emit("chat_recording_pref", from_address, body, message_counter);
			callbacks.ifOk();
			break;
		
		case "create_new_wallet":
			// {wallet: "base64", wallet_definition_template: [...]}
			walletDefinedByKeys.handleOfferToCreateNewWallet(body, from_address, callbacks);
			break;
		
		case "cancel_new_wallet":
			// {wallet: "base64"}
			if (!ValidationUtils.isNonemptyString(body.wallet))
				return callbacks.ifError("no wallet");
			walletDefinedByKeys.deleteWallet(body.wallet, from_address, callbacks.ifOk);
			break;
		
		case "my_xpubkey": // allowed from non-correspondents
			// {wallet: "base64", my_xpubkey: "base58"}
			if (!ValidationUtils.isNonemptyString(body.wallet))
				return callbacks.ifError("no wallet");
			if (!ValidationUtils.isNonemptyString(body.my_xpubkey))
				return callbacks.ifError("no my_xpubkey");
			if (body.my_xpubkey.length > 112)
				return callbacks.ifError("my_xpubkey too long");
			walletDefinedByKeys.addDeviceXPubKey(body.wallet, from_address, body.my_xpubkey, callbacks.ifOk);
			break;
		
		case "wallet_fully_approved": // allowed from non-correspondents
			// {wallet: "base64"}
			if (!ValidationUtils.isNonemptyString(body.wallet))
				return callbacks.ifError("no wallet");
			walletDefinedByKeys.handleNotificationThatWalletFullyApproved(body.wallet, from_address, callbacks.ifOk);
			break;
		
		case "new_wallet_address":
			// {wallet: "base64", is_change: (0|1), address_index: 1234, address: "BASE32"}
			if (!ValidationUtils.isNonemptyString(body.wallet))
				return callbacks.ifError("no wallet");
			if (!(body.is_change === 0 || body.is_change === 1))
				return callbacks.ifError("bad is_change");
			if (!ValidationUtils.isNonnegativeInteger(body.address_index))
				return callbacks.ifError("bad address_index");
			if (!ValidationUtils.isValidAddress(body.address))
				return callbacks.ifError("no address or bad address");
			walletDefinedByKeys.addNewAddress(body.wallet, body.is_change, body.address_index, body.address, function(err){
				if (err)
					return callbacks.ifError(err);
				callbacks.ifOk();
			});
			break;
			
		case "create_new_shared_address":
			// {address_definition_template: [...]}
			if (!ValidationUtils.isArrayOfLength(body.address_definition_template, 2))
				return callbacks.ifError("no address definition template");
			walletDefinedByAddresses.validateAddressDefinitionTemplate(
				body.address_definition_template, from_address, 
				function(err, assocMemberDeviceAddressesBySigningPaths){
					if (err)
						return callbacks.ifError(err);
					// this event should trigger a confirmatin dialog, user needs to approve creation of the shared address and choose his 
					// own address that is to become a member of the shared address
					eventBus.emit("create_new_shared_address", body.address_definition_template, assocMemberDeviceAddressesBySigningPaths);
					callbacks.ifOk();
				}
			);
			break;
		
		case "approve_new_shared_address":
			// {address_definition_template_chash: "BASE32", address: "BASE32", device_addresses_by_relative_signing_paths: {...}}
			if (!ValidationUtils.isValidAddress(body.address_definition_template_chash))
				return callbacks.ifError("invalid addr def c-hash");
			if (!ValidationUtils.isValidAddress(body.address))
				return callbacks.ifError("invalid address");
			if (typeof body.device_addresses_by_relative_signing_paths !== "object" 
					|| Object.keys(body.device_addresses_by_relative_signing_paths).length === 0)
				return callbacks.ifError("invalid device_addresses_by_relative_signing_paths");
			walletDefinedByAddresses.approvePendingSharedAddress(body.address_definition_template_chash, from_address, 
				body.address, body.device_addresses_by_relative_signing_paths);
			callbacks.ifOk();
			break;
			
		case "reject_new_shared_address":
			// {address_definition_template_chash: "BASE32"}
			if (!ValidationUtils.isValidAddress(body.address_definition_template_chash))
				return callbacks.ifError("invalid addr def c-hash");
			walletDefinedByAddresses.deletePendingSharedAddress(body.address_definition_template_chash);
			callbacks.ifOk();
			break;
			
		case "new_shared_address":
			// {address: "BASE32", definition: [...], signers: {...}}
			walletDefinedByAddresses.handleNewSharedAddress(body, {
				ifError: callbacks.ifError,
				ifOk: function(){
					callbacks.ifOk();
					eventBus.emit('maybe_new_transactions');
				}
			});
			break;
			
		// request to sign a unit created on another device
		// two use cases:
		// 1. multisig: same address hosted on several devices
		// 2. multilateral signing: different addresses signing the same message, such as a (dumb) contract
		case "sign":
			// {address: "BASE32", signing_path: "r.1.2.3", unsigned_unit: {...}}
			if (!ValidationUtils.isValidAddress(body.address))
				return callbacks.ifError("no address or bad address");
			if (!ValidationUtils.isNonemptyString(body.signing_path) || body.signing_path.charAt(0) !== 'r')
				return callbacks.ifError("bad signing path");
			var objUnit = body.unsigned_unit;
			if (typeof objUnit !== "object")
				return callbacks.ifError("no unsigned unit");
			// replace all existing signatures with placeholders so that signing requests sent to us on different stages of signing become identical,
			// hence the hashes of such unsigned units are also identical
			objUnit.authors.forEach(function(author){
				var authentifiers = author.authentifiers;
				for (var path in authentifiers)
					authentifiers[path] = authentifiers[path].replace(/./, '-'); 
			});
			var assocPrivatePayloads = body.private_payloads;
			if ("private_payloads" in body){
				if (typeof assocPrivatePayloads !== "object" || !assocPrivatePayloads)
					return callbacks.ifError("bad private payloads");
				for (var payload_hash in assocPrivatePayloads){
					var payload = assocPrivatePayloads[payload_hash];
					var hidden_payload = _.cloneDeep(payload);
					if (payload.denomination) // indivisible asset.  In this case, payload hash is calculated based on output_hash rather than address and blinding
						hidden_payload.outputs.forEach(function(o){
							delete o.address;
							delete o.blinding;
						});
					var calculated_payload_hash = objectHash.getBase64Hash(hidden_payload);
					if (payload_hash !== calculated_payload_hash)
						return callbacks.ifError("private payload hash does not match");
					if (!ValidationUtils.isNonemptyArray(objUnit.messages))
						return callbacks.ifError("no messages in unsigned unit");
					if (objUnit.messages.filter(function(objMessage){ return (objMessage.payload_hash === payload_hash); }).length !== 1)
						return callbacks.ifError("no such payload hash in the messages");
				}
			}
			// findAddress handles both types of addresses
			findAddress(body.address, body.signing_path, {
				ifError: callbacks.ifError,
				ifLocal: function(objAddress){
					// the commented check would make multilateral signing impossible
					//db.query("SELECT 1 FROM extended_pubkeys WHERE wallet=? AND device_address=?", [row.wallet, from_address], function(sender_rows){
					//    if (sender_rows.length !== 1)
					//        return callbacks.ifError("sender is not cosigner of this address");
						callbacks.ifOk();
						objUnit.unit = objectHash.getUnitHash(objUnit);
						var objJoint = {unit: objUnit, unsigned: true};
						eventBus.once("validated-"+objUnit.unit, function(bValid){
							if (!bValid){
								console.log("===== unit in signing request is invalid");
								return;
							}
							// This event should trigger a confirmation dialog.
							// If we merge coins from several addresses of the same wallet, we'll fire this event multiple times for the same unit.
							// The event handler must lock the unit before displaying a confirmation dialog, then remember user's choice and apply it to all
							// subsequent requests related to the same unit
							eventBus.emit("signing_request", objAddress, body.address, objUnit, assocPrivatePayloads, from_address, body.signing_path);
						});
						// if validation is already under way, handleOnlineJoint will quickly exit because of assocUnitsInWork.
						// as soon as the previously started validation finishes, it will trigger our event handler (as well as its own)
						network.handleOnlineJoint(ws, objJoint);
					//});
				},
				ifRemote: function(device_address){
					if (device_address === from_address){
						callbacks.ifError("looping signing request for address "+body.address+", path "+body.signing_path);
						throw Error("looping signing request for address "+body.address+", path "+body.signing_path);
					}
					var text_to_sign = objectHash.getUnitHashToSign(body.unsigned_unit).toString("base64");
					// I'm a proxy, wait for response from the actual signer and forward to the requestor
					eventBus.once("signature-"+device_address+"-"+body.address+"-"+body.signing_path+"-"+text_to_sign, function(sig){
						sendSignature(from_address, text_to_sign, sig, body.signing_path, body.address);
					});
					// forward the offer to the actual signer
					device.sendMessageToDevice(device_address, subject, body);
					callbacks.ifOk();
				},
				ifMerkle: function(bLocal){
					callbacks.ifError("there is merkle proof at signing path "+body.signing_path);
				},
				ifUnknownAddress: function(){
					callbacks.ifError("not aware of address "+body.address+" but will see if I learn about it later");
					eventBus.once("new_address-"+body.address, function(){
						// rewrite callbacks to avoid duplicate unlocking of mutex
						handleMessageFromHub(ws, json, device_pubkey, bIndirectCorrespondent, { ifOk: function(){}, ifError: function(){} });
					});
				}
			});
			break;
		
		case "signature":
			// {signed_text: "base64 of sha256", signing_path: "r.1.2.3", signature: "base64"}
			if (!ValidationUtils.isStringOfLength(body.signed_text, constants.HASH_LENGTH)) // base64 of sha256
				return callbacks.ifError("bad signed text");
			if (!ValidationUtils.isStringOfLength(body.signature, constants.SIG_LENGTH) && body.signature !== '[refused]')
				return callbacks.ifError("bad signature length");
			if (!ValidationUtils.isNonemptyString(body.signing_path) || body.signing_path.charAt(0) !== 'r')
				return callbacks.ifError("bad signing path");
			if (!ValidationUtils.isValidAddress(body.address))
				return callbacks.ifError("bad address");
			eventBus.emit("signature-" + from_address + "-" + body.address + "-" + body.signing_path + "-" + body.signed_text, body.signature);
			callbacks.ifOk();
			break;
			
		case 'private_payments':
			var arrChains = body.chains;
			if (!ValidationUtils.isNonemptyArray(arrChains))
				return callbacks.ifError("no chains found");
			profiler.increment();
			
			if (conf.bLight)
				network.requestUnfinishedPastUnitsOfPrivateChains(arrChains); // it'll work in the background
			
			var assocValidatedByKey = {};
			var bParsingComplete = false;
			var cancelAllKeys = function(){
				for (var key in assocValidatedByKey)
					eventBus.removeAllListeners(key);
			};

			var current_message_counter = ++message_counter;

			var checkIfAllValidated = function(){
				if (!assocValidatedByKey) // duplicate call - ignore
					return console.log('duplicate call of checkIfAllValidated');
				for (var key in assocValidatedByKey)
					if (!assocValidatedByKey[key])
						return console.log('not all private payments validated yet');
				assocValidatedByKey = null; // to avoid duplicate calls
				if (!body.forwarded)
					emitNewPrivatePaymentReceived(from_address, arrChains, current_message_counter);
				profiler.print();
			};
			
			async.eachSeries(
				arrChains,
				function(arrPrivateElements, cb){ // validate each chain individually
					var objHeadPrivateElement = arrPrivateElements[0];
					if (!!objHeadPrivateElement.payload.denomination !== ValidationUtils.isNonnegativeInteger(objHeadPrivateElement.output_index))
						return cb("divisibility doesn't match presence of output_index");
					var output_index = objHeadPrivateElement.payload.denomination ? objHeadPrivateElement.output_index : -1;
					var payload_hash = objectHash.getBase64Hash(objHeadPrivateElement.payload);
					var key = 'private_payment_validated-'+objHeadPrivateElement.unit+'-'+payload_hash+'-'+output_index;
					assocValidatedByKey[key] = false;
					network.handleOnlinePrivatePayment(ws, arrPrivateElements, true, {
						ifError: function(error){
							console.log("handleOnlinePrivatePayment error: "+error);
							cb("an error"); // do not leak error message to the hub
						},
						ifValidationError: function(unit, error){
							console.log("handleOnlinePrivatePayment validation error: "+error);
							cb("an error"); // do not leak error message to the hub
						},
						ifAccepted: function(unit){
							console.log("handleOnlinePrivatePayment accepted");
							assocValidatedByKey[key] = true;
							cb(); // do not leak unit info to the hub
						},
						// this is the most likely outcome for light clients
						ifQueued: function(){
							console.log("handleOnlinePrivatePayment queued, will wait for "+key);
							eventBus.once(key, function(bValid){
								if (!bValid)
									return cancelAllKeys();
								assocValidatedByKey[key] = true;
								if (bParsingComplete)
									checkIfAllValidated();
								else
									console.log('parsing incomplete yet');
							});
							cb();
						}
					});
				},
				function(err){
					bParsingComplete = true;
					if (err){
						cancelAllKeys();
						return callbacks.ifError(err);
					}
					checkIfAllValidated();
					callbacks.ifOk();
					// forward the chains to other members of output addresses
					if (!body.forwarded)
						forwardPrivateChainsToOtherMembersOfOutputAddresses(arrChains);
				}
			);
			break;
			
		case 'payment_notification':
			// note that since the payments are public, an evil user might notify us about a payment sent by someone else 
			// (we'll be fooled to believe it was sent by the evil user).  It is only possible if he learns our address, e.g. if we make it public.
			// Normally, we generate a one-time address and share it in chat session with the future payer only.
			var current_message_counter = ++message_counter;
			var unit = body;
			if (!ValidationUtils.isStringOfLength(unit, constants.HASH_LENGTH))
				return callbacks.ifError("invalid unit in payment notification");
			var bEmitted = false;
			var emitPn = function(objJoint){
				if (bEmitted)
					return;
				bEmitted = true;
				emitNewPublicPaymentReceived(from_address, objJoint.unit, current_message_counter);
			};
			eventBus.once('saved_unit-'+unit, emitPn);
			storage.readJoint(db, unit, {
				ifNotFound: function(){
					console.log("received payment notification for unit "+unit+" which is not known yet, will wait for it");
					callbacks.ifOk();
				},
				ifFound: function(objJoint){
					emitPn(objJoint);
					eventBus.removeListener('saved_unit-'+unit, emitPn);
					callbacks.ifOk();
				}
			});
			break;
			
		default:
			callbacks.ifError("unknnown subject: "+subject);
	}
}
1003093
function(item, callback) {
                for (var i = 0; i < chatUploadQueue.length; i++) {
                    if (chatUploadQueue[i].message.uniqueId == item.message.uniqueId) {
                        chatUploadQueue.splice(i, 1);
                    }
                }
                callback && callback();
            }
1121277
async def setChatStickerSet(self, chat_id, sticker_set_name):
        
        p = _strip(locals())
        return await self._api_request('setChatStickerSet', _rectify(p))
738712
def chat_delete(self, *, channel: str, ts: str, **kwargs) -> SlackResponse:
        
        kwargs.update({"channel": channel, "ts": ts})
        return self.api_call("chat.delete", json=kwargs)
168523
function sync(id)
      {
        if(id == last.json.id) return;
        // bottoms up, send older first
        sync(lib.base32.encode(lib.hashname.siphash(mesh.hashname, lib.base32.decode(id))));
        stream.write(chat.messages[id]);
      }
476914
function Conversation (params) {
  var self = this
  if (!(self instanceof Conversation)) return new Conversation(params)

  var convoMessages = params['conversation_messages'] || { }
  var lastChatActions = params['last_chat_actions']
  var lastTransaction = params['last_cash_transaction']
  var pendingRecievedSnaps = params['pending_received_snaps'] || []
  var messages = convoMessages['messages'] || []
  var lastSnap = params['last_snap']
  var lastInteraction = +params['last_interaction_ts']

  self.messagingAuth = convoMessages['messaging_auth']

  self.state = params['conversation_state']
  self.identifier = params['id']
  self.pagination = params['iter_token']

  self.lastSnap = lastSnap ? new Snap(lastSnap) : null
  self.lastTransaction = lastTransaction ? new Transaction(lastTransaction) : null
  self.lastInteraction = lastInteraction > 0 ? new Date(lastInteraction) : null
  if (lastChatActions) {
    // self.lastChatType = SKChatTypeFromString(lastChatActions['last_write_type'])
    // self.lastChatRead = +NSDate dateWithTimeIntervalSince1970:[lastChatActions['last_read_timestamp']/1000]
    // self.lastChatWrite = +NSDate dateWithTimeIntervalSince1970:[lastChatActions['last_write_timestamp']/1000]
    self.lastChatReader = lastChatActions['last_reader']
    self.lastChatWriter = lastChatActions['last_writer']
  }

  self.participants = params['participants'] || self.identifier.split('~')
  self.usersWithPendingChats = params['pending_chats_for'] || []

  // Messages
  self.messages = messages.map(function (message) {
    if (message['snap']) {
      return new Snap(message['snap'])
    } else if (message['chat_message']) {
      return new Message(message['chat_message'])
    } else if (message['cash_transaction']) {
      return new CashTransaction(message['chat_transaction'])
    } else {
      debug('Unhandled conversation message type:%s', message)
      return null
    }
  })

  // Pending recieved snaps
  self.pendingRecievedSnaps = pendingRecievedSnaps.map(function (snap) {
    return new Snap(snap)
  })
}
1222022
function(item, callback) {
                if (hasCache && typeof queueDb == 'object') {
                    queueDb.waitQ.where('uniqueId')
                        .equals(item.uniqueId)
                        .and(function(item) {
                            return item.owner == parseInt(userInfo.id);
                        })
                        .delete()
                        .then(function() {
                            callback && callback();
                        })
                        .catch(function(error) {
                            fireEvent('error', {
                                code: error.code,
                                message: error.message,
                                error: error
                            });
                        });
                }
                else {
                    for (var i = 0; i < chatWaitQueue.length; i++) {
                        if (chatWaitQueue[i].uniqueId == item.uniqueId) {
                            chatWaitQueue.splice(i, 1);
                        }
                    }
                    callback && callback();
                }
            }