<commit_before>//import {} from '../handler'
import * as NS from '../namespace'
import Log from '../../../util/Log'
import JID from '../../../JID'
import Message from '../../../Message'
import Utils from '../../../util/Utils'
import Translation from '../../../util/Translation'
import Client from '../../../Client'
import Contact from '../../../Contact'
import Notification from '../../../Notification'
import { SOUNDS } from '../../../CONST'
import Pipe from '../../../util/Pipe'
import AbstractHandler from '../AbstractHandler'

export default class extends AbstractHandler {

   public processStanza(stanza: Element) {
      let messageElement;

      try {
         messageElement = new MessageElement(stanza);
      } catch (err) {
         return this.PRESERVE_HANDLER;
      }

      let peerJid = new JID(messageElement.getOriginalFrom());
      let peerContact: Contact = this.account.getContact(peerJid);
      if (typeof peerContact === 'undefined') {
         this.handleUnknownSender();

         return this.PRESERVE_HANDLER;
      }

      // If we now the full jid, we use it
      peerContact.setResource(peerJid.resource);

      let message = new Message({
         uid: messageElement.getStanzaId(),
         attrId: messageElement.getId(),
         peer: peerJid,
         direction: messageElement.getDirection(),
         plaintextMessage: messageElement.getPlaintextBody(),
         htmlMessage: messageElement.getHtmlBody().html(),
         forwarded: messageElement.isForwarded(),
         stamp: messageElement.getTime(),
         unread: true //@REVIEW carbon copy?
      });

      let pipe = Pipe.get('afterReceiveMessage');

      pipe.run(peerContact, message, messageElement.get(0)).then(([contact, message]) => {
         contact.getTranscript().pushMessage(message);
      });

      return this.PRESERVE_HANDLER;
   }

   private handleUnknownSender() {
      Log.debug('Sender is not in our contact list');

      //@TODO add notice to signal message from unkown sender
   }
}

class MessageElement {
   private element;
   private originalElement;
   private forwarded = false;
   private carbon = false;
   private direction = Message.DIRECTION.IN;

   constructor(stanza: Element) {
      this.originalElement = $(stanza);

      this.findMessageElement(stanza);
   }

   private findMessageElement(stanza: Element) {
      let forwardedStanza = $(stanza).find('forwarded' + NS.getFilter('FORWARD'));

      let from = new JID($(stanza).attr('from'));
      let to = new JID($(stanza).attr('to'));

      if (forwardedStanza.length === 0) {
         this.element = $(stanza);

         return;
      }

      let carbonStanza = $(stanza).find('> ' + NS.getFilter('CARBONS'));

      this.element = forwardedStanza.find('> message');
      this.forwarded = true;

      if (carbonStanza.length === 0) {
         return;
      }

      if (from.bare === to.bare) {
         this.carbon = true;
         this.direction = (carbonStanza.prop('tagName') === 'sent') ? Message.DIRECTION.OUT : Message.DIRECTION.IN;

         return;
      }

      throw 'No message element found';
   }

   public isForwarded() {
      return this.forwarded;
   }

   public isCarbon() {
      return this.carbon;
   }

   public find(selector) {
      return this.element.find(selector);
   }

   public get(index?) {
      return this.element.get(index);
   }

   public getType() {
      return this.element.attr('type');
   }

   public getFrom() {
      return this.element.attr('from');
   }

   public getTo() {
      return this.element.attr('to');
   }

   public getOriginalFrom() {
      return this.originalElement.attr('from');
   }

   public getOriginalTo() {
      return this.originalElement.attr('to');
   }

   public getId() {
      return this.element.attr('id');
   }

   public getStanzaId() {
      //@REVIEW "by" attribute ?
      let stanzaIdElement = this.element.find('stanza-id[xmlns="urn:xmpp:sid:0"]');

      return stanzaIdElement.attr('id');
   }

   public getTime() {
      let delayElement = this.element.find('delay[xmlns="urn:xmpp:delay"]');
      let stamp = (delayElement.length > 0) ? new Date(delayElement.attr('stamp')) : new Date();

      return stamp;
   }

