twitter.ts 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. // Copyright (C) 2012-2023 Zammad Foundation, https://zammad-foundation.org/
  2. import type { FormValues } from '@shared/components/Form'
  3. import type { FieldEditorProps } from '@shared/components/Form/fields/FieldEditor/types'
  4. import { useSessionStore } from '@shared/stores/session'
  5. import type { ConfigList } from '@shared/types/store'
  6. import type { ConfidentTake } from '@shared/types/utils'
  7. import { getInitials } from '@shared/utils/formatter'
  8. import { isArray, isObject, uniq } from 'lodash-es'
  9. import type {
  10. TicketArticleAction,
  11. TicketArticleActionPlugin,
  12. TicketArticleType,
  13. } from './types'
  14. const replyToTwitterComment = ((
  15. ticket,
  16. article,
  17. { openReplyDialog, getNewArticleBody },
  18. ) => {
  19. const articleData: FormValues = {
  20. articleType: 'twitter status',
  21. inReplyTo: article.messageId,
  22. }
  23. const body = getNewArticleBody('text/plain')
  24. const recipients = article.from ? [article.from.raw] : []
  25. if (article.to) recipients.push(article.to.raw)
  26. const recipientsString = uniq(
  27. recipients.filter((recipient) => {
  28. recipient = recipient.trim().toLowerCase()
  29. if (body.toLowerCase().includes(recipient)) return false
  30. if (recipient === `@${ticket.preferences?.channel_screen_name}`)
  31. return false
  32. return true
  33. }),
  34. ).join(' ')
  35. if (body) articleData.body = `${recipientsString} ${body}&nbsp`
  36. else articleData.body = `${recipientsString}&nbsp`
  37. openReplyDialog(articleData)
  38. }) satisfies TicketArticleAction['perform']
  39. const replyToTwitterDm = ((ticket, article, { openReplyDialog }) => {
  40. const sender = article.sender?.name
  41. let to: string | undefined | null
  42. if (sender === 'Customer') to = article.from?.raw
  43. else if (sender === 'Agent') to = article.to?.raw
  44. if (!to) {
  45. const autorization = article.createdBy.authorizations?.find(
  46. (a) => a.provider === 'twitter',
  47. )
  48. to = autorization?.username || autorization?.uid
  49. }
  50. const articleData: FormValues = {
  51. articleType: 'twitter direct-message',
  52. body: '',
  53. to: to ? [to] : [],
  54. inReplyTo: article.messageId,
  55. }
  56. openReplyDialog(articleData)
  57. }) satisfies TicketArticleAction['perform']
  58. const getTwitterInitials = (config: ConfigList) => {
  59. if (config.ui_ticket_zoom_article_twitter_initials) {
  60. const { user } = useSessionStore()
  61. if (user) {
  62. const { firstname, lastname, email } = user
  63. return `/${getInitials(firstname, lastname, email)}`
  64. }
  65. }
  66. return null
  67. }
  68. const actionPlugin: TicketArticleActionPlugin = {
  69. order: 300,
  70. addActions(ticket, article) {
  71. const type = article.type?.name
  72. if (type !== 'twitter status' && type !== 'twitter direct-message')
  73. return []
  74. const action: TicketArticleAction = {
  75. apps: ['mobile'],
  76. label: __('Reply'),
  77. name: type,
  78. icon: { mobile: 'mobile-reply' },
  79. view: {
  80. agent: ['change'],
  81. },
  82. perform(ticket, article, options) {
  83. if (type === 'twitter status')
  84. return replyToTwitterComment(ticket, article, options)
  85. return replyToTwitterDm(ticket, article, options)
  86. },
  87. }
  88. return [action]
  89. },
  90. addTypes(ticket, { config }) {
  91. const descriptionType = ticket.createArticleType?.name
  92. if (
  93. descriptionType !== 'twitter status' &&
  94. descriptionType !== 'twitter direct-message'
  95. )
  96. return []
  97. const type: TicketArticleType = {
  98. apps: ['mobile'],
  99. value: descriptionType,
  100. label: __('Twitter'),
  101. icon: {
  102. mobile: 'mobile-twitter',
  103. },
  104. view: {
  105. agent: ['change'],
  106. },
  107. attributes: [],
  108. internal: false,
  109. contentType: 'text/plain',
  110. updateForm(values) {
  111. if (!isObject(values.article) || isArray(values.article)) return values
  112. if (typeof values.article.body === 'string') {
  113. const initials = getTwitterInitials(config)
  114. values.article.body += initials ? `\n${initials}` : ''
  115. }
  116. return values
  117. },
  118. }
  119. let footer: ConfidentTake<FieldEditorProps, 'meta.footer'>
  120. if (descriptionType === 'twitter status') {
  121. footer = {
  122. maxlength: 280,
  123. warningLength: 30,
  124. }
  125. } else {
  126. type.attributes = ['to']
  127. footer = {
  128. maxlength: 10000,
  129. warningLength: 500,
  130. }
  131. }
  132. const initials = getTwitterInitials(config)
  133. if (initials) footer.text = initials
  134. type.editorMeta = {
  135. footer,
  136. }
  137. return [type]
  138. },
  139. }
  140. export default actionPlugin