Compare commits

...
288 Commits
Author SHA1 Message Date
Sojan 20b96c7c60 Merge branch 'release/1.19.0' 2021-08-17 01:50:25 +05:30
Sojan 6a9c885926 Bump version to 1.19.0 2021-08-17 01:48:46 +05:30
Pranav Raj SandGitHub ed317c5bb3 feat: Use relay server for self-hosted mobile push notifications (#2757) 2021-08-17 01:44:16 +05:30
Vishnu NarayananandGitHub 319412f6dc feat: include installation method in data to cw hub (#2831) 2021-08-16 23:35:00 +05:30
Sivin VargheseandGitHub 0e4196c91a enhancement: Removes the focus of the first item in the dropdown when it is active. (#2833) 2021-08-16 22:11:20 +05:30
Nithin David ThomasandGitHub 70d41ffcdd fix: Smoothens out opening animation for widget (#2789) 2021-08-14 08:40:29 +05:30
Nithin David ThomasandGitHub e09941db1c fix: Use all: revert to avoid external css styles for widget bubble (#2809) 2021-08-13 20:16:33 +05:30
Pascal JuferandGitHub 37bf4b7dd8 fix: Use label for "Subscribe to updates" checkbox (#2788) 2021-08-13 19:10:47 +05:30
Aswin Dev P.SandGitHub 9e052fd5b2 chore: Set phone_number through Website SDK (#2803)
Fixes: #2599
2021-08-13 17:26:09 +05:30
dfcc33cbdd fix: Fixes error when dropdown button is focused (#2797)
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
2021-08-13 13:40:12 +05:30
0f148f7700 fix: Fixes parsing issue with custom attributes (#2796)
* fix: Fixes parsing issue with custom attributes

* Update app/javascript/dashboard/routes/dashboard/conversation/ContactCustomAttributes.vue

Co-authored-by: शैलेश कळमकर (Shailesh Kalamkar) <shailesh.kalamkar@gmail.com>

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Co-authored-by: शैलेश कळमकर (Shailesh Kalamkar) <shailesh.kalamkar@gmail.com>
Co-authored-by: Sojan Jose <sojan@pepalo.com>
2021-08-13 13:37:51 +05:30
Sojan JoseandGitHub acb39cbc8f chore: Ability to create contact with identifiers (#2802) 2021-08-13 13:02:46 +05:30
Nithin David ThomasandGitHub d492a65c24 fix: Checks JS window object for null before focusing (#2798) 2021-08-12 23:21:31 +05:30
3d807684bb fix: Reduce size of Docker image (#2779)
* fix: Ordering in Dockerfile

Folders node_modules & tmp/cache are currently not removed and thus the Docker image
size is pretty big.

I don't think we can remove app/assets & vendor/assets though as the
app won't work without those.

* Expose port 3000 in Docker image
* Further Dockerfile optimization

Co-authored-by: Vishnu Narayanan <vishnu@chatwoot.com>
2021-08-12 15:10:02 +05:30
21d2b4d918 chore: Consider inbox.timezone in open_at logic (#2183)
Co-authored-by: Sojan Jose <sojan@pepalo.com>
2021-08-12 02:29:15 +05:30
Sojan JoseandGitHub 1f59205f20 chore: handle more exceptions (#2793) 2021-08-12 01:32:22 +05:30
a065165bcb fix: undefined method contact in support mailbox (#2678)
if chatwoot receives an email of already existing contact with a different case say "Care@example.com", before this fix, it will throw an error. Now it will retrieve existing contact

Fixes: #2553


Co-authored-by: Sojan Jose <sojan@pepalo.com>
2021-08-12 01:28:07 +05:30
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
4907489ea8 chore: Bump path-parse from 1.0.6 to 1.0.7 (#2786)
Bumps [path-parse](https://github.com/jbgutierrez/path-parse) from 1.0.6 to 1.0.7.
- [Release notes](https://github.com/jbgutierrez/path-parse/releases)
- [Commits](https://github.com/jbgutierrez/path-parse/commits/v1.0.7)

---
updated-dependencies:
- dependency-name: path-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-12 00:28:14 +05:30
Muhsin KelothandGitHub 4d668d8db3 feat: Dedicated tab for campaigns (#2741) 2021-08-11 20:29:33 +05:30
Sojan JoseandGitHub 8daf1fe033 chore: Add down gem for Local file downloads (#2765)
- Add down gem to handle downloading files to host machine
- Remove the LocalResource class
- Introduce max limit for contact avatars send via SDK
2021-08-11 16:40:28 +05:30
29f54c1f26 chore: Removed the multi-select agent dropdown from the conversation header. (#2781)
Co-authored-by: Nithin David Thomas <1277421+nithindavid@users.noreply.github.com>
2021-08-10 23:25:55 -07:00
1a4faab381 feat: Creates story for cc bcc input component in reply box (#2763)
* feat: Creates cc bcc input component for reply box

* Adds email inputs for cc and bcc

* Cleans storybook code

* Fixes eslint issues

* Update app/javascript/dashboard/components/widgets/conversation/stories/ReplyEmailHead.stories.js

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>

* Review fixes

* Update app/javascript/dashboard/components/widgets/conversation/stories/ReplyEmailHead.stories.js

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>

* Update app/javascript/dashboard/components/widgets/conversation/stories/ReplyEmailHead.stories.js

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>

* Update app/javascript/dashboard/components/widgets/conversation/stories/ReplyEmailHead.stories.js

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
2021-08-11 11:34:47 +05:30
c3314dd186 feat: Creates a modal showing all the available keyboard shortcuts (#2728)
* feat: Adds modal showing all the available keyboard shortcuts

* Minor fixes

* Minor fixes

* Spacing fixes

* fix translations

* Adds i18n

* Review fixes

* Review fixes

* spacing fixes

* Review fixes

* Minor fixes

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
2021-08-09 21:23:40 -07:00
Pranav Raj SandGitHub bdc4ecffc1 fix: Save content_type, multipart information to email message model (#2774) 2021-08-09 18:04:34 +05:30
Nithin David ThomasandGitHub 462214aced fix: Reduces space for custom drop-downs (#2778) 2021-08-09 15:12:11 +05:30
c523a953f7 feat: Adds keyboard shortcuts for conversation actions (#2672)
* feat: Adds keyboard shortcuts for conversation actions

* Minor fixes

* Minor fixes

* Minor fixes and add new shortcut

* MInor fixes

* Review fixes

* Minor fixes

* Code cleanup

* Minor fixes

* Uses Alt or Option key instead of shift-key

* Review fixes

* Review fixes

Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
2021-08-09 00:38:52 -07:00
Sivin VargheseandGitHub c7482696d4 fix: Remove text retained in confirm delete modal (#2775) 2021-08-09 12:07:42 +05:30
2035d24cb8 enhancement: Show close button for unread view (#2758)
* enhancement: Show close button for unread view

* Fixes issue with button spacing

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
2021-08-07 11:56:40 +05:30
9c257578b0 fix: Fixes unread view blocking page scroll (#2727)
* fix: Fixes unread view blocking page scroll

* Update sdk.js

* Fixes the height issue for unread view

* Fix unread message position issue

* Code climate fixes

* Fixes height issue for large messages

* Fixes height issue on unread view

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
2021-08-07 11:56:15 +05:30
Vishnu NarayananandGitHub b58ca21f0b docs: Update README to add question/support sub-section (#2764)
- add questions sub-section and mention discord
- improve formatting
2021-08-07 00:44:07 +05:30
Sojan JoseandGitHub edec74c389 chore: Fix Twilio send message error (#2761) 2021-08-06 13:34:20 +05:30
f934a5eb04 fix: Broken Docker Builds (#2755)
Co-authored-by: Pascal Jufer <paescuj@users.noreply.github.com>
2021-08-06 01:21:44 +05:30
RaikasandGitHub f477c5281f chore: Fix Finnish translations (#2753) 2021-08-05 10:24:07 +05:30
Muhsin KelothandGitHub 6265c1c907 fix: Add brand color in CSAT card component (#2756) 2021-08-05 01:20:01 +05:30
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
e834b545ef chore: Bump tar from 6.1.0 to 6.1.3 (#2750)
Bumps [tar](https://github.com/npm/node-tar) from 6.1.0 to 6.1.3.
- [Release notes](https://github.com/npm/node-tar/releases)
- [Changelog](https://github.com/npm/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/npm/node-tar/compare/v6.1.0...v6.1.3)

---
updated-dependencies:
- dependency-name: tar
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-04 10:23:13 +05:30
Pascal JuferandGitHub c37966744f fix: Fix and rename pg_database_url.sh helper script (#2747)
- Fix and rename pg_database_url helper script
- Use single-quote and remove trailing whitespace
2021-08-03 22:56:55 +05:30
Sojan JoseandGitHub ab54d9c629 chore: Upgrade rails and ruby versions (#2400)
ruby version: 3.0.2
rails version: 6.1.4
2021-08-03 20:11:52 +05:30
Sivin VargheseandGitHub 7e960b7c72 feat: Add support for rich content for message in ongoing campaigns (#2577) 2021-08-03 19:38:51 +05:30
Muhsin KelothandGitHub 92c14fa87d feat: CSAT response collection public page (#2685) 2021-08-03 18:22:50 +05:30
Vishnu NarayananandGitHub 9b01b82cc7 docs: update chatwoot VDP guidelines (#2740) 2021-08-03 16:13:44 +05:30
Nithin David ThomasandGitHub 0475060245 enhancement: Updates icons to show last message type (#2743) 2021-08-03 12:13:24 +05:30
Pranav Raj SandGitHub faf104c1fe fix: Update tweet character count logic (#2709) 2021-08-02 16:07:30 +05:30
Pranav Raj SandGitHub d88e3e3596 fix: Resolve conversation with id instead of current conversation (#2731) 2021-08-02 13:11:07 +05:30
Muhsin KelothandGitHub 9f3f238cb5 chore: Show new account modal if user accounts are empty (#2448) 2021-08-02 08:54:53 +05:30
Pranav Raj SandGitHub 5b3339cd33 fix: Render line-breaks properly in messages (#2699) 2021-08-02 08:24:08 +05:30
Pranav Raj SandGitHub bfa00cc12d fix: Avoid flicker while updating the view with new messages (#2730) 2021-08-02 08:03:12 +05:30
Sojan JoseandGitHub e46aa1aa64 chore: Mark Facebook accounts for reconnection (#2405)
fixes: #2037
2021-08-01 18:15:39 +05:30
Sojan JoseandGitHub 2890339734 chore: Limit objects returned by conversation API (#2721) 2021-07-31 21:19:42 +05:30
Pranav Raj SandGitHub 915366c472 fix: Rename identity_hash to identifier_hash (#2726) 2021-07-29 20:11:21 +05:30
f2b5e328bb feat: Update agent and team multi-select with new multi-select dropdown (#2516)
* feat: Update agent/team multiselect styles

* Component name fixes

* Adds key control for our multiselect dropdown and component name spell fix

* Minor fixes

* Review fixes

* Minor fixes

* Minor fixes

* Review fixes

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Co-authored-by: Nithin David Thomas <1277421+nithindavid@users.noreply.github.com>
2021-07-29 17:44:37 +05:30
Nithin David ThomasandGitHub 672e5874fb fix: Appending markdown as HTML into editor (#2720) 2021-07-29 11:53:28 +05:30
Nithin David ThomasandGitHub 8f30abb98b feat: Show links for custom attributes (#2723) 2021-07-28 21:36:35 +05:30
223385d134 fix: Specify external db with non-standard port (#2711)
POSTGRES_PORT was not taking effect if provided separately
instead of using DATABASE_URL. This adds support for using
databases running on non-standard ports.

#1145
#1147

Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
2021-07-28 19:36:51 +05:30
Muhsin KelothandGitHub 7662fdce47 feat: CSAT response public APIs (#2670) 2021-07-28 14:59:13 +05:30
a47ca9cf4b feat: Show e-mail meta data for conversations (#2708)
Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
2021-07-28 14:43:44 +05:30
b44f9b792b chore: Block & throttle abusive requests (#2706)
Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
2021-07-27 21:27:23 +05:30
SanjuandGitHub 359c3c8ccb Fix: type error trim of undefined#2595 (#2702) 2021-07-27 07:38:27 +05:30
sbreiler-workandGitHub d7da6fbfb5 fix: Make "Search Results" in PopOverSearch translateable (#2705) 2021-07-26 14:33:42 +05:30
Pranav Raj SandGitHub 7664006625 chore: Improve search, list performance of contact/conversation APIs (#2696) 2021-07-23 18:39:24 +05:30
Pranav Raj SandGitHub 6e1493501a feat: Add APIs for custom attribute definitions (#2689) 2021-07-23 17:04:33 +05:30
SanjuandGitHub 7e0937f3ed chore: Cypress test case for create label flow 2021-07-23 16:38:44 +05:30
d955d8e7dc feat: Ability to snooze conversations (#2682)
Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
2021-07-23 15:24:07 +05:30
Vishnu NarayananandGitHub 4d45ac3bfc chore: Switch from addgroup to adduser (#2688) 2021-07-22 19:49:12 +05:30
ecdf977de7 Live chat widget preview #2441 (#2523)
* update widget preview on storybook

* removed default value for logo

* add online dot

* resolve PR comments

- split widget to head, body & footer
- updated reply time to a select box

* update spacing with variables

* update reply-time with i18

* update with spacing variables

* update padding with space variable

* resolved PR comments

* update background color

Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
Co-authored-by: Nithin David Thomas <1277421+nithindavid@users.noreply.github.com>
2021-07-22 18:36:33 +05:30
Muhsin KelothandGitHub 6b6df7a70d fix: Disable "none" option from agent dropdown If agent is not selected (#2687) 2021-07-22 16:31:53 +05:30
Sojan JoseandGitHub a7ca55c080 chore: Change the conversation bot status to pending (#2677)
fixes: #2649
2021-07-21 22:02:43 +05:30
Sojan JoseandGitHub a0886d37bc chore: Expose widget config via an API (#2645)
Expose widget config via an API so that the UI could be detached from the rails Application.
2021-07-21 01:26:32 +05:30
Sojan caf8669db5 Merge branch 'release/1.18.1' 2021-07-20 00:51:08 +05:30
Sojan d1619bdc87 Bump version to 1.18.1 2021-07-20 00:50:38 +05:30
Sojan JoseandGitHub 217dd8a6f0 chore: Add assigned_count to conversation APIs (#2665) 2021-07-19 19:10:58 +05:30
Pranav Raj SandGitHub a235e82a02 fix: Update attribute name to conversationsCount (#2662) 2021-07-19 14:41:16 +05:30
Pranav Raj SandGitHub a2fca2edb8 fix: Fix HTML only email rendering (#2644) 2021-07-16 19:56:54 +05:30
Sojan 9de36f2b9e Merge branch 'release/1.18.0' 2021-07-16 00:08:30 +05:30
Sojan fa3cc40059 Bump version to 1.18.0 2021-07-16 00:07:18 +05:30
Sojan JoseandGitHub 3941c6f32b chore: Translation updates (#2639) 2021-07-16 00:04:09 +05:30
Sojan JoseandGitHub eda52930be chore: Add Telemetry (#2631)
ref: https://www.chatwoot.com/docs/self-hosted/telemetry
2021-07-15 23:58:54 +05:30
Pranav Raj SandGitHub 5aac2acf56 fix: Improve mail content parsing (#2638) 2021-07-15 22:50:32 +05:30
Muhsin KelothandGitHub 85ea56b765 fix: Disable sender tooltip and copy button in deleted messages (#2637) 2021-07-15 21:06:54 +05:30
Pranav Raj S 72069e45ef fix: Use mail.decoded as fallback for text content to fix char-rendering 2021-07-15 20:06:43 +05:30
Mrunal SelokarandGitHub e0535168a0 chore: Removing duplicate callback to create auth token (#2624)
Removed after create callback in user model to create a new access token. This is already being taken care of by AccessTokenable module.

Fixes #2620
2021-07-15 15:24:38 +05:30
Muhsin KelothandGitHub b56512eb56 fix: Hide deleted messages on widget side (#2614) 2021-07-15 14:27:37 +05:30
Muhsin KelothandGitHub cf785123a5 feat: One off campaign UI (#2621) 2021-07-15 13:31:43 +05:30
Pranav Raj SandGitHub aa7db90cd2 fix: Use Dompurify to strip style characters (#2632) 2021-07-15 12:54:31 +05:30
Sojan JoseandGitHub d7982a6ffd chore: Add assigned option to conversation finder API (#2630)
- Adds the ability to filter all the conversations which are assigned
- Add rack timeout gem
- Remove size attribute from Sidekiq config
2021-07-14 22:51:27 +05:30
Pranav Raj SandGitHub 3d18ec9e40 feat: Add date-range filter on CSAT Reports (#2622) 2021-07-14 20:15:57 +05:30
Sojan JoseandGitHub a5bc81b304 chore: Suppress the unnecessary CSRF warning (#2606)
Suppress the unnecessary CSRF warning
2021-07-14 18:40:24 +05:30
Sojan JoseandGitHub dfddf9cacc chore: one off SMS campaign APIs (#2589) 2021-07-14 12:24:09 +05:30
Pranav Raj SandGitHub cb44eb2964 feat: Add CSAT reports (#2608) 2021-07-14 10:20:06 +05:30
Pranav Raj SandGitHub b7806fc210 fix: Update styles to fix the image resolution on smaller devices (#2618) 2021-07-13 18:54:33 +05:30
f9b55944ad feat: Adds custom date range component for reports (#2602)
* feat: Adds custom date range for reports

* Review fixes

* Minor fixes

* Review fixes

* Use computed property

Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
2021-07-13 03:41:03 -07:00
dependabot[bot]andGitHub fae8466a6c chore: Bump addressable from 2.7.0 to 2.8.0 (#2610) 2021-07-13 13:58:54 +05:30
Nithin David ThomasandGitHub deb4513584 bug: Fixes alignment issue with deleted messages (#2613) 2021-07-13 12:00:29 +05:30
Muhsin KelothandGitHub 15085cfb98 feat: Conversation transcript in widget (#2549) 2021-07-13 11:31:21 +05:30
Siddharth RameshandGitHub fc4ef1595b chore: Twilio to individual(SMS/Whatsapp) channels (#2566) 2021-07-12 22:33:39 +05:30
Muhsin KelothandGitHub ba547b3f60 chore: Delete related notifications if a conversation is deleted (#2592) 2021-07-12 12:28:16 +05:30
Pranav Raj SandGitHub 5726447e47 fix: Unescape Slack incoming messages (#2604) 2021-07-09 17:45:02 +05:30
a9ccded62b feat: Creates custom date range picker component (#2597)
* feat: Adds custom date range in reports

* error fixes

* Creates new component for date range and adds stories

* Revert files

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
2021-07-09 14:16:55 +05:30
Muhsin KelothandGitHub 64e69a85f8 chore: Update availability status everywhere if the user changes the status from the account menu (#2581)
* add agent mixin

* apply agent mixin in components

* review fixes

* fix specs
2021-07-09 13:20:54 +05:30
Muhsin KelothandGitHub 4a2195aeda chore: Disable delete option for deleted messages (#2600)
* disable delete option for deleted message

* review fixes
2021-07-09 12:34:09 +05:30
Nithin David ThomasandGitHub efd6f735dc fix: Align delete message menu on twitter bubble correctly (#2591)
* fix: Aligns delete message menu on twitter bubble correctly

* Fixes alignment issues
2021-07-09 10:53:28 +05:30
38cce3c1c0 chore: New translation updates (#2585)
* New translations contact.json (Vietnamese)

* New translations conversation.json (Vietnamese)

* New translations inboxMgmt.json (Vietnamese)

* New translations settings.json (Vietnamese)

* New translations en.json (Vietnamese)

* New translations inboxMgmt.json (Chinese Traditional)

* New translations contact.json (Chinese Traditional)

* New translations conversation.json (Chinese Traditional)

* New translations settings.json (Chinese Traditional)

* New translations en.json (Chinese Traditional)

* New translations en.yml (Chinese Simplified)

* New translations settings.json (Indonesian)

* New translations en.json (Indonesian)

* New translations contact.json (Persian)

* New translations conversation.json (Persian)

* New translations inboxMgmt.json (Persian)

* New translations inboxMgmt.json (Indonesian)

* New translations contact.json (Portuguese, Brazilian)

* New translations conversation.json (Portuguese, Brazilian)

* New translations inboxMgmt.json (Portuguese, Brazilian)

* New translations integrations.json (Portuguese, Brazilian)

* New translations settings.json (Portuguese, Brazilian)

* New translations en.json (Portuguese, Brazilian)

* New translations contact.json (Indonesian)

* New translations conversation.json (Indonesian)

* New translations en.json (Chinese Simplified)

* New translations devise.en.yml (Chinese Simplified)

* New translations inboxMgmt.json (Swedish)

* New translations settings.json (Swedish)

* New translations conversation.json (Swedish)

* New translations en.json (Swedish)

* New translations contact.json (Turkish)

* New translations conversation.json (Turkish)

* New translations contact.json (Swedish)

* New translations conversation.json (Slovak)

* New translations inboxMgmt.json (Slovak)

* New translations settings.json (Slovak)

* New translations en.json (Slovak)

* New translations inboxMgmt.json (Turkish)

* New translations en.json (Ukrainian)

* New translations contact.json (Chinese Simplified)

* New translations settings.json (Ukrainian)

* New translations conversation.json (Chinese Simplified)

* New translations inboxMgmt.json (Chinese Simplified)

* New translations settings.json (Chinese Simplified)

* New translations settings.json (Turkish)

* New translations en.json (Turkish)

* New translations contact.json (Ukrainian)

* New translations conversation.json (Ukrainian)

* New translations inboxMgmt.json (Ukrainian)

* New translations settings.json (Persian)

* New translations integrationApps.json (French)

* New translations en.json (Hebrew)

* New translations contact.json (Hebrew)

* New translations en.json (Nepali)

* New translations conversation.json (Hebrew)

* New translations inboxMgmt.json (Hebrew)

* New translations settings.json (Hebrew)

* New translations settings.json (Nepali)

* New translations csatMgmt.json (French)

* New translations contact.json (Thai)

* New translations conversation.json (Thai)

* New translations inboxMgmt.json (Thai)

* New translations settings.json (Thai)

* New translations en.json (Thai)

* New translations en.json (Tamil)

* New translations contact.json (Tamil)

* New translations en.json (Persian)

* New translations conversation.json (Tamil)

* New translations inboxMgmt.json (Tamil)

* New translations settings.json (Tamil)

* New translations settings.json (Malayalam)

* New translations en.json (Malayalam)

* New translations contact.json (Nepali)

* New translations conversation.json (Nepali)

* New translations inboxMgmt.json (Nepali)

* New translations inboxMgmt.json (Malayalam)

* New translations contact.json (Hindi)

* New translations conversation.json (Hindi)

* New translations inboxMgmt.json (Hindi)

* New translations settings.json (Hindi)

* New translations en.json (Hindi)

* New translations contact.json (Malayalam)

* New translations conversation.json (Malayalam)

* New translations contact.json (Slovak)

* New translations contact.json (Danish)

* New translations conversation.json (Danish)

* New translations inboxMgmt.json (Danish)

* New translations settings.json (Danish)

* New translations en.json (Danish)

* New translations inboxMgmt.json (Czech)

* New translations contact.json (Czech)

* New translations conversation.json (Czech)

* New translations settings.json (Czech)

* New translations en.json (Czech)

* New translations contact.json (German)

* New translations settings.json (Greek)

* New translations en.json (Greek)

* New translations contact.json (Finnish)

* New translations conversation.json (Finnish)

* New translations inboxMgmt.json (Finnish)

* New translations inboxMgmt.json (Greek)

* New translations conversation.json (German)

* New translations inboxMgmt.json (German)

* New translations integrations.json (German)

* New translations settings.json (German)

* New translations en.yml (German)

* New translations en.json (German)

* New translations contact.json (Greek)

* New translations conversation.json (Greek)

* New translations en.json (Catalan)

* New translations inboxMgmt.json (French)

* New translations integrations.json (French)

* New translations report.json (French)

* New translations settings.json (French)

* New translations en.yml (French)

* New translations conversation.json (French)

* New translations en.json (French)

* New translations labelsMgmt.json (French)

* New translations teamsSettings.json (French)

* New translations campaign.json (French)

* New translations contact.json (Spanish)

* New translations conversation.json (Spanish)

* New translations contact.json (French)

* New translations contact.json (Romanian)

* New translations conversation.json (Romanian)

* New translations inboxMgmt.json (Romanian)

* New translations settings.json (Romanian)

* New translations chatlist.json (French)

* New translations en.json (Romanian)

* New translations inboxMgmt.json (Spanish)

* New translations en.json (Arabic)

* New translations contact.json (Catalan)

* New translations settings.json (Arabic)

* New translations conversation.json (Catalan)

* New translations inboxMgmt.json (Catalan)

* New translations settings.json (Catalan)

* New translations settings.json (Spanish)

* New translations en.json (Spanish)

* New translations contact.json (Arabic)

* New translations conversation.json (Arabic)

* New translations inboxMgmt.json (Arabic)

* New translations settings.json (Norwegian)

* New translations en.json (Norwegian)

* New translations contact.json (Polish)

* New translations conversation.json (Polish)

* New translations inboxMgmt.json (Polish)

* New translations inboxMgmt.json (Norwegian)

* New translations conversation.json (Dutch)

* New translations inboxMgmt.json (Dutch)

* New translations settings.json (Dutch)

* New translations en.json (Dutch)

* New translations contact.json (Norwegian)

* New translations conversation.json (Norwegian)

* New translations settings.json (Finnish)

* New translations contact.json (Russian)

* New translations conversation.json (Russian)

* New translations inboxMgmt.json (Russian)

* New translations settings.json (Russian)

* New translations en.json (Russian)

* New translations en.json (Portuguese)

* New translations settings.json (Polish)

* New translations en.json (Polish)

* New translations contact.json (Portuguese)

* New translations conversation.json (Portuguese)

* New translations inboxMgmt.json (Portuguese)

* New translations settings.json (Portuguese)

* New translations contact.json (Dutch)

* New translations en.json (Hungarian)

* New translations contact.json (Italian)

* New translations conversation.json (Italian)

* New translations inboxMgmt.json (Italian)

* New translations settings.json (Italian)

* New translations en.json (Italian)

* New translations en.json (Finnish)

* New translations contact.json (Hungarian)

* New translations conversation.json (Hungarian)

* New translations inboxMgmt.json (Hungarian)

* New translations settings.json (Hungarian)

* New translations contact.json (Korean)

* New translations conversation.json (Korean)

* New translations inboxMgmt.json (Korean)

* New translations settings.json (Korean)

* New translations en.json (Korean)

* New translations en.json (Japanese)

* New translations contact.json (Japanese)

* New translations conversation.json (Japanese)

* New translations inboxMgmt.json (Japanese)

* New translations settings.json (Japanese)

* New translations en.yml (Portuguese, Brazilian)

* chore: revert key name

* Update zh_CN.yml

* New translations contact.json (Russian)

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
2021-07-07 21:25:04 +05:30
e89e184d3b chore: Add loader for email and csat submit buttons (#2569)
* fix the loader issue in submit button

* review fixes

Co-authored-by: Nithin David Thomas <1277421+nithindavid@users.noreply.github.com>
2021-07-07 11:24:08 +05:30
Pranav Raj SandGitHub 0d02142925 feat: Support messages along with the actions in Dialogflow response (#2579) 2021-07-07 10:04:48 +05:30
Sojan JoseandGitHub bbc47971d7 chore: Reduce steps to spin up Codespaces (#2576) 2021-07-06 21:21:40 +05:30
104ae8de2e feat: Create component to merge contacts (#2412)
Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
2021-07-06 13:02:37 +05:30
Sojan JoseandGitHub e11b3c4f17 chore: Expose contact custom attributes in events (#2562)
Expose contact custom attributes in events so the external system can behave accordingly without triggering an additional API call.
2021-07-05 14:35:53 +05:30
0bd48129b9 feat: Add settings for audio alert notifications (#2415)
Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
2021-07-05 12:01:54 +05:30
Muhsin KelothandGitHub 48127e00d7 feat: Enable/disable CSAT in web widget settings (#2531) 2021-07-05 11:07:02 +05:30
Sivin VargheseandGitHub abda86f9a7 bug: fixes typing indicator out of position in widget (#2551)
Fixes typing indicator out of position in the widget when the agent did not send the last message.
2021-07-02 15:28:32 +05:30
Muhsin KelothandGitHub 8341a43143 chore: Update CSAT submit action behaviour (#2556)
* update behaviour of CSAT submit

* code cleanup
2021-07-02 14:52:20 +05:30
f0f66c7da6 feat: Add ability to create a new conversation if the previous conversation is resolved (#2512)
Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
2021-06-30 21:09:44 +05:30
Sivin VargheseandGitHub e6e9916fdb feat: Add drag and drop for images/files in the agent input box (#2546) 2021-06-30 20:32:17 +05:30
dd9d5e410c feat: Add CSAT response APIs (#2503)
Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
2021-06-29 20:59:41 +05:30
Nithin David ThomasandGitHub 2e71006f9d feat: Add option to delete message content (#2532) 2021-06-29 20:06:40 +05:30
Pranav Raj SandGitHub 30832d8a34 feat: Add APIs to create custom views on the dashboard (#2498) 2021-06-29 19:29:57 +05:30
Sojan JoseandGitHub fa37f8e185 chore: Support Github Codespaces (#2510)
Note: Update the ruby version 2.7.3. For production Linux VM, install 2.7.3 and follow the upgrade steps as shown in https://www.chatwoot.com/docs/self-hosted/deployment/linux-vm#upgrading-to-a-newer-version-of-chatwoot
2021-06-29 19:18:10 +05:30
07a422219a chore: New translation updates (#2533)
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
2021-06-29 18:12:33 +05:30
Muhsin KelothandGitHub 8027c69c1c chore: Change the notification text for mention notification type (#2534) 2021-06-29 14:32:57 +05:30
Nithin David ThomasandGitHub 044b6872a4 feat: Add actions and API for deleting message via API (#2519) 2021-06-29 11:19:08 +05:30
Pranav Raj SandGitHub b705eb39e2 fix: Add missing Cache-Control headers in the production config (#2518) 2021-06-28 18:32:01 +05:30
Pranav Raj SandGitHub ac657e3bf9 fix: Remove style tags from email content (#2515) 2021-06-26 11:50:40 +05:30
Vishnu NarayananandGitHub bed1979986 fix: rvm gpg key issue (#2514)
sks-keyserver gpg pool is now deprecated due to GDPR issues and is
no longer reliable. Switching to use ubuntu keyserver for the time
being as this is the only server returning rvm maintainers gpg keys.
2021-06-25 21:19:08 +05:30
Sivin VargheseandGitHub 151bfbd1dd feat: Creates new component for multiselect. (#2446) 2021-06-25 17:41:24 +05:30
d840b7b13d feat: Adds storybook for search component. (#2444)
* feat: Adds storybook for search component.

* Review fixes

* Uses woot-label for conv-id

Co-authored-by: Nithin David Thomas <1277421+nithindavid@users.noreply.github.com>
2021-06-25 17:14:37 +05:30
Pranav Raj SandGitHub bd1b17baf3 feat: Show the CSAT response to the agent (#2511) 2021-06-25 13:37:51 +05:30
b769fc87d0 chore: Adds storybook for woot textarea (#2451)
* chore: Adds storybook for woot textarea

* Review fixes

* Minor fixes

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Co-authored-by: Nithin David Thomas <1277421+nithindavid@users.noreply.github.com>
2021-06-25 13:18:24 +05:30
Sivin VargheseandGitHub ad431c2008 chore: fixes gutter space between settings page content and sidebar (#2505)
* chore: fixes no gutter space between the settings page content and right content.

* Review fixes
2021-06-25 12:56:10 +05:30
dbddb1ece4 feat: Add CSAT response submit action (#2506)
Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
2021-06-25 12:00:49 +05:30
Sivin VargheseandGitHub 2f9637bde5 feat: Adds time zone in Business Hours (#2497) 2021-06-24 17:47:56 +05:30
Sivin VargheseandGitHub ae53aa6cfb feat: Add inbox info to previous conversations (#2491) 2021-06-23 19:29:50 +05:30
Sojan JoseandGitHub cd11efea1d feat: Add CSAT Message Template Hooks (#2494) 2021-06-23 19:29:27 +05:30
Sojan JoseandGitHub 640e028bde chore: Add help-text for slack integration (#2409) 2021-06-23 11:52:06 +05:30
Sojan JoseandGitHub bd9bac75ad fix: Adds condition to handle base and mergee contact being same (#2485)
When an agent updates a contact email in the dashboard and the contact also update his email via the email collect box,
Merge action receives the same base and mergee contacts which could cause data corruption.
2021-06-22 15:57:48 +05:30
Muhsin KelothandGitHub 29b9915d3b feat: Add customer satisfaction component (#2456) 2021-06-22 13:01:19 +05:30
Sivin VargheseandGitHub a357b657a8 chore: Adds story for woot input (#2449) 2021-06-22 07:05:23 +05:30
Muhsin KelothandGitHub 3d748aa8d4 chore: Add contact note model (#2462) 2021-06-21 15:16:26 +05:30
Sivin VargheseandGitHub f809431074 chore: Update the design of the attachments preview (#2447) 2021-06-20 19:54:57 +05:30
Muhsin KelothandGitHub e34fe1c614 fix: Fix the sender tooltip getting undefined in bot messages (#2453) 2021-06-20 19:25:36 +05:30
e45abebe39 feat: Improve scrolling for last message on agent side (#2421)
* Improve chat widget scrolling

* refactor the class names to snake-case

* refactor the scrollTop calculations to a helper

* Add tests for scrollTopCalculationHelper

Co-authored-by: Nithin David Thomas <webofnithin@gmail.com>
Co-authored-by: Nithin David Thomas <1277421+nithindavid@users.noreply.github.com>
2021-06-18 20:12:43 +05:30
6c49e58ff8 feat: Add contact label filter (#2454)
Co-authored-by: Sojan Jose <sojan@pepalo.com>
2021-06-18 20:08:58 +05:30
Sojan JoseandGitHub 50e4bb3e63 chore: Add CSAT survey toggle attribute to Inboxes (#2455)
ref: #1890
2021-06-18 15:55:48 +05:30
Nithin David ThomasandGitHub 6cdefb19a2 chore: Add multiselect support in stories (#2427) 2021-06-18 14:29:43 +05:30
Sojan 0a96ded5bd Merge branch 'release/1.17.1' 2021-06-16 00:48:15 +05:30
Sojan 6b997c6804 Bump version to 1.17.1 2021-06-16 00:47:21 +05:30
Sojan JoseandGitHub af8223545e chore: Swagger documentation for public APIs (#2417) 2021-06-16 00:14:09 +05:30
Pranav Raj SandGitHub 2b9c93a1b3 chore: Disallow unread/campaign view if widget is active (#2432) 2021-06-15 11:35:31 -07:00
Sojan 2d2382a3f8 Merge branch 'release/1.17.0' 2021-06-15 23:21:02 +05:30
Sojan a0331b40d4 Bump version to 1.17.0 2021-06-15 23:20:28 +05:30
7a7f6234b1 chore: Enable Polish (pl), update translations (#2403)
Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
2021-06-15 22:15:18 +05:30
Muhsin KelothandGitHub e41957ae02 fix: Campaign message is hiding the conversation view after opening the widget (#2428)
* Fix the widget triggered- campain collision

* fix spelling mistakes
2021-06-15 08:53:16 -07:00
fb2f3ff89f feat: Create campaign conversation only if user interacts with the bubble (#2335)
Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
2021-06-15 20:09:42 +05:30
Sojan JoseandGitHub 853db60f8e feat: Add Public APIs for API Channel (#2375) 2021-06-15 20:09:17 +05:30
0f377da109 feat: Add an option to delete campaigns (#2402)
Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
2021-06-15 14:14:59 +05:30
Nithin David ThomasandGitHub a56aa5656d chore: Fixes contact panel scroll issue (#2424) 2021-06-14 16:48:33 +05:30
Nithin David ThomasandGitHub fe3f1c3a40 Chore: Update style for new message buton (#2423)
- Changes button style for new message and edit contact in contact page side panel
2021-06-14 10:37:01 +05:30
d21c1c773b feat: Ability to add label for contact page (#2350)
* feat: Ability to add label for contact page

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
Co-authored-by: Nithin David Thomas <webofnithin@gmail.com>
2021-06-14 10:36:00 +05:30
fe2af370e0 Feat: Show notes panel on crm page (#2320)
* Feat: Show notes panel on CRM page

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
2021-06-14 09:49:17 +05:30
Nithin David ThomasandGitHub 75d05e55ae Chore: Refactors sidebar styles (#2416)
* Chore: Refactors sidebar styles

* Removes unused i18n
2021-06-11 17:11:13 +05:30
Sivin VargheseandGitHub e9fa9e5eff feat: Add assign to me button next to assign agents. (#2414) 2021-06-11 15:38:46 +05:30
534acfbf96 chore: Ensure privilege validations for API endpoints (#2224)
Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
2021-06-11 11:44:31 +05:30
Muhsin KelothandGitHub 5a95c74bf6 fix: Add missing translation in inbox settings page (#2411) 2021-06-10 03:41:55 -07:00
Muhsin KelothandGitHub b9e40d1452 feat: Add an option to enable/disable email collect box (#2399)
* add email collect enabled migration

* migrations

* expose enable_email_collect field

* add select for email collect

* add enable_email condition on new conversation

* add default value true for enable_email_collect

* add specs for email collect enabled

* rereun migration

* code cleanup

* update token life span to 2 months

* revert uuid column
2021-06-10 02:34:03 -07:00
Sivin VargheseandGitHub 8ca63f0b79 chore: Cleanup page-top-bar style conflicts (#2407)
* chore: Class name cleanup page-top-bar.

* prettier spacing fixes

* Review fixes and adds storybook

* Circleci fixes
2021-06-10 09:35:15 +05:30
Muhsin KelothandGitHub 174886671d feat: Add the ability to edit inbox name (#2406) 2021-06-09 21:26:23 +05:30
Nithin David ThomasandGitHub 77d4f86ec9 chore: Clean up contact note styles (#2404) 2021-06-09 18:25:05 +05:30
f79e489a0a chore: Use button component in ReplyBox
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
2021-06-09 14:01:43 +05:30
8a0afb912c chore: Add validation error for label create/edit modal (#2381)
Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
2021-06-09 13:57:42 +05:30
1bf7227843 chore: Fix emails being sent with the wrong translations (#2236)
Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
2021-06-08 22:45:01 +05:30
67ce6f5704 feat: Add regex validation on form message inputs (#2317)
Co-authored-by: Nithin David Thomas <webofnithin@gmail.com>
Co-authored-by: Sojan <sojan@pepalo.com>
2021-06-08 16:32:01 +05:30
0a087c95fd chore: Message API improvements (#2396)
Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
2021-06-08 01:11:06 +05:30
Pranav Raj SandGitHub 1ebab21cfa chore: Configure API channel name and thumbnail (#2398) 2021-06-08 00:22:36 +05:30
9e56565a30 chore: Add status param in conversation APIs (#2394)
Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
2021-06-07 20:04:31 +05:30
467b45b427 feat: Improved password security policy (#2345)
Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
2021-06-07 17:26:08 +05:30
Sojan JoseandGitHub d1b3c7b0c2 chore: Update Facebook Messenger gem (#2342) 2021-06-07 13:58:01 +05:30
Sojan JoseandGitHub ae3cbf4f79 chore: Expose contact identifier in API (#2391) 2021-06-07 11:46:47 +05:30
Pranav Raj SandGitHub 1155b49e42 fix: Add swagger definition for platform_user_id (#2385) 2021-06-06 21:18:47 +05:30
94a83ea995 feat: Add an option to edit webhook URL (#2316)
Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
2021-06-06 16:59:47 +05:30
Muhsin KelothandGitHub 14b51e108a feat: Add Integration hooks UI (#2301) 2021-06-06 16:59:05 +05:30
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
c6487877bf chore: Bump ws from 6.2.1 to 6.2.2 (#2382)
Bumps [ws](https://github.com/websockets/ws) from 6.2.1 to 6.2.2.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/commits)

---
updated-dependencies:
- dependency-name: ws
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-05 18:58:59 +05:30
c028f30f82 chore: Add a different color for messages from bot (#2374)
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
2021-06-04 21:54:06 +05:30
Muhsin KelothandGitHub 8a283a7783 feat: Ask to confirm with name before inbox/team deletion (#2370) 2021-06-04 13:17:44 +05:30
Nithin David ThomasandGitHub 562a65a70d chore: Cleanup unused components (#2379) 2021-06-04 12:54:16 +05:30
Nithin David ThomasandGitHub 58846e7ad8 chore: Add new color shade + tint for violet (#2373) 2021-06-03 23:21:39 +05:30
f731002b70 fix: Retain focus after sending message in rich content editor (#2372)
Co-authored-by: Nithin David Thomas <webofnithin@gmail.com>
2021-06-03 23:20:30 +05:30
b1228f805e feat: Creates event timeline card component for CRM (#2291)
* feat: Creates event timeline card component for CRM


Co-authored-by: Nithin David Thomas <webofnithin@gmail.com>
2021-06-02 23:09:43 +05:30
9a9bcf8153 feat: Create add reminder component for CRM (#2264)
* feat: Creates add reminder component for CRM

Co-authored-by: Nithin David Thomas <webofnithin@gmail.com>
2021-06-02 22:53:13 +05:30
2fca87e3e7 Feat: Creates reminder list component for CRM (#2258)
* Feat: Creates reminder list component for CRM


Co-authored-by: Nithin David Thomas <webofnithin@gmail.com>
2021-06-02 22:48:09 +05:30
Pranav Raj SandGitHub 75fe851345 fix: Update the translation key for Custom Attributes (#2364) 2021-06-02 21:29:53 +05:30
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>Pranav Raj S
b8aaf6c90d chore: Bump dns-packet from 1.3.1 to 1.3.4 (#2346)
Bumps [dns-packet](https://github.com/mafintosh/dns-packet) from 1.3.1 to 1.3.4.
- [Release notes](https://github.com/mafintosh/dns-packet/releases)
- [Changelog](https://github.com/mafintosh/dns-packet/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mafintosh/dns-packet/compare/v1.3.1...v1.3.4)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
2021-06-02 21:21:20 +05:30
49be9e21c3 chore: Universal Linking for Android (#2324)
* android assetlinks.json config

* try renaming html to json

* update env example

* chore: fix styling

* chore: styling part 2

* test: add spec for android assetlinks

* minor fixes

* minor fixes

* remove android sha256 value from env example

* add default sh256 value

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Co-authored-by: Sojan Jose <sojan@pepalo.com>
2021-06-02 08:46:45 -07:00
b0b4d9d6f5 enhancement: Current password confirmation in change password (#2108)
* add current password field in change password form

* locale changes

* chore: update password API

* chore: rubocop fixes

* replace currentPassword with current_password

* code cleanup

* replace input with woot-input

* code cleanup

Co-authored-by: Sojan <sojan@pepalo.com>
2021-06-02 05:22:24 -07:00
Nithin David ThomasandGitHub 2c42e70637 feat: Add support for canned response command anywhere on rich text editor (#2356) 2021-06-02 10:20:40 +05:30
Sojan JoseandGitHub 22965be6dc feat: Add AgentBot APIs (#2323) 2021-06-01 22:34:25 +05:30
Sojan JoseandGitHub 9d16e52e33 chore: Add Swagger docs for Platform APIs (#2349) 2021-05-31 10:58:43 +05:30
Sivin VargheseandGitHub cc75a668cb feat: Create section header component (#2296) 2021-05-31 10:07:07 +05:30
Pranav Raj SandGitHub 25ba852b68 chore: Adjust Chatwoot Config to deploy frontend as a separate app (#2347) 2021-05-28 19:21:16 +05:30
Sojan JoseandGitHub 3b39eb3e33 chore: ability to run Chatwoot as API only server (#2344)
- Ability to run chatwoot as an API only server
- Removes action cable testing gem since it is merged to rails 6
2021-05-27 20:07:21 +05:30
Nithin David ThomasandGitHub 9c555e70c9 fix: Uses label component for add label button (#2337)
* fix: Uses label component for add label button

* Use button instead of span
2021-05-27 08:24:29 +05:30
Sojan JoseandGitHub 9ffdb0ec70 chore: Add select field example to WootMessageSeeder (#2338)
Fixes: #2076
2021-05-25 21:41:53 +05:30
Pranav Raj SandGitHub 12c68e8a3c chore: Upgrade @sentry/vue to 6.4.1 (#2332) 2021-05-25 14:10:09 +05:30
Muhsin KelothandGitHub cfdf4a12c8 chore: Add missing frontend specs (#2329)
* complete dshboard api specs

* code cleanup

* add conversation mixin spec

* add isadmin mixin spec

* add agent details component spec

* add notification badge spec

* spec for thumbnail exist in agent details

* fix the deprecation warnings

* add agent details spec

* add account selector specs

* code cleanup

* refactor contact spec

* review fixes

* review fixes

* add shared spec helper

* update api spec helper

* review fixes
2021-05-25 01:30:21 -07:00
Nithin David ThomasandGitHub 652d988993 fix: Fixes email overflow issue in conversation. (#2331) 2021-05-25 12:53:00 +05:30
Muhsin KelothandGitHub 62d19911be chore: Add chatwoot utils package (#2307)
* add chatwoot utils package

* remove color helper

* replace contrasting text color  with chatwoot util
2021-05-24 00:13:03 -07:00
Sojan JoseandGitHub 58dd6d37e0 chore: Update language translations (#2321) 2021-05-21 23:52:38 +05:30
Nithin David ThomasandGitHub ac2c5c93b9 chore: Update webhook delete button styles(#2322)
* Chore: Fixes style issue of webhook button

* Review fixes
2021-05-21 07:19:57 -07:00
26ba8e6ff7 feat: Create custom attributes for a contact from CRM (#2299)
* feat: Creates cutom attributes for a contact from CRM

* Review fixes

* Change inline forms edit icon size

* Review fixes

* Fix validation labels color

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
2021-05-21 19:22:47 +05:30
Sojan JoseandGitHub 64718eb879 fix: Broken Docker production config (#2319)
fixes: #2110
2021-05-21 18:55:42 +05:30
Sojan JoseandGitHub bb1c028926 fix: Slack integration redirect url missing the client ID (#2309)
Fixes: #2304
2021-05-21 00:03:09 +05:30
Nithin David ThomasandGitHub 2a492b4162 Chore: Refactors view note component (#2310) 2021-05-20 23:41:15 +05:30
Nithin David ThomasandGitHub 047617713e Chore: Refactor add note to use foundation card classes (#2308) 2021-05-20 23:08:05 +05:30
Sojan JoseandGitHub 46356d0bf9 docs: Add documentation for integration APIs (#2300) 2021-05-20 20:46:45 +05:30
Nithin David ThomasandGitHub c681e8a01b chore: Refactor button styles (#2259) 2021-05-20 13:51:46 +05:30
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>Sojan Jose
af8e681f2a chore: Bump puma from 4.3.6 to 4.3.8 (#2294)
Bumps [puma](https://github.com/puma/puma) from 4.3.6 to 4.3.8.
- [Release notes](https://github.com/puma/puma/releases)
- [Changelog](https://github.com/puma/puma/blob/master/History.md)
- [Commits](https://github.com/puma/puma/compare/v4.3.6...v4.3.8)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Sojan Jose <sojan@pepalo.com>
2021-05-19 17:14:15 +05:30
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>Sojan Jose
71d1f229f0 chore: Bump nokogiri from 1.11.3 to 1.11.4 (#2293)
Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.11.3 to 1.11.4.
- [Release notes](https://github.com/sparklemotion/nokogiri/releases)
- [Changelog](https://github.com/sparklemotion/nokogiri/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sparklemotion/nokogiri/compare/v1.11.3...v1.11.4)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Sojan Jose <sojan@pepalo.com>
2021-05-19 16:37:05 +05:30
Sojan 090b5f46e8 Merge branch 'release/1.16.2' 2021-05-18 16:05:18 +05:30
Sojan 10c18aa7f1 Bump version to 1.16.2 2021-05-18 16:04:26 +05:30
20a0d381a7 fix: Resolve infinite loop with campaign API call (#2290)
Co-authored-by: Muhsin <muhsinkeramam@gmail.com>
2021-05-18 12:15:23 +05:30
Sojan bc9db36d62 Merge branch 'release/1.16.1' 2021-05-18 00:25:05 +05:30
Sojan b8836aa41e Bump version to 1.16.1 2021-05-18 00:24:18 +05:30
1290e32150 fix: Resolve bot name if sender value is empty (#2283)
Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
2021-05-18 00:22:43 +05:30
Sojan 10f3d321de Merge branch 'release/1.16.0' 2021-05-18 00:08:00 +05:30
Sojan 525718c8c5 Bump version to 1.16.0 2021-05-18 00:06:53 +05:30
cf09c2af4d chore: Update language translations (#2280)
Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
2021-05-17 23:53:19 +05:30
f925bc22e2 chore: Add sender details in Unread message (#2277)
Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
2021-05-17 21:38:59 +05:30
Muhsin KelothandGitHub 610a7c661e chore: Execute campaigns based on matching URL (#2254) 2021-05-17 21:38:35 +05:30
Muhsin KelothandGitHub 18cea3b0ac chore: Add inbox id in get campaigns API (#2278) 2021-05-17 21:38:12 +05:30
Sivin VargheseandGitHub 60177ef418 feat: Update UX for adding label in a conversation (#2243) 2021-05-17 12:00:08 +05:30
Pranav Raj SandGitHub ef2611164d fix: Use reply content if available (#2025)
* fix: Use text_content reply if available
* Use reply instead of full message from html_content
2021-05-17 11:16:50 +05:30
Sojan JoseandGitHub d5215fea93 feat: APIs for Integration Hooks (#2250)
- Introduces JSON Schema validations via JSONSchemer
- Add CRUD APIs for integration hooks
2021-05-17 10:32:59 +05:30
Pranav Raj SandGitHub 4aa35953c4 fix: Upgrade chart.js to 2.9.4, fix grid-line option (#2268) 2021-05-16 16:41:30 +05:30
Nithin David ThomasandGitHub d35e8cfd98 feat: Creates component show all contact info (#2252)
* feat: Adds component to show contact fields
* feat: Creates component show all contact info
2021-05-13 15:40:36 +05:30
73d30b7f69 chore: Refactor sidebar button with woot-button (#2231)
Co-authored-by: Nithin David Thomas <webofnithin@gmail.com>
2021-05-13 15:39:10 +05:30
Pranav Raj SandGitHub 836b317b8a feat: Remove restrictions on API channel webhook_url (#2261) 2021-05-13 15:03:25 +05:30
Nithin David ThomasandGitHub 35f8d01a0c chore: Fix warnings in jest (#2262) 2021-05-13 15:03:04 +05:30
Pranav Raj SandGitHub 0e6cd699e8 feat: Sort contacts via name, email, phone_number, last_activity_at (#1870) 2021-05-13 13:32:19 +05:30
368bab2553 chore: Refactor contact page button with button component (#2234)
* chore: Refactor contact page buttons with woot-button component


Co-authored-by: Nithin David Thomas <webofnithin@gmail.com>
2021-05-13 12:05:24 +05:30
Sojan JoseandGitHub 3639fb1d71 chore: Add documentation for APIs (#2248) 2021-05-11 22:38:46 +05:30
Nithin David ThomasandGitHub 585dd1b005 feat: Create component to show contact fields on CRM (#2251) 2021-05-11 21:17:10 +05:30
Pranav Raj SandGitHub c1a519db43 chore: Add Message API, cleanup conversation_id param (#2249) 2021-05-11 20:20:52 +05:30
Muhsin KelothandGitHub b5ef9f14e1 chore: Add bot in campaign add/edit form (#2246)
* fix empty message condition in campaign table

* add bot in campaign

* locale changes

* review fixes
2021-05-10 21:41:43 +05:30
Nithin David ThomasandGitHub 627a89faf0 feat: Add inline editing support for CRM attributes (#2241) 2021-05-10 20:01:45 +05:30
a216297ce1 feat: Increase the width of the message bubble (#2235)
* feat: Increase the width of the message bubble

Co-authored-by: Nithin David Thomas <webofnithin@gmail.com>
2021-05-10 19:58:44 +05:30
a33617cadd feat: Adds 3 column layout for contact page (#2174)
* Feat: Adds 3 column layout for contact page

* Makes a mixin for 3 column layouts

* Fixes story meta

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
2021-05-10 04:41:08 -07:00
3fc646f330 feat: Add campaigns in web widget (#2227)
* add campaign store(getter, actions and mutations)

* add campaign store module

* add get campaigns api

* add fetch campaign action widget load

* add specs

* code cleanup

* trigger campaig api fixes

* integrate campaign trigger action

* code cleanup

* revert changes

* trigger api fixes

* review fixes

* code beautification

* chore: Fix multiple campaigns being send because of race condition

* chore: rubocop

* chore: Fix specs

* disable campaigns

Co-authored-by: Nithin David Thomas <webofnithin@gmail.com>
Co-authored-by: Sojan <sojan@pepalo.com>
2021-05-10 00:31:00 -07:00
Muhsin KelothandGitHub db31bfcee4 feat: Edit campaigns (#2230)
* add edit campaign component
2021-05-10 12:40:48 +05:30
7f6abdc987 fix: Add plus icon in sidebar items to settings create page (#2141)
* [Issue-#1965] Add changes

* Added newLink prop with default value true to show the new icon

* Fix button alignment

Co-authored-by: Nithin David Thomas <webofnithin@gmail.com>
2021-05-08 19:06:41 +05:30
055008cf03 chore: refactor css styles for forms (#2240)
* chore: refactor css styles for forms

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
2021-05-07 20:02:19 +05:30
899ddcef0d fix: Fixes overlaps for name in conversation header (#1978)
* Fix width of text

* Review fixes

* Use mixins

* user name textoverflow

* Review fixes: Remove changes in text-truncate class

Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
Co-authored-by: Nithin David Thomas <webofnithin@gmail.com>
2021-05-07 18:28:47 +05:30
4c47d6fd97 fix: Upgrade rails to fix CVE-2021-22904 (#2233)
* fix: Upgrade rails to fix CVE-2021-22904
* chore: Update administrate

Co-authored-by: Sojan <sojan@pepalo.com>
2021-05-07 00:59:22 +05:30
Pranav Raj SandGitHub df04450ab9 fix: Rename migration for Campaigns (#2232) 2021-05-07 00:56:32 +05:30
2839454bc0 feat: Creates label dropdown component (#2220)
* Feat: Creates label dropdown component

* fixes conflicts in i18n

Co-authored-by: Nithin David Thomas <webofnithin@gmail.com>
2021-05-06 20:01:26 +05:30
Sivin VargheseandGitHub 5c80d0ac42 feat: Creates add button component for label (#2223) 2021-05-06 19:33:36 +05:30
Muhsin KelothandGitHub 6245a10a70 feat: Campaign table (#2212)
* code cleanup

* add campaign table

* update locale texts

* locale text cleanup

* Rename selectedAgent with selectedSender in add campaign form

* code cleanup

* remove timer mixin

* update avatar size to 20px

* add border for table

* add campaigns get action specs

* rename campaign table component

* fix style issues

* update sender list based on inbox permission

* style fixes

* review fixes

* add campaign sender component

* replace wootsubmit button with wootbutton

* update scroll width

* replace campaign status with woot label

* changes as per review

* style fixes

* remove unused code

* disable campaign in inbox settings page

* review fixes
2021-05-05 22:16:59 -07:00
Nithin David ThomasandGitHub 381c358ffd chore: Add color variants to label component (#2226)
* Chore: Adds color variants to label

* Move component to UI folder
2021-05-05 22:53:23 +05:30
b30ecb27a6 feat: Add APIs for Dialogflow integration V1 (#2155)
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
2021-05-05 21:06:11 +05:30
Pranav Raj SandGitHub fd0c26cdae chore: Add API documentation for teams (#2221)
Add API documentation for teams
2021-05-05 20:39:00 +05:30
Sivin VargheseandGitHub 6469acc750 Enhancement: Adds story for label component (#2222)
* Enhancement: Adds story for label component
2021-05-05 16:46:23 +05:30
05310e25d4 feat: Creates notes list component for CRM (#2195)
* Feat: Creates note list component for CRM

Co-authored-by: Nithin David Thomas <webofnithin@gmail.com>
2021-05-05 13:36:45 +05:30
262d153c18 feat: Add a component to display contact attributes in CRM (#2217)
Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
2021-05-05 12:09:34 +05:30
Nithin David ThomasandGitHub fd67a5795a Chore: Removes unused impports in ContactIntro (#2218) 2021-05-05 00:16:02 +05:30
Muhsin KelothandGitHub 2d4acaf8fe change button component name (#2216) 2021-05-04 21:48:20 +05:30
35f0fba51d fix: Add validation on the message length in Vue (#2209)
Co-authored-by: Nithin David Thomas <webofnithin@gmail.com>
2021-05-04 19:31:00 +05:30
8459d09b6a feat: Add a component to show contact information (#2204)
* Feat: Add component to show contact introduction.

* fix: Hide sperator if company is not present

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
2021-05-04 19:28:18 +05:30
Nithin David ThomasandGitHub 8bcac2aab0 fix: Truncates overlapping name in contact table (#2208) 2021-05-04 15:21:34 +05:30
Muhsin KelothandGitHub 7c7f91e70f feat: Add campaign (#2177) 2021-05-04 15:08:41 +05:30
Pranav Raj SandGitHub eaa61c3745 fix: Use full width for branding (#2203) 2021-05-03 21:59:57 +05:30
Sojan JoseandGitHub 98e2a9b8b5 feat: Builder for creating Campaign conversations (#2192)
- Builder for creating Campaign conversations
- Widget endpoint to fetch the campaigns
2021-05-03 20:23:09 +05:30
Muhsin KelothandGitHub b649516fbb fix: Change storybook relative path (#2199)
* fix the storybook realtive path issue

* move stories to realtive to compoent
2021-05-02 14:40:46 +05:30
Sivin VargheseandGitHub 6a7b98b345 Feat: Creates add note component for CRM (#2187)
* Feat: Creates add note component for CRM
2021-05-01 19:26:17 +05:30
Pranav Raj SandGitHub 49c7bf5ae4 fix: Set babel/preset-env to 7.13.15 (#2191) 2021-04-30 20:54:29 +05:30
Sojan JoseandGitHub a07200bedf chore: Campaign ID migration for existing accounts (#2189)
* chore: Campaign ID migration for existing accounts

* chore: update factory

* chore: minor fixes

* chore: fixes
2021-04-30 06:15:24 -07:00
Nithin David ThomasandGitHub bd7e9d2790 chore: Add i18n and components into storybook (#2188)
* chore: Add i18n and store into storybook

* Use empty store to init storybook
2021-04-30 15:24:00 +05:30
b6f57d7fc1 feat: Creates note component for CRM (#2181)
* Feat: Creates note component for CRM


Co-authored-by: Nithin David <webofnithin@gmail.com>
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
2021-04-30 12:02:20 +05:30
Sojan JoseandGitHub b89cc9cf57 feat: Add APIs for Campaigns (#2175) 2021-04-29 22:23:32 +05:30
Nithin David ThomasandGitHub 3afc9b5f5b chore: Setup storybook for Vue components (#2139) 2021-04-29 22:12:06 +05:30
Pranav Raj SandGitHub 979d4fe76b chore: Upgrade rexml to 3.2.5 to fix CVE-2021-28965 (#2179) 2021-04-29 17:23:25 +05:30
4b49d21543 chore: Add event for conversations transferred to Bot (#2167)
* chore:  Event for conversation transferred to Bot

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Co-authored-by: Nithin David <webofnithin@gmail.com>
2021-04-28 20:53:23 +05:30
aa47bad4c1 chore: Add campaign empty page (#2173)
* Enhancement: Create route for contact details page

* add campaign page, add button, campaign description

* add locale texts

* update locale texts

* update grid

Co-authored-by: Nithin David <webofnithin@gmail.com>
2021-04-28 06:27:47 -07:00
6ea1e0d047 chore: Add route for contact details page (#2168)
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
2021-04-27 21:31:11 -07:00
8ea35802b4 fix: Handle email with no subject line and no body (#2164)
* fix: Add a default email subject line if no subject is present

* Add a default on the frontend

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
2021-04-27 10:51:04 -07:00
Sivin VargheseandGitHub c10efd2c7b chore: inbox member clean up (#2144)
Delete unused logic and tests
Co-authored-by: Nithin David Thomas <webofnithin@gmail.com>
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
2021-04-27 20:59:00 +05:30
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
f1f4a35b2b chore: Bump oauth from 0.5.4 to 0.5.6 (#2151)
Bumps [oauth](https://github.com/oauth-xx/oauth-ruby) from 0.5.4 to 0.5.6.
- [Release notes](https://github.com/oauth-xx/oauth-ruby/releases)
- [Changelog](https://github.com/oauth-xx/oauth-ruby/blob/master/HISTORY)
- [Commits](https://github.com/oauth-xx/oauth-ruby/compare/v0.5.4...v0.5.6)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-27 18:19:09 +05:30
Pranav Raj SandGitHub 0bb10cff2d chore: Add font-display: swap to Ionicons (#2161) 2021-04-26 10:39:33 -07:00
Niklas HaugandGitHub 78996937ce fixed object destructuring to fallback to {} when meta.team is null (#2158) 2021-04-26 15:30:26 +05:30
285f7bbeb5 Enhancement: Ability to assign administrators as conversation assignees (#2142)
* Enhancement: Ability to assign administrators as conversation assignee

Co-authored-by: Nithin David Thomas <webofnithin@gmail.com>
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
2021-04-22 23:17:29 +05:30
Niklas HaugandGitHub 87078e1abf fix: Clear search input which navigating to other tab/page (#2061) 2021-04-22 13:58:04 +05:30
Sivin VargheseandGitHub 6c7c5064d1 Fix: Broken empty state for agents dropdown. (#2143) 2021-04-21 14:24:03 +05:30
fdbc32f7c2 enhancement: Show agent availability status in conversation assignee select list (#2122)
* show agent status on conversation assignation

* add agent status in contact sidebar

* availability status badge component

* review fixes

* review fixes

* chore: Fixes issue with status badge (#2136)

* add empty case in agent list

* Chore: Availability badge shape issue. #2140

Co-authored-by: Nithin David <webofnithin@gmail.com>
Co-authored-by: Sojan <sojan@pepalo.com>
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
2021-04-21 12:18:48 +05:30
1517 changed files with 54788 additions and 10252 deletions
+1 -1
View File
@@ -7,7 +7,7 @@ defaults: &defaults
working_directory: ~/build
docker:
# specify the version you desire here
- image: circleci/ruby:2.7.2-node-browsers
- image: circleci/ruby:3.0.2-node-browsers
# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images
+2
View File
@@ -30,3 +30,5 @@ exclude_patterns:
- "**/*.md"
- "**/*.yml"
- "app/javascript/dashboard/i18n/locale"
- "**/*.stories.js"
- "stories/"
+4 -47
View File
@@ -1,51 +1,8 @@
# pre-build stage
ARG VARIANT=2.7
FROM mcr.microsoft.com/vscode/devcontainers/ruby:${VARIANT}
# Update args in docker-compose.yaml to set the UID/GID of the "vscode" user.
ARG USER_UID=1000
ARG USER_GID=$USER_UID
RUN if [ "$USER_GID" != "1000" ] || [ "$USER_UID" != "1000" ]; then \
groupmod --gid $USER_GID vscode \
&& usermod --uid $USER_UID --gid $USER_GID vscode \
&& chmod -R $USER_UID:$USER_GID /home/vscode; \
fi
# [Option] Install Node.js
ARG INSTALL_NODE="true"
ARG NODE_VERSION="lts/*"
RUN if [ "${INSTALL_NODE}" = "true" ]; then su vscode -c "source /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi
# tmux is for overmind
# TODO : install foreman in future
# packages: postgresql-server-dev-all
# may be postgres in same machine
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
&& apt-get -y install --no-install-recommends \
libssl-dev \
tar \
tzdata \
postgresql-client \
yarn \
git \
imagemagick \
tmux \
zsh
# [Optional] Uncomment this line to install global node packages.
# RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g <your-package-here>" 2>&1
# The below image is created out of the Dockerfile.base
# It has the dependencies already installed so that codespace will boot up fast
FROM ghcr.io/chatwoot/chatwoot_codespace:latest
# Do the set up required for chatwoot app
WORKDIR /workspace
COPY . /workspace
# TODO: figure out installing rvm
# RUN rvm install
COPY Gemfile Gemfile.lock ./
RUN gem install bundler
RUN bundle install
COPY package.json yarn.lock ./
RUN yarn install
RUN yarn && gem install bundler && bundle install
+45
View File
@@ -0,0 +1,45 @@
# pre-build stage
ARG VARIANT=3
FROM mcr.microsoft.com/vscode/devcontainers/ruby:${VARIANT}
# Update args in docker-compose.yaml to set the UID/GID of the "vscode" user.
ARG USER_UID=1000
ARG USER_GID=$USER_UID
RUN if [ "$USER_GID" != "1000" ] || [ "$USER_UID" != "1000" ]; then \
groupmod --gid $USER_GID vscode \
&& usermod --uid $USER_UID --gid $USER_GID vscode \
&& chmod -R $USER_UID:$USER_GID /home/vscode; \
fi
# [Option] Install Node.js
ARG INSTALL_NODE="true"
ARG NODE_VERSION="lts/*"
RUN if [ "${INSTALL_NODE}" = "true" ]; then su vscode -c "source /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
&& apt-get -y install --no-install-recommends \
libssl-dev \
tar \
tzdata \
postgresql-client \
yarn \
git \
imagemagick \
tmux \
zsh
# Install overmind
RUN curl -L https://github.com/DarthSim/overmind/releases/download/v2.1.0/overmind-v2.1.0-linux-amd64.gz > overmind.gz \
&& gunzip overmind.gz \
&& sudo mv overmind /usr/local/bin \
&& chmod +x /usr/local/bin/overmind
# Do the set up required for chatwoot app
WORKDIR /workspace
COPY . /workspace
RUN yarn
COPY Gemfile Gemfile.lock ./
RUN gem install bundler && bundle install
+15 -9
View File
@@ -12,22 +12,28 @@
"extensions": [
"rebornix.Ruby",
"misogi.ruby-rubocop",
"wingrunr21.vscode-ruby"
"wingrunr21.vscode-ruby",
"davidpallinder.rails-test-runner",
"eamodio.gitlens",
"github.copilot",
"mrmlnc.vscode-duplicate"
],
// TODO: figure whether we can get all this ports work properly
// 3000 rails
// 3035 webpacker
// 5432 postgres
// 6379 redis
// 1025,8025 mailhog
"forwardPorts": [5432, 6379, 1025, 8025],
"forwardPorts": [8025],
//your application may need to listen on all interfaces (0.0.0.0) not just localhost for it to be available externally. Defaults to []
"appPort": [3000, 3035],
// Use 'postCreateCommand' to run commands after the container is created.
// #TODO: can we move logic of copy env file into dockerfile ?
"postCreateCommand": "cp .env.example .env",
"postCreateCommand": ".devcontainer/scripts/setup.sh && bundle exec rake db:chatwoot_prepare && yarn",
"portsAttributes": {
"3000": {
"label": "Rails Server"
},
"8025": {
"label": "Mailhog UI"
}
},
}
+2 -2
View File
@@ -10,8 +10,8 @@ services:
context: ..
dockerfile: .devcontainer/Dockerfile
args:
# Update 'VARIANT' to pick a Ruby version: 2, 2.7, 2.6, 2.5
VARIANT: 2.7
# Update 'VARIANT' to pick a Ruby version: https://github.com/microsoft/vscode-dev-containers/tree/main/containers/ruby
VARIANT: 3
# [Choice] Install Node.js
INSTALL_NODE: "true"
NODE_VERSION: "lts/*"
+8
View File
@@ -0,0 +1,8 @@
cp .env.example .env
sed -i -e '/REDIS_URL/ s/=.*/=redis:\/\/localhost:6379/' .env
sed -i -e '/POSTGRES_HOST/ s/=.*/=localhost/' .env
sed -i -e '/SMTP_ADDRESS/ s/=.*/=localhost/' .env
sed -i -e "/FRONTEND_URL/ s/=.*/=https:\/\/$CODESPACE_NAME-3000.githubpreview.dev/" .env
sed -i -e "/WEBPACKER_DEV_SERVER_PUBLIC/ s/=.*/=https:\/\/$CODESPACE_NAME-3035.githubpreview.dev/" .env
# uncomment the webpacker env variable
sed -i -e '/WEBPACKER_DEV_SERVER_PUBLIC/s/^# //' .env
+19
View File
@@ -116,6 +116,10 @@ SLACK_CLIENT_SECRET=
### Change this env variable only if you are using a custom build mobile app
## Mobile app env variables
IOS_APP_ID=6C953F3RX2.com.chatwoot.app
ANDROID_BUNDLE_ID=com.chatwoot.app
# https://developers.google.com/android/guides/client-auth (use keytool to print the fingerprint in the first section)
ANDROID_SHA256_CERT_FINGERPRINT=AC:73:8E:DE:EB:56:EA:CC:10:87:02:A7:65:37:7B:38:D4:5D:D4:53:F8:3B:FB:D3:C6:28:64:1D:AA:08:1E:D8
### Smart App Banner
@@ -143,6 +147,21 @@ USE_INBOX_AVATAR_FOR_BOT=true
# maxmindb api key to use geoip2 service
# IP_LOOKUP_API_KEY=
## Rack Attack configuration
## To prevent and throttle abusive requests
# ENABLE_RACK_ATTACK=false
## Running chatwoot as an API only server
## setting this value to true will disable the frontend dashboard endpoints
# CW_API_ONLY_SERVER=false
## Development Only Config
# if you want to use letter_opener for local emails
# LETTER_OPENER=true
# meant to be used in github codespaces
# WEBPACKER_DEV_SERVER_PUBLIC=
# If you want to use official mobile app,
# the notifications would be relayed via a Chatwoot server
ENABLE_PUSH_RELAY_SERVER=true
@@ -0,0 +1,23 @@
name: Publish Codespace Base Image
on:
workflow_dispatch:
jobs:
publish-code-space-image:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Login to GitHub Container Registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build the Codespace Base Image
run: |
docker build . -t ghcr.io/chatwoot/chatwoot_codespace:latest -f .devcontainer/Dockerfile.base
docker push ghcr.io/chatwoot/chatwoot_codespace:latest
+1 -1
View File
@@ -1 +1 @@
12.16.1
14.17.4
+2 -1
View File
@@ -1,5 +1,6 @@
{
"printWidth": 80,
"singleQuote": true,
"trailingComma": "es5"
"trailingComma": "es5",
"arrowParens": "avoid"
}
+41 -5
View File
@@ -13,6 +13,7 @@ Metrics/ClassLength:
- 'app/models/conversation.rb'
- 'app/mailers/conversation_reply_mailer.rb'
- 'app/models/message.rb'
- 'app/builders/messages/facebook/message_builder.rb'
RSpec/ExampleLength:
Max: 25
Style/Documentation:
@@ -24,12 +25,13 @@ Style/FrozenStringLiteralComment:
Style/SymbolArray:
Enabled: false
Style/OptionalBooleanParameter:
Exclude:
Exclude:
- 'app/services/email_templates/db_resolver_service.rb'
- 'app/dispatchers/dispatcher.rb'
Style/GlobalVars:
Exclude:
- 'config/initializers/redis.rb'
- 'config/initializers/01_redis.rb'
- 'config/initializers/rack_attack.rb'
- 'lib/redis/alfred.rb'
- 'lib/global_config.rb'
Style/ClassVars:
@@ -38,12 +40,23 @@ Style/ClassVars:
Lint/MissingSuper:
Exclude:
- 'app/drops/base_drop.rb'
Lint/SymbolConversion:
Enabled: false
Lint/EmptyBlock:
Exclude:
- 'app/views/api/v1/accounts/conversations/toggle_status.json.jbuilder'
Lint/OrAssignmentToConstant:
Exclude:
- 'lib/redis/config.rb'
Metrics/BlockLength:
Exclude:
- spec/**/*
- '**/routes.rb'
- 'config/environments/*'
- db/schema.rb
Metrics/ModuleLength:
Exclude:
- lib/woot_message_seeder.rb
Rails/ApplicationController:
Exclude:
- 'app/controllers/api/v1/widget/messages_controller.rb'
@@ -51,6 +64,12 @@ Rails/ApplicationController:
- 'app/controllers/widget_tests_controller.rb'
- 'app/controllers/widgets_controller.rb'
- 'app/controllers/platform_controller.rb'
- 'app/controllers/public_controller.rb'
- 'app/controllers/survey/responses_controller.rb'
Rails/EnvironmentVariableAccess:
Enabled: false
Rails/TimeZoneAssignment:
Enabled: false
Style/ClassAndModuleChildren:
EnforcedStyle: compact
Exclude:
@@ -60,6 +79,10 @@ RSpec/NestedGroups:
Max: 4
RSpec/MessageSpies:
Enabled: false
RSpec/StubbedMock:
Enabled: false
Naming/VariableNumber:
Enabled: false
Metrics/MethodLength:
Exclude:
- 'db/migrate/20161123131628_devise_token_auth_create_users.rb'
@@ -73,7 +96,7 @@ Style/GuardClause:
- 'app/models/message.rb'
- 'db/migrate/20190819005836_add_missing_indexes_on_taggings.acts_as_taggable_on_engine.rb'
Metrics/AbcSize:
Exclude:
Exclude:
- 'app/controllers/concerns/auth_helper.rb'
- 'db/migrate/20190819005836_add_missing_indexes_on_taggings.acts_as_taggable_on_engine.rb'
- 'db/migrate/20161123131628_devise_token_auth_create_users.rb'
@@ -101,19 +124,25 @@ Rails/BulkChangeTable:
- 'db/migrate/20170511134418_latlong.rb'
- 'db/migrate/20191027054756_create_contact_inboxes.rb'
- 'db/migrate/20191130164019_add_template_type_to_messages.rb'
Rails/UniqueValidationWithoutIndex:
- 'db/migrate/20210425093724_convert_integration_hook_settings_field.rb'
Rails/UniqueValidationWithoutIndex:
Exclude:
- 'app/models/channel/twitter_profile.rb'
- 'app/models/webhook.rb'
- 'app/models/contact.rb'
- 'app/models/integrations/hook.rb'
Rails/RenderInline:
Exclude:
- 'app/controllers/swagger_controller.rb'
Performance/CollectionLiteralInLoop:
Exclude:
- 'db/migrate/20210315101919_enable_email_channel.rb'
RSpec/NamedSubject:
Enabled: false
# we should bring this down
RSpec/MultipleMemoizedHelpers:
Max: 12
AllCops:
NewCops: enable
Exclude:
@@ -128,4 +157,11 @@ AllCops:
- 'tmp/**/*'
- 'storage/**/*'
- 'db/migrate/20200225162150_init_schema.rb'
- 'config/initializers/azure_storage_service_patch.rb'
- 'db/migrate/20210611180222_create_active_storage_variant_records.active_storage.rb'
- 'db/migrate/20210611180221_add_service_name_to_active_storage_blobs.active_storage.rb'
- db/migrate/20200309213132_add_account_id_to_agent_bot_inboxes.rb
- db/migrate/20200331095710_add_identifier_to_contact.rb
- db/migrate/20200429082655_add_medium_to_twilio_sms.rb
- db/migrate/20200503151130_add_account_feature_flag.rb
- db/migrate/20200927135222_add_last_activity_at_to_conversation.rb
- db/migrate/20210306170117_add_last_activity_at_to_contacts.rb
+1 -1
View File
@@ -1 +1 @@
2.7.2
3.0.2
+44
View File
@@ -0,0 +1,44 @@
const path = require('path');
const resolve = require('../config/webpack/resolve');
// Chatwoot's webpack.config.js
process.env.NODE_ENV = 'development';
const custom = require('../config/webpack/environment');
module.exports = {
stories: [
'../stories/**/*.stories.mdx',
'../app/javascript/**/*.stories.@(js|jsx|ts|tsx)',
],
addons: [
{
name: '@storybook/addon-docs',
options: {
vueDocgenOptions: {
alias: {
'@': path.resolve(__dirname, '../'),
},
},
},
},
'@storybook/addon-links',
'@storybook/addon-essentials',
],
webpackFinal: config => {
const newConfig = {
...config,
resolve: {
...config.resolve,
modules: custom.resolvedModules.map(i => i.value),
},
};
newConfig.module.rules.push({
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
include: path.resolve(__dirname, '../app/javascript'),
});
return newConfig;
},
};
+42
View File
@@ -0,0 +1,42 @@
import { addDecorator } from '@storybook/vue';
import Vue from 'vue';
import Vuex from 'vuex';
import VueI18n from 'vue-i18n';
import Vuelidate from 'vuelidate';
import Multiselect from 'vue-multiselect';
import WootUiKit from '../app/javascript/dashboard/components';
import i18n from '../app/javascript/dashboard/i18n';
import '../app/javascript/dashboard/assets/scss/storybook.scss';
Vue.use(VueI18n);
Vue.use(Vuelidate);
Vue.use(WootUiKit);
Vue.use(Vuex);
Vue.component('multiselect', Multiselect);
const store = new Vuex.Store({});
const i18nConfig = new VueI18n({
locale: 'en',
messages: i18n,
});
addDecorator(() => ({
template: '<story/>',
i18n: i18nConfig,
store,
beforeCreate: function() {
this.$root._i18n = this.$i18n;
},
}));
export const parameters = {
actions: { argTypesRegex: '^on[A-Z].*' },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
};
+23 -11
View File
@@ -1,6 +1,6 @@
source 'https://rubygems.org'
ruby '2.7.2'
ruby '3.0.2'
##-- base gems for rails --##
gem 'rack-cors', require: 'rack/cors'
@@ -31,19 +31,24 @@ gem 'haikunator'
gem 'liquid'
# Parse Markdown to HTML
gem 'commonmarker'
# Validate Data against JSON Schema
gem 'json_schemer'
# Rack middleware for blocking & throttling abusive requests
gem 'rack-attack'
# a utility tool for streaming, flexible and safe downloading of remote files
gem 'down', '~> 5.0'
##-- for active storage --##
gem 'aws-sdk-s3', require: false
gem 'azure-storage-blob', require: false
gem 'google-cloud-storage', require: false
gem 'mini_magick'
gem 'image_processing'
##-- gems for database --#
gem 'groupdate'
gem 'pg'
gem 'redis'
gem 'redis-namespace'
gem 'redis-rack-cache'
# super fast record imports in bulk
gem 'activerecord-import'
@@ -51,12 +56,14 @@ gem 'activerecord-import'
gem 'dotenv-rails'
gem 'foreman'
gem 'puma'
gem 'rack-timeout'
gem 'webpacker', '~> 5.x'
# metrics on heroku
gem 'barnes'
##--- gems for authentication & authorization ---##
gem 'devise'
gem 'devise-secure_password', '~> 2.0'
gem 'devise_token_auth'
# authorization
gem 'jwt'
@@ -70,7 +77,7 @@ gem 'wisper', '2.0.0'
##--- gems for channels ---##
# TODO: bump up gem to 2.0
gem 'facebook-messenger', '1.5.0'
gem 'facebook-messenger'
gem 'telegram-bot-ruby'
gem 'twilio-ruby', '~> 5.32.0'
# twitty will handle subscription of twitter account events
@@ -80,12 +87,17 @@ gem 'twitty'
gem 'koala'
# slack client
gem 'slack-ruby-client'
# for dialogflow integrations
gem 'google-cloud-dialogflow'
##--- gems for debugging and error reporting ---##
# static analysis
gem 'brakeman'
gem 'ddtrace'
gem 'scout_apm'
gem 'sentry-raven'
gem 'sentry-rails'
gem 'sentry-ruby'
gem 'sentry-sidekiq'
##-- background job processing --##
gem 'sidekiq'
@@ -105,6 +117,8 @@ gem 'maxminddb'
# to create db triggers
gem 'hairtrigger'
gem 'procore-sift'
group :development do
gem 'annotate'
gem 'bullet'
@@ -112,7 +126,7 @@ group :development do
gem 'web-console'
# used in swagger build
gem 'json_refs', git: 'https://github.com/tzmfreedom/json_refs', ref: 'e32deb0'
gem 'json_refs'
# When we want to squash migrations
gem 'squasher'
@@ -126,21 +140,19 @@ group :test do
end
group :development, :test do
# locking until https://github.com/codeclimate/test-reporter/issues/418 is resolved
gem 'action-cable-testing'
gem 'active_record_query_trace'
gem 'bundle-audit', require: false
gem 'byebug', platform: :mri
gem 'factory_bot_rails'
gem 'faker'
gem 'listen'
gem 'mock_redis', git: 'https://github.com/sds/mock_redis', ref: '16d00789f0341a3aac35126c0ffe97a596753ff9'
gem 'mock_redis'
gem 'pry-rails'
gem 'rspec-rails', '~> 4.0.0.beta2'
gem 'rspec-rails', '~> 5.0.0'
gem 'rubocop', require: false
gem 'rubocop-performance', require: false
gem 'rubocop-rails', require: false
gem 'rubocop-rspec', require: false
gem 'scss_lint', require: false
gem 'seed_dump'
gem 'shoulda-matchers'
gem 'simplecov', '0.17.1', require: false
+332 -275
View File
@@ -1,89 +1,76 @@
GIT
remote: https://github.com/sds/mock_redis
revision: 16d00789f0341a3aac35126c0ffe97a596753ff9
ref: 16d00789f0341a3aac35126c0ffe97a596753ff9
specs:
mock_redis (0.22.0)
GIT
remote: https://github.com/tzmfreedom/json_refs
revision: e32deb073ce9aef39bdd63556bffd7fe7c2a803d
ref: e32deb0
specs:
json_refs (0.1.2)
hana
GEM
remote: https://rubygems.org/
specs:
action-cable-testing (0.6.1)
actioncable (>= 5.0)
actioncable (6.0.3.6)
actionpack (= 6.0.3.6)
actioncable (6.1.4)
actionpack (= 6.1.4)
activesupport (= 6.1.4)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
actionmailbox (6.0.3.6)
actionpack (= 6.0.3.6)
activejob (= 6.0.3.6)
activerecord (= 6.0.3.6)
activestorage (= 6.0.3.6)
activesupport (= 6.0.3.6)
actionmailbox (6.1.4)
actionpack (= 6.1.4)
activejob (= 6.1.4)
activerecord (= 6.1.4)
activestorage (= 6.1.4)
activesupport (= 6.1.4)
mail (>= 2.7.1)
actionmailer (6.0.3.6)
actionpack (= 6.0.3.6)
actionview (= 6.0.3.6)
activejob (= 6.0.3.6)
actionmailer (6.1.4)
actionpack (= 6.1.4)
actionview (= 6.1.4)
activejob (= 6.1.4)
activesupport (= 6.1.4)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0)
actionpack (6.0.3.6)
actionview (= 6.0.3.6)
activesupport (= 6.0.3.6)
rack (~> 2.0, >= 2.0.8)
actionpack (6.1.4)
actionview (= 6.1.4)
activesupport (= 6.1.4)
rack (~> 2.0, >= 2.0.9)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0)
actiontext (6.0.3.6)
actionpack (= 6.0.3.6)
activerecord (= 6.0.3.6)
activestorage (= 6.0.3.6)
activesupport (= 6.0.3.6)
actiontext (6.1.4)
actionpack (= 6.1.4)
activerecord (= 6.1.4)
activestorage (= 6.1.4)
activesupport (= 6.1.4)
nokogiri (>= 1.8.5)
actionview (6.0.3.6)
activesupport (= 6.0.3.6)
actionview (6.1.4)
activesupport (= 6.1.4)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0)
activejob (6.0.3.6)
activesupport (= 6.0.3.6)
active_record_query_trace (1.8)
activejob (6.1.4)
activesupport (= 6.1.4)
globalid (>= 0.3.6)
activemodel (6.0.3.6)
activesupport (= 6.0.3.6)
activerecord (6.0.3.6)
activemodel (= 6.0.3.6)
activesupport (= 6.0.3.6)
activerecord-import (1.0.7)
activemodel (6.1.4)
activesupport (= 6.1.4)
activerecord (6.1.4)
activemodel (= 6.1.4)
activesupport (= 6.1.4)
activerecord-import (1.2.0)
activerecord (>= 3.2)
activestorage (6.0.3.6)
actionpack (= 6.0.3.6)
activejob (= 6.0.3.6)
activerecord (= 6.0.3.6)
activestorage (6.1.4)
actionpack (= 6.1.4)
activejob (= 6.1.4)
activerecord (= 6.1.4)
activesupport (= 6.1.4)
marcel (~> 1.0.0)
activesupport (6.0.3.6)
mini_mime (>= 1.1.0)
activesupport (6.1.4)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
tzinfo (~> 1.1)
zeitwerk (~> 2.2, >= 2.2.2)
acts-as-taggable-on (6.5.0)
activerecord (>= 5.0, < 6.1)
addressable (2.7.0)
i18n (>= 1.6, < 2)
minitest (>= 5.1)
tzinfo (~> 2.0)
zeitwerk (~> 2.3)
acts-as-taggable-on (8.1.0)
activerecord (>= 5.0, < 6.2)
addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0)
administrate (0.14.0)
actionpack (>= 4.2)
actionview (>= 4.2)
activerecord (>= 4.2)
autoprefixer-rails (>= 6.0)
administrate (0.16.0)
actionpack (>= 5.0)
actionview (>= 5.0)
activerecord (>= 5.0)
datetime_picker_rails (~> 0.0.7)
jquery-rails (>= 4.0)
kaminari (>= 1.0)
@@ -93,25 +80,23 @@ GEM
annotate (3.1.1)
activerecord (>= 3.2, < 7.0)
rake (>= 10.4, < 14.0)
ast (2.4.1)
ast (2.4.2)
attr_extras (6.2.4)
autoprefixer-rails (9.8.6.3)
execjs
aws-eventstream (1.1.0)
aws-partitions (1.360.0)
aws-sdk-core (3.105.0)
aws-eventstream (1.1.1)
aws-partitions (1.482.0)
aws-sdk-core (3.119.0)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.239.0)
aws-sigv4 (~> 1.1)
jmespath (~> 1.0)
aws-sdk-kms (1.37.0)
aws-sdk-core (~> 3, >= 3.99.0)
aws-sdk-kms (1.46.0)
aws-sdk-core (~> 3, >= 3.119.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.79.1)
aws-sdk-core (~> 3, >= 3.104.3)
aws-sdk-s3 (1.98.0)
aws-sdk-core (~> 3, >= 3.119.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.1)
aws-sigv4 (1.2.2)
aws-sigv4 (1.2.4)
aws-eventstream (~> 1, >= 1.0.2)
axiom-types (0.1.1)
descendants_tracker (~> 0.0.4)
@@ -125,155 +110,208 @@ GEM
faraday_middleware (~> 1.0.0.rc1)
net-http-persistent (~> 4.0)
nokogiri (~> 1.11.0.rc2)
barnes (0.0.8)
barnes (0.0.9)
multi_json (~> 1)
statsd-ruby (~> 1.1)
bcrypt (3.1.15)
bcrypt (3.1.16)
bindex (0.8.1)
bootsnap (1.4.8)
bootsnap (1.7.6)
msgpack (~> 1.0)
brakeman (4.9.0)
brakeman (5.1.1)
browser (5.3.1)
builder (3.2.4)
bullet (6.1.0)
bullet (6.1.4)
activesupport (>= 3.0.0)
uniform_notifier (~> 1.11)
bundle-audit (0.1.0)
bundler-audit
bundler-audit (0.7.0.1)
bundler-audit (0.8.0)
bundler (>= 1.2.0, < 3)
thor (>= 0.18, < 2)
thor (~> 1.0)
byebug (11.1.3)
coderay (1.1.3)
coercible (1.0.0)
descendants_tracker (~> 0.0.1)
commonmarker (0.21.1)
ruby-enum (~> 0.5)
concurrent-ruby (1.1.8)
connection_pool (2.2.3)
crack (0.4.3)
safe_yaml (~> 1.0.0)
commonmarker (0.22.0)
concurrent-ruby (1.1.9)
connection_pool (2.2.5)
crack (0.4.5)
rexml
crass (1.0.6)
cypress-on-rails (1.8.0)
cypress-on-rails (1.10.1)
rack
database_cleaner (1.8.5)
database_cleaner (2.0.1)
database_cleaner-active_record (~> 2.0.0)
database_cleaner-active_record (2.0.1)
activerecord (>= 5.a)
database_cleaner-core (~> 2.0.0)
database_cleaner-core (2.0.1)
datetime_picker_rails (0.0.7)
momentjs-rails (>= 2.8.1)
ddtrace (0.51.1)
ffi (~> 1.0)
msgpack
declarative (0.0.20)
declarative-option (0.1.0)
descendants_tracker (0.0.4)
thread_safe (~> 0.3, >= 0.3.1)
devise (4.7.2)
devise (4.8.0)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 4.1.0)
responders
warden (~> 1.2.3)
devise_token_auth (1.1.4)
devise-secure_password (2.0.1)
devise (>= 4.0.0, < 5.0.0)
railties (>= 5.0.0, < 7.0.0)
devise_token_auth (1.2.0)
bcrypt (~> 3.0)
devise (> 3.5.2, < 5)
rails (>= 4.2.0, < 6.1)
sprockets (= 3.7.2)
rails (>= 4.2.0, < 6.2)
diff-lcs (1.4.4)
digest-crc (0.6.1)
rake (~> 13.0)
docile (1.3.2)
digest-crc (0.6.4)
rake (>= 12.0.0, < 14.0.0)
docile (1.4.0)
domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0)
dotenv (2.7.6)
dotenv-rails (2.7.6)
dotenv (= 2.7.6)
railties (>= 3.2)
equalizer (0.0.11)
down (5.2.3)
addressable (~> 2.8)
dry-inflector (0.2.1)
ecma-re-validator (0.3.0)
regexp_parser (~> 2.0)
erubi (1.10.0)
et-orbi (1.2.4)
tzinfo
execjs (2.7.0)
facebook-messenger (1.5.0)
execjs (2.8.1)
facebook-messenger (2.0.1)
httparty (~> 0.13, >= 0.13.7)
rack (>= 1.4.5)
factory_bot (6.1.0)
factory_bot (6.2.0)
activesupport (>= 5.0.0)
factory_bot_rails (6.1.0)
factory_bot (~> 6.1.0)
factory_bot_rails (6.2.0)
factory_bot (~> 6.2.0)
railties (>= 5.0.0)
faker (2.13.0)
faker (2.18.0)
i18n (>= 1.6, < 2)
faraday (1.0.1)
multipart-post (>= 1.2, < 3)
faraday_middleware (1.0.0)
faraday (~> 1.0)
fcm (1.0.2)
faraday (~> 1.0.0)
ffi (1.14.2)
fcm (1.0.3)
faraday (~> 1)
ffi (1.15.3)
flag_shih_tzu (0.3.23)
foreman (0.87.2)
fugit (1.4.1)
fugit (1.5.0)
et-orbi (~> 1.1, >= 1.1.8)
raabro (~> 1.4)
geocoder (1.6.3)
gli (2.19.2)
globalid (0.4.2)
activesupport (>= 4.2.0)
google-api-client (0.43.0)
addressable (~> 2.5, >= 2.5.1)
gapic-common (0.3.4)
google-protobuf (~> 3.12, >= 3.12.2)
googleapis-common-protos (>= 1.3.9, < 2.0)
googleapis-common-protos-types (>= 1.0.4, < 2.0)
googleauth (~> 0.9)
httpclient (>= 2.8.1, < 3.0)
grpc (~> 1.25)
geocoder (1.6.7)
gli (2.20.1)
globalid (0.5.1)
activesupport (>= 5.0)
google-apis-core (0.4.1)
addressable (~> 2.5, >= 2.5.1)
googleauth (>= 0.16.2, < 2.a)
httpclient (>= 2.8.1, < 3.a)
mini_mime (~> 1.0)
representable (~> 3.0)
retriable (>= 2.0, < 4.0)
signet (~> 0.12)
google-cloud-core (1.5.0)
retriable (>= 2.0, < 4.a)
rexml
webrick
google-apis-iamcredentials_v1 (0.6.0)
google-apis-core (>= 0.4, < 2.a)
google-apis-storage_v1 (0.6.0)
google-apis-core (>= 0.4, < 2.a)
google-cloud-core (1.6.0)
google-cloud-env (~> 1.0)
google-cloud-errors (~> 1.0)
google-cloud-env (1.3.3)
google-cloud-dialogflow (1.2.0)
google-cloud-core (~> 1.5)
google-cloud-dialogflow-v2 (~> 0.1)
google-cloud-dialogflow-v2 (0.6.4)
gapic-common (~> 0.3)
google-cloud-errors (~> 1.0)
google-cloud-env (1.5.0)
faraday (>= 0.17.3, < 2.0)
google-cloud-errors (1.0.1)
google-cloud-storage (1.28.0)
google-cloud-errors (1.1.0)
google-cloud-storage (1.34.1)
addressable (~> 2.5)
digest-crc (~> 0.4)
google-api-client (~> 0.33)
google-cloud-core (~> 1.2)
googleauth (~> 0.9)
google-apis-iamcredentials_v1 (~> 0.1)
google-apis-storage_v1 (~> 0.1)
google-cloud-core (~> 1.6)
googleauth (>= 0.16.2, < 2.a)
mini_mime (~> 1.0)
googleauth (0.13.1)
google-protobuf (3.17.3-universal-darwin)
google-protobuf (3.17.3-x86_64-linux)
googleapis-common-protos (1.3.11)
google-protobuf (~> 3.14)
googleapis-common-protos-types (>= 1.0.6, < 2.0)
grpc (~> 1.27)
googleapis-common-protos-types (1.1.0)
google-protobuf (~> 3.14)
googleauth (0.17.0)
faraday (>= 0.17.3, < 2.0)
jwt (>= 1.4, < 3.0)
memoist (~> 0.16)
multi_json (~> 1.11)
os (>= 0.9, < 2.0)
signet (~> 0.14)
groupdate (5.1.0)
groupdate (5.2.2)
activesupport (>= 5)
haikunator (1.1.0)
hairtrigger (0.2.23)
grpc (1.38.0-universal-darwin)
google-protobuf (~> 3.15)
googleapis-common-protos-types (~> 1.0)
grpc (1.38.0-x86_64-linux)
google-protobuf (~> 3.15)
googleapis-common-protos-types (~> 1.0)
haikunator (1.1.1)
hairtrigger (0.2.24)
activerecord (>= 5.0, < 7)
ruby2ruby (~> 2.4)
ruby_parser (~> 3.10)
hana (1.3.6)
hana (1.3.7)
hashdiff (1.0.1)
hashie (4.1.0)
hkdf (0.3.0)
http-accept (1.7.0)
http-cookie (1.0.3)
http-cookie (1.0.4)
domain_name (~> 0.5)
httparty (0.18.1)
mime-types (~> 3.0)
multi_xml (>= 0.5.2)
httpclient (2.8.3)
i18n (1.8.9)
i18n (1.8.10)
concurrent-ruby (~> 1.0)
ice_nine (0.11.2)
inflecto (0.0.2)
jbuilder (2.10.0)
image_processing (1.12.1)
mini_magick (>= 4.9.5, < 5)
ruby-vips (>= 2.0.17, < 3)
jbuilder (2.11.2)
activesupport (>= 5.0.0)
jmespath (1.4.0)
jquery-rails (4.4.0)
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
json (2.3.1)
jwt (2.2.2)
json (2.5.1)
json_refs (0.1.6)
hana
json_schemer (0.2.18)
ecma-re-validator (~> 0.3)
hana (~> 1.3)
regexp_parser (~> 2.0)
uri_template (~> 0.7)
jwt (2.2.3)
kaminari (1.2.1)
activesupport (>= 4.1.0)
kaminari-actionview (= 1.2.1)
@@ -294,110 +332,112 @@ GEM
addressable (~> 2.7)
letter_opener (1.7.0)
launchy (~> 2.2)
liquid (4.0.3)
listen (3.3.3)
liquid (5.0.1)
listen (3.6.0)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
loofah (2.9.0)
loofah (2.11.0)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
mail (2.7.1)
mini_mime (>= 0.1.1)
marcel (1.0.0)
marcel (1.0.1)
maxminddb (0.1.22)
memoist (0.16.2)
method_source (1.0.0)
mime-types (3.3.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2020.0512)
mini_magick (4.10.1)
mini_mime (1.0.3)
mini_portile2 (2.5.0)
mime-types-data (3.2021.0704)
mini_magick (4.11.0)
mini_mime (1.1.0)
minitest (5.14.4)
mock_redis (0.28.0)
ruby2_keywords
momentjs-rails (2.20.1)
railties (>= 3.1)
msgpack (1.3.3)
msgpack (1.4.2)
multi_json (1.15.0)
multi_xml (0.6.0)
multipart-post (2.1.1)
net-http-persistent (4.0.0)
net-http-persistent (4.0.1)
connection_pool (~> 2.2)
netrc (0.11.0)
nio4r (2.5.7)
nokogiri (1.11.2)
mini_portile2 (~> 2.5.0)
nokogiri (1.11.7-arm64-darwin)
racc (~> 1.4)
oauth (0.5.4)
nokogiri (1.11.7-x86_64-darwin)
racc (~> 1.4)
nokogiri (1.11.7-x86_64-linux)
racc (~> 1.4)
oauth (0.5.6)
orm_adapter (0.5.0)
os (1.1.1)
parallel (1.19.2)
parser (2.7.1.4)
parallel (1.20.1)
parser (3.0.2.0)
ast (~> 2.4.1)
pg (1.2.3)
pry (0.13.1)
procore-sift (0.16.0)
rails (> 4.2.0)
pry (0.14.1)
coderay (~> 1.1)
method_source (~> 1.0)
pry-rails (0.3.9)
pry (>= 0.10.4)
public_suffix (4.0.5)
puma (4.3.6)
public_suffix (4.0.6)
puma (5.4.0)
nio4r (~> 2.0)
pundit (2.1.0)
activesupport (>= 3.0.0)
raabro (1.4.0)
racc (1.5.2)
rack (2.2.3)
rack-cache (1.12.0)
rack (>= 0.4)
rack-attack (6.5.0)
rack (>= 1.0, < 3)
rack-cors (1.1.1)
rack (>= 2.0.0)
rack-proxy (0.6.5)
rack-proxy (0.7.0)
rack
rack-test (1.1.0)
rack (>= 1.0, < 3)
rails (6.0.3.6)
actioncable (= 6.0.3.6)
actionmailbox (= 6.0.3.6)
actionmailer (= 6.0.3.6)
actionpack (= 6.0.3.6)
actiontext (= 6.0.3.6)
actionview (= 6.0.3.6)
activejob (= 6.0.3.6)
activemodel (= 6.0.3.6)
activerecord (= 6.0.3.6)
activestorage (= 6.0.3.6)
activesupport (= 6.0.3.6)
bundler (>= 1.3.0)
railties (= 6.0.3.6)
rack-timeout (0.6.0)
rails (6.1.4)
actioncable (= 6.1.4)
actionmailbox (= 6.1.4)
actionmailer (= 6.1.4)
actionpack (= 6.1.4)
actiontext (= 6.1.4)
actionview (= 6.1.4)
activejob (= 6.1.4)
activemodel (= 6.1.4)
activerecord (= 6.1.4)
activestorage (= 6.1.4)
activesupport (= 6.1.4)
bundler (>= 1.15.0)
railties (= 6.1.4)
sprockets-rails (>= 2.0.0)
rails-dom-testing (2.0.3)
activesupport (>= 4.2.0)
nokogiri (>= 1.6)
rails-html-sanitizer (1.3.0)
loofah (~> 2.3)
railties (6.0.3.6)
actionpack (= 6.0.3.6)
activesupport (= 6.0.3.6)
railties (6.1.4)
actionpack (= 6.1.4)
activesupport (= 6.1.4)
method_source
rake (>= 0.8.7)
thor (>= 0.20.3, < 2.0)
rake (>= 0.13)
thor (~> 1.0)
rainbow (3.0.0)
rake (13.0.3)
rb-fsevent (0.10.4)
rake (13.0.6)
rb-fsevent (0.11.0)
rb-inotify (0.10.1)
ffi (~> 1.0)
redis (4.2.1)
redis-namespace (1.8.0)
redis (4.4.0)
redis-namespace (1.8.1)
redis (>= 3.0.4)
redis-rack-cache (2.2.1)
rack-cache (>= 1.10, < 2)
redis-store (>= 1.6, < 2)
redis-store (1.9.0)
redis (>= 4, < 5)
regexp_parser (1.7.1)
representable (3.0.4)
regexp_parser (2.1.1)
representable (3.1.1)
declarative (< 0.1.0)
declarative-option (< 0.2.0)
trailblazer-option (>= 0.1.1, < 0.2.0)
uber (< 0.2.0)
responders (3.0.1)
actionpack (>= 5.0)
@@ -408,58 +448,54 @@ GEM
mime-types (>= 1.16, < 4.0)
netrc (~> 0.8)
retriable (3.1.2)
rexml (3.2.4)
rspec-core (3.9.2)
rspec-support (~> 3.9.3)
rspec-expectations (3.9.2)
rexml (3.2.5)
rspec-core (3.10.1)
rspec-support (~> 3.10.0)
rspec-expectations (3.10.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.9.0)
rspec-mocks (3.9.1)
rspec-support (~> 3.10.0)
rspec-mocks (3.10.2)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.9.0)
rspec-rails (4.0.1)
actionpack (>= 4.2)
activesupport (>= 4.2)
railties (>= 4.2)
rspec-core (~> 3.9)
rspec-expectations (~> 3.9)
rspec-mocks (~> 3.9)
rspec-support (~> 3.9)
rspec-support (3.9.3)
rubocop (0.89.1)
rspec-support (~> 3.10.0)
rspec-rails (5.0.1)
actionpack (>= 5.2)
activesupport (>= 5.2)
railties (>= 5.2)
rspec-core (~> 3.10)
rspec-expectations (~> 3.10)
rspec-mocks (~> 3.10)
rspec-support (~> 3.10)
rspec-support (3.10.2)
rubocop (1.18.4)
parallel (~> 1.10)
parser (>= 2.7.1.1)
parser (>= 3.0.0.0)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.7)
regexp_parser (>= 1.8, < 3.0)
rexml
rubocop-ast (>= 0.3.0, < 1.0)
rubocop-ast (>= 1.8.0, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 2.0)
rubocop-ast (0.3.0)
parser (>= 2.7.1.4)
rubocop-performance (1.7.1)
rubocop (>= 0.82.0)
rubocop-rails (2.8.1)
unicode-display_width (>= 1.4.0, < 3.0)
rubocop-ast (1.8.0)
parser (>= 3.0.1.1)
rubocop-performance (1.11.4)
rubocop (>= 1.7.0, < 2.0)
rubocop-ast (>= 0.4.0)
rubocop-rails (2.11.3)
activesupport (>= 4.2.0)
rack (>= 1.1)
rubocop (>= 0.87.0)
rubocop-rspec (1.43.2)
rubocop (~> 0.87)
ruby-enum (0.9.0)
i18n
ruby-progressbar (1.10.1)
rubocop (>= 1.7.0, < 2.0)
rubocop-rspec (2.4.0)
rubocop (~> 1.0)
rubocop-ast (>= 1.1.0)
ruby-progressbar (1.11.0)
ruby-vips (2.1.2)
ffi (~> 1.12)
ruby2_keywords (0.0.5)
ruby2ruby (2.4.4)
ruby_parser (~> 3.1)
sexp_processor (~> 4.6)
ruby_parser (3.15.0)
rubocop (>= 0.87.0)
sexp_processor (~> 4.9)
safe_yaml (1.0.5)
sass (3.7.4)
sass-listen (~> 4.0.0)
sass-listen (4.0.0)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
ruby_parser (3.16.0)
sexp_processor (~> 4.15, >= 4.15.1)
sassc (2.4.0)
ffi (~> 1.9)
sassc-rails (2.1.2)
@@ -468,28 +504,37 @@ GEM
sprockets (> 3.0)
sprockets-rails
tilt
scout_apm (2.6.9)
scout_apm (4.1.1)
parser
scss_lint (0.59.0)
sass (~> 3.5, >= 3.5.5)
seed_dump (3.3.1)
activerecord (>= 4)
activesupport (>= 4)
selectize-rails (0.12.6)
semantic_range (2.3.0)
sentry-raven (3.0.3)
semantic_range (3.0.0)
sentry-rails (4.6.4)
railties (>= 5.0)
sentry-ruby-core (~> 4.6.0)
sentry-ruby (4.6.4)
concurrent-ruby (~> 1.0, >= 1.0.2)
faraday (>= 1.0)
sexp_processor (4.15.1)
shoulda-matchers (4.4.1)
activesupport (>= 4.2.0)
sidekiq (6.1.1)
sentry-ruby-core (= 4.6.4)
sentry-ruby-core (4.6.4)
concurrent-ruby
faraday
sentry-sidekiq (4.6.4)
sentry-ruby-core (~> 4.6.0)
sidekiq (>= 3.0)
sexp_processor (4.15.3)
shoulda-matchers (5.0.0)
activesupport (>= 5.2.0)
sidekiq (6.2.1)
connection_pool (>= 2.2.2)
rack (~> 2.0)
redis (>= 4.2.0)
sidekiq-cron (1.2.0)
fugit (~> 1.1)
sidekiq (>= 4.2.1)
signet (0.14.0)
signet (0.15.0)
addressable (~> 2.3)
faraday (>= 0.17.3, < 2.0)
jwt (>= 1.5, < 3.0)
@@ -499,7 +544,7 @@ GEM
json (>= 1.8, < 3)
simplecov-html (~> 0.10.0)
simplecov-html (0.10.2)
slack-ruby-client (0.15.0)
slack-ruby-client (0.17.0)
faraday (>= 1.0)
faraday_middleware
gli
@@ -509,7 +554,7 @@ GEM
spring-watcher-listen (2.0.1)
listen (>= 2.7, < 4.0)
spring (>= 1.2, < 3.0)
sprockets (3.7.2)
sprockets (4.0.2)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-rails (3.2.2)
@@ -517,27 +562,28 @@ GEM
activesupport (>= 4.0)
sprockets (>= 3.0.0)
squasher (0.6.2)
statsd-ruby (1.4.0)
telegram-bot-ruby (0.12.0)
statsd-ruby (1.5.0)
telegram-bot-ruby (0.16.0)
dry-inflector
faraday
inflecto
virtus
telephone_number (1.4.9)
virtus (~> 2.0)
telephone_number (1.4.12)
thor (1.1.0)
thread_safe (0.3.6)
tilt (2.0.10)
time_diff (0.3.0)
activesupport
i18n
trailblazer-option (0.1.1)
twilio-ruby (5.32.0)
faraday (~> 1.0.0)
jwt (>= 1.5, <= 2.5)
nokogiri (>= 1.6, < 2.0)
twitty (0.1.1)
twitty (0.1.4)
oauth
tzinfo (1.2.9)
thread_safe (~> 0.1)
tzinfo-data (1.2020.1)
tzinfo (2.0.4)
concurrent-ruby (~> 1.0)
tzinfo-data (1.2021.1)
tzinfo (>= 1.0.0)
uber (0.1.0)
uglifier (4.2.0)
@@ -545,46 +591,49 @@ GEM
unf (0.1.4)
unf_ext
unf_ext (0.0.7.7)
unicode-display_width (1.7.0)
uniform_notifier (1.13.0)
valid_email2 (3.3.1)
unicode-display_width (2.0.0)
uniform_notifier (1.14.2)
uri_template (0.7.0)
valid_email2 (4.0.0)
activemodel (>= 3.2)
mail (~> 2.5)
virtus (1.0.5)
virtus (2.0.0)
axiom-types (~> 0.1)
coercible (~> 1.0)
descendants_tracker (~> 0.0, >= 0.0.3)
equalizer (~> 0.0, >= 0.0.9)
warden (1.2.8)
rack (>= 2.0.6)
web-console (4.0.4)
warden (1.2.9)
rack (>= 2.0.9)
web-console (4.1.0)
actionview (>= 6.0.0)
activemodel (>= 6.0.0)
bindex (>= 0.4.0)
railties (>= 6.0.0)
webmock (3.8.3)
webmock (3.13.0)
addressable (>= 2.3.6)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
webpacker (5.2.1)
webpacker (5.4.0)
activesupport (>= 5.2)
rack-proxy (>= 0.6.1)
railties (>= 5.2)
semantic_range (>= 2.3.0)
webpush (1.0.0)
webpush (1.1.0)
hkdf (~> 0.2)
jwt (~> 2.0)
websocket-driver (0.7.3)
webrick (1.7.0)
websocket-driver (0.7.5)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
wisper (2.0.0)
zeitwerk (2.4.2)
PLATFORMS
ruby
arm64-darwin-20
x86_64-darwin-21
x86_64-linux
DEPENDENCIES
action-cable-testing
active_record_query_trace
activerecord-import
acts-as-taggable-on
administrate
@@ -602,23 +651,29 @@ DEPENDENCIES
commonmarker
cypress-on-rails (~> 1.0)
database_cleaner
ddtrace
devise
devise-secure_password (~> 2.0)
devise_token_auth
dotenv-rails
facebook-messenger (= 1.5.0)
down (~> 5.0)
facebook-messenger
factory_bot_rails
faker
fcm
flag_shih_tzu
foreman
geocoder
google-cloud-dialogflow
google-cloud-storage
groupdate
haikunator
hairtrigger
hashie
image_processing
jbuilder
json_refs!
json_refs
json_schemer
jwt
kaminari
koala
@@ -626,28 +681,30 @@ DEPENDENCIES
liquid
listen
maxminddb
mini_magick
mock_redis!
mock_redis
pg
procore-sift
pry-rails
puma
pundit
rack-attack
rack-cors
rack-timeout
rails
redis
redis-namespace
redis-rack-cache
responders
rest-client
rspec-rails (~> 4.0.0.beta2)
rspec-rails (~> 5.0.0)
rubocop
rubocop-performance
rubocop-rails
rubocop-rspec
scout_apm
scss_lint
seed_dump
sentry-raven
sentry-rails
sentry-ruby
sentry-sidekiq
shoulda-matchers
sidekiq
sidekiq-cron
@@ -671,7 +728,7 @@ DEPENDENCIES
wisper (= 2.0.0)
RUBY VERSION
ruby 2.7.2p137
ruby 3.0.2p107
BUNDLED WITH
2.1.4
2.2.25
+18 -17
View File
@@ -29,9 +29,8 @@ ___
Chatwoot is an open-source omnichannel customer support software. The development of Chatwoot started in 2016. It failed to succeed as a business and eventually shut up shop in 2017. During 2019 #Hacktoberfest, the maintainers decided to make it open-source, instead of letting the code rust in a private repo. With a pleasant surprise, Chatwoot became a trending project on Hacker News and best of all, got lots of love from the community.
Now, a failed project is back on track and the prospects are looking great. The team is back to working on the project and this time, we are building it in the open. Thanks to the ideas and contributions from the community.
---
### Features
## Features
Chatwoot gives an integrated view of conversations happening in different communication channels.
@@ -57,28 +56,22 @@ Other features include:
- **Powerful API & Webhooks**: Extend the capability of the software using Chatwoots webhooks and APIs.
- **Integrations**: Chatwoot natively integrates with Slack right now. Manage your conversations in Slack without logging into the dashboard.
---
## Documentation
### Documentation
Detailed documentation is available at [chatwoot.com/help-center](https://www.chatwoot.com/help-center).
Detailed documentation is available at [www.chatwoot.com/help-center](https://www.chatwoot.com/help-center).
### Translation process
## Translation process
The translation process for Chatwoot web and mobile app is managed at [https://translate.chatwoot.com](https://translate.chatwoot.com) using Crowdin. Please read the [translation guide](https://www.chatwoot.com/docs/contributing/translating-chatwoot-to-your-language) for contributing to Chatwoot.
---
### Branching model
## Branching model
We use the [git-flow](https://nvie.com/posts/a-successful-git-branching-model/) branching model. The base branch is `develop`.
If you are looking for a stable version, please use the `master` or tags labelled as `v1.x.x`.
---
## Deployment
### Deployment
#### Heroku one-click deploy
### Heroku one-click deploy
Deploying Chatwoot to Heroku is a breeze. It's as simple as clicking this button:
@@ -86,13 +79,21 @@ Deploying Chatwoot to Heroku is a breeze. It's as simple as clicking this button
Follow this [link](https://www.chatwoot.com/docs/environment-variables) to understand setting the correct environment variables for the app to work with all the features. There might be breakages if you do not set the relevant environment variables.
#### Other deployment options
### Other deployment options
Please follow [deployment architecture guide](https://www.chatwoot.com/docs/deployment/architecture) to deploy with Docker or Caprover.
---
## Security
### Contributors ✨
Looking to report a vulnerability? Please refer our [SECURITY.md](./SECURITY.md) file.
## Community? Questions? Support ?
If you need help or just want to hang out, come, say hi on our [Discord](https://discord.gg/cJXdrwS) server.
## Contributors ✨
Thanks goes to all these [wonderful people](https://www.chatwoot.com/docs/contributors):
+24 -1
View File
@@ -1,8 +1,31 @@
# Security Policy
Chatwoot is looking forward to working with security researchers across the world to keep Chatwoot and our users safe. If you have found an issue in our systems/applications, please reach out to us.
## Reporting a Vulnerability
We use [huntr.dev](https://huntr.dev/) for security issues that affect our project. If you believe you have found a vulnerability, please disclose it via this [form](https://huntr.dev/bounties/disclose).
This will enable us to review the vulnerability, fix it promptly, and reward you for your efforts.
If you have any questions about the process, feel free to reach out to hello@chatwoot.com.
If you have any questions about the process, feel free to reach out to security@chatwoot.com.
## Out of scope
Please do not perform testing against Chatwoot production services. Use a self hosted instance to perform tests.
We consider the following to be out of scope, though there may be exceptions.
- Missing HTTP security headers
- Self XSS
- HTTP Host Header XSS without working proof-of-concept
- Incomplete/Missing SPF/DKIM
- Denial of Service attacks
- DNSSEC
- Social Engineering attacks
If you are not sure about the scope, please create a report.
## Thanks
Thank you for keeping Chatwoot and our users safe. 🙇
+4
View File
@@ -28,6 +28,10 @@
"FRONTEND_URL": {
"description": "Public root URL of the Chatwoot installation. This will be used in the emails.",
"value": "https://CHANGE.herokuapp.com"
},
"INSTALLATION_ENV": {
"description": "Installation method used for Chatwoot.",
"value": "heroku"
}
},
"formation": {
+24 -3
View File
@@ -3,8 +3,9 @@ class ContactIdentifyAction
def perform
ActiveRecord::Base.transaction do
@contact = merge_contact(existing_identified_contact, @contact) if merge_contacts?(existing_identified_contact, @contact)
@contact = merge_contact(existing_email_contact, @contact) if merge_contacts?(existing_email_contact, @contact)
merge_if_existing_identified_contact
merge_if_existing_email_contact
merge_if_existing_phone_number_contact
update_contact
end
@contact
@@ -16,6 +17,18 @@ class ContactIdentifyAction
@account ||= @contact.account
end
def merge_if_existing_identified_contact
@contact = merge_contact(existing_identified_contact, @contact) if merge_contacts?(existing_identified_contact, @contact)
end
def merge_if_existing_email_contact
@contact = merge_contact(existing_email_contact, @contact) if merge_contacts?(existing_email_contact, @contact)
end
def merge_if_existing_phone_number_contact
@contact = merge_contact(existing_phone_number_contact, @contact) if merge_contacts?(existing_phone_number_contact, @contact)
end
def existing_identified_contact
return if params[:identifier].blank?
@@ -28,6 +41,12 @@ class ContactIdentifyAction
@existing_email_contact ||= Contact.where(account_id: account.id).find_by(email: params[:email])
end
def existing_phone_number_contact
return if params[:phone_number].blank?
@existing_phone_number_contact ||= Contact.where(account_id: account.id).find_by(phone_number: params[:phone_number])
end
def merge_contacts?(existing_contact, _contact)
existing_contact && existing_contact.id != @contact.id
end
@@ -36,7 +55,9 @@ class ContactIdentifyAction
custom_attributes = params[:custom_attributes] ? @contact.custom_attributes.merge(params[:custom_attributes]) : @contact.custom_attributes
# blank identifier or email will throw unique index error
# TODO: replace reject { |_k, v| v.blank? } with compact_blank when rails is upgraded
@contact.update!(params.slice(:name, :email, :identifier).reject { |_k, v| v.blank? }.merge({ custom_attributes: custom_attributes }))
@contact.update!(params.slice(:name, :email, :identifier, :phone_number).reject do |_k, v|
v.blank?
end.merge({ custom_attributes: custom_attributes }))
ContactAvatarJob.perform_later(@contact, params[:avatar_url]) if params[:avatar_url].present?
end
+4
View File
@@ -2,6 +2,10 @@ class ContactMergeAction
pattr_initialize [:account!, :base_contact!, :mergee_contact!]
def perform
# This case happens when an agent updates a contact email in dashboard,
# while the contact also update his email via email collect box
return @base_contact if base_contact.id == mergee_contact.id
ActiveRecord::Base.transaction do
validate_contacts
merge_conversations
+3 -5
View File
@@ -2,7 +2,7 @@
class AccountBuilder
include CustomExceptions::Account
pattr_initialize [:account_name!, :email!, :confirmed!, :user, :user_full_name, :user_password]
pattr_initialize [:account_name!, :email!, :confirmed, :user, :user_full_name, :user_password]
def perform
if @user.nil?
@@ -61,11 +61,9 @@ class AccountBuilder
end
def create_user
password = user_password || SecureRandom.alphanumeric(12)
@user = User.new(email: @email,
password: password,
password_confirmation: password,
password: user_password,
password_confirmation: user_password,
name: @user_full_name)
@user.confirm if @confirmed
@user.save!
@@ -0,0 +1,38 @@
class Campaigns::CampaignConversationBuilder
pattr_initialize [:contact_inbox_id!, :campaign_display_id!, :conversation_additional_attributes]
def perform
@contact_inbox = ContactInbox.find(@contact_inbox_id)
@campaign = @contact_inbox.inbox.campaigns.find_by!(display_id: campaign_display_id)
ActiveRecord::Base.transaction do
@contact_inbox.lock!
# We won't send campaigns if a conversation is already present
return if @contact_inbox.reload.conversations.present?
@conversation = ::Conversation.create!(conversation_params)
Messages::MessageBuilder.new(@campaign.sender, @conversation, message_params).perform
end
@conversation
end
private
def message_params
ActionController::Parameters.new({
content: @campaign.message
})
end
def conversation_params
{
account_id: @campaign.account_id,
inbox_id: @contact_inbox.inbox_id,
contact_id: @contact_inbox.contact_id,
contact_inbox_id: @contact_inbox.id,
campaign_id: @campaign.id,
additional_attributes: conversation_additional_attributes
}
end
end
+26 -11
View File
@@ -1,5 +1,5 @@
class ContactBuilder
pattr_initialize [:source_id!, :inbox!, :contact_attributes!]
pattr_initialize [:source_id!, :inbox!, :contact_attributes!, :hmac_verified]
def perform
contact_inbox = inbox.contact_inboxes.find_by(source_id: source_id)
@@ -18,7 +18,8 @@ class ContactBuilder
::ContactInbox.create!(
contact_id: contact.id,
inbox_id: inbox.id,
source_id: source_id
source_id: source_id,
hmac_verified: hmac_verified || false
)
end
@@ -28,7 +29,7 @@ class ContactBuilder
def create_contact
account.contacts.create!(
name: contact_attributes[:name],
name: contact_attributes[:name] || ::Haikunator.haikunate(1000),
phone_number: contact_attributes[:phone_number],
email: contact_attributes[:email],
identifier: contact_attributes[:identifier],
@@ -37,17 +38,30 @@ class ContactBuilder
end
def find_contact
contact = nil
contact = account.contacts.find_by(identifier: contact_attributes[:identifier]) if contact_attributes[:identifier].present?
contact ||= account.contacts.find_by(email: contact_attributes[:email]) if contact_attributes[:email].present?
contact ||= account.contacts.find_by(phone_number: contact_attributes[:phone_number]) if contact_attributes[:phone_number].present?
contact = find_contact_by_identifier(contact_attributes[:identifier])
contact ||= find_contact_by_email(contact_attributes[:email])
contact ||= find_contact_by_phone_number(contact_attributes[:phone_number])
contact
end
def find_contact_by_identifier(identifier)
return if identifier.blank?
account.contacts.find_by(identifier: identifier)
end
def find_contact_by_email(email)
return if email.blank?
account.contacts.find_by(email: email.downcase)
end
def find_contact_by_phone_number(phone_number)
return if phone_number.blank?
account.contacts.find_by(phone_number: phone_number)
end
def build_contact_inbox
ActiveRecord::Base.transaction do
contact = find_contact || create_contact
@@ -56,6 +70,7 @@ class ContactBuilder
contact_inbox
rescue StandardError => e
Rails.logger.info e
raise e
end
end
end
@@ -0,0 +1,28 @@
class CsatSurveys::ResponseBuilder
pattr_initialize [:message]
def perform
raise 'Invalid Message' unless message.input_csat?
conversation = message.conversation
rating = message.content_attributes.dig('submitted_values', 'csat_survey_response', 'rating')
feedback_message = message.content_attributes.dig('submitted_values', 'csat_survey_response', 'feedback_message')
return if rating.blank?
process_csat_response(conversation, rating, feedback_message)
end
private
def process_csat_response(conversation, rating, feedback_message)
csat_survey_response = message.csat_survey_response || CsatSurveyResponse.new(
message_id: message.id, account_id: message.account_id, conversation_id: message.conversation_id,
contact_id: conversation.contact_id, assigned_agent: conversation.assignee
)
csat_survey_response.rating = rating
csat_survey_response.feedback_message = feedback_message
csat_survey_response.save!
csat_survey_response
end
end
@@ -13,15 +13,22 @@ class Messages::Facebook::MessageBuilder
@outgoing_echo = outgoing_echo
@sender_id = (@outgoing_echo ? @response.recipient_id : @response.sender_id)
@message_type = (@outgoing_echo ? :outgoing : :incoming)
@attachments = (@response.attachments || [])
end
def perform
# This channel might require reauthorization, may be owner might have changed the fb password
return if @inbox.channel.reauthorization_required?
ActiveRecord::Base.transaction do
build_contact
build_message
end
ensure_contact_avatar
rescue Koala::Facebook::AuthenticationError
Rails.logger.info "Facebook Authorization expired for Inbox #{@inbox.id}"
rescue StandardError => e
Raven.capture_exception(e)
Sentry.capture_exception(e)
true
end
@@ -35,24 +42,40 @@ class Messages::Facebook::MessageBuilder
return if contact.present?
@contact = Contact.create!(contact_params.except(:remote_avatar_url))
ContactAvatarJob.perform_later(@contact, contact_params[:remote_avatar_url]) if contact_params[:remote_avatar_url]
@contact_inbox = ContactInbox.create(contact: contact, inbox: @inbox, source_id: @sender_id)
end
def build_message
@message = conversation.messages.create!(message_params)
(response.attachments || []).each do |attachment|
attachment_obj = @message.attachments.new(attachment_params(attachment).except(:remote_file_url))
attachment_obj.save!
attach_file(attachment_obj, attachment_params(attachment)[:remote_file_url]) if attachment_params(attachment)[:remote_file_url]
@attachments.each do |attachment|
process_attachment(attachment)
end
end
def process_attachment(attachment)
return if attachment['type'].to_sym == :template
attachment_obj = @message.attachments.new(attachment_params(attachment).except(:remote_file_url))
attachment_obj.save!
attach_file(attachment_obj, attachment_params(attachment)[:remote_file_url]) if attachment_params(attachment)[:remote_file_url]
end
def attach_file(attachment, file_url)
file_resource = LocalResource.new(file_url)
attachment.file.attach(io: file_resource.file, filename: file_resource.filename, content_type: file_resource.encoding)
rescue *ExceptionList::URI_EXCEPTIONS => e
Rails.logger.info "invalid url #{file_url} : #{e.message}"
attachment_file = Down.download(
file_url
)
attachment.file.attach(
io: attachment_file,
filename: attachment_file.original_filename,
content_type: attachment_file.content_type
)
end
def ensure_contact_avatar
return if contact_params[:remote_avatar_url].blank?
return if @contact.avatar.attached?
ContactAvatarJob.perform_later(@contact, contact_params[:remote_avatar_url])
end
def conversation
@@ -129,9 +152,12 @@ class Messages::Facebook::MessageBuilder
begin
k = Koala::Facebook::API.new(@inbox.channel.page_access_token) if @inbox.facebook?
result = k.get_object(@sender_id) || {}
rescue Koala::Facebook::AuthenticationError
@inbox.channel.authorization_error!
raise
rescue StandardError => e
result = {}
Raven.capture_exception(e)
Sentry.capture_exception(e)
end
{
name: "#{result['first_name'] || 'John'} #{result['last_name'] || 'Doe'}",
+12 -2
View File
@@ -39,7 +39,17 @@ class Messages::MessageBuilder
end
def sender
message_type == 'outgoing' ? @user : @conversation.contact
message_type == 'outgoing' ? (message_sender || @user) : @conversation.contact
end
def external_created_at
@params[:external_created_at].present? ? { external_created_at: @params[:external_created_at] } : {}
end
def message_sender
return if @params[:sender_type] != 'AgentBot'
AgentBot.where(account_id: [nil, @conversation.account.id]).find_by(id: @params[:sender_id])
end
def message_params
@@ -54,6 +64,6 @@ class Messages::MessageBuilder
items: @items,
in_reply_to: @in_reply_to,
echo_id: @params[:echo_id]
}
}.merge(external_created_at)
end
end
+1 -11
View File
@@ -1,4 +1,5 @@
class V2::ReportBuilder
include DateRangeHelper
attr_reader :account, :params
def initialize(account, params)
@@ -83,10 +84,6 @@ class V2::ReportBuilder
.average(:value)
end
def range
parse_date_time(params[:since])..parse_date_time(params[:until])
end
# Taking average of average is not too accurate
# https://en.wikipedia.org/wiki/Simpson's_paradox
# TODO: Will optimize this later
@@ -101,11 +98,4 @@ class V2::ReportBuilder
(avg_first_response_time.values.sum / avg_first_response_time.values.length)
end
def parse_date_time(datetime)
return datetime if datetime.is_a?(DateTime)
return datetime.to_datetime if datetime.is_a?(Time) || datetime.is_a?(Date)
DateTime.strptime(datetime, '%s')
end
end
@@ -0,0 +1,5 @@
class AndroidAppController < ApplicationController
def assetlinks
render layout: false
end
end
+4
View File
@@ -16,4 +16,8 @@ class Api::BaseController < ApplicationController
authorize(model)
end
def check_admin_authorization?
raise Pundit::NotAuthorizedError unless Current.account_user.administrator?
end
end
@@ -0,0 +1,35 @@
class Api::V1::Accounts::AgentBotsController < Api::V1::Accounts::BaseController
before_action :current_account
before_action :check_authorization
before_action :agent_bot, except: [:index, :create]
def index
@agent_bots = AgentBot.where(account_id: [nil, Current.account.id])
end
def show; end
def create
@agent_bot = Current.account.agent_bots.create!(permitted_params)
end
def update
@agent_bot.update!(permitted_params)
end
def destroy
@agent_bot.destroy
head :ok
end
private
def agent_bot
@agent_bot = AgentBot.where(account_id: [nil, Current.account.id]).find(params[:id]) if params[:action] == 'show'
@agent_bot ||= Current.account.agent_bots.find(params[:id])
end
def permitted_params
params.permit(:name, :description, :outgoing_url)
end
end
@@ -38,6 +38,8 @@ class Api::V1::Accounts::AgentsController < Api::V1::Accounts::BaseController
@user = User.find_by(email: new_agent_params[:email])
end
# TODO: move this to a builder and combine the save account user method into a builder
# ensure the account user association is also created in a single transaction
def create_user
return if @user
@@ -58,9 +60,10 @@ class Api::V1::Accounts::AgentsController < Api::V1::Accounts::BaseController
end
def new_agent_params
time = Time.now.to_i
# intial string ensures the password requirements are met
temp_password = "1!aA#{SecureRandom.alphanumeric(12)}"
params.require(:agent).permit(:email, :name, :role)
.merge!(password: time, password_confirmation: time, inviter: current_user)
.merge!(password: temp_password, password_confirmation: temp_password, inviter: current_user)
end
def agents
@@ -14,7 +14,7 @@ class Api::V1::Accounts::BaseController < Api::BaseController
account = Account.find(params[:account_id])
if current_user
account_accessible_for_user?(account)
elsif @resource&.is_a?(AgentBot)
elsif @resource.is_a?(AgentBot)
account_accessible_for_bot?(account)
end
account
@@ -69,39 +69,15 @@ class Api::V1::Accounts::CallbacksController < Api::V1::Accounts::BaseController
return [] if data.empty?
data.inject([]) do |result, page_detail|
page_detail[:exists] = Current.account.facebook_pages.exists?(page_id: page_detail['id']) ? true : false
page_detail[:exists] = Current.account.facebook_pages.exists?(page_id: page_detail['id'])
result << page_detail
end
end
def set_avatar(facebook_inbox, page_id)
uri = get_avatar_url(page_id)
return unless uri
avatar_resource = LocalResource.new(uri)
facebook_inbox.avatar.attach(io: avatar_resource.file, filename: avatar_resource.tmp_filename, content_type: avatar_resource.encoding)
rescue *ExceptionList::URI_EXCEPTIONS => e
Rails.logger.info "invalid url #{file_url} : #{e.message}"
end
def get_avatar_url(page_id)
begin
url = 'http://graph.facebook.com/' << page_id << '/picture?type=large'
uri = URI.parse(url)
tries = 3
begin
response = uri.open(redirect: false)
rescue OpenURI::HTTPRedirect => e
uri = e.uri # assigned from the "Location" response header
retry if (tries -= 1).positive?
raise
end
pic_url = response.base_uri.to_s
rescue StandardError => e
Rails.logger.debug "Rescued: #{e.inspect}"
pic_url = nil
end
pic_url
avatar_file = Down.download(
"http://graph.facebook.com/#{page_id}/picture?type=large"
)
facebook_inbox.avatar.attach(io: avatar_file, filename: avatar_file.original_filename, content_type: avatar_file.content_type)
end
end
@@ -0,0 +1,34 @@
class Api::V1::Accounts::CampaignsController < Api::V1::Accounts::BaseController
before_action :campaign, except: [:index, :create]
before_action :check_authorization
def index
@campaigns = Current.account.campaigns
end
def create
@campaign = Current.account.campaigns.create!(campaign_params)
end
def destroy
@campaign.destroy
head :ok
end
def show; end
def update
@campaign.update(campaign_params)
end
private
def campaign
@campaign ||= Current.account.campaigns.find_by(display_id: params[:id])
end
def campaign_params
params.require(:campaign).permit(:title, :description, :message, :enabled, :inbox_id, :sender_id,
:scheduled_at, audience: [:type, :id], trigger_rules: {})
end
end
@@ -6,9 +6,8 @@ class Api::V1::Accounts::Channels::TwilioChannelsController < Api::V1::Accounts:
authenticate_twilio
build_inbox
setup_webhooks if @twilio_channel.sms?
rescue Twilio::REST::TwilioError => e
render_could_not_create_error(e.message)
rescue StandardError => e
Sentry.capture_exception(e)
render_could_not_create_error(e.message)
end
end
@@ -11,6 +11,7 @@ class Api::V1::Accounts::Contacts::ContactInboxesController < Api::V1::Accounts:
def ensure_inbox
@inbox = Current.account.inboxes.find(params[:inbox_id])
authorize @inbox, :show?
end
def ensure_contact
@@ -8,9 +8,7 @@ class Api::V1::Accounts::Contacts::ConversationsController < Api::V1::Accounts::
private
def inbox_ids
if Current.user.administrator?
Current.account.inboxes.pluck(:id)
elsif Current.user.agent?
if Current.user.administrator? || Current.user.agent?
Current.user.assigned_inboxes.pluck(:id)
else
[]
@@ -1,25 +1,32 @@
class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController
include Sift
sort_on :email, type: :string
sort_on :name, type: :string
sort_on :phone_number, type: :string
sort_on :last_activity_at, type: :datetime
RESULTS_PER_PAGE = 15
protect_from_forgery with: :null_session
before_action :check_authorization
before_action :set_current_page, only: [:index, :active, :search]
before_action :fetch_contact, only: [:show, :update, :contactable_inboxes]
before_action :set_include_contact_inboxes, only: [:index, :search]
def index
@contacts_count = resolved_contacts.count
@contacts = fetch_contact_last_seen_at(resolved_contacts)
@contacts = fetch_contacts_with_conversation_count(resolved_contacts)
end
def search
render json: { error: 'Specify search string with parameter q' }, status: :unprocessable_entity if params[:q].blank? && return
contacts = resolved_contacts.where(
'name ILIKE :search OR email ILIKE :search OR phone_number ILIKE :search',
'name ILIKE :search OR email ILIKE :search OR phone_number ILIKE :search OR contacts.identifier LIKE :search',
search: "%#{params[:q]}%"
)
@contacts_count = contacts.count
@contacts = fetch_contact_last_seen_at(contacts)
@contacts = fetch_contacts_with_conversation_count(contacts)
end
def import
@@ -41,7 +48,8 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController
def show; end
def contactable_inboxes
@contactable_inboxes = Contacts::ContactableInboxesService.new(contact: @contact).get
@all_contactable_inboxes = Contacts::ContactableInboxesService.new(contact: @contact).get
@contactable_inboxes = @all_contactable_inboxes.select { |contactable_inbox| policy(contactable_inbox[:inbox]).show? }
end
def create
@@ -64,23 +72,31 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController
private
# TODO: Move this to a finder class
def resolved_contacts
@resolved_contacts ||= Current.account.contacts
.where.not(email: [nil, ''])
.or(Current.account.contacts.where.not(phone_number: [nil, '']))
.order('LOWER(name)')
return @resolved_contacts if @resolved_contacts
@resolved_contacts = Current.account.contacts
.where.not(email: [nil, ''])
.or(Current.account.contacts.where.not(phone_number: [nil, '']))
@resolved_contacts = @resolved_contacts.tagged_with(params[:labels], any: true) if params[:labels].present?
@resolved_contacts
end
def set_current_page
@current_page = params[:page] || 1
end
def fetch_contact_last_seen_at(contacts)
contacts.left_outer_joins(:conversations)
.select('contacts.*, COUNT(conversations.id) as conversations_count, MAX(conversations.contact_last_seen_at) as last_seen_at')
.group('contacts.id')
.includes([{ avatar_attachment: [:blob] }, { contact_inboxes: [:inbox] }])
.page(@current_page).per(RESULTS_PER_PAGE)
def fetch_contacts_with_conversation_count(contacts)
contacts_with_conversation_count = filtrate(contacts).left_outer_joins(:conversations)
.select('contacts.*, COUNT(conversations.id) as conversations_count')
.group('contacts.id')
.includes([{ avatar_attachment: [:blob] }])
.page(@current_page).per(RESULTS_PER_PAGE)
return contacts_with_conversation_count.includes([{ contact_inboxes: [:inbox] }]) if @include_contact_inboxes
contacts_with_conversation_count
end
def build_contact_inbox
@@ -92,7 +108,7 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController
end
def contact_params
params.require(:contact).permit(:name, :email, :phone_number, additional_attributes: {}, custom_attributes: {})
params.require(:contact).permit(:name, :identifier, :email, :phone_number, additional_attributes: {}, custom_attributes: {})
end
def contact_custom_attributes
@@ -106,6 +122,14 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController
contact_params.except(:custom_attributes).merge({ custom_attributes: contact_custom_attributes })
end
def set_include_contact_inboxes
@include_contact_inboxes = if params[:include_contact_inboxes].present?
params[:include_contact_inboxes] == 'true'
else
true
end
end
def fetch_contact
@contact = Current.account.contacts.includes(contact_inboxes: [:inbox]).find(params[:id])
end
@@ -5,5 +5,6 @@ class Api::V1::Accounts::Conversations::BaseController < Api::V1::Accounts::Base
def conversation
@conversation ||= Current.account.conversations.find_by(display_id: params[:conversation_id])
authorize @conversation.inbox, :show?
end
end
@@ -1,7 +1,8 @@
class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseController
include Events::Types
include DateRangeHelper
before_action :conversation, except: [:index]
before_action :conversation, except: [:index, :meta, :search, :create]
before_action :contact_inbox, only: [:create]
def index
@@ -41,14 +42,16 @@ class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseContro
end
def transcript
ConversationReplyMailer.conversation_transcript(@conversation, params[:email])&.deliver_later if params[:email].present?
render json: { error: 'email param missing' }, status: :unprocessable_entity and return if params[:email].blank?
ConversationReplyMailer.with(account: @conversation.account).conversation_transcript(@conversation, params[:email])&.deliver_later
head :ok
end
def toggle_status
if params[:status]
@conversation.status = params[:status]
@status = @conversation.save
set_conversation_status
@status = @conversation.save!
else
@status = @conversation.toggle_status
end
@@ -71,40 +74,56 @@ class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseContro
private
def set_conversation_status
status = params[:status] == 'bot' ? 'pending' : params[:status]
@conversation.status = status
@conversation.snoozed_until = parse_date_time(params[:snoozed_until].to_s) if params[:snoozed_until]
end
def trigger_typing_event(event)
user = current_user.presence || @resource
Rails.configuration.dispatcher.dispatch(event, Time.zone.now, conversation: @conversation, user: user)
end
def conversation
@conversation ||= Current.account.conversations.find_by(display_id: params[:id])
@conversation ||= Current.account.conversations.find_by!(display_id: params[:id])
authorize @conversation.inbox, :show?
end
def contact_inbox
@contact_inbox = build_contact_inbox
@contact_inbox ||= ::ContactInbox.find_by!(source_id: params[:source_id])
authorize @contact_inbox.inbox, :show?
end
def build_contact_inbox
return if params[:contact_id].blank? || params[:inbox_id].blank?
inbox = Current.account.inboxes.find(params[:inbox_id])
authorize inbox, :show?
ContactInboxBuilder.new(
contact_id: params[:contact_id],
inbox_id: params[:inbox_id],
inbox_id: inbox.id,
source_id: params[:source_id]
).perform
end
def conversation_params
additional_attributes = params[:additional_attributes]&.permit! || {}
status = params[:status].present? ? { status: params[:status] } : {}
# TODO: temporary fallback for the old bot status in conversation, we will remove after couple of releases
status = { status: 'pending' } if status[:status] == 'bot'
{
account_id: Current.account.id,
inbox_id: @contact_inbox.inbox_id,
contact_id: @contact_inbox.contact_id,
contact_inbox_id: @contact_inbox.id,
additional_attributes: additional_attributes
}
additional_attributes: additional_attributes,
snoozed_until: params[:snoozed_until]
}.merge(status)
end
def conversation_finder
@@ -0,0 +1,44 @@
class Api::V1::Accounts::CsatSurveyResponsesController < Api::V1::Accounts::BaseController
include Sift
include DateRangeHelper
RESULTS_PER_PAGE = 25
before_action :check_authorization
before_action :set_csat_survey_responses, only: [:index, :metrics]
before_action :set_current_page, only: [:index]
before_action :set_current_page_surveys, only: [:index]
before_action :set_total_sent_messages_count, only: [:metrics]
sort_on :created_at, type: :datetime
def index; end
def metrics
@total_count = @csat_survey_responses.count
@ratings_count = @csat_survey_responses.group(:rating).count
end
private
def set_total_sent_messages_count
@csat_messages = Current.account.messages.input_csat
@csat_messages = @csat_messages.where(created_at: range) if range.present?
@total_sent_messages_count = @csat_messages.count
end
def set_csat_survey_responses
@csat_survey_responses = filtrate(
Current.account.csat_survey_responses.includes([:conversation, :assigned_agent, :contact])
)
@csat_survey_responses = @csat_survey_responses.where(created_at: range) if range.present?
end
def set_current_page_surveys
@csat_survey_responses = @csat_survey_responses.page(@current_page).per(RESULTS_PER_PAGE)
end
def set_current_page
@current_page = params[:page] || 1
end
end
@@ -0,0 +1,50 @@
class Api::V1::Accounts::CustomAttributeDefinitionsController < Api::V1::Accounts::BaseController
before_action :fetch_custom_attributes_definitions, except: [:create]
before_action :fetch_custom_attribute_definition, only: [:show, :update, :destroy]
DEFAULT_ATTRIBUTE_MODEL = 'conversation_attribute'.freeze
def index; end
def show; end
def create
@custom_attribute_definition = Current.account.custom_attribute_definitions.create!(
permitted_payload
)
end
def update
@custom_attribute_definition.update!(permitted_payload)
end
def destroy
@custom_attribute_definition.destroy
head :no_content
end
private
def fetch_custom_attributes_definitions
@custom_attribute_definitions = Current.account.custom_attribute_definitions.where(
attribute_model: permitted_params[:attribute_model] || DEFAULT_ATTRIBUTE_MODEL
)
end
def fetch_custom_attribute_definition
@custom_attribute_definition = @custom_attribute_definitions.find(permitted_params[:id])
end
def permitted_payload
params.require(:custom_attribute_definition).permit(
:attribute_display_name,
:attribute_display_type,
:attribute_key,
:attribute_model,
:default_value
)
end
def permitted_params
params.permit(:id, :filter_type)
end
end
@@ -0,0 +1,49 @@
class Api::V1::Accounts::CustomFiltersController < Api::V1::Accounts::BaseController
before_action :fetch_custom_filters, except: [:create]
before_action :fetch_custom_filter, only: [:show, :update, :destroy]
DEFAULT_FILTER_TYPE = 'conversation'.freeze
def index; end
def show; end
def create
@custom_filter = current_user.custom_filters.create!(
permitted_payload.merge(account_id: Current.account.id)
)
end
def update
@custom_filter.update!(permitted_payload)
end
def destroy
@custom_filter.destroy
head :no_content
end
private
def fetch_custom_filters
@custom_filters = current_user.custom_filters.where(
account_id: Current.account.id,
filter_type: permitted_params[:filter_type] || DEFAULT_FILTER_TYPE
)
end
def fetch_custom_filter
@custom_filter = @custom_filters.find(permitted_params[:id])
end
def permitted_payload
params.require(:custom_filter).permit(
:name,
:filter_type,
query: {}
)
end
def permitted_params
params.permit(:id, :filter_type)
end
end
@@ -1,55 +0,0 @@
class Api::V1::Accounts::FacebookIndicatorsController < Api::V1::Accounts::BaseController
before_action :set_access_token
around_action :handle_with_exception
def mark_seen
fb_bot.deliver(payload('mark_seen'), access_token: @access_token)
head :ok
end
def typing_on
fb_bot.deliver(payload('typing_on'), access_token: @access_token)
head :ok
end
def typing_off
fb_bot.deliver(payload('typing_off'), access_token: @access_token)
head :ok
end
private
def fb_bot
::Facebook::Messenger::Bot
end
def handle_with_exception
yield
rescue Facebook::Messenger::Error => e
Rails.logger.debug "Rescued: #{e.inspect}"
true
end
def payload(action)
{
recipient: { id: contact.source_id },
sender_action: action
}
end
def inbox
@inbox ||= Current.account.inboxes.find(permitted_params[:inbox_id])
end
def set_access_token
@access_token = inbox.channel.page_access_token
end
def contact
@contact ||= inbox.contact_inboxes.find_by!(contact_id: permitted_params[:contact_id])
end
def permitted_params
params.permit(:inbox_id, :contact_id)
end
end
@@ -3,15 +3,19 @@ class Api::V1::Accounts::InboxMembersController < Api::V1::Accounts::BaseControl
before_action :current_agents_ids, only: [:create]
def create
# update also done via same action
update_agents_list
head :ok
rescue StandardError => e
Rails.logger.debug "Rescued: #{e.inspect}"
render_could_not_create_error('Could not add agents to inbox')
authorize @inbox, :create?
begin
# update also done via same action
update_agents_list
head :ok
rescue StandardError => e
Rails.logger.debug { "Rescued: #{e.inspect}" }
render_could_not_create_error('Could not add agents to inbox')
end
end
def show
authorize @inbox, :show?
@agents = Current.account.users.where(id: @inbox.members.select(:user_id))
end
@@ -11,6 +11,10 @@ class Api::V1::Accounts::InboxesController < Api::V1::Accounts::BaseController
@assignable_agents = (Current.account.users.where(id: @inbox.members.select(:user_id)) + Current.account.administrators).uniq
end
def campaigns
@campaigns = @inbox.campaigns
end
def create
ActiveRecord::Base.transaction do
channel = create_channel
@@ -34,6 +38,10 @@ class Api::V1::Accounts::InboxesController < Api::V1::Accounts::BaseController
update_channel_feature_flags
end
def agent_bot
@agent_bot = @inbox.agent_bot
end
def set_agent_bot
if @agent_bot
agent_bot_inbox = @inbox.agent_bot_inbox || AgentBotInbox.new(inbox: @inbox)
@@ -54,6 +62,7 @@ class Api::V1::Accounts::InboxesController < Api::V1::Accounts::BaseController
def fetch_inbox
@inbox = Current.account.inboxes.find(params[:id])
authorize @inbox, :show?
end
def fetch_agent_bot
@@ -79,12 +88,12 @@ class Api::V1::Accounts::InboxesController < Api::V1::Accounts::BaseController
end
def permitted_params
params.permit(:id, :avatar, :name, :greeting_message, :greeting_enabled, channel:
params.permit(:id, :avatar, :name, :greeting_message, :greeting_enabled, :enable_email_collect, :csat_survey_enabled, channel:
[:type, :website_url, :widget_color, :welcome_title, :welcome_tagline, :webhook_url, :email, :reply_time])
end
def inbox_update_params
params.permit(:enable_auto_assignment, :name, :avatar, :greeting_message, :greeting_enabled,
params.permit(:enable_auto_assignment, :enable_email_collect, :name, :avatar, :greeting_message, :greeting_enabled, :csat_survey_enabled,
:working_hours_enabled, :out_of_office_message, :timezone,
channel: [
:website_url,
@@ -1,4 +1,5 @@
class Api::V1::Accounts::Integrations::AppsController < Api::V1::Accounts::BaseController
before_action :check_admin_authorization?
before_action :fetch_apps, only: [:index]
before_action :fetch_app, only: [:show]
@@ -0,0 +1,31 @@
class Api::V1::Accounts::Integrations::HooksController < Api::V1::Accounts::BaseController
before_action :fetch_hook, only: [:update, :destroy]
before_action :check_authorization
def create
@hook = Current.account.hooks.create!(permitted_params)
end
def update
@hook.update!(permitted_params.slice(:status, :settings))
end
def destroy
@hook.destroy
head :ok
end
private
def fetch_hook
@hook = Current.account.hooks.find(params[:id])
end
def check_authorization
authorize(:hook)
end
def permitted_params
params.require(:hook).permit(:app_id, :inbox_id, :status, settings: {})
end
end
@@ -1,4 +1,5 @@
class Api::V1::Accounts::Integrations::SlackController < Api::V1::Accounts::BaseController
before_action :check_admin_authorization?
before_action :fetch_hook, only: [:update, :destroy]
def create
@@ -1,7 +1,6 @@
class Api::V1::Accounts::NotificationsController < Api::V1::Accounts::BaseController
RESULTS_PER_PAGE = 15
protect_from_forgery with: :null_session
before_action :fetch_notification, only: [:update]
before_action :set_primary_actor, only: [:read_all]
before_action :set_current_page, only: [:index]
@@ -1,7 +1,6 @@
class Api::V1::AccountsController < Api::BaseController
include AuthHelper
skip_before_action :verify_authenticity_token, only: [:create]
skip_before_action :authenticate_user!, :set_current_user, :handle_with_exception,
only: [:create], raise: false
before_action :check_signup_enabled, only: [:create]
@@ -18,7 +17,7 @@ class Api::V1::AccountsController < Api::BaseController
account_name: account_params[:account_name],
user_full_name: account_params[:user_full_name],
email: account_params[:email],
confirmed: confirmed?,
user_password: account_params[:password],
user: current_user
).perform
if @user
@@ -46,17 +45,13 @@ class Api::V1::AccountsController < Api::BaseController
private
def confirmed?
super_admin? && params[:confirmed]
end
def fetch_account
@account = current_user.accounts.find(params[:id])
@current_account_user = @account.account_users.find_by(user_id: current_user.id)
end
def account_params
params.permit(:account_name, :email, :name, :locale, :domain, :support_email, :auto_resolve_duration, :user_full_name)
params.permit(:account_name, :email, :name, :password, :locale, :domain, :support_email, :auto_resolve_duration, :user_full_name)
end
def check_signup_enabled
@@ -1,7 +0,0 @@
class Api::V1::AgentBotsController < Api::BaseController
skip_before_action :authenticate_user!
def index
render json: AgentBot.all
end
end
+14 -2
View File
@@ -6,6 +6,12 @@ class Api::V1::ProfilesController < Api::BaseController
end
def update
if password_params[:password].present?
render_could_not_create_error('Invalid current password') and return unless @user.valid_password?(password_params[:current_password])
@user.update!(password_params.except(:current_password))
end
@user.update!(profile_params)
end
@@ -20,11 +26,17 @@ class Api::V1::ProfilesController < Api::BaseController
:email,
:name,
:display_name,
:password,
:password_confirmation,
:avatar,
:availability,
ui_settings: {}
)
end
def password_params
params.require(:profile).permit(
:current_password,
:password,
:password_confirmation
)
end
end
@@ -10,7 +10,7 @@ class Api::V1::WebhooksController < ApplicationController
twitter_consumer.consume
head :ok
rescue StandardError => e
Raven.capture_exception(e)
Sentry.capture_exception(e)
head :ok
end
@@ -94,6 +94,10 @@ class Api::V1::Widget::BaseController < ApplicationController
{ timestamp: permitted_params[:message][:timestamp] }
end
def permitted_params
params.permit(:website_token)
end
def message_params
{
account_id: conversation.account_id,
@@ -0,0 +1,7 @@
class Api::V1::Widget::CampaignsController < Api::V1::Widget::BaseController
skip_before_action :set_contact
def index
@campaigns = @web_widget.inbox.campaigns.where(enabled: true)
end
end
@@ -0,0 +1,41 @@
class Api::V1::Widget::ConfigsController < Api::V1::Widget::BaseController
before_action :set_global_config
def create
build_contact
set_token
end
private
def set_global_config
@global_config = GlobalConfig.get('LOGO_THUMBNAIL', 'BRAND_NAME', 'WIDGET_BRAND_URL')
end
def set_contact
@contact_inbox = @web_widget.inbox.contact_inboxes.find_by(
source_id: auth_token_params[:source_id]
)
@contact = @contact_inbox&.contact
end
def build_contact
return if @contact.present?
@contact_inbox = @web_widget.create_contact_inbox(additional_attributes)
@contact = @contact_inbox.contact
end
def set_token
payload = { source_id: @contact_inbox.source_id, inbox_id: @web_widget.inbox.id }
@token = ::Widget::TokenService.new(payload: payload).generate_token
end
def additional_attributes
if @web_widget.inbox.account.feature_enabled?('ip_lookup')
{ created_at_ip: request.remote_ip }
else
{}
end
end
end
@@ -29,6 +29,6 @@ class Api::V1::Widget::ContactsController < Api::V1::Widget::BaseController
end
def permitted_params
params.permit(:website_token, :identifier, :identifier_hash, :email, :name, :avatar_url, custom_attributes: {})
params.permit(:website_token, :identifier, :identifier_hash, :email, :name, :avatar_url, :phone_number, custom_attributes: {})
end
end
@@ -23,7 +23,7 @@ class Api::V1::Widget::ConversationsController < Api::V1::Widget::BaseController
def transcript
if permitted_params[:email].present? && conversation.present?
ConversationReplyMailer.conversation_transcript(
ConversationReplyMailer.with(account: conversation.account).conversation_transcript(
conversation,
permitted_params[:email]
)&.deliver_later
@@ -2,7 +2,8 @@ class Api::V1::Widget::EventsController < Api::V1::Widget::BaseController
include Events::Types
def create
Rails.configuration.dispatcher.dispatch(permitted_params[:name], Time.zone.now, contact_inbox: @contact_inbox, event_info: event_info)
Rails.configuration.dispatcher.dispatch(permitted_params[:name], Time.zone.now, contact_inbox: @contact_inbox,
event_info: permitted_params[:event_info].to_h.merge(event_info))
head :no_content
end
@@ -17,6 +18,6 @@ class Api::V1::Widget::EventsController < Api::V1::Widget::BaseController
end
def permitted_params
params.permit(:name, :website_token)
params.permit(:name, :website_token, event_info: {})
end
end
@@ -4,10 +4,4 @@ class Api::V1::Widget::InboxMembersController < Api::V1::Widget::BaseController
def index
@inbox_members = @web_widget.inbox.inbox_members.includes(:user)
end
private
def permitted_params
params.permit(:website_token)
end
end
@@ -20,7 +20,7 @@ class Api::V1::Widget::MessagesController < Api::V1::Widget::BaseController
@message.update!(message_update_params[:message])
end
rescue StandardError => e
render json: { error: @contact.errors, message: e.message }.to_json, status: 500
render json: { error: @contact.errors, message: e.message }.to_json, status: :internal_server_error
end
private
@@ -54,10 +54,11 @@ class Api::V1::Widget::MessagesController < Api::V1::Widget::BaseController
end
def message_update_params
params.permit(message: [{ submitted_values: [:name, :title, :value] }])
params.permit(message: [{ submitted_values: [:name, :title, :value, { csat_survey_response: [:feedback_message, :rating] }] }])
end
def permitted_params
# timestamp parameter is used in create conversation method
params.permit(:id, :before, :website_token, contact: [:name, :email], message: [:content, :referer_url, :timestamp, :echo_id])
end
@@ -1,4 +1,6 @@
class Api::V2::Accounts::ReportsController < Api::V1::Accounts::BaseController
before_action :check_authorization
def account
builder = V2::ReportBuilder.new(Current.account, account_report_params)
data = builder.build
@@ -23,6 +25,10 @@ class Api::V2::Accounts::ReportsController < Api::V1::Accounts::BaseController
private
def check_authorization
raise Pundit::NotAuthorizedError unless Current.account_user.administrator?
end
def account_summary_params
{
type: :account,
+2 -3
View File
@@ -3,13 +3,12 @@ class ApplicationController < ActionController::Base
include Pundit
include SwitchLocale
protect_from_forgery with: :null_session
skip_before_action :verify_authenticity_token
before_action :set_current_user, unless: :devise_controller?
around_action :switch_locale
around_action :handle_with_exception, unless: :devise_controller?
# after_action :verify_authorized
rescue_from ActiveRecord::RecordInvalid, with: :render_record_invalid
private
@@ -17,7 +16,7 @@ class ApplicationController < ActionController::Base
def handle_with_exception
yield
rescue ActiveRecord::RecordNotFound => e
Raven.capture_exception(e)
Sentry.capture_exception(e)
render_not_found_error('Resource could not be found')
rescue Pundit::NotAuthorizedError
render_unauthorized('You are not authorized to do this action')
@@ -17,13 +17,8 @@ module AccessTokenAuthHelper
Current.user = @resource if current_user.is_a?(User)
end
def super_admin?
@resource.present? && @resource.is_a?(SuperAdmin)
end
def validate_bot_access_token!
return if Current.user.is_a?(User)
return if super_admin?
return if agent_bot_accessible?
render_unauthorized('Access to this endpoint is not authorized for bots')
+2 -2
View File
@@ -1,8 +1,8 @@
module AuthHelper
def send_auth_headers(user)
data = user.create_new_auth_token
response.headers[DeviseTokenAuth.headers_names[:"access-token"]] = data['access-token']
response.headers[DeviseTokenAuth.headers_names[:"token-type"]] = 'Bearer'
response.headers[DeviseTokenAuth.headers_names[:'access-token']] = data['access-token']
response.headers[DeviseTokenAuth.headers_names[:'token-type']] = 'Bearer'
response.headers[DeviseTokenAuth.headers_names[:client]] = data['client']
response.headers[DeviseTokenAuth.headers_names[:expiry]] = data['expiry']
response.headers[DeviseTokenAuth.headers_names[:uid]] = data['uid']
+3 -1
View File
@@ -21,7 +21,9 @@ class DashboardController < ActionController::Base
'PRIVACY_URL',
'DISPLAY_MANIFEST',
'CREATE_NEW_ACCOUNT_FROM_DASHBOARD',
'CHATWOOT_INBOX_TOKEN'
'CHATWOOT_INBOX_TOKEN',
'API_CHANNEL_NAME',
'API_CHANNEL_THUMBNAIL'
).merge(
APP_VERSION: Chatwoot.config[:version]
)
@@ -1,34 +1,29 @@
class DeviseOverrides::ConfirmationsController < Devise::ConfirmationsController
include AuthHelper
skip_before_action :require_no_authentication, raise: false
skip_before_action :authenticate_user!, raise: false
def create
@confirmable = User.find_by(confirmation_token: params[:confirmation_token])
render_confirmation_success and return if @confirmable&.confirm
if confirm
render_confirmation_success
else
render_confirmation_error
end
render_confirmation_error
end
protected
def confirm
@confirmable&.confirm || (@confirmable&.confirmed_at && @confirmable&.reset_password_token)
end
private
def render_confirmation_success
render json: { "message": 'Success', "redirect_url": create_reset_token_link(@confirmable) }, status: :ok
send_auth_headers(@confirmable)
render partial: 'devise/auth.json', locals: { resource: @confirmable }
end
def render_confirmation_error
if @confirmable.blank?
render json: { "message": 'Invalid token', "redirect_url": '/' }, status: 422
render json: { message: 'Invalid token', redirect_url: '/' }, status: :unprocessable_entity
elsif @confirmable.confirmed_at
render json: { "message": 'Already confirmed', "redirect_url": '/' }, status: 422
render json: { message: 'Already confirmed', redirect_url: '/' }, status: :unprocessable_entity
else
render json: { "message": 'Failure', "redirect_url": '/' }, status: 422
render json: { message: 'Failure', redirect_url: '/' }, status: :unprocessable_entity
end
end
@@ -13,7 +13,7 @@ class DeviseOverrides::PasswordsController < Devise::PasswordsController
send_auth_headers(@recoverable)
render partial: 'devise/auth.json', locals: { resource: @recoverable }
else
render json: { "message": 'Invalid token', "redirect_url": '/' }, status: 422
render json: { message: 'Invalid token', redirect_url: '/' }, status: :unprocessable_entity
end
end
@@ -27,7 +27,7 @@ class DeviseOverrides::PasswordsController < Devise::PasswordsController
end
end
protected
private
def reset_password_and_confirmation(recoverable)
recoverable.confirm unless recoverable.confirmed? # confirm if user resets password without confirming anytime before
@@ -40,7 +40,7 @@ class DeviseOverrides::PasswordsController < Devise::PasswordsController
def build_response(message, status)
render json: {
"message": message
message: message
}, status: status
end
end
@@ -27,7 +27,13 @@ class Installation::OnboardingController < ApplicationController
def finish_onboarding
::Redis::Alfred.delete(::Redis::Alfred::CHATWOOT_INSTALLATION_ONBOARDING)
ChatwootHub.register_instance(onboarding_params) if onboarding_params[:subscribe_to_updates]
return if onboarding_params[:subscribe_to_updates].blank?
ChatwootHub.register_instance(
onboarding_params.dig(:user, :company),
onboarding_params.dig(:user, :name),
onboarding_params.dig(:user, :email)
)
end
def ensure_installation_onboarding
@@ -0,0 +1,35 @@
class Platform::Api::V1::AgentBotsController < PlatformController
before_action :set_resource, except: [:index, :create]
before_action :validate_platform_app_permissible, except: [:index, :create]
def index
@resources = @platform_app.platform_app_permissibles.where(permissible_type: 'AgentBot').all
end
def create
@resource = AgentBot.new(agent_bot_params)
@resource.save!
@platform_app.platform_app_permissibles.find_or_create_by(permissible: @resource)
end
def show; end
def update
@resource.update!(agent_bot_params)
end
def destroy
@resource.destroy!
head :ok
end
private
def set_resource
@resource = AgentBot.find(params[:id])
end
def agent_bot_params
params.permit(:name, :description, :account_id, :outgoing_url)
end
end
+1 -3
View File
@@ -1,6 +1,4 @@
class PlatformController < ActionController::Base
protect_from_forgery with: :null_session
class PlatformController < ActionController::API
before_action :ensure_access_token
before_action :set_platform_app
before_action :set_resource, only: [:update, :show, :destroy]
@@ -0,0 +1,32 @@
class Public::Api::V1::CsatSurveyController < PublicController
before_action :set_conversation
before_action :set_message
def show; end
def update
render json: { error: 'You cannot update the CSAT survey after 14 days' }, status: :unprocessable_entity and return if check_csat_locked
@message.update!(message_update_params[:message])
end
private
def set_conversation
return if params[:id].blank?
@conversation = Conversation.find_by!(uuid: params[:id])
end
def set_message
@message = @conversation.messages.find_by!(content_type: 'input_csat')
end
def message_update_params
params.permit(message: [{ submitted_values: [:name, :title, :value, { csat_survey_response: [:feedback_message, :rating] }] }])
end
def check_csat_locked
(Time.zone.now.to_date - @message.created_at.to_date).to_i > 14
end
end
@@ -0,0 +1,48 @@
class Public::Api::V1::Inboxes::ContactsController < Public::Api::V1::InboxesController
before_action :contact_inbox, except: [:create]
before_action :process_hmac
def create
source_id = params[:source_id] || SecureRandom.uuid
@contact_inbox = ::ContactBuilder.new(
source_id: source_id,
inbox: @inbox_channel.inbox,
contact_attributes: permitted_params.except(:identifier, :identifier_hash)
).perform
end
def show; end
def update
contact_identify_action = ContactIdentifyAction.new(
contact: @contact_inbox.contact,
params: permitted_params.to_h.deep_symbolize_keys.except(:identifier)
)
render json: contact_identify_action.perform
end
private
def contact_inbox
@contact_inbox = @inbox_channel.inbox.contact_inboxes.find_by!(source_id: params[:id])
end
def process_hmac
return if params[:identifier_hash].blank? && !@inbox_channel.hmac_mandatory
raise StandardError, 'HMAC failed: Invalid Identifier Hash Provided' unless valid_hmac?
@contact_inbox.update(hmac_verified: true) if @contact_inbox.present?
end
def valid_hmac?
params[:identifier_hash] == OpenSSL::HMAC.hexdigest(
'sha256',
@inbox_channel.hmac_token,
params[:identifier].to_s
)
end
def permitted_params
params.permit(:identifier, :identifier_hash, :email, :name, :avatar_url, custom_attributes: {})
end
end
@@ -0,0 +1,24 @@
class Public::Api::V1::Inboxes::ConversationsController < Public::Api::V1::InboxesController
def index
@conversations = @contact_inbox.hmac_verified? ? @contact.conversations : @contact_inbox.conversations
end
def create
@conversation = create_conversation
end
private
def create_conversation
::Conversation.create!(conversation_params)
end
def conversation_params
{
account_id: @contact_inbox.contact.account_id,
inbox_id: @contact_inbox.inbox_id,
contact_id: @contact_inbox.contact_id,
contact_inbox_id: @contact_inbox.id
}
end
end
@@ -0,0 +1,68 @@
class Public::Api::V1::Inboxes::MessagesController < Public::Api::V1::InboxesController
before_action :set_message, only: [:update]
def index
@messages = @conversation.nil? ? [] : message_finder.perform
end
def create
@message = @conversation.messages.new(message_params)
@message.save
build_attachment
end
def update
@message.update!(message_update_params)
rescue StandardError => e
render json: { error: @contact.errors, message: e.message }.to_json, status: :internal_server_error
end
private
def build_attachment
return if params[:attachments].blank?
params[:attachments].each do |uploaded_attachment|
attachment = @message.attachments.new(
account_id: @message.account_id,
file_type: helpers.file_type(uploaded_attachment&.content_type)
)
attachment.file.attach(uploaded_attachment)
end
@message.save!
end
def message_finder_params
{
filter_internal_messages: true,
before: params[:before]
}
end
def message_finder
@message_finder ||= MessageFinder.new(@conversation, message_finder_params)
end
def message_update_params
params.permit(submitted_values: [:name, :title, :value])
end
def permitted_params
params.permit(:content, :echo_id)
end
def set_message
@message = @conversation.messages.find(params[:id])
end
def message_params
{
account_id: @conversation.account_id,
sender: @contact_inbox.contact,
content: permitted_params[:content],
inbox_id: @conversation.inbox_id,
echo_id: permitted_params[:echo_id],
message_type: :incoming
}
end
end
@@ -0,0 +1,23 @@
class Public::Api::V1::InboxesController < PublicController
before_action :set_inbox_channel
before_action :set_contact_inbox
before_action :set_conversation
private
def set_inbox_channel
@inbox_channel = ::Channel::Api.find_by!(identifier: params[:inbox_id])
end
def set_contact_inbox
return if params[:contact_id].blank?
@contact_inbox = @inbox_channel.inbox.contact_inboxes.find_by!(source_id: params[:contact_id])
end
def set_conversation
return if params[:conversation_id].blank?
@conversation = @contact_inbox.contact.conversations.find_by!(display_id: params[:conversation_id])
end
end
+5
View File
@@ -0,0 +1,5 @@
# TODO: we should switch to ActionController::API for the base classes
# One of the specs is failing when I tried doing that, lets revisit in future
class PublicController < ActionController::Base
skip_before_action :verify_authenticity_token
end
@@ -0,0 +1,10 @@
class Survey::ResponsesController < ActionController::Base
before_action :set_global_config
def show; end
private
def set_global_config
@global_config = GlobalConfig.get('LOGO_THUMBNAIL', 'BRAND_NAME', 'WIDGET_BRAND_URL')
end
end
+1 -1
View File
@@ -3,7 +3,7 @@ class SwaggerController < ApplicationController
if Rails.env.development? || Rails.env.test?
render inline: File.read(Rails.root.join('swagger', derived_path))
else
head 404
head :not_found
end
end
+1
View File
@@ -1,3 +1,4 @@
# TODO : Delete this and associated spec once 'api/widget/config' end point is merged
class WidgetsController < ActionController::Base
before_action :set_global_config
before_action :set_web_widget
+1 -4
View File
@@ -15,8 +15,7 @@ class AgentBotDashboard < Administrate::BaseDashboard
description: Field::String,
outgoing_url: Field::String,
created_at: Field::DateTime,
updated_at: Field::DateTime,
hide_input_for_bot_conversations: Field::Boolean
updated_at: Field::DateTime
}.freeze
# COLLECTION_ATTRIBUTES
@@ -39,7 +38,6 @@ class AgentBotDashboard < Administrate::BaseDashboard
name
description
outgoing_url
hide_input_for_bot_conversations
].freeze
# FORM_ATTRIBUTES
@@ -49,7 +47,6 @@ class AgentBotDashboard < Administrate::BaseDashboard
name
description
outgoing_url
hide_input_for_bot_conversations
].freeze
# COLLECTION_FILTERS
-2
View File
@@ -11,7 +11,6 @@ class SuperAdminDashboard < Administrate::BaseDashboard
id: Field::Number,
email: Field::String,
password: Field::Password,
access_token: Field::HasOne,
remember_created_at: Field::DateTime,
sign_in_count: Field::Number,
current_sign_in_at: Field::DateTime,
@@ -30,7 +29,6 @@ class SuperAdminDashboard < Administrate::BaseDashboard
COLLECTION_ATTRIBUTES = %i[
id
email
access_token
].freeze
# SHOW_PAGE_ATTRIBUTES
+5 -2
View File
@@ -10,9 +10,12 @@ class AsyncDispatcher < BaseDispatcher
def listeners
[
CampaignListener.instance,
CsatSurveyListener.instance,
EventListener.instance,
WebhookListener.instance,
InstallationWebhookListener.instance, HookListener.instance
HookListener.instance,
InstallationWebhookListener.instance,
WebhookListener.instance
]
end
end
+31 -27
View File
@@ -21,6 +21,27 @@ class ConversationFinder
end
def perform
set_up
mine_count, unassigned_count, all_count, = set_count_for_all_conversations
assigned_count = all_count - unassigned_count
filter_by_assignee_type
{
conversations: conversations,
count: {
mine_count: mine_count,
assigned_count: assigned_count,
unassigned_count: unassigned_count,
all_count: all_count
}
}
end
private
def set_up
set_inboxes
set_team
set_assignee_type
@@ -30,33 +51,14 @@ class ConversationFinder
filter_by_team if @team
filter_by_labels if params[:labels]
filter_by_query if params[:q]
mine_count, unassigned_count, all_count = set_count_for_all_conversations
filter_by_assignee_type
{
conversations: conversations,
count: {
mine_count: mine_count,
unassigned_count: unassigned_count,
all_count: all_count
}
}
end
private
def set_inboxes
if params[:inbox_id]
@inbox_ids = current_account.inboxes.where(id: params[:inbox_id])
else
if @current_user.administrator?
@inbox_ids = current_account.inboxes.pluck(:id)
elsif @current_user.agent?
@inbox_ids = @current_user.assigned_inboxes.pluck(:id)
end
end
@inbox_ids = if params[:inbox_id]
current_account.inboxes.where(id: params[:inbox_id])
else
@current_user.assigned_inboxes.pluck(:id)
end
end
def set_assignee_type
@@ -77,6 +79,8 @@ class ConversationFinder
@conversations = @conversations.assigned_to(current_user)
when 'unassigned'
@conversations = @conversations.unassigned
when 'assigned'
@conversations = @conversations.assigned
end
@conversations
end
@@ -110,13 +114,13 @@ class ConversationFinder
end
def current_page
params[:page]
params[:page] || 1
end
def conversations
@conversations = @conversations.includes(
:taggings, :inbox, { assignee: { avatar_attachment: [:blob] } }, { contact: { avatar_attachment: [:blob] } }
:taggings, :inbox, { assignee: { avatar_attachment: [:blob] } }, { contact: { avatar_attachment: [:blob] } }, :team
)
current_page ? @conversations.latest.page(current_page) : @conversations.latest
@conversations.latest.page(current_page)
end
end
+19
View File
@@ -0,0 +1,19 @@
##############################################
# Helpers to implement date range filtering to APIs
# Include in your controller or service class where params is available
##############################################
module DateRangeHelper
def range
return if params[:since].blank? || params[:until].blank?
parse_date_time(params[:since])..parse_date_time(params[:until])
end
def parse_date_time(datetime)
return datetime if datetime.is_a?(DateTime)
return datetime.to_datetime if datetime.is_a?(Time) || datetime.is_a?(Date)
DateTime.strptime(datetime, '%s')
end
end
+6
View File
@@ -0,0 +1,6 @@
module MessageFormatHelper
include RegexHelper
def transform_user_mention_content(message_content)
message_content.gsub(MENTION_REGEX, '\1')
end
end
+27
View File
@@ -3,12 +3,17 @@
<transition name="fade" mode="out-in">
<router-view></router-view>
</transition>
<add-account-modal
:show="showAddAccountModal"
:has-accounts="hasAccounts"
/>
<woot-snackbar-box />
</div>
</template>
<script>
import { mapGetters } from 'vuex';
import AddAccountModal from '../dashboard/components/layout/sidebarComponents/AddAccountModal';
import WootSnackbarBox from './components/SnackbarContainer';
import { accountIdFromPathname } from './helper/URLHelper';
@@ -17,14 +22,36 @@ export default {
components: {
WootSnackbarBox,
AddAccountModal,
},
data() {
return {
showAddAccountModal: false,
};
},
computed: {
...mapGetters({
getAccount: 'accounts/getAccount',
currentUser: 'getCurrentUser',
}),
hasAccounts() {
return (
this.currentUser &&
this.currentUser.accounts &&
this.currentUser.accounts.length !== 0
);
},
},
watch: {
currentUser() {
if (!this.hasAccounts) {
this.showAddAccountModal = true;
}
},
},
mounted() {
this.$store.dispatch('setUser');
this.setLocale(window.chatwootConfig.selectedLocale);
+13 -2
View File
@@ -29,6 +29,7 @@ export default {
account_name: creds.accountName.trim(),
user_full_name: creds.fullName.trim(),
email: creds.email,
password: creds.password,
})
.then(response => {
setAuthCredentials(response);
@@ -95,8 +96,18 @@ export default {
},
verifyPasswordToken({ confirmationToken }) {
return axios.post('auth/confirmation', {
confirmation_token: confirmationToken,
return new Promise((resolve, reject) => {
axios
.post('auth/confirmation', {
confirmation_token: confirmationToken,
})
.then(response => {
setAuthCredentials(response);
resolve(response);
})
.catch(error => {
reject(error.response);
});
});
},
@@ -0,0 +1,9 @@
import ApiClient from './ApiClient';
class CampaignsAPI extends ApiClient {
constructor() {
super('campaigns', { accountScoped: true });
}
}
export default new CampaignsAPI();
@@ -0,0 +1,9 @@
import ApiClient from './ApiClient';
class ContactNotes extends ApiClient {
constructor() {
super('contact_notes', { accountScoped: true });
}
}
export default new ContactNotes();
+35 -4
View File
@@ -1,13 +1,30 @@
/* global axios */
import ApiClient from './ApiClient';
export const buildContactParams = (page, sortAttr, label, search) => {
let params = `include_contact_inboxes=false&page=${page}&sort=${sortAttr}`;
if (search) {
params = `${params}&q=${search}`;
}
if (label) {
params = `${params}&labels[]=${label}`;
}
return params;
};
class ContactAPI extends ApiClient {
constructor() {
super('contacts', { accountScoped: true });
}
get(page) {
return axios.get(`${this.url}?page=${page}`);
get(page, sortAttr = 'name', label = '') {
let requestURL = `${this.url}?${buildContactParams(
page,
sortAttr,
label,
''
)}`;
return axios.get(requestURL);
}
getConversations(contactId) {
@@ -18,8 +35,22 @@ class ContactAPI extends ApiClient {
return axios.get(`${this.url}/${contactId}/contactable_inboxes`);
}
search(search = '', page = 1) {
return axios.get(`${this.url}/search?q=${search}&page=${page}`);
getContactLabels(contactId) {
return axios.get(`${this.url}/${contactId}/labels`);
}
updateContactLabels(contactId, labels) {
return axios.post(`${this.url}/${contactId}/labels`, { labels });
}
search(search = '', page = 1, sortAttr = 'name', label = '') {
let requestURL = `${this.url}/search?${buildContactParams(
page,
sortAttr,
label,
search
)}`;
return axios.get(requestURL);
}
}
@@ -0,0 +1,22 @@
/* global axios */
import ApiClient from './ApiClient';
class CSATReportsAPI extends ApiClient {
constructor() {
super('csat_survey_responses', { accountScoped: true });
}
get({ page, from, to } = {}) {
return axios.get(this.url, {
params: { page, since: from, until: to, sort: '-created_at' },
});
}
getMetrics({ from, to } = {}) {
return axios.get(`${this.url}/metrics`, {
params: { since: from, until: to },
});
}
}
export default new CSATReportsAPI();
@@ -28,9 +28,10 @@ class ConversationApi extends ApiClient {
});
}
toggleStatus({ conversationId, status }) {
toggleStatus({ conversationId, status, snoozedUntil = null }) {
return axios.post(`${this.url}/${conversationId}/toggle_status`, {
status,
snoozed_until: snoozedUntil,
});
}
+38 -9
View File
@@ -2,6 +2,33 @@
/* global axios */
import ApiClient from '../ApiClient';
export const buildCreatePayload = ({
message,
isPrivate,
contentAttributes,
echoId,
file,
}) => {
let payload;
if (file) {
payload = new FormData();
payload.append('attachments[]', file, file.name);
if (message) {
payload.append('content', message);
}
payload.append('private', isPrivate);
payload.append('echo_id', echoId);
} else {
payload = {
content: message,
private: isPrivate,
echo_id: echoId,
content_attributes: contentAttributes,
};
}
return payload;
};
class MessageApi extends ApiClient {
constructor() {
super('conversations', { accountScoped: true });
@@ -15,21 +42,23 @@ class MessageApi extends ApiClient {
echo_id: echoId,
file,
}) {
const formData = new FormData();
if (file) formData.append('attachments[]', file, file.name);
if (message) formData.append('content', message);
if (contentAttributes)
formData.append('content_attributes', JSON.stringify(contentAttributes));
formData.append('private', isPrivate);
formData.append('echo_id', echoId);
return axios({
method: 'post',
url: `${this.url}/${conversationId}/messages`,
data: formData,
data: buildCreatePayload({
message,
isPrivate,
contentAttributes,
echoId,
file,
}),
});
}
delete(conversationID, messageId) {
return axios.delete(`${this.url}/${conversationID}/messages/${messageId}`);
}
getPreviousMessages({ conversationId, before }) {
return axios.get(`${this.url}/${conversationId}/messages`, {
params: { before },
+9
View File
@@ -1,9 +1,18 @@
/* global axios */
import ApiClient from './ApiClient';
class Inboxes extends ApiClient {
constructor() {
super('inboxes', { accountScoped: true });
}
getAssignableAgents(inboxId) {
return axios.get(`${this.url}/${inboxId}/assignable_agents`);
}
getCampaigns(inboxId) {
return axios.get(`${this.url}/${inboxId}/campaigns`);
}
}
export default new Inboxes();
@@ -16,6 +16,14 @@ class IntegrationsAPI extends ApiClient {
delete(integrationId) {
return axios.delete(`${this.baseUrl()}/integrations/${integrationId}`);
}
createHook(hookData) {
return axios.post(`${this.baseUrl()}/integrations/hooks`, hookData);
}
deleteHook(hookId) {
return axios.delete(`${this.baseUrl()}/integrations/hooks/${hookId}`);
}
}
export default new IntegrationsAPI();
@@ -0,0 +1,26 @@
import accountAPI from '../account';
import ApiClient from '../ApiClient';
import describeWithAPIMock from './apiSpecHelper';
describe('#accountAPI', () => {
it('creates correct instance', () => {
expect(accountAPI).toBeInstanceOf(ApiClient);
expect(accountAPI).toHaveProperty('get');
expect(accountAPI).toHaveProperty('show');
expect(accountAPI).toHaveProperty('create');
expect(accountAPI).toHaveProperty('update');
expect(accountAPI).toHaveProperty('delete');
expect(accountAPI).toHaveProperty('createAccount');
});
describeWithAPIMock('API calls', context => {
it('#createAccount', () => {
accountAPI.createAccount({
name: 'Chatwoot',
});
expect(context.axiosMock.post).toHaveBeenCalledWith('/api/v1/accounts', {
name: 'Chatwoot',
});
});
});
});

Some files were not shown because too many files have changed in this diff Show More