OpenGroup class and sending to Groups

pull/1174/head
Vincent 5 years ago
parent 8d0829e8e4
commit 83f70f1ff0

@ -1,11 +1,6 @@
import { Message, MessageParams } from './Message';
import { AttachmentPointer, Preview, Quote } from './content';
interface OpenGroup {
server: string;
channel: number;
conversationId: string;
}
import { OpenGroup } from '../../types/OpenGroup';
interface OpenGroupMessageParams extends MessageParams {
group: OpenGroup;

@ -1,47 +1,35 @@
import { SignalService } from '../../../../../protobuf';
import { MessageParams } from '../../Message';
import { ContentMessage, SyncMessage } from '..';
import { ContentMessage, SyncMessage } from '../';
import { ConversationController, textsecure, libloki, Whisper } from '../../../../../window';
import { PubKey } from '../../../../types';
import * as Data from '../../../../../../js/modules/data';
import { ChatMessage, DataMessage } from '../data';
interface ContactSyncMessageParams extends MessageParams {
interface ContactSyncMessageParams extends MessageParams {
sendTo: Array<PubKey>;
blocked: Array<PubKey>;
dataMessage?: DataMessage;
}
export class ContactSyncMessage extends SyncMessage {
private readonly sendTo: Array<PubKey>;
private readonly blocked: Array<PubKey>; // <--- convert to Array<string>
private readonly options: any;
constructor(params: MessageParams) {
super(params);
}
public from(message: ContentMessage) {
const { timestamp, identifier } = message;
return new ContactSyncMessage({timestamp, identifier});
}
protected syncProto() {
const request = new SignalService.SyncMessage.Request();
request.type = SignalService.SyncMessage.Request.Type.CONTACTS;
const syncMessage = new SignalService.SyncMessage();
syncMessage.request = request;
// const contacts = new SignalService.SyncMessage.Contacts();
// contacts.
SignalService.SyncMessage.Configuration
SignalService.SyncMessage.Contacts.create(
// SignalService.SyncMessage.Configuration
// SignalService.SyncMessage.Contacts.create(
);
SignalService.SyncMessage.Groups
SignalService.SyncMessage.OpenGroupDetails
SignalService.SyncMessage.Read
// );
// SignalService.SyncMessage.Groups
// SignalService.SyncMessage.OpenGroupDetails
// SignalService.SyncMessage.Read
const conversations = await Data.getAllConversations({ ConversationCollection: Whisper.ConversationCollection });
const contacts = conversations.filter((conversation: any) => {
@ -53,61 +41,18 @@ export class ContactSyncMessage extends SyncMessage {
);
});
const syncMessage = await libloki.api.createContactSyncProtoMessage(contacts);
// const rawContacts = this.sendTo.map(async (device: PubKey) => {
// const conversation = ConversationController.get(device.key);
// const profile = conversation.getLokiProfile();
// const name = profile
// ? profile.displayName
// : conversation.getProfileName();
// const status = await conversation.safeGetVerified();
// const protoState = textsecure.storage.protocol.convertVerifiedStatusToProtoState(
// status
// );
// const verified = new SignalService.Verified({
// state: protoState,
// destination: device.key,
// identityKey: textsecure.StringView.hexToArrayBuffer(device.key),
// });
// return {
// name,
// verified,
// number: device.key,
// nickname: conversation.getNickname(),
// blocked: conversation.isBlocked(),
// expireTimer: conversation.get('expireTimer'),
// };
// });
// // Convert raw contacts to an array of buffers
// const contactDetails = rawContacts
// .filter(x => x.number !== textsecure.storage.user.getNumber())
// .map(x => new textsecure.protobuf.ContactDetails(x))
// .map(x => x.encode());
// // Serialise array of byteBuffers into 1 byteBuffer
// const byteBuffer = serialiseByteBuffers(contactDetails);
// const data = new Uint8Array(byteBuffer.toArrayBuffer());
// const contacts = new textsecure.protobuf.SyncMessage.Contacts({
// data,
// });
// const syncMessage = new textsecure.protobuf.SyncMessage({
// contacts,
// });
// return syncMessage;
const syncMessage = await libloki.api.createContactSyncProtoMessage(contacts) as SignalService.SyncMessage;
// TODO: Is this a request sync message or a basic sync message?
// Set request type
const request = new SignalService.SyncMessage.Request();
request.type = SignalService.SyncMessage.Request.Type.CONTACTS;
syncMessage.request = request;
protected dataProto() {
if dataMess
return syncMessage;
}
}
const contactSyncMessage = new ContactSyncMessage({timestamp: Date.now(), identifier: 'sdfgfdsgfdsgsfdgfdsg'});
// protected dataProto() {
// if dataMess
// }
}

@ -1,47 +1,26 @@
import { ContentMessage } from '../ContentMessage';
import { SignalService } from '../../../../../protobuf';
import { DataMessage, AttachmentPointer } from '../data';
interface UnidentifiedDeliveryStatus {
destination?: string;
unidentified?: boolean;
import { ContactSyncMessage } from '.';
// Matches SyncMessage definition in SignalService protobuf
export enum SyncMessageEnum {
UNKNONWN = 0,
CONTACTS = 1,
GROUPS = 2,
BLOCKED = 3,
CONFIGURATION = 4,
}
interface Sent {
UnidentifiedDeliveryStatus: UnidentifiedDeliveryStatus;
desination?: string;
timestamp?: number;
message?: DataMessage;
expirationStartTimestamp?: number;
unidentifiedStatus: Array<UnidentifiedDeliveryStatus>;
}
interface Contact {
blob?: AttachmentPointer;
complete?: boolean;
data: any;
}
export interface SyncMessageParams {
}
export type SyncMessageType = ContactSyncMessage; // | GroupSyncMessage
export abstract class SyncMessage extends ContentMessage {
public static canSync(message: ContentMessage): boolean {
return message instanceof SyncMessage;
}
public abstract from(message: ContentMessage): SyncMessage;
public ttl(): number {
return this.getDefaultTTL();
}
protected contentProto(): SignalService.Content {
const dataMessage = new SignalService.DataMessage({
});
const dataMessage = new SignalService.DataMessage({});
return new SignalService.Content({
dataMessage,

@ -1,4 +1,5 @@
import { SyncMessage } from './SyncMessage';
import { SyncMessage, SyncMessageEnum } from './SyncMessage';
import { ContactSyncMessage } from './ContactSyncMessage';
// import { GroupSyncMessage } from './GroupSyncMessage';
export { ContactSyncMessage, SyncMessage };
export { ContactSyncMessage, SyncMessage, SyncMessageEnum };

@ -3,7 +3,7 @@ import { SessionRequestMessage } from '../messages/outgoing';
import { createOrUpdateItem, getItemById } from '../../../js/modules/data';
import { libloki, libsignal, textsecure } from '../../window';
import { MessageSender } from '../sending';
import * as MessageUtils from '../utils';
import { MessageUtils } from '../utils';
import { PubKey } from '../types';
interface StringToNumberMap {

@ -19,12 +19,14 @@ import { PendingMessageCache } from './PendingMessageCache';
import {
JobQueue,
TypedEventEmitter,
toRawMessage,
MessageUtils,
SyncMessageUtils,
} from '../utils';
import { PubKey } from '../types';
import { ConversationController } from '../../window';
import { MessageSender } from '.';
import { SessionProtocol } from '../protocols';
import { generateFakePubkey } from '../../test/test-utils/testUtils';
export class MessageQueue implements MessageQueueInterface {
public readonly events: TypedEventEmitter<MessageQueueInterfaceEvents>;
@ -54,9 +56,9 @@ export class MessageQueue implements MessageQueueInterface {
) {
let currentDevices = [...devices];
if (message.canSync(message)) {
if (SyncMessageUtils.canSync(message)) {
// Sync to our devices
const syncMessage = SyncMessage.from(message);
const syncMessage = SyncMessageUtils.from(message);
const ourDevices = await this.sendSyncMessage(syncMessage);
// Remove our devices from currentDevices
@ -68,36 +70,45 @@ export class MessageQueue implements MessageQueueInterface {
});
}
public async sendToGroup(message: OpenGroupMessage | ContentMessage) {
public async sendToGroup(message: OpenGroupMessage | ContentMessage): Promise<boolean> {
if (
!(message instanceof OpenGroupMessage) &&
!(message instanceof ClosedGroupMessage)
) {
return;
return false;
}
// Closed groups
if (message instanceof ClosedGroupMessage) {
// Get devices in closed group
const conversation = ConversationController.get(message.groupId);
const recipients = 5;
const recipientsModels = conversation.contactCollection.models;
const recipients: Array<PubKey> = recipientsModels.map(
(recipient: any) => new PubKey(recipient.id)
);
await this.sendMessageToDevices(recipients, message);
return true;
}
// Open groups
if (message instanceof OpenGroupMessage) {
// No queue needed for Open Groups; send directly
const rawMessage = MessageUtils.toRawMessage(message);
await MessageSender.send(message);
return true;
}
return false;
}
public async sendSyncMessage(
message: ContentMessage
): Promise<Array<PubKey>> {
): Promise<Array<PubKey> | undefined> {
// Sync with our devices
const syncMessage = SyncMessage.from(message);
if (!syncMessage.canSync()) {
const syncMessage = SyncMessageUtils.from(message);
if (!SyncMessageUtils.canSync(syncMessage)) {
return;
}

@ -2,7 +2,7 @@ import { createOrUpdateItem, getItemById } from '../../../js/modules/data';
import { PartialRawMessage, RawMessage } from '../types/RawMessage';
import { ContentMessage } from '../messages/outgoing';
import { PubKey } from '../types';
import * as MessageUtils from '../utils';
import { MessageUtils } from '../utils';
// This is an abstraction for storing pending messages.
// Ideally we want to store pending messages in the database so that

@ -0,0 +1,67 @@
// This is the Open Group equivalent to the PubKey type.
interface OpenGroupParams {
server?: string;
channel?: number;
conversationId: string;
}
export class OpenGroup {
private static readonly conversationIdRegex: RegExp = new RegExp('^publicChat:[0-9]*@([\\w-]{2,}.){1,2}[\\w-]{2,}$');
public readonly server: string;
public readonly channel: number;
public readonly conversationId: string;
private readonly isValid: boolean;
constructor(params: OpenGroupParams) {
this.isValid = OpenGroup.validate(params);
if (!this.isValid) {
throw Error('an invalid conversationId was provided');
}
this.conversationId = params.conversationId;
this.server = params.server ?? this.getServer(params.conversationId);
this.channel = params.channel ?? this.getChannel(params.conversationId);
}
public static from(conversationId: string): OpenGroup | undefined {
// Returns a new instance if conversationId is valid
if (OpenGroup.validate({conversationId})) {
return new OpenGroup({conversationId});
}
return undefined;
}
private static validate(openGroup: OpenGroupParams): boolean {
// Validate conversationId
const { server, channel, conversationId } = openGroup;
if (!this.conversationIdRegex.test(conversationId)) {
return false;
}
// Validate channel and server if provided
if (server && channel) {
const contrivedId = `publicChat:${String(channel)}@${server}`;
if (contrivedId !== conversationId) {
return false;
}
}
return true;
}
private getServer(conversationId: string): string {
// conversationId is already validated in constructor; no need to re-check
return conversationId.split('@')[1];
}
private getChannel(conversationId: string): number {
// conversationId is already validated in constructor; no need to re-check
const channelMatch = conversationId.match(/^.*\:([0-9]*)\@.*$/);
return channelMatch ? Number(channelMatch[1]) : 1;
}
}

@ -1,7 +1,7 @@
export class PubKey {
public static readonly PUBKEY_LEN = 66;
private static readonly regex: string = `^05[0-9a-fA-F]{${PubKey.PUBKEY_LEN -
2}}$`;
private static readonly regex: RegExp = new RegExp(`^05[0-9a-fA-F]{${PubKey.PUBKEY_LEN -
2}}$`);
public readonly key: string;
constructor(pubkeyString: string) {
@ -19,7 +19,7 @@ export class PubKey {
}
public static validate(pubkeyString: string): boolean {
if (pubkeyString.match(PubKey.regex)) {
if (this.regex.test(pubkeyString)) {
return true;
}

@ -1,24 +1,29 @@
import { RawMessage } from '../types/RawMessage';
import { ContentMessage, SyncMessage } from '../messages/outgoing';
import { EncryptionType, PubKey } from '../types';
import { OpenGroup } from '../types/OpenGroup';
export function toRawMessage(
device: PubKey,
device: PubKey | OpenGroup,
message: ContentMessage
): RawMessage {
const ttl = message.ttl();
const timestamp = message.timestamp;
const plainTextBuffer = message.plainTextBuffer();
const sendTo = device instanceof PubKey
? device.key
: device.conversationId;
// tslint:disable-next-line: no-unnecessary-local-variable
const rawMessage: RawMessage = {
identifier: message.identifier,
plainTextBuffer,
timestamp,
device: device.key,
device: sendTo,
ttl,
encryption: EncryptionType.Signal,
};
return rawMessage;
}
}

@ -1,9 +1,36 @@
import { RawMessage } from '../types/RawMessage';
import { ContentMessage, SyncMessage } from '../messages/outgoing';
import {
ChatMessage,
ContentMessage,
SyncMessage,
SyncMessageEnum,
ContactSyncMessage,
} from '../messages/outgoing';
import { EncryptionType, PubKey } from '../types';
import { SignalService } from '../../protobuf';
import { SyncMessageType } from '../messages/outgoing/content/sync/SyncMessage';
export function from(message: ContentMessage): SyncMessage | undefined {
return new SyncMessage({})
// export function from(message: ContentMessage): SyncMessage | undefined {
// testtttingggg
export function from(
message: ContentMessage,
syncType: SyncMessageEnum = SyncMessageEnum.CONTACTS
): SyncMessageType {
// Detect Sync Message Type
const plainText = message.plainTextBuffer();
const decoded = SignalService.Content.decode(plainText);
console.log('[vince] decoded:', decoded);
let syncMessage: SyncMessage;
switch (syncType) {
case SyncMessageEnum.CONTACTS:
syncMessage = new ContactSyncMessage({});
break;
}
return syncMessage;
}
export function canSync(message: ContentMessage): boolean {

@ -1,3 +1,7 @@
import * as MessageUtils from './Messages';
import * as SyncMessageUtils from './SyncMessageUtils';
export * from './TypedEmitter';
export * from './JobQueue';
export * from './Messages';
export { MessageUtils, SyncMessageUtils };

@ -0,0 +1,43 @@
import { expect } from 'chai';
import * as _ from 'lodash';
import { MessageUtils, SyncMessageUtils } from '../../../session/utils';
import { TestUtils } from '../../../test/test-utils';
import { PendingMessageCache } from '../../../session/sending/PendingMessageCache';
import { generateChatMessage } from '../../test-utils/testUtils';
import { OpenGroup } from '../../../session/types/OpenGroup';
// Equivalent to Data.StorageItem
interface StorageItem {
id: string;
value: any;
}
describe('PendingMessageCache', () => {
// Initialize new stubbed cache
let data: StorageItem;
let pendingMessageCacheStub: PendingMessageCache;
beforeEach(async () => {
//
});
afterEach(() => {
//
});
it('can init queue', async () => {
const device = TestUtils.generateFakePubkey();
const message = TestUtils.generateChatMessage();
const rawMessage = MessageUtils.toRawMessage(device, message);
SyncMessageUtils.from(message);
const myOpenGroup = new OpenGroup({conversationId: 'publicChat:1@feedback.getsession.org'});
console.log('[vince] myOpenGroup.server:', myOpenGroup.server);
console.log('[vince] myOpenGroup.channel:', myOpenGroup.channel);
console.log('[vince] myOpenGroup.conversationId:', myOpenGroup.conversationId);
});
});

@ -1,6 +1,6 @@
import { expect } from 'chai';
import * as _ from 'lodash';
import * as MessageUtils from '../../../session/utils';
import { MessageUtils } from '../../../session/utils';
import { TestUtils } from '../../../test/test-utils';
import { PendingMessageCache } from '../../../session/sending/PendingMessageCache';
@ -53,7 +53,7 @@ describe('PendingMessageCache', () => {
it('can add to cache', async () => {
const device = TestUtils.generateFakePubkey();
const message = TestUtils.generateUniqueChatMessage();
const message = TestUtils.generateChatMessage();
const rawMessage = MessageUtils.toRawMessage(device, message);
await pendingMessageCacheStub.add(device, message);
@ -70,7 +70,7 @@ describe('PendingMessageCache', () => {
it('can remove from cache', async () => {
const device = TestUtils.generateFakePubkey();
const message = TestUtils.generateUniqueChatMessage();
const message = TestUtils.generateChatMessage();
const rawMessage = MessageUtils.toRawMessage(device, message);
await pendingMessageCacheStub.add(device, message);
@ -91,15 +91,15 @@ describe('PendingMessageCache', () => {
const cacheItems = [
{
device: TestUtils.generateFakePubkey(),
message: TestUtils.generateUniqueChatMessage(),
message: TestUtils.generateChatMessage(),
},
{
device: TestUtils.generateFakePubkey(),
message: TestUtils.generateUniqueChatMessage(),
message: TestUtils.generateChatMessage(),
},
{
device: TestUtils.generateFakePubkey(),
message: TestUtils.generateUniqueChatMessage(),
message: TestUtils.generateChatMessage(),
},
];
@ -123,11 +123,11 @@ describe('PendingMessageCache', () => {
const cacheItems = [
{
device: TestUtils.generateFakePubkey(),
message: TestUtils.generateUniqueChatMessage(),
message: TestUtils.generateChatMessage(),
},
{
device: TestUtils.generateFakePubkey(),
message: TestUtils.generateUniqueChatMessage(),
message: TestUtils.generateChatMessage(),
},
];
@ -150,7 +150,7 @@ describe('PendingMessageCache', () => {
it('can find nothing when empty', async () => {
const device = TestUtils.generateFakePubkey();
const message = TestUtils.generateUniqueChatMessage();
const message = TestUtils.generateChatMessage();
const rawMessage = MessageUtils.toRawMessage(device, message);
const foundMessage = pendingMessageCacheStub.find(rawMessage);
@ -159,7 +159,7 @@ describe('PendingMessageCache', () => {
it('can find message in cache', async () => {
const device = TestUtils.generateFakePubkey();
const message = TestUtils.generateUniqueChatMessage();
const message = TestUtils.generateChatMessage();
const rawMessage = MessageUtils.toRawMessage(device, message);
await pendingMessageCacheStub.add(device, message);
@ -176,15 +176,15 @@ describe('PendingMessageCache', () => {
const cacheItems = [
{
device: TestUtils.generateFakePubkey(),
message: TestUtils.generateUniqueChatMessage(),
message: TestUtils.generateChatMessage(),
},
{
device: TestUtils.generateFakePubkey(),
message: TestUtils.generateUniqueChatMessage(),
message: TestUtils.generateChatMessage(),
},
{
device: TestUtils.generateFakePubkey(),
message: TestUtils.generateUniqueChatMessage(),
message: TestUtils.generateChatMessage(),
},
];
@ -206,15 +206,15 @@ describe('PendingMessageCache', () => {
const cacheItems = [
{
device: TestUtils.generateFakePubkey(),
message: TestUtils.generateUniqueChatMessage(),
message: TestUtils.generateChatMessage(),
},
{
device: TestUtils.generateFakePubkey(),
message: TestUtils.generateUniqueChatMessage(),
message: TestUtils.generateChatMessage(),
},
{
device: TestUtils.generateFakePubkey(),
message: TestUtils.generateUniqueChatMessage(),
message: TestUtils.generateChatMessage(),
},
];

@ -6,7 +6,11 @@ import { v4 as uuid } from 'uuid';
import { ImportMock } from 'ts-mock-imports';
import { PubKey } from '../../../ts/session/types';
import { ChatMessage } from '../../session/messages/outgoing';
import {
ChatMessage,
OpenGroupMessage,
ClosedGroupChatMessage,
} from '../../session/messages/outgoing';
const sandbox = sinon.createSandbox();
@ -55,7 +59,7 @@ export function generateFakePubkey(): PubKey {
return new PubKey(pubkeyString);
}
export function generateUniqueChatMessage(): ChatMessage {
export function generateChatMessage(): ChatMessage {
return new ChatMessage({
body: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit',
identifier: uuid(),
@ -67,3 +71,30 @@ export function generateUniqueChatMessage(): ChatMessage {
preview: undefined,
});
}
export function generateClosedGroupChatMessage(): ClosedGroupChatMessage {
const chatMessage = generateChatMessage();
return new ClosedGroupChatMessage({
chatMessage,
groupId: 'example-closed-group',
});
}
export function generateOpenGroupMessage(): OpenGroupMessage {
const openGroup = {
server: 'example.server',
channel: 1,
conversationId: '@example.server',
};
return new OpenGroupMessage({
identifier: uuid(),
timestamp: Date.now(),
group: openGroup,
body: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit',
attachments: undefined,
quote: undefined,
preview: undefined,
});
}

Loading…
Cancel
Save