<commit_before>import WrapperMessage from '.'
import * as Constants from '../../../../constants/chat2'
import * as TeamConstants from '../../../../constants/teams'
import * as MessageConstants from '../../../../constants/chat2/message'
import * as Chat2Gen from '../../../../actions/chat2-gen'
import * as ProfileGen from '../../../../actions/profile-gen'
import * as Tracker2Gen from '../../../../actions/tracker2-gen'
import * as Types from '../../../../constants/types/chat2'
import * as Container from '../../../../util/container'
import * as RPCChatTypes from '../../../../constants/types/rpc-chat-gen'

type OwnProps = {
  conversationIDKey: Types.ConversationIDKey
  measure?: () => void
  ordinal: Types.Ordinal
  previous?: Types.Ordinal
}

// If there is no matching message treat it like a deleted
const missingMessage = MessageConstants.makeMessageDeleted({})

// Used to decide whether to show the author for sequential messages
const authorIsCollapsible = (m: Types.Message) =>
  m.type === 'text' || m.type === 'deleted' || m.type === 'attachment'

const getUsernameToShow = (
  message: Types.Message,
  previous: Types.Message | undefined,
  you: string,
  orangeLineAbove: boolean
) => {
  const sequentialUserMessages =
    previous &&
    previous.author === message.author &&
    authorIsCollapsible(message) &&
    authorIsCollapsible(previous)

  const sequentialBotKeyed =
    previous &&
    previous.author === message.author &&
    previous.type === 'text' &&
    message.type === 'text' &&
    previous.botUsername === message.botUsername &&
    authorIsCollapsible(message) &&
    authorIsCollapsible(previous)

  const enoughTimeBetween = MessageConstants.enoughTimeBetweenMessages(message, previous)
  const timestamp = orangeLineAbove || !previous || enoughTimeBetween ? message.timestamp : null
  switch (message.type) {
    case 'attachment':
    case 'requestPayment':
    case 'sendPayment':
    case 'text':
      return !sequentialBotKeyed || !previous || !sequentialUserMessages || !!timestamp ? message.author : ''
    case 'setChannelname':
      // suppress this message for the #general channel, it is redundant.
      return (!previous || !sequentialUserMessages || !!timestamp) && message.newChannelname !== 'general'
        ? message.author
        : ''
    case 'systemAddedToTeam':
      return message.adder
    case 'systemInviteAccepted':
      return message.invitee === you ? '' : message.invitee
    case 'setDescription':
      return message.author
    case 'pin':
      return message.author
    case 'systemUsersAddedToConversation':
      return message.author
    case 'systemJoined':
      return message.joiners.length + message.leavers.length > 1 ? '' : message.author
    case 'systemSBSResolved':
      return message.prover
    case 'journeycard':
      return 'placeholder'
  }
  return message.author
}

const getFailureDescriptionAllowCancel = (message: Types.Message, you: string) => {
  let failureDescription = ''
  let allowCancel = false
  let allowRetry = false
  let resolveByEdit = false
  if ((message.type === 'text' || message.type === 'attachment') && message.errorReason) {
    failureDescription = message.errorReason
    if (you && ['pending', 'failed'].includes(message.submitState as string)) {
      // This is a message still in the outbox, we can retry/edit to fix, but
      // for flip messages, don't allow retry/cancel
      allowCancel = allowRetry = message.type === 'attachment' || !message.flipGameID
      const messageType = message.type === 'attachment' ? 'attachment' : 'message'
      failureDescription = `This ${messageType} failed to send`
      resolveByEdit = !!message.outboxID && !!you && message.errorTyp === RPCChatTypes.OutboxErrorType.toolong
      if (resolveByEdit) {
        failureDescription += `, ${message.errorReason}`
      }
      if (!!message.outboxID && !!you) {
        switch (message.errorTyp) {
          case RPCChatTypes.OutboxErrorType.minwriter:
          case RPCChatTypes.OutboxErrorType.restrictedbot:
            failureDescription = `Unable to send, ${message.errorReason}`
            allowRetry = false
        }
      }
    }
  }
  return {allowCancel, allowRetry, failureDescription, resolveByEdit}
}

const getDecorate = (message: Types.Message) => {
  switch (message.type) {
    case 'text':
      return !message.exploded && !message.errorReason
    case 'attachment':
      return !message.exploded && !message.errorReason
    default:
      return true
  }
}

