be sure to send open group messages in order

pull/1381/head
Audric Ackermann 5 years ago
parent 220de2cd0a
commit efa45f7cbe

@ -1127,7 +1127,7 @@
async sendMessageJob(message) { async sendMessageJob(message) {
try { try {
const uploads = await message.uploadData(); const uploads = await message.uploadData();
const id = message.id; const { id } = message;
const expireTimer = this.get('expireTimer'); const expireTimer = this.get('expireTimer');
const destination = this.id; const destination = this.id;
@ -1236,6 +1236,7 @@
throw new TypeError(`Invalid conversation type: '${this.get('type')}'`); throw new TypeError(`Invalid conversation type: '${this.get('type')}'`);
} catch (e) { } catch (e) {
await message.saveErrors(e); await message.saveErrors(e);
return null;
} }
}, },
async sendMessage( async sendMessage(
@ -1252,100 +1253,102 @@
const expireTimer = this.get('expireTimer'); const expireTimer = this.get('expireTimer');
const recipients = this.getRecipients(); const recipients = this.getRecipients();
this.queueJob(async () => { const now = Date.now();
const now = Date.now();
window.log.info( window.log.info(
'Sending message to conversation', 'Sending message to conversation',
this.idForLogging(), this.idForLogging(),
'with timestamp', 'with timestamp',
now now
); );
// be sure an empty quote is marked as undefined rather than being empty // be sure an empty quote is marked as undefined rather than being empty
// otherwise upgradeMessageSchema() will return an object with an empty array // otherwise upgradeMessageSchema() will return an object with an empty array
// and this.get('quote') will be true, even if there is no quote. // and this.get('quote') will be true, even if there is no quote.
const editedQuote = _.isEmpty(quote) ? undefined : quote; const editedQuote = _.isEmpty(quote) ? undefined : quote;
const messageWithSchema = await upgradeMessageSchema({ const messageWithSchema = await upgradeMessageSchema({
type: 'outgoing', type: 'outgoing',
body, body,
conversationId: destination, conversationId: destination,
quote: editedQuote, quote: editedQuote,
preview, preview,
attachments, attachments,
sent_at: now, sent_at: now,
received_at: now, received_at: now,
expireTimer, expireTimer,
recipients, recipients,
}); });
if (this.isPublic()) { if (this.isPublic()) {
// Public chats require this data to detect duplicates // Public chats require this data to detect duplicates
messageWithSchema.source = textsecure.storage.user.getNumber(); messageWithSchema.source = textsecure.storage.user.getNumber();
messageWithSchema.sourceDevice = 1; messageWithSchema.sourceDevice = 1;
} else { } else {
messageWithSchema.destination = destination; messageWithSchema.destination = destination;
} }
const { sessionRestoration = false } = otherOptions; const { sessionRestoration = false } = otherOptions;
const attributes = { const attributes = {
...messageWithSchema, ...messageWithSchema,
groupInvitation, groupInvitation,
sessionRestoration, sessionRestoration,
id: window.getGuid(), id: window.getGuid(),
}; };
const model = this.addSingleMessage(attributes); const model = this.addSingleMessage(attributes);
const message = MessageController.register(model.id, model); const message = MessageController.register(model.id, model);
await message.commit(true); await message.commit(true);
if (this.isPrivate()) { if (this.isPrivate()) {
message.set({ destination }); message.set({ destination });
} }
if (this.isPublic()) { if (this.isPublic()) {
message.setServerTimestamp(new Date().getTime()); message.setServerTimestamp(new Date().getTime());
} }
const id = await message.commit(); const id = await message.commit();
message.set({ id }); message.set({ id });
window.Whisper.events.trigger('messageAdded', { window.Whisper.events.trigger('messageAdded', {
conversationKey: this.id, conversationKey: this.id,
messageModel: message, messageModel: message,
}); });
this.set({ this.set({
lastMessage: model.getNotificationText(), lastMessage: model.getNotificationText(),
lastMessageStatus: 'sending', lastMessageStatus: 'sending',
active_at: now, active_at: now,
timestamp: now, timestamp: now,
isArchived: false, isArchived: false,
}); });
await this.commit(); await this.commit();
// We're offline! // We're offline!
if (!textsecure.messaging) { if (!textsecure.messaging) {
let errors; let errors;
if (this.contactCollection.length) { if (this.contactCollection.length) {
errors = this.contactCollection.map(contact => { errors = this.contactCollection.map(contact => {
const error = new Error('Network is not available');
error.name = 'SendMessageNetworkError';
error.number = contact.id;
return error;
});
} else {
const error = new Error('Network is not available'); const error = new Error('Network is not available');
error.name = 'SendMessageNetworkError'; error.name = 'SendMessageNetworkError';
error.number = this.id; error.number = contact.id;
errors = [error]; return error;
} });
await message.saveErrors(errors); } else {
return null; const error = new Error('Network is not available');
error.name = 'SendMessageNetworkError';
error.number = this.id;
errors = [error];
} }
void this.sendMessageJob(message); await message.saveErrors(errors);
return null;
}
this.queueJob(async () => {
await this.sendMessageJob(message);
}); });
return null;
}, },
async updateAvatarOnPublicChat({ url, profileKey }) { async updateAvatarOnPublicChat({ url, profileKey }) {

@ -1723,122 +1723,126 @@ class LokiPublicChannelAPI {
} }
const pubKey = adnMessage.user.username; const pubKey = adnMessage.user.username;
try {
const messengerData = await this.getMessengerData(adnMessage);
if (messengerData === false) {
return false;
}
// eslint-disable-next-line no-param-reassign
adnMessage.timestamp = messengerData.timestamp;
// eslint-disable-next-line no-param-reassign
adnMessage.body = messengerData.text;
const {
timestamp,
serverTimestamp,
quote,
attachments,
preview,
avatar,
profileKey,
} = messengerData;
if (!timestamp) {
return false; // Invalid message
}
const messengerData = await this.getMessengerData(adnMessage); // Duplicate check
if (messengerData === false) { const isDuplicate = (message, testedMessage) =>
return false; dataMessage.isDuplicate(
} message,
// eslint-disable-next-line no-param-reassign testedMessage,
adnMessage.timestamp = messengerData.timestamp; testedMessage.user.username
// eslint-disable-next-line no-param-reassign );
adnMessage.body = messengerData.text;
const { // Filter out any messages that we got previously
timestamp, if (this.lastMessagesCache.some(m => isDuplicate(m, adnMessage))) {
serverTimestamp, return false; // Duplicate message
quote, }
attachments,
preview,
avatar,
profileKey,
} = messengerData;
if (!timestamp) {
return false; // Invalid message
}
// Duplicate check
const isDuplicate = (message, testedMessage) =>
dataMessage.isDuplicate(
message,
testedMessage,
testedMessage.user.username
);
// Filter out any messages that we got previously
if (this.lastMessagesCache.some(m => isDuplicate(m, adnMessage))) {
return false; // Duplicate message
}
// FIXME: maybe move after the de-multidev-decode // FIXME: maybe move after the de-multidev-decode
// Add the message to the lastMessage cache and keep the last 5 recent messages // Add the message to the lastMessage cache and keep the last 5 recent messages
this.lastMessagesCache = [ this.lastMessagesCache = [
...this.lastMessagesCache, ...this.lastMessagesCache,
{ {
attributes: { attributes: {
source: pubKey, source: pubKey,
body: adnMessage.text, body: adnMessage.text,
sent_at: timestamp, sent_at: timestamp,
},
}, },
}, ].splice(-5);
].splice(-5);
const from = adnMessage.user.name || 'Anonymous'; // profileName const from = adnMessage.user.name || 'Anonymous'; // profileName
// if us // if us
if (pubKey === ourNumberProfile || pubKey === ourNumberDevice) { if (pubKey === ourNumberProfile || pubKey === ourNumberDevice) {
// update the last name we saw from ourself // update the last name we saw from ourself
lastProfileName = from; lastProfileName = from;
} }
// track sources for multidevice support // track sources for multidevice support
// sort it by home server // sort it by home server
let homeServer = window.getDefaultFileServer(); let homeServer = window.getDefaultFileServer();
if (adnMessage.user && adnMessage.user.annotations.length) { if (adnMessage.user && adnMessage.user.annotations.length) {
const homeNotes = adnMessage.user.annotations.filter( const homeNotes = adnMessage.user.annotations.filter(
note => note.type === HOMESERVER_USER_ANNOTATION_TYPE note => note.type === HOMESERVER_USER_ANNOTATION_TYPE
); );
// FIXME: this annotation should probably be signed and verified... // FIXME: this annotation should probably be signed and verified...
homeServer = homeNotes.reduce( homeServer = homeNotes.reduce(
(curVal, note) => (note.value ? note.value : curVal), (curVal, note) => (note.value ? note.value : curVal),
homeServer homeServer
); );
} }
if (homeServerPubKeys[homeServer] === undefined) { if (homeServerPubKeys[homeServer] === undefined) {
homeServerPubKeys[homeServer] = []; homeServerPubKeys[homeServer] = [];
} }
if (homeServerPubKeys[homeServer].indexOf(`@${pubKey}`) === -1) { if (homeServerPubKeys[homeServer].indexOf(`@${pubKey}`) === -1) {
homeServerPubKeys[homeServer].push(`@${pubKey}`); homeServerPubKeys[homeServer].push(`@${pubKey}`);
} }
// generate signal message object // generate signal message object
const messageData = { const messageData = {
serverId: adnMessage.id, serverId: adnMessage.id,
clientVerified: true, clientVerified: true,
isSessionRequest: false, isSessionRequest: false,
source: pubKey, source: pubKey,
sourceDevice: 1, sourceDevice: 1,
timestamp, // sender timestamp timestamp, // sender timestamp
serverTimestamp, // server created_at, used to order messages serverTimestamp, // server created_at, used to order messages
receivedAt, receivedAt,
isPublic: true, isPublic: true,
message: { message: {
body: body:
adnMessage.text === timestamp.toString() ? '' : adnMessage.text, adnMessage.text === timestamp.toString() ? '' : adnMessage.text,
attachments, attachments,
group: { group: {
id: this.conversationId, id: this.conversationId,
type: textsecure.protobuf.GroupContext.Type.DELIVER, type: textsecure.protobuf.GroupContext.Type.DELIVER,
},
flags: 0,
expireTimer: 0,
profileKey,
timestamp,
received_at: receivedAt,
sent_at: timestamp, // sender timestamp inner
quote,
contact: [],
preview,
profile: {
displayName: from,
avatar,
},
}, },
flags: 0, };
expireTimer: 0, receivedAt += 1; // Ensure different arrival times
profileKey,
timestamp, // now process any user meta data updates
received_at: receivedAt, // - update their conversation with a potentially new avatar
sent_at: timestamp, // sender timestamp inner return messageData;
quote, } catch (e) {
contact: [], window.log.error('pollOnceForMessages: caught error:', e);
preview, return false;
profile: { }
displayName: from,
avatar,
},
},
};
receivedAt += 1; // Ensure different arrival times
// now process any user meta data updates
// - update their conversation with a potentially new avatar
return messageData;
}) })
); );

Loading…
Cancel
Save