<commit_before>const isFunction = require('lodash/isFunction');
const isNumber = require('lodash/isNumber');
const isObject = require('lodash/isObject');
const isString = require('lodash/isString');
const random = require('lodash/random');
const range = require('lodash/range');
const sample = require('lodash/sample');

const Message = require('./types/message');
const { deferredToPromise } = require('./deferred_to_promise');
const { sleep } = require('./sleep');


// See: https://en.wikipedia.org/wiki/Fictitious_telephone_number#North_American_Numbering_Plan
const SENDER_ID = '+12126647665';

exports.createConversation = async ({
  ConversationController,
  numMessages,
  WhisperMessage,
} = {}) => {
  if (!isObject(ConversationController) ||
      !isFunction(ConversationController.getOrCreateAndWait)) {
    throw new TypeError('"ConversationController" is required');
  }

  if (!isNumber(numMessages) || numMessages <= 0) {
    throw new TypeError('"numMessages" must be a positive number');
  }

  if (!isFunction(WhisperMessage)) {
    throw new TypeError('"WhisperMessage" is required');
  }

  const conversation =
    await ConversationController.getOrCreateAndWait(SENDER_ID, 'private');
  conversation.set({
    active_at: Date.now(),
    unread: numMessages,
  });
  await deferredToPromise(conversation.save());

  const conversationId = conversation.get('id');

  await Promise.all(range(0, numMessages).map(async (index) => {
    await sleep(index * 100);
    console.log(`Create message ${index + 1}`);
    const message = new WhisperMessage(createRandomMessage({ conversationId }));
    return deferredToPromise(message.save());
  }));
};

const SAMPLE_MESSAGES = [
  'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
  'Integer et rutrum leo, eu ultrices ligula.',
  'Nam vel aliquam quam.',
  'Suspendisse posuere nunc vitae pulvinar lobortis.',
  'Nunc et sapien ex.',
  'Duis nec neque eu arcu ultrices ullamcorper in et mauris.',
  'Praesent mi felis, hendrerit a nulla id, mattis consectetur est.',
  'Duis venenatis posuere est sit amet congue.',
  'Vestibulum vitae sapien ultricies, auctor purus vitae, laoreet lacus.',
  'Fusce laoreet nisi dui, a bibendum metus consequat in.',
  'Nulla sed iaculis odio, sed lobortis lacus.',
  'Etiam massa felis, gravida at nibh viverra, tincidunt convallis justo.',
  'Maecenas ut egestas urna.',
  'Pellentesque consectetur mattis imperdiet.',
  'Maecenas pulvinar efficitur justo a cursus.',
];

const ATTACHMENT_SAMPLE_RATE = 0.33;
const createRandomMessage = ({ conversationId } = {}) => {
  if (!isString(conversationId)) {
    throw new TypeError('"conversationId" must be a string');
  }

  const sentAt = Date.now() - random(100 * 24 * 60 * 60 * 1000);
  const receivedAt = sentAt + random(30 * 1000);

  const hasAttachment = Math.random() <= ATTACHMENT_SAMPLE_RATE;
  const attachments = hasAttachment
    ? [createRandomInMemoryAttachment()] : [];
  const type = sample(['incoming', 'outgoing']);
  const commonProperties = {
    attachments,
    body: sample(SAMPLE_MESSAGES),
    conversationId,
    received_at: receivedAt,
    sent_at: sentAt,
    timestamp: receivedAt,
    type,
  };

  const message = (() => {
    switch (type) {
      case 'incoming':
        return Object.assign({}, commonProperties, {
          flags: 0,
          source: conversationId,
          sourceDevice: 1,
        });
      case 'outgoing':
        return Object.assign({}, commonProperties, {
          delivered: 1,
          delivered_to: [conversationId],
          expireTimer: 0,
          recipients: [conversationId],
          sent_to: [conversationId],
          synced: true,
        });
      default:
        throw new TypeError(`Unknown message type: '${type}'`);
    }
  })();

  return Message.initializeSchemaVersion(message);
};

const MEGA_BYTE = 1e6;
const createRandomInMemoryAttachment = () => {
  const numBytes = (1 + Math.ceil((Math.random() * 50))) * MEGA_BYTE;
  const array = new Uint32Array(numBytes).fill(1);
  const data = array.buffer;
  const fileName = Math.random().toString().slice(2);

  return {
    contentType: 'application/octet-stream',
    data,
    fileName,
    size: numBytes,
  };
};
<commit_msg>Extract IIFE into separate function<commit_after>const isFunction = require('lodash/isFunction');
const isNumber = require('lodash/isNumber');
const isObject = require('lodash/isObject');
const isString = require('lodash/isString');
const random = require('lodash/random');
const range = require('lodash/range');
const sample = require('lodash/sample');

