twitter.ts 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. // Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. import { isArray, isObject, uniq } from 'lodash-es'
  3. import type { FieldEditorProps } from '#shared/components/Form/fields/FieldEditor/types.ts'
  4. import type { FormValues } from '#shared/components/Form/types.ts'
  5. import { useSessionStore } from '#shared/stores/session.ts'
  6. import type { ConfigList } from '#shared/types/store.ts'
  7. import type { ConfidentTake } from '#shared/types/utils.ts'
  8. import { getInitials } from '#shared/utils/formatter.ts'
  9. import type {
  10. TicketArticleAction,
  11. TicketArticleActionPlugin,
  12. TicketArticleType,
  13. } from './types.ts'
  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} `
  36. else articleData.body = `${recipientsString} `
  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.author.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: '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: 'twitter',
  102. view: {
  103. agent: ['change'],
  104. },
  105. fields: {
  106. body: {
  107. required: true,
  108. },
  109. to: {},
  110. },
  111. internal: false,
  112. contentType: 'text/plain',
  113. updateForm(values) {
  114. if (!isObject(values.article) || isArray(values.article)) return values
  115. if (typeof values.article.body === 'string') {
  116. const initials = getTwitterInitials(config)
  117. values.article.body += initials ? `\n${initials}` : ''
  118. }
  119. return values
  120. },
  121. }
  122. let footer: ConfidentTake<FieldEditorProps, 'meta.footer'> = {}
  123. if (descriptionType === 'twitter status' && type.fields.body) {
  124. type.fields.body.validation = 'length:1,280'
  125. footer = {
  126. maxlength: 280,
  127. warningLength: 30,
  128. }
  129. } else if (type.fields.to && type.fields.body) {
  130. type.fields.to.required = true
  131. type.fields.body.validation = 'length:1,10000'
  132. footer = {
  133. maxlength: 10000,
  134. warningLength: 500,
  135. }
  136. }
  137. const initials = getTwitterInitials(config)
  138. if (initials) footer.text = initials
  139. type.editorMeta = {
  140. footer,
  141. }
  142. return [type]
  143. },
  144. }
  145. export default actionPlugin