article.rb 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. FactoryBot.define do
  3. factory :'ticket/article', aliases: %i[ticket_article] do
  4. inbound_email
  5. ticket factory: :ticket, strategy: :create # or else build(:ticket_article).save fails
  6. from { 'factory-customer-1@example.com' }
  7. to { 'factory-customer-1@example.com' }
  8. subject { 'factory article' }
  9. message_id { 'factory@id_com_1' }
  10. body { 'some message 123' }
  11. internal { false }
  12. sender { Ticket::Article::Sender.find_by(name: sender_name) }
  13. type { Ticket::Article::Type.find_by(name: type_name) }
  14. updated_by_id { 1 }
  15. created_by_id { 1 }
  16. trait :inbound_email do
  17. transient do
  18. type_name { 'email' }
  19. sender_name { 'Customer' }
  20. end
  21. end
  22. trait :outbound_email do
  23. transient do
  24. type_name { 'email' }
  25. sender_name { 'Agent' }
  26. end
  27. from { ticket.group.name }
  28. to { "#{ticket.customer.fullname} <#{ticket.customer.email}>" }
  29. end
  30. trait :inbound_phone do
  31. transient do
  32. type_name { 'phone' }
  33. sender_name { 'Customer' }
  34. end
  35. end
  36. trait :outbound_phone do
  37. transient do
  38. type_name { 'phone' }
  39. sender_name { 'Agent' }
  40. end
  41. from { nil }
  42. to { ticket.customer.fullname }
  43. end
  44. trait :outbound_note do
  45. transient do
  46. type_name { 'note' }
  47. sender_name { 'Agent' }
  48. end
  49. from { ticket.group.name }
  50. end
  51. trait :internal_note do
  52. transient do
  53. type_name { 'note' }
  54. sender_name { 'Agent' }
  55. end
  56. from { ticket.group.name }
  57. internal { true }
  58. end
  59. trait :inbound_web do
  60. transient do
  61. type_name { 'web' }
  62. sender_name { 'Customer' }
  63. end
  64. end
  65. trait :outbound_web do
  66. transient do
  67. type_name { 'web' }
  68. sender_name { 'Agent' }
  69. end
  70. end
  71. factory :twitter_article do
  72. transient do
  73. type_name { 'twitter status' }
  74. sender_name { 'Agent' }
  75. end
  76. ticket factory: %i[twitter_ticket]
  77. subject { nil }
  78. body { Faker::Lorem.sentence }
  79. content_type { 'text/plain' }
  80. message_id { Faker::Number.unique.number(digits: 18) }
  81. after(:create) do |article, context|
  82. next if context.sender_name == 'Agent'
  83. context.ticket.title = article.body
  84. context.ticket.save!
  85. end
  86. trait :inbound do
  87. transient do
  88. sender_name { 'Customer' }
  89. username { Faker::Twitter.screen_name }
  90. sender_id { Faker::Number.unique.number(digits: 18) }
  91. recipient_id { Faker::Number.unique.number(digits: 19) }
  92. end
  93. from { "@#{username}" }
  94. to { "@#{ticket.preferences['channel_screen_name']}" }
  95. body { "#{to} #{Faker::Lorem.question}" }
  96. preferences do
  97. {
  98. twitter: {
  99. mention_ids: [recipient_id],
  100. geo: {},
  101. retweeted: false,
  102. possibly_sensitive: false,
  103. in_reply_to_user_id: recipient_id,
  104. place: {},
  105. retweet_count: 0,
  106. source: '<a href="https://mobile.twitter.com" rel="nofollow">Twitter Web App</a>',
  107. favorited: false,
  108. truncated: false
  109. },
  110. links: [
  111. {
  112. url: "https://twitter.com/_/status/#{sender_id}",
  113. target: '_blank',
  114. name: 'on Twitter',
  115. },
  116. ],
  117. }
  118. end
  119. end
  120. trait :outbound do
  121. transient do
  122. username { Faker::Twitter.screen_name }
  123. sender_id { Faker::Number.unique.number(digits: 18) }
  124. recipient_id { Faker::Number.unique.number(digits: 19) }
  125. end
  126. from { "@#{ticket.preferences['channel_screen_name']}" }
  127. to { "@#{username}" }
  128. body { "#{to} #{Faker::Lorem.sentence}" }
  129. in_reply_to { Faker::Number.unique.number(digits: 19) }
  130. preferences do
  131. {
  132. twitter: {
  133. mention_ids: [recipient_id],
  134. geo: {},
  135. retweeted: false,
  136. possibly_sensitive: false,
  137. in_reply_to_user_id: recipient_id,
  138. place: {},
  139. retweet_count: 0,
  140. source: '<a href="https://www.canva.com" rel="nofollow">Canva</a>',
  141. favorited: false,
  142. truncated: false
  143. },
  144. links: [
  145. {
  146. url: "https://twitter.com/_/status/#{sender_id}",
  147. target: '_blank',
  148. name: 'on Twitter',
  149. },
  150. ],
  151. }
  152. end
  153. end
  154. trait :reply do
  155. in_reply_to { Faker::Number.unique.number(digits: 19) }
  156. end
  157. end
  158. factory :twitter_dm_article do
  159. transient do
  160. type_name { 'twitter direct-message' }
  161. end
  162. ticket factory: %i[twitter_ticket]
  163. body { Faker::Lorem.sentence }
  164. trait :pending_delivery do
  165. transient do
  166. recipient { association :twitter_authorization }
  167. sender_id { Faker::Number.unique.number(digits: 10) }
  168. end
  169. from { ticket.owner.fullname }
  170. to { recipient.username }
  171. in_reply_to { Faker::Number.unique.number(digits: 19) }
  172. content_type { 'text/plain' }
  173. end
  174. trait :delivered do
  175. pending_delivery
  176. message_id { Faker::Number.unique.number(digits: 19) }
  177. preferences do
  178. {
  179. delivery_retry: 1,
  180. twitter: {
  181. recipient_id: recipient.uid,
  182. sender_id: sender_id
  183. },
  184. links: [
  185. {
  186. url: "https://twitter.com/messages/#{recipient.uid}-#{sender_id}",
  187. target: '_blank',
  188. name: 'on Twitter'
  189. }
  190. ],
  191. delivery_status_message: nil,
  192. delivery_status: 'success',
  193. delivery_status_date: Time.current
  194. }
  195. end
  196. end
  197. end
  198. factory :sms_article do
  199. inbound
  200. transient do
  201. type_name { 'sms' }
  202. end
  203. ticket factory: %i[sms_ticket]
  204. from { Faker::PhoneNumber.cell_phone_in_e164 }
  205. to { Faker::PhoneNumber.cell_phone_in_e164 }
  206. subject { nil }
  207. body { Faker::Lorem.sentence }
  208. message_id { Faker::Number.unique.number(digits: 19) }
  209. content_type { 'text/plain' }
  210. after(:create) do |article, context|
  211. next if context.sender_name == 'Agent'
  212. context.ticket.title = article.body
  213. context.ticket.preferences.tap do |p|
  214. p['sms'] = {
  215. originator: article.from,
  216. recipient: article.to,
  217. }
  218. end
  219. context.ticket.save!
  220. end
  221. trait :inbound do
  222. transient do
  223. sender_name { 'Customer' }
  224. end
  225. preferences do
  226. {
  227. channel_id: ticket.preferences['channel_id'],
  228. sms: {
  229. reference: message_id,
  230. },
  231. }
  232. end
  233. end
  234. trait :outbound do
  235. transient do
  236. sender_name { 'Agent' }
  237. end
  238. in_reply_to { Faker::Number.unique.number(digits: 19) }
  239. preferences do
  240. {
  241. delivery_retry: 1,
  242. delivery_status_message: nil,
  243. delivery_status: 'success',
  244. delivery_status_date: Time.current,
  245. }
  246. end
  247. end
  248. end
  249. factory :whatsapp_article do
  250. inbound
  251. transient do
  252. type_name { 'whatsapp message' }
  253. channel { Channel.find(ticket.preferences[:channel_id]) }
  254. from_phone_number { Faker::PhoneNumber.cell_phone_in_e164 }
  255. from_name { Faker::Name.unique.name }
  256. timestamp_incoming { Time.zone.now.to_i.to_s }
  257. end
  258. ticket factory: %i[whatsapp_ticket]
  259. to { "#{channel.options[:name]} (#{channel.options[:phone_number]})" }
  260. subject { nil }
  261. body { Faker::Lorem.sentence }
  262. content_type { 'text/plain' }
  263. before(:create) do |_article, context|
  264. next if context.sender_name == 'Agent' && context.ticket.preferences[:whatsapp].present?
  265. context.ticket.preferences.tap do |p|
  266. p['whatsapp'] = {
  267. from: {
  268. phone_number: context.from_phone_number.delete('+'),
  269. display_name: context.from_name,
  270. },
  271. timestamp_incoming: context.timestamp_incoming,
  272. }
  273. end
  274. context.ticket.title = "New WhatsApp message from #{context.from_name} (#{context.from_phone_number})"
  275. context.ticket.save!
  276. end
  277. trait :inbound do
  278. transient do
  279. sender_name { 'Customer' }
  280. end
  281. message_id { "wamid.#{Faker::Number.unique.number}" }
  282. from { "#{from_name} (#{from_phone_number})" }
  283. created_by_id { ticket.customer_id } # NB: influences the value for the from field!
  284. preferences do
  285. {
  286. whatsapp: {
  287. entry_id: channel[:options][:phone_number_id],
  288. message_id: message_id,
  289. }
  290. }
  291. end
  292. end
  293. trait :pending_delivery do
  294. transient do
  295. sender_name { 'Agent' }
  296. end
  297. preferences { {} }
  298. created_by_id { create(:agent).id } # NB: influences the value for the from field!
  299. in_reply_to { "wamid.#{Faker::Number.unique.number}" }
  300. end
  301. trait :outbound do
  302. pending_delivery
  303. message_id { "wamid.#{Faker::Number.unique.number}" }
  304. preferences do
  305. {
  306. delivery_retry: 1,
  307. whatsapp: {
  308. message_id:,
  309. },
  310. delivery_status_message: nil,
  311. delivery_status: 'success',
  312. delivery_status_date: Time.current,
  313. }
  314. end
  315. end
  316. trait :with_attachment_media_document do
  317. after(:create) do |article, _context|
  318. create(:store,
  319. object: article.class.name,
  320. o_id: article.id,
  321. data: Faker::Lorem.unique.sentence,
  322. filename: 'test.txt',
  323. preferences: { 'Content-Type' => 'text/plain' })
  324. article.preferences.tap do |prefs|
  325. prefs['whatsapp'] = {
  326. entry_id: Faker::Number.unique.number.to_s,
  327. message_id: "wamid.#{Faker::Number.unique.number}",
  328. type: 'document',
  329. media_id: Faker::Number.unique.number.to_s
  330. }
  331. end
  332. article.save!
  333. end
  334. end
  335. end
  336. factory :telegram_article do
  337. inbound
  338. transient do
  339. type_name { 'telegram personal-message' }
  340. channel { Channel.find(ticket.preferences[:channel_id]) }
  341. username { Faker::Internet.username }
  342. end
  343. ticket factory: %i[telegram_ticket]
  344. to { "@#{channel[:options][:bot][:username]}" }
  345. subject { nil }
  346. body { Faker::Lorem.sentence }
  347. message_id { "#{Faker::Number.unique.decimal(l_digits: 1, r_digits: 10)}@telegram" }
  348. content_type { 'text/plain' }
  349. after(:create) do |article, context|
  350. next if context.sender_name == 'Agent'
  351. context.ticket.title = article.body
  352. context.ticket.preferences.tap do |p|
  353. p['telegram'] = {
  354. bid: context.channel[:options][:bot][:id],
  355. chat_id: (article.preferences[:telegram] && article.preferences[:telegram][:chat_id]) || Faker::Number.unique.number(digits: 10),
  356. }
  357. end
  358. context.ticket.save!
  359. end
  360. trait :inbound do
  361. transient do
  362. sender_name { 'Customer' }
  363. end
  364. created_by_id { ticket.customer_id } # NB: influences the value for the from field!
  365. preferences do
  366. {
  367. message: {
  368. created_at: Time.current.to_i,
  369. message_id: message_id,
  370. from: ActionController::Parameters.new(
  371. 'id' => Faker::Number.unique.number,
  372. 'is_bot' => false,
  373. 'first_name' => Faker::Name.unique.first_name,
  374. 'last_name' => Faker::Name.unique.last_name,
  375. 'username' => username,
  376. 'language_code' => 'en',
  377. ),
  378. },
  379. update_id: Faker::Number.unique.number(digits: 8),
  380. }
  381. end
  382. end
  383. trait :outbound do
  384. transient do
  385. sender_name { 'Agent' }
  386. end
  387. to { "@#{username}" }
  388. created_by_id { create(:agent).id } # NB: influences the value for the from field!
  389. in_reply_to { "#{Faker::Number.unique.decimal(l_digits: 1, r_digits: 10)}@telegram" }
  390. preferences do
  391. {
  392. delivery_retry: 1,
  393. telegram: {
  394. date: Time.current.to_i,
  395. from_id: Faker::Number.unique.number(digits: 10),
  396. chat_id: Faker::Number.unique.number(digits: 10),
  397. message_id: Faker::Number.unique.number,
  398. },
  399. delivery_status_message: nil,
  400. delivery_status: 'success',
  401. delivery_status_date: Time.current,
  402. }
  403. end
  404. end
  405. end
  406. factory :facebook_article do
  407. inbound
  408. transient do
  409. channel { Channel.find(ticket.preferences[:channel_id]) }
  410. post_id { Faker::Number.unique.number(digits: 15) }
  411. permalink_url { "https://www.facebook.com/#{channel[:options][:pages][0][:id]}/posts/#{post_id}/?comment_id=#{post_id}" }
  412. end
  413. ticket factory: %i[facebook_ticket]
  414. subject { nil }
  415. body { Faker::Lorem.sentence }
  416. message_id { "#{Faker::Number.unique.number(digits: 16)}_#{Faker::Number.unique.number(digits: 15)}" }
  417. content_type { 'text/plain' }
  418. after(:create) do |article, context|
  419. next if context.sender_name == 'Agent'
  420. context.ticket.title = article.body
  421. context.ticket.preferences.tap do |p|
  422. p['channel_fb_object_id'] = context.post_id,
  423. p['facebook'] = {
  424. permalink_url: context.permalink_url,
  425. }
  426. end
  427. context.ticket.save!
  428. end
  429. trait :inbound do
  430. transient do
  431. type_name { 'facebook feed post' }
  432. sender_name { 'Customer' }
  433. end
  434. from { ticket.customer.fullname }
  435. to { channel[:options][:pages][0][:name] }
  436. preferences do
  437. {
  438. links: [
  439. {
  440. url: permalink_url,
  441. target: '_blank',
  442. name: 'on Facebook',
  443. },
  444. ],
  445. }
  446. end
  447. end
  448. trait :outbound do
  449. transient do
  450. type_name { 'facebook feed comment' }
  451. sender_name { 'Agent' }
  452. end
  453. from { channel[:options][:pages][0][:name] }
  454. to { ticket.customer.fullname }
  455. in_reply_to { "#{Faker::Number.unique.number(digits: 16)}_#{Faker::Number.unique.number(digits: 15)}" }
  456. preferences do
  457. {
  458. delivery_retry: 1,
  459. delivery_status_message: nil,
  460. delivery_status: 'success',
  461. delivery_status_date: Time.current,
  462. }
  463. end
  464. end
  465. end
  466. trait :with_attachment do
  467. transient do
  468. attachment { File.open('spec/fixtures/files/upload/hello_world.txt') }
  469. end
  470. after(:create) do |article, context|
  471. create(:store,
  472. object: article.class.name,
  473. o_id: article.id,
  474. data: context.attachment.read,
  475. filename: File.basename(context.attachment.path),
  476. preferences: {})
  477. end
  478. end
  479. trait :with_prepended_attachment do
  480. transient do
  481. attachment { File.open('spec/fixtures/files/upload/hello_world.txt') }
  482. override_content_type { nil }
  483. attachments_count { 1 }
  484. end
  485. after(:build) do |article, context|
  486. filename = File.basename(context.attachment.path)
  487. content_type = context.override_content_type || MIME::Types.type_for(filename).first&.content_type
  488. attachments = []
  489. context.attachments_count.times do
  490. attachments << create(:store,
  491. data: context.attachment.read,
  492. filename: filename,
  493. preferences: { 'Content-Type' => content_type })
  494. end
  495. article.attachments = attachments
  496. end
  497. end
  498. end
  499. end