const Message = require('./types/message');
const { deferredToPromise } = require('./deferred_to_promise');
const { sleep } = require('./sleep');


// See: https://en.wikipedia.org/wiki/Fictitious_telephone_number#North_American_Numbering_Plan
const SENDER_ID = '+12126647665';

exports.createConversation = async ({
  ConversationController,
  numMessages,
  WhisperMessage,
} = {}) => {
  if (!isObject(ConversationController) ||
      !isFunction(ConversationController.getOrCreateAndWait)) {
    throw new TypeError('"ConversationController" is required');
  }

  if (!isNumber(numMessages) || numMessages <= 0) {
    throw new TypeError('"numMessages" must be a positive number');
  }

  if (!isFunction(WhisperMessage)) {
    throw new TypeError('"WhisperMessage" is required');
  }

  const conversation =
    await ConversationController.getOrCreateAndWait(SENDER_ID, 'private');
  conversation.set({
    active_at: Date.now(),
    unread: numMessages,
  });
  await deferredToPromise(conversation.save());

  const conversationId = conversation.get('id');

  await Promise.all(range(0, numMessages).map(async (index) => {
    await sleep(index * 100);
    console.log(`Create message ${index + 1}`);
    const message = new WhisperMessage(createRandomMessage({ conversationId }));
    return deferredToPromise(message.save());
  }));
};

const SAMPLE_MESSAGES = [
  'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
  'Integer et rutrum leo, eu ultrices ligula.',
  'Nam vel aliquam quam.',
  'Suspendisse posuere nunc vitae pulvinar lobortis.',
  'Nunc et sapien ex.',
  'Duis nec neque eu arcu ultrices ullamcorper in et mauris.',
  'Praesent mi felis, hendrerit a nulla id, mattis consectetur est.',
  'Duis venenatis posuere est sit amet congue.',
  'Vestibulum vitae sapien ultricies, auctor purus vitae, laoreet lacus.',
  'Fusce laoreet nisi dui, a bibendum metus consequat in.',
  'Nulla sed iaculis odio, sed lobortis lacus.',
  'Etiam massa felis, gravida at nibh viverra, tincidunt convallis justo.',
  'Maecenas ut egestas urna.',
  'Pellentesque consectetur mattis imperdiet.',
  'Maecenas pulvinar efficitur justo a cursus.',
];

const ATTACHMENT_SAMPLE_RATE = 0.33;
const createRandomMessage = ({ conversationId } = {}) => {
  if (!isString(conversationId)) {
    throw new TypeError('"conversationId" must be a string');
  }

  const sentAt = Date.now() - random(100 * 24 * 60 * 60 * 1000);
  const receivedAt = sentAt + random(30 * 1000);

  const hasAttachment = Math.random() <= ATTACHMENT_SAMPLE_RATE;
  const attachments = hasAttachment
    ? [createRandomInMemoryAttachment()] : [];
  const type = sample(['incoming', 'outgoing']);
  const commonProperties = {
    attachments,
    body: sample(SAMPLE_MESSAGES),
    conversationId,
    received_at: receivedAt,
    sent_at: sentAt,
    timestamp: receivedAt,
    type,
  };

  const message = _createMessage({ commonProperties, conversationId, type });
  return Message.initializeSchemaVersion(message);
};

const _createMessage = ({ commonProperties, conversationId, type } = {}) => {
  switch (type) {
    case 'incoming':
      return Object.assign({}, commonProperties, {
        flags: 0,
        source: conversationId,
        sourceDevice: 1,
      });
    case 'outgoing':
      return Object.assign({}, commonProperties, {
        delivered: 1,
        delivered_to: [conversationId],
        expireTimer: 0,
        recipients: [conversationId],
        sent_to: [conversationId],
        synced: true,
      });
    default:
      throw new TypeError(`Unknown message type: '${type}'`);
  }
};

const MEGA_BYTE = 1e6;
const createRandomInMemoryAttachment = () => {
  const numBytes = (1 + Math.ceil((Math.random() * 50))) * MEGA_BYTE;
  const array = new Uint32Array(numBytes).fill(1);
  const data = array.buffer;
  const fileName = Math.random().toString().slice(2);

  return {
    contentType: 'application/octet-stream',
    data,
    fileName,
    size: numBytes,
  };
};

{'fraction_non_alphanumeric': 0.13016577165538173, 'fraction_numerical': 0.012141022647676861, 'mean_word_length': 4.018746338605741, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 13, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5244137', 'n_tokens_mistral': 2813, 'n_tokens_neox': 2707, 'n_words': 671}