export default Container.namedConnect(
  (state: Container.TypedState, ownProps: OwnProps) => {
    const _participantInfo = Constants.getParticipantInfo(state, ownProps.conversationIDKey)
    const message =
      Constants.getMessage(state, ownProps.conversationIDKey, ownProps.ordinal) || missingMessage
    const previous =
      (ownProps.previous && Constants.getMessage(state, ownProps.conversationIDKey, ownProps.previous)) ||
      undefined
    const orangeLineAbove = state.chat2.orangeLineMap.get(ownProps.conversationIDKey) === message.id
    let hasUnfurlPrompts = false
    if (message.type === 'text') {
      const mm = state.chat2.unfurlPromptMap.get(message.conversationIDKey)
      if (mm) {
        const unfurlPrompts = mm.get(message.id)
        hasUnfurlPrompts = !!unfurlPrompts && unfurlPrompts.size > 0
      }
    }
    const centeredOrdinalInfo = state.chat2.messageCenterOrdinals.get(message.conversationIDKey)
    const centeredOrdinal =
      centeredOrdinalInfo && centeredOrdinalInfo.ordinal === ownProps.ordinal
        ? centeredOrdinalInfo.highlightMode
        : 'none'
    const meta = Constants.getMeta(state, message.conversationIDKey)
    const teamname = meta.teamname
    const authorIsAdmin = teamname
      ? TeamConstants.userIsRoleInTeam(state, teamname, message.author, 'admin')
      : false
    const authorIsBot = teamname
      ? TeamConstants.userIsRoleInTeam(state, teamname, message.author, 'restrictedbot') ||
        TeamConstants.userIsRoleInTeam(state, teamname, message.author, 'bot')
      : !_participantInfo.name.includes(message.author) // if adhoc, check if author in participants
    const authorIsOwner = teamname
      ? TeamConstants.userIsRoleInTeam(state, teamname, message.author, 'owner')
      : false
    const ordinals = [...Constants.getMessageOrdinals(state, ownProps.conversationIDKey)]
    const botAlias = meta.botAliases[message.author] ?? ''
    return {
      _you: state.config.username,
      authorIsAdmin,
      authorIsBot,
      authorIsOwner,
      botAlias,
      centeredOrdinal,
      conversationIDKey: ownProps.conversationIDKey,
      hasUnfurlPrompts,
      isLastInThread: ordinals[ordinals.length - 1] === ownProps.ordinal,
      isPendingPayment: Constants.isPendingPaymentMessage(state, message),
      message,<commit_msg>fix: add additional checks for authorIsBot (#22012)<commit_after>import WrapperMessage from '.'
import * as Constants from '../../../../constants/chat2'
import * as TeamConstants from '../../../../constants/teams'
import * as MessageConstants from '../../../../constants/chat2/message'
import * as Chat2Gen from '../../../../actions/chat2-gen'
import * as ProfileGen from '../../../../actions/profile-gen'
import * as Tracker2Gen from '../../../../actions/tracker2-gen'
import * as Types from '../../../../constants/types/chat2'
import * as Container from '../../../../util/container'
import * as RPCChatTypes from '../../../../constants/types/rpc-chat-gen'

type OwnProps = {
  conversationIDKey: Types.ConversationIDKey
  measure?: () => void
  ordinal: Types.Ordinal
  previous?: Types.Ordinal
}

// If there is no matching message treat it like a deleted
const missingMessage = MessageConstants.makeMessageDeleted({})

// Used to decide whether to show the author for sequential messages
const authorIsCollapsible = (m: Types.Message) =>
  m.type === 'text' || m.type === 'deleted' || m.type === 'attachment'

const getUsernameToShow = (
  message: Types.Message,
  previous: Types.Message | undefined,
  you: string,
  orangeLineAbove: boolean
) => {
  const sequentialUserMessages =
    previous &&
    previous.author === message.author &&
    authorIsCollapsible(message) &&
    authorIsCollapsible(previous)

  const sequentialBotKeyed =
    previous &&
    previous.author === message.author &&
    previous.type === 'text' &&
    message.type === 'text' &&
    previous.botUsername === message.botUsername &&
    authorIsCollapsible(message) &&
    authorIsCollapsible(previous)

  const enoughTimeBetween = MessageConstants.enoughTimeBetweenMessages(message, previous)
  const timestamp = orangeLineAbove || !previous || enoughTimeBetween ? message.timestamp : null
  switch (message.type) {
    case 'attachment':
    case 'requestPayment':
    case 'sendPayment':
    case 'text':
      return !sequentialBotKeyed || !previous || !sequentialUserMessages || !!timestamp ? message.author : ''
    case 'setChannelname':
      // suppress this message for the #general channel, it is redundant.
      return (!previous || !sequentialUserMessages || !!timestamp) && message.newChannelname !== 'general'
        ? message.author
        : ''
    case 'systemAddedToTeam':
      return message.adder
    case 'systemInviteAccepted':
      return message.invitee === you ? '' : message.invitee
    case 'setDescription':
      return message.author
    case 'pin':
      return message.author
    case 'systemUsersAddedToConversation':
      return message.author
    case 'systemJoined':
      return message.joiners.length + message.leavers.length > 1 ? '' : message.author
    case 'systemSBSResolved':
      return message.prover
    case 'journeycard':
      return 'placeholder'
  }
  return message.author
}

const getFailureDescriptionAllowCancel = (message: Types.Message, you: string) => {
  let failureDescription = ''
  let allowCancel = false
  let allowRetry = false
  let resolveByEdit = false
  if ((message.type === 'text' || message.type === 'attachment') && message.errorReason) {
    failureDescription = message.errorReason
    if (you && ['pending', 'failed'].includes(message.submitState as string)) {
      // This is a message still in the outbox, we can retry/edit to fix, but
      // for flip messages, don't allow retry/cancel
      allowCancel = allowRetry = message.type === 'attachment' || !message.flipGameID
      const messageType = message.type === 'attachment' ? 'attachment' : 'message'
      failureDescription = `This ${messageType} failed to send`
      resolveByEdit = !!message.outboxID && !!you && message.errorTyp === RPCChatTypes.OutboxErrorType.toolong
      if (resolveByEdit) {
        failureDescription += `, ${message.errorReason}`
      }
      if (!!message.outboxID && !!you) {
        switch (message.errorTyp) {
          case RPCChatTypes.OutboxErrorType.minwriter:
          case RPCChatTypes.OutboxErrorType.restrictedbot:
            failureDescription = `Unable to send, ${message.errorReason}`
            allowRetry = false
        }
      }
    }
  }
  return {allowCancel, allowRetry, failureDescription, resolveByEdit}
}

const getDecorate = (message: Types.Message) => {
  switch (message.type) {
    case 'text':
      return !message.exploded && !message.errorReason
    case 'attachment':
      return !message.exploded && !message.errorReason
    default:
      return true
  }
}

export default Container.namedConnect(
  (state: Container.TypedState, ownProps: OwnProps) => {
    const _participantInfo = Constants.getParticipantInfo(state, ownProps.conversationIDKey)
    const message =
      Constants.getMessage(state, ownProps.conversationIDKey, ownProps.ordinal) || missingMessage
    const previous =
      (ownProps.previous && Constants.getMessage(state, ownProps.conversationIDKey, ownProps.previous)) ||
      undefined
    const orangeLineAbove = state.chat2.orangeLineMap.get(ownProps.conversationIDKey) === message.id
    let hasUnfurlPrompts = false
    if (message.type === 'text') {
      const mm = state.chat2.unfurlPromptMap.get(message.conversationIDKey)
      if (mm) {
        const unfurlPrompts = mm.get(message.id)
        hasUnfurlPrompts = !!unfurlPrompts && unfurlPrompts.size > 0
      }
    }
    const centeredOrdinalInfo = state.chat2.messageCenterOrdinals.get(message.conversationIDKey)
    const centeredOrdinal =
      centeredOrdinalInfo && centeredOrdinalInfo.ordinal === ownProps.ordinal
        ? centeredOrdinalInfo.highlightMode
        : 'none'
    const meta = Constants.getMeta(state, message.conversationIDKey)
    const teamname = meta.teamname
    const authorIsAdmin = teamname
      ? TeamConstants.userIsRoleInTeam(state, teamname, message.author, 'admin')
      : false
    const authorIsBot = teamname
      ? TeamConstants.userIsRoleInTeam(state, teamname, message.author, 'restrictedbot') ||
        TeamConstants.userIsRoleInTeam(state, teamname, message.author, 'bot')
      : meta.teamType === 'adhoc' && _participantInfo.name.length > 0 // teams without info may have type adhoc with an empty participant name list
      ? !_participantInfo.name.includes(message.author) // if adhoc, check if author in participants
      : false // if we don't have team information, don't show bot icon
    const authorIsOwner = teamname
      ? TeamConstants.userIsRoleInTeam(state, teamname, message.author, 'owner')
      : false
    const ordinals = [...Constants.getMessageOrdinals(state, ownProps.conversationIDKey)]
    const botAlias = meta.botAliases[message.author] ?? ''
    return {
      _you: state.config.username,
      authorIsAdmin,
      authorIsBot,
      authorIsOwner,
      botAlias,
      centeredOrdinal,
      conversationIDKey: ownProps.conversationIDKey,
      hasUnfurlPrompts,
      isLastInThread: ordinals[ordinals.length - 1] === ownProps.ordinal,
      isPendingPayment: Constants.isPendingPaymentMessage(state, message),
      message,
{'fraction_non_alphanumeric': 0.12160804020100502, 'fraction_numerical': 0.002297200287150036, 'mean_word_length': 3.3960239823288103, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 20, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, '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': '2648715', 'n_tokens_mistral': 3901, 'n_tokens_neox': 3887, 'n_words': 933}