class_docstr: This publisher attaches slack attachments generated from the Alert's full record
  
  The full record is likely to be significantly longer than the slack max messages size.
  So we cut up the record by rows and send it as a series of 1 or more attachments.
  The attachments are rendered in slack in a way such that a mouse drag and copy will
  copy the entire JSON in-tact.
  
  The first attachment is slightly different as it includes the source entity where the
  record originated from. The last attachment includes a footer.
class_docstr_tok: ["This","publisher","attaches","slack","attachments","generated","from","the","Alert","'s","full","record","The","full","record","is","likely","to","be","significantly","longer","than","the","slack","max","messages","size",".","So","we","cut","up","the","record","by","rows","and","send","it","as","a","series","of","1","or","more","attachments",".","The","attachments","are","rendered","in","slack","in","a","way","such","that","a","mouse","drag","and","copy","will","copy","the","entire","JSON","in-tact",".","The","first","attachment","is","slightly","different","as","it","includes","the","source","entity","where","the","record","originated","from",".","The","last","attachment","includes","a","footer","."]
code: def publish(self, alert, publication):
      publication["@slack.attachments"] = publication.get(
          "@slack.attachments", []
      )
  
      # Generate the record and then dice it up into parts
      record_document = json.dumps(
          alert.record, indent=2, sort_keys=True, separators=(",", ": ")
      )
  
      # Escape the document FIRST because it can increase character length which can throw off
      # document slicing
      record_document = cgi.escape(record_document)
      record_document_lines = record_document.split("\n")
  
      def make_attachment(document, is_first, is_last):
          footer = ""
          if is_last:
              footer_url = self._source_service_url(
                  alert.source_service
              )
              if footer_url:
                  footer = "via <{}|{}>".format(
                      footer_url, alert.source_service
                  )
              else:
                  "via {}".format(alert.source_service)
  
          return {
              "color": self._color(),
              "author": alert.source_entity if is_first else "",
              "title": "Record" if is_first else "",
              "text": "```\n{}\n```".format(document),
              "fields": [
                  {
                      "title": "Alert Id",
                      "value": alert.alert_id,
                  }
              ]
              if is_last
              else [],
              "footer": footer,
              "footer_icon": self._footer_icon_from_service(
                  alert.source_service
              ),
              "mrkdwn_in": ["text"],
          }
  
      character_limit = (
          self._SLACK_MAXIMUM_ATTACHMENT_CHARACTER_LENGTH
          - self._LENGTH_PADDING
      )
      is_first_document = True
      next_document = ""
      while len(record_document_lines) > 0:
          # Loop, removing one line at a time and attempting to attach it to the next document
          # When the next document nears the maximum attachment size, it is flushed, generating
          # a new attachment, and the document is reset before the loop pops off the next line.
  
          next_item_length = len(record_document_lines[0])
          next_length = next_item_length + len(next_document)
          if next_document and next_length > character_limit:
              # Do not pop off the item just yet.
              publication["@slack.attachments"].append(
                  make_attachment(
                      next_document, is_first_document, False
                  )
              )
              next_document = ""
              is_first_document = False
  
          next_document += "\n" + record_document_lines.pop(0)
  
      # Attach last document, if any remains
      if next_document:
          publication["@slack.attachments"].append(
              make_attachment(next_document, is_first_document, True)
          )
  
      return publication
  
imports: import calendar
  import cgi
  import json
  import urllib.request, urllib.parse, urllib.error
  from streamalert.shared.publisher import AlertPublisher, Register
  from streamalert.shared.description import RuleDescriptionParser
label_desc: Use image node_id 1 for calling the AttachFullRecord obj's underlying member method code with example usage: obj.publish(alert, publication) and returns: publication, dict