   public getPlaintextBody() {
      let body = Utils.removeHTML(this.element.find('> body').text());

      if (this.forwarded && !this.carbon) {
         return `${this.getOriginalFrom()} ${Translation.t('to')} ${this.getOriginalFrom()} "${body}"`;
      }<commit_msg>fix carbon copy<commit_after>//import {} from '../handler'
import * as NS from '../namespace'
import Log from '../../../util/Log'
import JID from '../../../JID'
import Message from '../../../Message'
import Utils from '../../../util/Utils'
import Translation from '../../../util/Translation'
import Client from '../../../Client'
import Contact from '../../../Contact'
import Notification from '../../../Notification'
import { SOUNDS } from '../../../CONST'
import Pipe from '../../../util/Pipe'
import AbstractHandler from '../AbstractHandler'

export default class extends AbstractHandler {

   public processStanza(stanza: Element) {
      let messageElement;

      try {
         messageElement = new MessageElement(stanza);
      } catch (err) {
         return this.PRESERVE_HANDLER;
      }

      let peerJid = new JID(messageElement.getOriginalFrom());
      let peerContact: Contact = this.account.getContact(peerJid);
      if (typeof peerContact === 'undefined') {
         this.handleUnknownSender();

         return this.PRESERVE_HANDLER;
      }

      // If we now the full jid, we use it
      peerContact.setResource(peerJid.resource);

      let message = new Message({
         uid: messageElement.getStanzaId(),
         attrId: messageElement.getId(),
         peer: peerJid,
         direction: messageElement.getDirection(),
         plaintextMessage: messageElement.getPlaintextBody(),
         htmlMessage: messageElement.getHtmlBody().html(),
         forwarded: messageElement.isForwarded(),
         stamp: messageElement.getTime(),
         unread: true //@REVIEW carbon copy?
      });

      let pipe = Pipe.get('afterReceiveMessage');

      pipe.run(peerContact, message, messageElement.get(0)).then(([contact, message]) => {
         contact.getTranscript().pushMessage(message);
      });

      return this.PRESERVE_HANDLER;
   }

   private handleUnknownSender() {
      Log.debug('Sender is not in our contact list');

      //@TODO add notice to signal message from unkown sender
   }
}

class MessageElement {
   private element;
   private originalElement;
   private forwarded = false;
   private carbon = false;
   private direction = Message.DIRECTION.IN;

   constructor(stanza: Element) {
      this.originalElement = $(stanza);

      this.findMessageElement(stanza);
   }

   private findMessageElement(stanza: Element) {
      let forwardedStanza = $(stanza).find('forwarded' + NS.getFilter('FORWARD'));

      let from = new JID($(stanza).attr('from'));
      let to = new JID($(stanza).attr('to'));

      if (forwardedStanza.length === 0) {
         this.element = $(stanza);

         return;
      }

      let carbonStanza = $(stanza).find('> ' + NS.getFilter('CARBONS'));

      this.element = forwardedStanza.find('> message');
      this.forwarded = true;

      if (carbonStanza.length === 0) {
         return;
      }

      if (from.bare === to.bare) {
         this.carbon = true;
         this.direction = (carbonStanza.prop('tagName') === 'sent') ? Message.DIRECTION.OUT : Message.DIRECTION.IN;

         return;
      }

      throw 'No message element found';
   }

   public isForwarded() {
      return this.forwarded;
   }

   public isCarbon() {
      return this.carbon;
   }

   public find(selector) {
      return this.element.find(selector);
   }

   public get(index?) {
      return this.element.get(index);
   }

   public getType() {
      return this.element.attr('type');
   }

   public getFrom() {
      return this.element.attr('from');
   }

   public getTo() {
      return this.element.attr('to');
   }

   public getOriginalFrom() {
      return this.isCarbon() ? this.getFrom() : this.originalElement.attr('from');
   }

   public getOriginalTo() {
      return this.originalElement.attr('to');
   }

   public getId() {
      return this.element.attr('id');
   }

   public getStanzaId() {
      //@REVIEW "by" attribute ?
      let stanzaIdElement = this.element.find('stanza-id[xmlns="urn:xmpp:sid:0"]');

      return stanzaIdElement.attr('id');
   }

   public getTime() {
      let delayElement = this.element.find('delay[xmlns="urn:xmpp:delay"]');
      let stamp = (delayElement.length > 0) ? new Date(delayElement.attr('stamp')) : new Date();

      return stamp;
   }

   public getPlaintextBody() {
      let body = Utils.removeHTML(this.element.find('> body').text());

      if (this.forwarded && !this.carbon) {
         return `${this.getOriginalFrom()} ${Translation.t('to')} ${this.getOriginalFrom()} "${body}"`;
      }
{'fraction_non_alphanumeric': 0.151646158962421, 'fraction_numerical': 0.0011085245538188671, 'mean_word_length': 2.6102440976390557, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 4}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4813272', 'n_tokens_mistral': 2704, 'n_tokens_neox': 2618, 'n_words': 598}