Compare commits

..
267 Commits
Author SHA1 Message Date
Sojan 2063e248a9 Merge branch 'release/1.7.0' 2020-08-11 10:17:32 +05:30
Sojan JoseandGitHub 4216d63311 feat: Ability to reply to specific tweets (#1117)
Ability to choose a specific tweet to reply to

Fixes #982
Co-authored-by: Pranav Raj S <pranav@thoughtwoot.com>
2020-08-11 09:57:42 +05:30
a6a62d92bf chore: Add Additional Contact APIs (#1130)
Co-authored-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>
2020-08-10 12:15:29 +05:30
Pranav Raj SandGitHub ec3c2ed4bb chore: Use 'Lax' instead of 'None' (#1129)
fixes: #919 

Signed-off-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>
2020-08-09 18:13:17 +05:30
Pranav Raj SandGitHub 0adbc346df feat: Add an extended bubble design for the widget (#1123)
* feat: Add a new design for chat bubble

Signed-off-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>

* Add i18n

* Fix stye issues

* Set fixed font-size

* Update docs for bubble
2020-08-09 16:07:32 +05:30
Sojan JoseandGitHub a04ca24def feat: Customisable Email Templates (#1095) 2020-08-06 15:21:06 +05:30
Pranav Raj SandGitHub db877453a4 feat: Disable attachments and emoji picker in the web widget (#1102)
Signed-off-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>
2020-08-05 17:46:17 +05:30
Pranav Raj SandGitHub 3b23aa7913 Bugfix: Use server timestamp to set agent_last_seen_at (#1114) 2020-08-03 13:40:20 +05:30
Pranav Raj SandGitHub 941272cccd Bugfix: Move integration logos out of public/assets (#1115) 2020-08-03 13:22:51 +05:30
Pranav Raj SandGitHub a5262a6ff3 chore: Fix scroll in message view (#1113)
* chore: Fix scrolling in messages view

* Remove console.log

* Remove inline-style
2020-08-01 23:36:59 +05:30
Pranav Raj SandGitHub 5e5f34bedc chore: Add an indicator for incoming emails (#1112) 2020-08-01 20:56:47 +05:30
Pranav Raj SandGitHub 6d4cfcceba Chore: Update the usage of available_name instead of name (#1110)
Update the usage of available_name instead of the name
2020-08-01 18:30:47 +05:30
Pranav Raj SandGitHub 1dd3573c39 bug: Fix emails if assignee is nil (#1106) 2020-07-31 15:57:19 +05:30
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
4b773f2a0e Chore: Bump elliptic from 6.5.2 to 6.5.3 (#1104)
Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.2 to 6.5.3.
- [Release notes](https://github.com/indutny/elliptic/releases)
- [Commits](https://github.com/indutny/elliptic/compare/v6.5.2...v6.5.3)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-07-31 00:08:06 +05:30
Pranav Raj SandGitHub a16d2c653d Chore: Update mailer to remove hardcoded twitter link (#1103) 2020-07-30 23:58:42 +05:30
Pranav Raj SandGitHub 2b1d445003 feat: Use display_name instead of name of the agent (#1097)
* feat: Use display_name instead of name of the agent
2020-07-27 22:19:26 +05:30
Pranav Raj Sreepuram f30c8943d9 chore: Display app version on settings/admin area 2020-07-26 21:50:59 +05:30
Pranav Raj SandGitHub 703027b834 chore: Add showBadge prop to tabs-item component (#1096) 2020-07-26 17:46:29 +05:30
89ed0b425b feat: Ability for the logged in user to create a new account (#985)
Co-authored-by: Divyesh <dkothari@box8.in>
Co-authored-by: Pranav Raj S <pranav@thoughtwoot.com>
2020-07-26 12:54:50 +05:30
Bharat PatodiandGitHub 858b72a404 chore: Update documentation in sdk, channels (#1094)
Fix channels/whatsapp-sms-twilio.md
2020-07-26 12:18:54 +05:30
0f2d3418f9 fix: Add a check for 24 hour window before sending a message (#1084)
Co-authored-by: Sojan Jose <sojan@pepalo.com>
2020-07-25 22:54:45 +05:30
Pranav Raj SandGitHub 12ee7e5d82 feat: Add chatwoot:ready event listener on window (#1091)
* feat: Add `chatwoot:ready` event listener on window

* Add specs

* Rename customEventHelper.js -> CustomEventHelper.js
2020-07-25 21:54:58 +05:30
Bharat PatodiandGitHub 80b083c7fc Chore: Fix Docs for FB Twitter Website Channel (#1085) 2020-07-24 23:34:43 +05:30
Karan SharmaandGitHub bb83d6ee4d Chore: modify docker-compose for prod and local setups (#1090) 2020-07-24 23:34:09 +05:30
Pranav Raj SandGitHub c7d962b20f Chore: Remove selectedChat from store (#1087)
* Chore: Remove selected chat from store
2020-07-23 23:33:17 +05:30
e72f29a26e Chore: Update storage to support s3 compatible services (#1082)
This change is to allow using s3 compatible services such as DigitalOcean spaces, Minio, etc

Co-authored-by: petebytes <pete@apsion.com>
Co-authored-by: Pranav Raj S <pranav@thoughtwoot.com>
2020-07-22 11:53:47 +05:30
d6f309ce22 Chore: Initialize Cypress tests (#1078)
Addresses: #412

Co-authored-by: Pranav Raj S <pranav@thoughtwoot.com>
2020-07-21 20:11:22 +05:30
Sony MathewandGitHub fcb7625616 Bug: Fixed quoted text parsing in incoming email (#1076)
* Bugfix: Fixed quoted text parsing in incoming email

We had changed the support email from an account based
only value to account based with fallback on global config
and environment variable.

Incoming email quoted text parsing was still based on
account based support email. Changed this to utilize
the newly introduced fallback values from global config
and environment for parsing quoted text.

* Bugfix: Added one more sender agnostic regex for quoted text parsing
2020-07-21 12:20:46 +05:30
Sojan JoseandGitHub 8079bf50a0 Feature: API Channel (#1052) 2020-07-21 12:15:24 +05:30
Sojan JoseandGitHub fa04098c20 Chore: Run ConfigLoader on migrate (#1072)
We have been duplicating the config loader migration. Hence let's hook it to rake db:migrate.
2020-07-20 19:38:25 +05:30
Sojan JoseandGitHub d800b55ac6 Chore: Add display name to the user (#1067)
* Chore: Adding browser info to web widget triggered event

fixes: #970

* Chore: Rename nickname to display name

references: #972

* Chore: Change nickname in code

* chore: fix errors

* Chore: update nginx config

fixes: #1057

* Chore: Fix specs
2020-07-20 18:28:14 +05:30
Bharat PatodiandGitHub 29838f9424 Fix grammatical issues in Ubuntu Environment Setup (#1070) 2020-07-20 17:43:38 +05:30
Pranav Raj SandGitHub 440f449e24 Enhancement: Remove v-model.trim from reply box (#1071) 2020-07-20 17:39:36 +05:30
Sony MathewandGitHub b803ae3300 Bugfix: Private notes in emails & broken Signup (#1068)
* Bugfix: Private notes in emails

Private notes were sent in the emails as part of
conversation continuity. Fixed this issue.

Also made the changes to not even queue the mails
if message is a private note.

* Bugfix: Change issue with featurable in signup - passing array
* Bugfix: Added specs for checking  private notes being sent in email
2020-07-20 12:47:02 +05:30
Sony MathewandGitHub 96efc44b82 Chore: Feature lock email settings in UI (#1065)
* Chore: Feature lock email settings in UI

The email settings under account settings needed to be
feature locked in a way different from teh current way for it
to be enabled for accounts in a self hosted scenario.

Some refactorings were also done along with this change.

1. There was a feature flag defined in code in account model called
domain_emails_enabled was used to check if the inbound emails was
enabled for the account. But there was already a feature flag called
"inbound_emails" defined in features.yml. So changed to use this to
check if inbound emails are enabled for an account.
2. Renamed and re-purposed existing `domain_emails_enabled` to
`custom_email_domain_enabled` to use for feature toggling the UI
for email settings.
3. To enable & disable multiple features using the featurable concern
we were passing an array of values. Changed this to accept a comma
separated set of values.

* Chore: Feature lock email settings in UI

Fixed the specs for accounts controller & removed
unneccessary code from Account seetings component in UI

* Chore: Convert newlines to <br>s

Removed the layout used while sending replies in
conversation continuity.

Converted the newlines in the messages to <br/> tags
for the correct HTML rendering.

* Chore: Bug fix in reply email domain

Renamed the function custom_email_domain_enabled  to
inbound_email_enabled.

Fixed bug on setting reply emails's domain.
2020-07-19 23:08:07 +05:30
Sojan JoseandGitHub 7607e8edb4 Chore: Enable bot conversations in status dropdown (#1041) 2020-07-19 21:22:12 +05:30
Sony MathewandGitHub 7ca192615d Chore: Move some email configs to ENV variables (#1064)
For the outgoing emails which has dependency on the incoming
part as well like the conversation continuity part, some of the
config variables used were entirely based on the account attributes.

But this is not true in case of self hosted situations where you have
multiple accounts and have a common config for incoming emails.

So moved out some of the attributes entirely dependednt on the account
to ENV with a fallback to the Global config.

Also, with this changes the name of the agent will be shown in the
email clinet with in the conversation rather than just the support
email address. This has a huge UX impact on the cutomer.

Modified all the necessary unit tests to reflect these changes.

Updated the .env.example file for the new ENV variable.
2020-07-19 15:35:55 +05:30
Sony MathewandGitHub 7ef45e5844 Chore: Change the prefix in reply emails (#1060)
* Chore: Change the prefix in reply emails

In conversation continuity the reply to email was looking like

reply+to+{some_random_hex_id}@reply.chatwoot.com

Changed the prefix to just `reply` instead of `reply+to`.

* Chore: Change reply email prefix in outbound emails

Changed the prefix from `reply+to+` to just `reply+` in
the reply emails in the converstaion related outbound emails.
2020-07-19 13:34:34 +05:30
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
642efe8d40 Chore: Bump lodash from 4.17.15 to 4.17.19 (#1056)
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-07-18 12:05:04 +05:30
Bharat PatodiandGitHub 9e50d22472 Chore: Corrections in docs (#1054) 2020-07-17 20:38:21 +05:30
a18d54b706 Chore: Cleanup attachment handling for Facebook & Whatsapp (#1051)
* Chore: Enable file upload for facebook messenger
* Chore: Fix attachments
* Chore: Fix Specs
* Fix ReplyBox file attachment logic
* Set default value for message

Co-authored-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>
2020-07-17 00:32:32 +05:30
Pranav Raj SandGitHub 196741d975 Bug: Fix translated keys for availability statuses in Greek (#1050) 2020-07-15 22:55:57 +05:30
Sony MathewandGitHub d5a6f5e352 Enhancement: ability to send out an email without summary #1048 (#1049)
Right now as part of conversation continuity, we are using the
ConversationReplyMailer which sends a summary of messages including
the incoming messages when an agent replies. Ideally, we want
to send only the reply of that agent and not a summary when
Conversation continuity is enabled. Added the functionality
to send the reply email without summary. Added required unit
tests to cover the changes.

ref: #1048
2020-07-15 16:33:52 +05:30
Bharat PatodiandGitHub a8ce9ae59c Chore: grammatical changes to readme (#1035) 2020-07-14 22:52:48 +05:30
Sojan 196ffe3ea2 Merge branch 'hotfix/1.6.3' into develop 2020-07-14 21:59:52 +05:30
Sojan 80dcc8339e Merge branch 'hotfix/1.6.3' 2020-07-14 21:59:16 +05:30
TianluandSojan ba5539d9bf Chore: dup module name RoundRobin (#1036)
Fix the duplicate module name in migration
2020-07-14 21:56:49 +05:30
TianluandGitHub a260495f3d Chore: dup module name RoundRobin (#1036)
Fix the duplicate module name in migration
2020-07-14 20:16:03 +05:30
Sojan aac3c27483 Merge branch 'hotfix/1.6.2' into develop
# Conflicts:
#	app.json
2020-07-14 11:38:09 +05:30
Sojan a3d14c1884 Merge branch 'hotfix/1.6.2' 2020-07-14 11:37:09 +05:30
Sojan JoseandGitHub 7483796a14 Chore: Update PostgreSQL version to 12 (#1034)
Fixes: #1031
2020-07-14 11:36:31 +05:30
Pranav Raj SandGitHub 5dcc8a9e81 Chore: Add docs on supported features per channel (#1032)
* Chore: Add docs on supported features per channel
2020-07-12 20:35:47 +05:30
Dmitry ShurcoandGitHub 6f47c07362 Update Heroku deploy (#1030) 2020-07-12 17:10:29 +05:30
24101eb117 Feature: Add persian translation (#1017)
Co-authored-by: Pranav Raj S <pranav@thoughtwoot.com>
2020-07-10 21:08:59 +05:30
e8994165ea Chore: Add migration for feature flags (#1024)
Co-authored-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>
2020-07-10 00:09:40 +05:30
Pranav Raj SandGitHub 929575f11a Chore: Update copy on Signup page (#1023) 2020-07-09 16:28:09 +05:30
Sojan 3593a84f26 Merge branch 'hotfix/1.6.1' into develop 2020-07-08 13:38:54 +05:30
Sojan 6ede64cb6b Merge branch 'hotfix/1.6.1' 2020-07-08 13:38:42 +05:30
Sojan acbb79a7c2 Chore: Fix attribute in reply with summary mailer 2020-07-08 13:37:47 +05:30
Pranav Raj SandGitHub abc335cc8e Feature: Add support for labels in form (#1019) 2020-07-08 13:13:48 +05:30
Sojan d03fad3e1d Merge branch 'release/1.6.0' 2020-07-08 01:00:45 +05:30
a77cc713c2 Chore: Include Tamil, Arabic, other language updates (#1018)
Co-authored-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>
2020-07-08 00:59:30 +05:30
0fc0dc1683 Chore: Refactor round robin logic (#1015)
Co-authored-by: Pranav Raj S <pranav@thoughtwoot.com>
2020-07-08 00:14:07 +05:30
49db9c5d8a Adds unread message bubbles for widget (#943)
Co-authored-by: Sojan <sojan@pepalo.com>
Co-authored-by: Pranav Raj S <pranav@thoughtwoot.com>
2020-07-08 00:04:44 +05:30
6a7d810c95 Bug: Set auto width to email input (#1013)
Co-authored-by: Nithin David Thomas <webofnithin@gmail.com>
2020-07-06 18:14:19 +05:30
Pranav Raj SandGitHub f8a3f2df27 Chore: Add documentation for Mailgun & Mailchimp (#1014) 2020-07-06 13:28:50 +05:30
Sojan JoseandGitHub e41bd56f41 Chore: Feature Flags for Channels (#1010) 2020-07-06 11:16:19 +05:30
Pranav Raj SandGitHub b633126b35 Chore: Fix spelling mistakes in docs (#1012) 2020-07-06 10:37:51 +05:30
43147b3163 Chore: Fix presence for current user (#1001)
Co-authored-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>
2020-07-04 20:03:16 +05:30
4612494923 Chore: Fix issues with conversation data models (#1000)
Co-authored-by: Sojan <sojan@pepalo.com>
2020-07-04 19:46:17 +05:30
36661ea45d Enhancement: Add focus state for reply box in dashboard (#999)
Co-authored-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>
2020-07-04 13:50:44 +05:30
c98907db49 Feature: Availability Statuses (#874)
Co-authored-by: Pranav Raj S <pranav@thoughtwoot.com>
2020-07-04 11:42:47 +05:30
Nithin David ThomasandGitHub bd87927576 Add icon show last reply is from agent on conversation list #997 2020-07-03 23:25:33 +05:30
1b1deb9626 Proposed Enhancements for Chat Bubble Looks in the Dashboard (#992)
* Update Private Note border color

* Added box-shadow to chat bubbles

* Adjustments as suggested in PR #992

Adjustments as suggested in PR #992

* Reintroduced @include border-normal; in L-253

Co-authored-by: Pranav Raj S <pranav@thoughtwoot.com>
2020-07-03 23:17:39 +05:30
c0cd747f29 Bug: Fix duplicate messages while using enter key on slow network (#987)
Co-authored-by: Pranav Raj S <pranav@thoughtwoot.com>
2020-07-02 11:58:02 +05:30
Steven BucciniandGitHub a699d7bdfe Update documentation with Sendgrid instructions (#994)
Add information about how to integrate with Sendgrid.
2020-07-01 23:02:59 +05:30
83be2e1edb Enhancement: Improve the UX for unread conversations (#986)
Co-authored-by: Nithin David Thomas <webofnithin@gmail.com>
2020-07-01 14:40:21 +05:30
MohmmdandGitHub 534409d25a Enhancement: Use Tahoma as fallback for Arabic characters (#991) 2020-07-01 14:16:08 +05:30
Sojan JoseandGitHub cc02611007 Chore: Convert Message Sender to polymorphic (#740)
Fixes #680
2020-06-27 21:34:53 +05:30
Sojan JoseandGitHub 4f83d5451e Chore: Routine Bugfixes and enhancements (#979)
- Fix slack scopes
- Docs for authentication
Fixes: #704 , #973
2020-06-25 23:35:16 +05:30
Pranav Raj SandGitHub 0aab717bb3 Chore: Add default value for label color (#981) 2020-06-25 21:33:56 +05:30
97ad39713b Feature: Improve label experience (#975)
Co-authored-by: Sojan <sojan@pepalo.com>
2020-06-25 21:04:03 +05:30
Pranav Raj SandGitHub 8b61452d56 Chore: Fix typo in Slack documentation (#977)
- Fix typo in slack-integration-setup.md
2020-06-22 13:40:31 +05:30
1ef8d03e18 Feature: Slack - receive messages, create threads, send replies (#974)
Co-authored-by: Pranav Raj S <pranav@thoughtwoot.com>
2020-06-22 13:19:26 +05:30
Sojan aa8a85b8bd Merge branch 'hotfix/1.5.3' into develop 2020-06-22 12:02:58 +05:30
Sojan 77c16932f8 Merge branch 'hotfix/1.5.3' 2020-06-22 12:02:50 +05:30
Sojan d4214cd78f Bugfix: Invalid argument expiry in global config
references: #976
2020-06-22 12:01:26 +05:30
963f173730 Feature: ResizableTextArea in widget and dashboard (#969)
* Create ResizableTextArea component
* Rubocop fixes and spec fixes

Co-authored-by: Sojan <sojan@pepalo.com>
2020-06-18 15:17:45 +05:30
Sojan JoseandGitHub 04f6460afb Chore: Fix failing sidekiq events for contact create (#966) 2020-06-16 19:39:57 +05:30
b0bbd757b5 Chore: FCM Notification Improvements (#957)
Co-authored-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>
2020-06-15 13:36:56 +05:30
Pranav Raj SandGitHub 667e3abbe1 Fix: Set widget bg color for file-bubble (#965) 2020-06-15 13:35:52 +05:30
Pranav Raj SandGitHub 7c6ea3a05a Chore: Fix HTML rendering in ConversationCard (#963) 2020-06-14 18:30:36 +05:30
Pranav Raj SandGitHub 5cb88237f5 Chore: Move conversationStats to a seperate module (#962)
* Chore: Move conversationStats to a seperate module

* Move toggleTyping to conversationTypingStatus

* Remove unused agentTyping flag

* Fix review comments
2020-06-14 14:07:52 +05:30
Nithin David ThomasandGitHub 5ec9af9325 Bug: Add min height to widget input box (#960) 2020-06-14 11:28:38 +05:30
Pranav Raj SandGitHub ecd5e20133 Bug: Replace non-numeric characters in Twilio phonenumber (#958) 2020-06-13 22:29:12 +05:30
944fbf27b7 Enhancement: Improve scrolling on widget #954 (#955)
Co-authored-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>
2020-06-13 20:33:23 +05:30
f9e8bb8e10 Chore: Send browser language in webwidget events (#952)
Co-authored-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>
2020-06-13 00:19:43 +05:30
Subin T PandGitHub ed1c871633 Feature: Slack integration (#783)
- Integrations architecture
- Slack integration
2020-06-12 23:12:47 +05:30
Pranav Raj SandGitHub 4f3b483066 Chore: Add Github Sponsors link 2020-06-12 19:37:00 +05:30
ca7554f88c Chore: Add deployment documentation (#904)
Co-authored-by: Pranav Raj S <pranav@thoughtwoot.com>
2020-06-11 22:34:22 +05:30
Pranav Raj SandGitHub e769282e7f BugFix: Fix missing tagline in inbox settings (#949) 2020-06-10 00:30:37 +05:30
Sojan 3823ad8fef Merge branch 'master' into develop 2020-06-10 00:25:52 +05:30
Sojan 04a0aa5f1b BugFix: Fix twitter DM view
- Fix twitter DMs gettings getting appended to tweet conversation

Fixes: #939
2020-06-10 00:25:05 +05:30
Sojan 991648edcf Merge branch 'hotfix/1.5.2' 2020-06-10 00:24:12 +05:30
Sojan bfe10618fb BugFix: Fix twitter DM view
- Fix twitter DMs gettings getting appended to tweet conversation

Fixes: #939
2020-06-10 00:23:34 +05:30
Pranav Raj SandGitHub eadc33d230 Use empty string as fallback greeting_message if null (#948) 2020-06-10 00:16:34 +05:30
432dad203b Feature: Inbox greeting message (#927)
Co-authored-by: Pranav Raj S <pranav@thoughtwoot.com>
2020-06-09 23:54:35 +05:30
Sojan JoseandGitHub 8b022311c0 Chore: FCM Push Fixes (#934) 2020-06-09 17:42:18 +05:30
Pranav Raj SandGitHub 40481f6462 Bugfix: Update conversation counters in realtime (#944)
* Bug: Update conversation counters in realtime
2020-06-09 16:26:33 +05:30
Pranav Raj SandGitHub 602132119b Fix socket reconnect if the pubsub token is same (#942) 2020-06-08 19:13:33 +05:30
Sojan JoseandGitHub 52d28105e4 Chore: Remove dead code related to billing (#935)
- remove subscription model
- remove billing-related code
2020-06-07 20:31:48 +05:30
Sojan JoseandGitHub 051871a3cd Chore: Code Cleanup in API controllers (#932)
* Chore: Code Cleanup in API controllers

* Remove unnecessary scoping for accounts controller
2020-06-07 13:58:05 +05:30
Sojan JoseandGitHub 3d84568a37 Feature: Label APIs (#931) 2020-06-07 11:17:13 +05:30
Sojan JoseandGitHub 915eefdc36 Bugfix: FCM push subscription builder fix (#930) 2020-06-06 20:53:30 +05:30
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
ede4dd9cb0 Chore: Bump websocket-extensions from 0.1.3 to 0.1.4 (#929)
Bumps [websocket-extensions](https://github.com/faye/websocket-extensions-node) from 0.1.3 to 0.1.4.
- [Release notes](https://github.com/faye/websocket-extensions-node/releases)
- [Changelog](https://github.com/faye/websocket-extensions-node/blob/master/CHANGELOG.md)
- [Commits](https://github.com/faye/websocket-extensions-node/compare/0.1.3...0.1.4)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-06-06 18:56:58 +05:30
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
b7f7b54f9b Chore: Bump websocket-extensions from 0.1.4 to 0.1.5 (#928)
Bumps [websocket-extensions](https://github.com/faye/websocket-extensions-ruby) from 0.1.4 to 0.1.5.
- [Release notes](https://github.com/faye/websocket-extensions-ruby/releases)
- [Changelog](https://github.com/faye/websocket-extensions-ruby/blob/master/CHANGELOG.md)
- [Commits](https://github.com/faye/websocket-extensions-ruby/compare/0.1.4...0.1.5)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-06-06 15:56:54 +05:30
Sojan JoseandGitHub a13a474c23 Chore: Firebase Cloud Messaging Support (#858) 2020-06-05 00:15:50 +05:30
Sojan e783c3af5d Merge branch 'release/1.5.1' 2020-06-04 23:15:08 +05:30
68be7e3ea8 Use db:prepare instead of db:migrate (#924)
Use db:prepare instead of db:migrate

Co-authored-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>
2020-06-04 23:13:10 +05:30
Koichi MATSUMOTOandGitHub 72a25ffdc5 BugFix: Fix undefined method compare_and_save (#921)
Fix undefined method `compare_and_save` in config loader
2020-06-04 22:33:10 +05:30
Sojan 89dafa366e Merge branch 'release/1.5.0' 2020-06-02 23:53:19 +05:30
Sojan JoseandGitHub dafabac796 Chore: Minor bugfixes and housekeeping tasks (#896) 2020-06-02 23:50:39 +05:30
Sojan JoseandGitHub 93d8a25877 Enable Spanish (#913) 2020-06-02 23:43:03 +05:30
f78df91dd2 Chore: Contact Sidebar, conversation cleanup (#908)
- Update sidebar design
- Move every contact data to contacts module
- Revert go to next conversation feature
- Fix issues with new conversation in action cable
- Escape HTML content
- Broadcast event when conversation.contact changes.

Co-authored-by: Sojan <sojan@pepalo.com>
2020-06-02 22:59:02 +05:30
Sojan JoseandGitHub 8c52a3a953 Chore: Remove duplication of user JBuilder (#911)
* Chore: Reduce duplication of user JBuilder
2020-05-31 23:10:20 +05:30
Pranav Raj SandGitHub d4783db6fd Chore: Update app logo, add FRONTEND_URL env var (#910)
Chore: Update app logo, add FRONTEND_URL env var
2020-05-31 21:49:28 +05:30
Pranav Raj SandGitHub 586c616ae3 Bug: Render header title and tagline as HTML (#906)
* Render header title and tagline as HTML
2020-05-31 16:35:10 +05:30
Pranav Raj SandGitHub 884a1c5971 Chore: Add an option to upload a business logo to inbox (#905)
Chore: Add an option to upload a business logo to inbox
2020-05-31 16:16:22 +05:30
Pranav Raj SandGitHub 47ec7ad7c9 Feature: Ability to customise widget color (#903)
- Use Chrome style color-picker
2020-05-30 17:28:00 +05:30
Pranav Raj SandGitHub ec197b077d Chore: Add db migrate to Heroku release phase (#901) 2020-05-29 14:53:27 +05:30
Ronald WalkerandGitHub 10a1758261 Bug: Fix false positive in #toggleAgentTypingStatus spec (#899) 2020-05-29 12:37:18 +05:30
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
a9d93b750c Chore: Bump kaminari from 1.2.0 to 1.2.1 (#898)
Bumps [kaminari](https://github.com/kaminari/kaminari) from 1.2.0 to 1.2.1.
- [Release notes](https://github.com/kaminari/kaminari/releases)
- [Changelog](https://github.com/kaminari/kaminari/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kaminari/kaminari/compare/v1.2.0...v1.2.1)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-05-29 11:14:27 +05:30
Pranav Raj SandGitHub efc59bb43f Bug: Fix account_id in URLs (#894)
* Bug: Fix account_id in URLs
* Fix accountMixin specs
2020-05-26 23:36:42 +05:30
b7a583b2c4 Feature: Ability to switch between multiple accounts (#881)
* Feature: Ability to switch between multiple accounts

* Fix rubocop

* Fix assigned inboxes

* fix auth json

* Add account switcher in UI

* fix ordering on administrate

* Add switch accounts to sidebar

* add account id

* Fix schema.rb timestamp

* Revert "add account id"

This reverts commit 27570f50ef584cb9a5f69454f43f630b318c8807.

* Add a check for account

Co-authored-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>
2020-05-26 22:38:48 +05:30
Abdulkadir PoyrazandGitHub b1aab228ae Feature: Ability to mute contacts (#891)
fixes: #867
2020-05-26 17:43:59 +05:30
Sojan JoseandGitHub d8d14fc4a4 Feature: Rewamp super admin dashboard (#882) 2020-05-24 22:44:26 +05:30
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
e49a200ee0 Chore: Bump puma from 4.3.4 to 4.3.5 (#890)
Bumps [puma](https://github.com/puma/puma) from 4.3.4 to 4.3.5.
- [Release notes](https://github.com/puma/puma/releases)
- [Changelog](https://github.com/puma/puma/blob/master/History.md)
- [Commits](https://github.com/puma/puma/commits)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-05-23 14:21:13 +05:30
Jean TraulléandGitHub 8ccc00b5ba Add crowdin badge on README.md (#889) 2020-05-22 19:59:26 +05:30
Sony MathewandGitHub 6230f4d796 Enhancement: Threaded email replies for a conversation (#885) (#886)
* Added custom Message-ID and In-Reply-To headers for conversation reply emails
* Added new global config for the default domain (This is used in the above headers)
* Added migration to run the config loader to load the new global config value
* The subject of the conversation reply mailer was made static (This is required for threaded emails)
* Added required tests
2020-05-22 18:14:18 +05:30
Sony MathewandGitHub 11b4b4ea3f Chore: Parse quoted text in incoming emails (#883) (#884)
* Chore: Parse quoted text in incoming emails (#883)
* Parsed the quoted text and replies in incoming emails and store them separately
* Did this parsing for plain text and html part of emails
* In the chat window, we will only show the parsed reply alone

* Conversation mailbox test fixes (#883)
2020-05-22 18:07:06 +05:30
Sony MathewandGitHub 00093fa408 Fixed rspec due to unescaped html check for user name (#887) (#888)
* Fixed the flaky failure in rspec for super admin user controller due to unescaped user name check
2020-05-22 17:58:08 +05:30
Sojan JoseandGitHub f6a9f791cf Chore: Configure Letter opener with environment variable (#880) 2020-05-20 20:02:28 +05:30
Pranav Raj SandGitHub 8af200ad20 Fix missing translations, enable dutch language (#878)
* Fix missing translations, enable dutch language
2020-05-19 19:05:10 +05:30
Pranav Raj SandGitHub 3f5ce2ddbf Bug: Fix autoload_paths for facebook bot (#877)
* Fix autoload_paths for facebook bot
2020-05-18 15:32:26 +05:30
6e92d9be9e Bug: Fix Facebook v7.0 API issues (#863)
* Fix v7.0 API issues

Co-authored-by: Sojan <sojan@pepalo.com>
2020-05-17 23:44:50 +05:30
Pranav Raj SandGitHub b7129785c6 Chore: View form responses as user messages (#876)
* Chore: View form responses as user messages
2020-05-17 22:49:45 +05:30
Pranav Raj SandGitHub 246d0b407f Chore: UI Cleanup in modals (#873)
* Chore: UI Cleanup in modals

* Fix review comments
2020-05-17 21:07:45 +05:30
59f4dd7ebe Feature: Introduce locale in web-widget SDK (#871)
* Introduce setLocale in SDK

Co-authored-by: Sojan <sojan@pepalo.com>
2020-05-17 16:15:53 +05:30
Sojan JoseandGitHub 2e8cd59a06 Add new translations (#862)
* New translations agentMgmt.json (Dutch)

* New translations login.json (Dutch)

* New translations settings.json (French)

* New translations en.json (French)

* New translations en.json (Arabic)

* New translations en.json (Greek)

* New translations settings.json (Portuguese, Brazilian)

* New translations settings.json (Dutch)

* New translations settings.json (Greek)

* New translations signup.json (Ukrainian)

* New translations chatlist.json (Dutch)

* New translations contact.json (Dutch)

* New translations billing.json (Dutch)

* New translations cannedMgmt.json (Dutch)

* New translations inboxMgmt.json (Dutch)

* New translations conversation.json (Dutch)

* New translations generalSettings.json (Dutch)

* New translations en.json (Dutch)

* New translations en.json (Portuguese, Brazilian)

* New translations devise.en.yml (Dutch)

* New translations en.yml (Dutch)
2020-05-16 18:07:19 +05:30
Pranav Raj SandGitHub 467fcda6fc Chore: Add docs for push notifications env (#865) 2020-05-15 12:15:28 +05:30
Sojan JoseandGitHub e6aa851987 Chore: Add Account Users to Admin Panel (#859)
- Show errors on login page
- Dashboard for admin panel
2020-05-14 22:51:51 +05:30
20f39caa42 Chore: Twitter Integration house cleaning (#855)
Co-authored-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>
2020-05-14 22:51:07 +05:30
Bruno AlmeidaandGitHub 1108446974 Feature: Move to the next conversation when I resolve a the current c… (#757)
* Feature: Move to the next conversation when I resolve a the current conversation

* check if nextId is present before emitting the event

* use es6 string literals

* use a named variable for better reading

* create a variable for better readability

* better sintax to get clickable element

* after last, go to first chat when resolve

* use state and action to set next chat

* remove not used emit

* clear selected state when there is not next chat

* Remove deprecated scope from FB Channel (#761)

Remove deprecated scope from FB Channel

* Feature: Customise the position of messenger (#767)

Co-authored-by: Nithin David Thomas <webofnithin@gmail.com>

* Bug: Redirect user to set new password screen (#772)

* auto linter

* fix js linter

* sort chats on getter / filter before getting next chat

* Revert not related changes on ConversationCard.vue

* add test for getNextChatConversation getter

* remove not used module

* add test for getAllConversations getter
2020-05-14 13:43:02 +05:30
Ronald WalkerandGitHub e6bf81caf4 Bug: Fix widget icon alignment in Firefox (#857) 2020-05-14 11:42:53 +05:30
005f332776 Chore: Fixed auto-generated German translations
"canned responses" are no food: "Eingemachte Antwort" -> "Vorgefertigte Antwort"
"open", "closed" status are no verbs, so "Öffnen" -> "Offen", ...
"mine" has nothing to do with mining :) "Bergwerk" -> "Meine"
"home" isn't your home, but the main page "Hauptseite"
"resolve action" is about solutions, not determination, so
"Entschlossenheit" -> "Fall schließen"
"settings" are simply "Einstellungen"

Co-authored-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>
2020-05-13 22:47:31 +05:30
Pranav Raj SandGitHub d219b0a45e Chore: Add an option to toggle web manifest (#848) 2020-05-12 12:45:28 +05:30
f819bc0f33 Chore: Use installation config in frontend (#847)
* Use installation config in widget
* Add configuration for installation in UI
* Add config for mailer

Co-authored-by: Sojan <sojan@pepalo.com>
2020-05-12 01:31:40 +05:30
c74b5c21d7 Feature: Introduce Super Admins (#705)
* Feature: Introduce Super Admins

- added new devise model for super user
- added administrate gem
- sample dashboards for users and accounts

Co-authored-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>
2020-05-11 23:07:22 +05:30
Sony MathewandGitHub 8859880e55 Feature: Global Config helper (#844) (#845)
* Added a global config helper to easily access installation/global configs
  * this will fetch the keys from cache with fallback to DB on cache miss
  * ability to query multiple keys simultaneously
  * interface to delete the existing global config cache
* Added tests for this new helper module
2020-05-11 19:00:33 +05:30
Pranav Raj Sreepuram 6e0f02a38b Merge branch 'hotfix/1.4.2' into develop 2020-05-11 11:43:49 +05:30
Pranav Raj Sreepuram 0f8689b701 Merge branch 'hotfix/1.4.2' 2020-05-11 11:42:46 +05:30
Pranav Raj SandGitHub 70341df9b8 Bug: Use v2 reports instead of v1 reports (#843) 2020-05-11 11:40:14 +05:30
Sony MathewandGitHub 905c93b8f8 Feature: Installation global config (#839) (#840)
* Renamed concern from Feature to Featurable

* Feature: Installation config (#839)
* Added new model installtion config with corresponding migrations and specs
* Created an installation config yml (key value store model)
* Created a config loader module to load the installaltion configs
* Added this to the config loader seeder
* Changed the account before create hook for default feature enabling to use the feature values from installtion config
* Renamed the feature concern to Featurable to follow the naming pattern for concerns
* Added comments and specs for modules and places that deemed necessary

* Refactored config loader to reduce cognitive complexity (#839)
2020-05-10 22:40:36 +05:30
Pranav Raj SandGitHub 76b98cbed4 Bug: Fix inbox.agent_bot nil case (#841) 2020-05-10 22:20:45 +05:30
sony-mathew dedbe08a1c Hotfix: Fixed private notes in email notifications - Merge branch 'master' into develop 2020-05-10 16:38:55 +05:30
sony-mathew a5cc3bae6c Hotfix: Fix for private note in conversation email notifications - Merge branch 'hotfix/1.4.1' into develop 2020-05-10 16:22:53 +05:30
sony-mathew 0d7c1e5e11 Hotfix: Fix for private note in emails - Merge branch 'hotfix/1.4.1' 2020-05-10 16:21:30 +05:30
sony-mathew 35b2bdd074 Removed the un-neccessary attribute from msg create factory 2020-05-10 16:21:02 +05:30
sony-mathew 1a123a4f56 Spec for private messages not to be included in mails (#837) 2020-05-10 16:21:02 +05:30
sony-mathew a75f49ddcc Bug: Fixed private note being sent to customer (#837) 2020-05-10 16:21:02 +05:30
Pranav Raj SandGitHub f28ec29b8c Feature: Customise widget for bot conversations (#834)
* Feature: Customise widget for bot conversations
2020-05-09 22:02:43 +05:30
Sojan JoseandGitHub 05ea6308f2 Chore: Update ruby and rails versions (#831) 2020-05-08 12:13:23 +05:30
Sojan 628e0c8423 Merge branch 'release/1.4.0' 2020-05-06 14:26:24 +05:30
217f33b2b2 Chore: Add ro, fr, pt_BR translation files (#827)
Co-authored-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>
2020-05-06 14:24:20 +05:30
c7120e9637 Chore: Add translated languages to account settings (#826)
* Add available languages in account settings

Co-authored-by: Sojan <sojan@pepalo.com>
2020-05-06 13:38:36 +05:30
Sojan Jose d6d15c8c3c Update Crowdin configuration file 2020-05-06 13:20:31 +05:30
Pranav Raj SandGitHub b50aeb7ae4 Chore: Fix broken conversation mailer layout (#825) 2020-05-06 12:40:41 +05:30
e9131ea558 Feature: Add web push notification permission in frontend (#766)
Add webpush notification permission in frontend

Co-authored-by: Sojan <sojan@pepalo.com>
2020-05-06 00:10:56 +05:30
Vishnu NarayananandGitHub 5bd7a4c511 Feature: Add installation script (#776)
* Feature: Add installation script
* Add systemd service files for chatwoot
* Enable static file serving by rails on prod
* Fix systemd files path
2020-05-05 01:59:38 +05:30
Pranav Raj SandGitHub 44fae4a4a1 Feature: Add default value to ChatForm (#816)
* Feature: Add adefault value to ChatForm
2020-05-04 23:48:47 +05:30
5bc8219db5 Feature: Typing Indicator on widget and dashboard (#811)
* Adds typing indicator for widget
* typing indicator for agents in dashboard

Co-authored-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>
2020-05-04 23:07:56 +05:30
Sony MathewandGitHub fabc3170b7 Feature: Feature flags on account (#814) (#815)
* Feature: Feature flags on account (#814)
* Added the new column on account model for managing feature flags
* Added the inbound email flag
* Locked the settings of this eature in account settings based on this
* Encapsulated the feature flaging as a concern. With this, we can re-use the same concern if we introduce this in other models like user or inbox or so.
* Added the features in the account api

* Changed Rails/FilePath style - rubocop issue

* Revert "Changed Rails/FilePath style - rubocop issue"

This reverts commit 3a42d3b9c9b3a2fde8bc7256fd704b6fcaf54040.

* Disabling rubocop on codeclimate as we already have this in CICD
2020-05-04 23:06:42 +05:30
Pranav Raj SandGitHub 98af47aae2 Fix missing translation key (#810) 2020-05-04 19:07:31 +05:30
Sojan JoseandGitHub ae3f8c02e1 Chore: Add pt and pt_BR translations (#813) 2020-05-04 17:33:55 +05:30
Pranav Raj SandGitHub 19886b40bd Chore: Add enter key listener for email input box (#809)
* Chore: Add enter key listener for email input box
2020-05-03 14:45:33 +05:30
Sojan JoseandGitHub dc6398ab56 Feature: Api to toggle typing status on a conversation (#807)
- api to toggle typing status on a conversation
- clients receive webhook events on the same

Addresses: #718 , #719 , #775
2020-05-03 12:17:27 +05:30
Sojan JoseandGitHub ba1e0dbda0 Chore: Apple site association file for deep linking (#805) 2020-05-03 12:16:11 +05:30
Sojan JoseandGitHub db6e3fb43e Chore: API to get conversations count (#808)
Addresses: #497
2020-05-03 12:15:10 +05:30
Sony MathewandGitHub d8131e450c Chore: Moved the email ingress method to an env variable (#777) (#806)
* Chore: Moved the email ingress method to an env variable (#777)
* Moved the email ingress method in config to an environment variable
* Updated documentation to reflect this

* Added ingress config to staging env config (#777)
* Re-arranged the order in env configs so that email related configs are together
2020-05-02 23:29:09 +05:30
Pranav Raj SandGitHub 8655466fa5 Chore: Add docs for Twilio Whatsapp/SMS channel (#804) 2020-05-02 16:25:57 +05:30
Subin T PandGitHub 7e62000e1b Chore: Remove redis backed reporting (#669) 2020-05-02 16:04:52 +05:30
Pranav Raj SandGitHub ddbc612f0b Bug: Use beginAtZero option for reports (#803) 2020-05-02 15:03:43 +05:30
a951fb20cb Chore: UI fixes and enhancements (#796)
Co-authored-by: Pranav Raj S <pranavrajs@gmail.com>
2020-05-02 09:47:36 +05:30
Pranav Raj SandGitHub 8906fd808d Bug: Fix missing contact_id in conversation list (#798) 2020-05-01 23:00:33 +05:30
Pranav Raj SandGitHub 8f0c4281c7 Chore: Fix data migration in notification settings (#797) 2020-05-01 22:30:57 +05:30
Sojan JoseandGitHub d4ff9f5b45 Chore: Add additional information to conversation meta (#795)
Chore: Add additional information to conversation meta
2020-05-01 16:49:28 +05:30
96da27f1f6 Feature: User Notification Objects (#752)
Co-authored-by: Pranav Raj S <pranavrajs@gmail.com>
2020-05-01 14:53:43 +05:30
Sojan JoseandGitHub 66aace7c13 Chore: Add Romanian, Portuguese, Brazilian, Ukrainian languages (#790)
* New translations integrations.json (Romanian)

* New translations integrations.json (Portuguese, Brazilian)

* New translations integrations.json (Ukrainian)

* New translations integrations.json (Portuguese)

* New translations inboxMgmt.json (French)

* New translations report.json (Portuguese, Brazilian)

* New translations resetPassword.json (Portuguese, Brazilian)

* New translations login.json (Portuguese)

* New translations report.json (Ukrainian)

* New translations report.json (Portuguese)

* New translations cannedMgmt.json (Portuguese)

* New translations billing.json (Portuguese)

* New translations agentMgmt.json (Spanish)

* New translations agentMgmt.json (Portuguese, Brazilian)

* New translations generalSettings.json (French)

* New translations conversation.json (Portuguese, Brazilian)

* New translations conversation.json (Portuguese)

* New translations chatlist.json (Portuguese)

* New translations contact.json (Portuguese)

* New translations chatlist.json (Portuguese, Brazilian)

* New translations en.yml (Portuguese, Brazilian)

* New translations devise.en.yml (Portuguese, Brazilian)

* New translations en.yml (French)

* New translations en.yml (Spanish)

* New translations settings.json (Romanian)

* New translations setNewPassword.json (Portuguese)

* New translations setNewPassword.json (Ukrainian)

* New translations signup.json (Portuguese, Brazilian)

* New translations settings.json (Ukrainian)

* New translations settings.json (Portuguese, Brazilian)
2020-05-01 12:03:46 +05:30
29ae232e54 Feature: Add spanish (Spain) language (#671)
Co-authored-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>
2020-05-01 11:45:46 +05:30
Nithin David ThomasandGitHub 1ebe9fdba9 Fixes broken seeds issue (#788) 2020-05-01 01:22:13 +05:30
0690d3191d Chore: Review docker issues (#787)
* Fix docker issues

Addresses: #701

* Add action cable configurations

Co-authored-by: Sony Mathew <ynos1234@gmail.com>
2020-04-30 21:32:34 +05:30
Sony MathewandGitHub 0b65526b85 Feature: Conversation Continuity with Email (#770)
* Added POC for mail inbox reply email
* created mailbox and migratuion for the same
* cleaned up sidekiq queues and added the queues for action mailbox and active storage
* created conversation mailbox and functionlaity to create a message on the conversation when it's replied

* Added UUID to conversation to be used in email replies

* added migration to add uuid for conversation
* changed parsing and resource fetching to reflect matching uuid and
  loading conversation alone
* cleaned up conversation mailbox.rb

* Added content type & attribute for message

* Added the new reply email to outgoing emails
* Added migration to accounts for adding domain and settings
* Modified seeds to reflect this changes
* Added the flag based column on account for boolean settings
* Added the new reply to email in outgoing conversation emails based on conditions

* Added dynamic email routing in application mailbox
* Added dynamic email routing in application mailbox
* Added a catch all deafult empty mailbox
* Added annotation for account

* Added the complete email details & attachments to the message
* Added the complete email details to the message in content_attributes, like subject, to, cc, bcc etc
* Modified the mail extractor to give a serilaized version of email
* Handled storing attachments of email on the message

* Added incoming email settings, env variables

* [#138] Added documentation regarding different email settings and variables

* Fixed the mail attachments blob issue (#138)
* Decoided attachments were strings and had to construct blobs out fo them to work with active storage
* Fixed the content encoding issue with mail body
* Fixed issue with Proc used in apllication mailbox routing
* Fixed couple of typos and silly mistakes

* Set appropriate from email for conversation reply mails (#138)
* From email was taken from a env variable, changed it to take from account settings if enabled
* Set the reply to email correctly based on conversation UUID
* Added commented config ind development.rb for mailbox ingress

* Added account settings for domain and support email (#138)
* Added the new attributes in accounts controller params whitelisting, api responses
* Added options for the the new fields in account settings

* Fixed typos in email continuity docs and warnings

* Added specs for conversation reply mailer changes (#138)
* Added specs for
  * conversation reply mailer
  * Accounts controller
  * Account and Conversation models

* Added tests for email presenter (#138)

* Specs for inbound email routing and mailboxes (#138)
2020-04-30 20:20:26 +05:30
0cb7333977 Feature: Twilio Whatsapp Integration (#779)
Twilio Whatsapp Integration

Co-authored-by: Sojan <sojan@pepalo.com>
2020-04-30 01:41:13 +05:30
Nithin David ThomasandGitHub 168042f9a4 Feature: Support an Emoji selector in the chat widget (#773)
* Adds emoji widget to web widget
* Style fixes for the send button
* Adds cursor to emoji widget action buttons
2020-04-29 13:54:56 +05:30
Pranav Raj SandGitHub fde4f9271b Bug: Redirect user to set new password screen (#772) 2020-04-27 15:15:05 +05:30
6770126de0 Feature: Customise the position of messenger (#767)
Co-authored-by: Nithin David Thomas <webofnithin@gmail.com>
2020-04-25 23:18:00 +05:30
Andrei PopoviciandGitHub 94192e8184 Remove deprecated scope from FB Channel (#761)
Remove deprecated scope from FB Channel
2020-04-25 22:43:54 +05:30
Sojan JoseandGitHub 3fcd367952 Chore: New Crowdin translations (#747)
Brings in new translation files from Crowdin.
2020-04-23 23:52:16 +05:30
Pranav Raj SandGitHub 7d41b7a5dc Bug: Add account scoping in CTA (#754) 2020-04-23 12:21:45 +05:30
Pranav Raj SandGitHub 645d53db1c Bug: Fix webwidget.triggered event (#750) 2020-04-22 13:14:09 +05:30
Subin T PandGitHub dba6866b6d Bug: Force scoping message metrics to account_id (#748) 2020-04-22 12:49:46 +05:30
Pranav Raj SandGitHub b98a64d2dd Chore: Add S3 bucket policy (#746) 2020-04-20 15:40:45 +05:30
Pranav Raj SandGitHub 06153a96ed Chore: Update swagger API documentation (#745)
* Chore: Update swagger documentation
2020-04-20 11:55:06 +05:30
Vishnu NarayananandGitHub 5736f687e8 Chore: Bump ruby version to 2.7.0 in docs (#744) 2020-04-19 23:41:16 +05:30
941fbb0d72 Chore: Web widget Inbox Tech Debts (#738)
* Chore: Webwidget Inbox Tech Debts

* Additional customization options creating Web Widget
* Changes to edit Page for Web Widget
* Remove the WebWidget API end points
* Minor chores

Address: #680, #502

Co-authored-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>
2020-04-19 23:40:28 +05:30
c0ce70e87b Chore: Events Cleanup (#739)
* Chore: Events Cleanup

- fix failing tests
- add additional webhook events
- clean up event logic

* Fix conversation status update in action cable

Co-authored-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>
2020-04-18 20:25:58 +05:30
Sojan JoseandGitHub ecccb103a0 Chore: Add a real-time event for contact resolution/update (#696)
* Chore: Add a real-time event for contact resolution/update
* Chore: Ensure Events are sent to administrators

Addresses: #419
2020-04-18 13:47:51 +05:30
818c769bb7 Chore: Message to support multiple attachments (#730)
* Changes for the message to have multiple attachments
* changed the message association to attachments from has_one to has_many
* changed all the references of this association in building and fetching to reflect this change

* Added number of attachments validation to the message model

* Modified the backend responses and endpoints to reflect multiple attachment support (#737)

* Changing the frontend components for multiple attachments
* changed the request structure to reflect the multiple attachment structures
* changed the message bubbles to support multiple attachments
* bugfix: agent side attachment was not showing because of a missing await
* broken message was shown because of the store filtering
* Added documentation for ImageMagick

* spec fixes

* refactored code to reflect more apt namings

* Added updated message listener for the dashboard (#727)
* Added the publishing for message updated event
* Implemented the listener for dashboard

Co-authored-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>
2020-04-17 21:15:20 +05:30
Pranav Raj SandGitHub 0817414957 Chore: Upgrade babel packages (#736)
Chore: Upgrade babel
2020-04-17 18:49:14 +05:30
Pranav Raj SandGitHub e3ccfc4662 Bug: Fix IE issues in Widget (#734) 2020-04-17 16:45:12 +05:30
Sony MathewandGitHub 2aa92d46fc Fixed confirmation email flaky tests (#534) (#735) 2020-04-17 15:48:23 +05:30
c2167fcaaf Feature: Add catalan (Catalonia) language (#672)
Co-authored-by: Pranav Raj S <pranavrajs@gmail.com>
2020-04-16 15:28:23 +05:30
Sojan JoseandGitHub 743bddc065 Chore: Housekeeping tasks (#721)
- End point to return api version
- Move agent bot listener to sync dispatcher
- gem update
2020-04-16 15:05:01 +05:30
55892e37f9 Feature: Add Malayalam(മലയാളം) localization (#707) (#708)
* Add Malayalam localization

* Added Malayalam translations for dashboard locales (UI)

 Translations for
* webhooks, signup, settings, set a new password, reset password, report, login, integrations

* Added Malayalam translations for dashboard translations for UI

* Added dashboard Malayalam translations for contact, conversation, general settings and inbox management.

* [707] Add Malayalam translations for dashboard

* [707] Add support for Malayalam

* [707] Fix review comments

* [707] fix review commetns

Co-authored-by: sony-mathew <ynos1234@gmail.com>
2020-04-13 21:14:17 +05:30
Sojan Jose 9db8adab5f Update Crowdin configuration file 2020-04-13 19:01:00 +05:30
Sojan Jose 991585264e Update Crowdin configuration file 2020-04-13 16:47:31 +05:30
b0950d6880 Feature: Rich Message Types (#610)
Co-authored-by: Pranav Raj S <pranavrajs@gmail.com>
Co-authored-by: Nithin David Thomas <webofnithin@gmail.com>
2020-04-10 16:42:37 +05:30
Pranav Raj SandGitHub 48f603798b Chore: Fix URLs in README 2020-04-08 22:46:58 +05:30
1cfa756d49 Chore: APIs for agent bots (#676)
Co-authored-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>
2020-04-07 10:41:18 +05:30
4feca1d88d Feature: Business logo API for web widget (#674)
Co-authored-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>
2020-04-07 10:19:19 +05:30
Sojan e04b37dfcf Merge branch 'release/1.3.0' 2020-04-06 22:58:20 +05:30
99eaf59509 Feature: Ability to set an account name (#667)
* Ability to change the account name 
* Ability to set a language to the account

Addresses: #667  #307  

Co-authored-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>
2020-04-06 22:17:07 +05:30
Pranav Raj SandGitHub a1a81e3799 Feature: Twilio SMS Channel (#658)
Twilio SMS Channel
Fixes :  #350
2020-04-05 22:11:27 +05:30
Sojan JoseandGitHub 8e59564793 Chore: Update ruby version (#665) 2020-04-05 12:11:50 +05:30
cb22b396eb Feature: Website SDK (#653)
Add SDK functions

Co-authored-by: Sojan <sojan@pepalo.com>
2020-04-03 13:04:58 +05:30
7fcd2d0e85 Feature: Support file type messages on widget and dashboard (#659)
- Adds support for file upload

Co-authored-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>
Co-authored-by: Sojan <sojan@pepalo.com>
2020-04-02 12:28:38 +05:30
Sony MathewandGitHub 0afa5c297f Bug: Fixed conversation reply mail issue when an attachment is sent (#655)
* When an attachment was sent in the reply mailer it was breaking due to the
truncating function applied on the last message content. Here the content was empty
and so it broke.
* Fixed this breakage
* Also addressed the issue that if the attachment was there the content was coming empty.
With these changes, made the content to have a link to the attachment.
2020-03-30 16:07:01 +05:30
Nithin David ThomasandGitHub 6c4e1fdaac Feature: Send images from widget 2020-03-30 12:15:06 +05:30
Subin T PandGitHub e56132c506 Non blocking event dispatch (#652)
- Performance improvements for event dispatch
2020-03-29 19:18:30 +05:30
Sojan JoseandGitHub bab9d663d2 Feature: API for updating account settings (#645)
* Feature: API for updating account settings

- API to update account locale
- API to update account name
- API to show account info
2020-03-29 12:16:31 +05:30
Pranav Raj SandGitHub 6c48f2e789 Bug: Update conversation urls in email (#649)
Update the conversation URLs in email templates.
2020-03-29 09:34:57 +05:30
50ebbc13b6 Chore: Add Twitter documentation (#648)
* Chore: Add Twitter documentation

Co-authored-by: Sojan <sojan@pepalo.com>
2020-03-28 11:51:42 +05:30
a3c2d4e5bd Chore: Add Facebook app set up documentation (#647)
Co-authored-by: Pranav Raj S <pranavrajs@gmail.com>
2020-03-28 11:43:02 +05:30
Sony MathewandGitHub 209e0a0fb4 [Feature] Change conversation ordering (#640)
* Update conversation's updated_at when a message is created/saved
* changed teh ordering of conversation to be based on `updated_at` rather than `created_at`
2020-03-24 23:34:22 +05:30
Pranav Raj Sreepuram 2eac97c705 Merge branch 'hotfix/1.2.4' into develop 2020-03-22 20:43:58 +05:30
Pranav Raj Sreepuram bf80a37cfb Merge branch 'hotfix/1.2.4' 2020-03-22 20:41:51 +05:30
Pranav Raj SandGitHub a94f14ca22 Bug: Disable logging to file on asset precompile (#637) 2020-03-22 20:38:49 +05:30
Nithin David ThomasandGitHub f7e5f1fabf Feature: Send attachments to widget user (#621) 2020-03-22 15:54:36 +05:30
Nithin David ThomasandGitHub fe70843fae Feature: Improve Dashboard UI (#630) 2020-03-22 11:44:40 +05:30
Pranav Raj SandGitHub 29bb2bff3a Security: Upgrade eslint, eslint-config-airbnb (#635) 2020-03-21 21:01:55 +05:30
Pranav Raj SandGitHub 4c40d738a5 Security: Upgrade rails to 6.0.2.2 (#636) 2020-03-21 20:54:04 +05:30
Pranav Raj SandGitHub 1c4566df66 Bug: Migrate messages while merging contact (#624)
Bug: Migrate messages while merging contact
2020-03-19 01:52:38 +05:30
Subin T PandGitHub 8f6f07177d Enhancement: Move reporting metrics to postgres (#606) 2020-03-18 16:53:35 +05:30
Sojan f69eb7e542 Merge branch 'hotfix/1.2.3' into develop 2020-03-18 01:53:24 +05:30
Arief LuthfiandGitHub 71d48854b1 Chore: Refactor Facebook send reply service
Refactor Facebook send reply service
2020-03-17 21:09:20 +05:30
Pranav Raj SandGitHub 4d0f46176b Chore: Add web_widget_script in the Inboxes API (#615)
* Chore: Add web_widget_script in the Inboxes API
2020-03-16 12:32:34 +05:30
Nithin David ThomasandGitHub d8599c62dd Feature: Support options message type on widget and dashboard (#614) 2020-03-16 12:08:40 +05:30
ghosteathumanandGitHub bb47b5f086 Reduce long peer dependencies warning (#568)
Co-authored-by: ghosteathuman <46582963+ghosteathuman@users.noreply.github.com>
2020-03-12 22:00:58 +05:30
Nithin David ThomasandGitHub fb17d34b8a Feature: Support card options in widget and dashboard #594 (#609) 2020-03-12 21:23:52 +05:30
Pranav Raj SandGitHub 260e40831a Chore: Update Swagger API docs (#608) 2020-03-11 16:19:13 +05:30
a5b1e2b650 Feature: Access tokens for API access (#604)
Co-authored-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>
2020-03-11 00:02:15 +05:30
19ab0fe108 Chore: Scope URLs with account_id (#601)
* Chore: Enable Users to create multiple accounts

Addresses: #402
- migrations to split roles and other attributes from users table
- make changes in code to accommodate this change

Co-authored-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>
2020-03-09 23:27:10 +05:30
Pranav Raj Sreepuram 2a6670f0da Merge branch 'hotfix/1.2.2' into develop 2020-03-09 10:01:15 +05:30
Pranav Raj Sreepuram 1db35caa56 Merge branch 'release/1.2.1' into develop 2020-03-08 22:15:58 +05:30
60dc564f37 Bug: Fix missing close button on mobile chat window (#600)
Co-authored-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>
2020-03-07 23:39:41 +05:30
8b4df986bf Chore: Enable Users to create multiple accounts (#440)
Addresses: #402
- migrations to split roles and other attributes from users table
- make changes in code to accommodate this change

Co-authored-by: Sojan Jose <sojan@pepalo.com>
Co-authored-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>
2020-03-07 12:18:16 +05:30
b2d5cc7b05 Feature: Introduce bots (#545)
Co-authored-by: Pranav Raj S <pranavrajs@gmail.com>
Co-authored-by: Sojan Jose <sojan@pepalo.com>
2020-03-06 01:43:12 +05:30
1640 changed files with 64610 additions and 8659 deletions
+17 -15
View File
@@ -7,7 +7,7 @@ defaults: &defaults
working_directory: ~/build
docker:
# specify the version you desire here
- image: circleci/ruby:2.6.5-node-browsers
- image: circleci/ruby:2.7.1-node-browsers
# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images
@@ -16,7 +16,7 @@ defaults: &defaults
- image: circleci/redis:alpine
environment:
- CC_TEST_REPORTER_ID: b1b5c4447bf93f6f0b06a64756e35afd0810ea83649f03971cbf303b4449456f
- RAILS_LOG_TO_STDOUT: false
jobs:
build:
<<: *defaults
@@ -69,11 +69,11 @@ jobs:
- run:
name: Download cc-test-reporter
command: |
mkdir -p tmp/
curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./tmp/cc-test-reporter
chmod +x ./tmp/cc-test-reporter
mkdir -p ~/tmp
curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ~/tmp/cc-test-reporter
chmod +x ~/tmp/cc-test-reporter
- persist_to_workspace:
root: tmp
root: ~/tmp
paths:
- cc-test-reporter
@@ -98,10 +98,10 @@ jobs:
- run:
name: Run backend tests
command: |
bundle exec rspec $(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings)
./tmp/cc-test-reporter format-coverage -t simplecov -o tmp/codeclimate.backend.json coverage/backend/.resultset.json
bundle exec rspec $(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings) --profile=10
~/tmp/cc-test-reporter format-coverage -t simplecov -o ~/tmp/codeclimate.backend.json coverage/backend/.resultset.json
- persist_to_workspace:
root: tmp
root: ~/tmp
paths:
- codeclimate.backend.json
@@ -109,21 +109,23 @@ jobs:
name: Run frontend tests
command: |
yarn test:coverage
./tmp/cc-test-reporter format-coverage -t lcov -o tmp/codeclimate.frontend.json buildreports/lcov.info
~/tmp/cc-test-reporter format-coverage -t lcov -o ~/tmp/codeclimate.frontend.json buildreports/lcov.info
- persist_to_workspace:
root: tmp
root: ~/tmp
paths:
- codeclimate.frontend.json
# collect reports
- store_test_results:
path: /tmp/test-results
path: ~/tmp/test-results
- store_artifacts:
path: /tmp/test-results
path: ~/tmp/test-results
destination: test-results
- store_artifacts:
path: log
- run:
name: Upload coverage results to Code Climate
command: |
./tmp/cc-test-reporter sum-coverage tmp/codeclimate.*.json -p 2 -o tmp/codeclimate.total.json
./tmp/cc-test-reporter upload-coverage -i tmp/codeclimate.total.json
~/tmp/cc-test-reporter sum-coverage ~/tmp/codeclimate.*.json -p 2 -o ~/tmp/codeclimate.total.json
~/tmp/cc-test-reporter upload-coverage -i ~/tmp/codeclimate.total.json
+5 -1
View File
@@ -1,7 +1,7 @@
version: "2"
plugins:
rubocop:
enabled: true
enabled: false
channel: rubocop-0-73
eslint:
enabled: false
@@ -26,3 +26,7 @@ exclude_patterns:
- "node_modules/**/*"
- "lib/tasks/auto_annotate_models.rake"
- "app/test-matchers.js"
- "docs/*"
- "**/*.md"
- "**/*.yml"
- "app/javascript/dashboard/i18n/locale"
+71 -33
View File
@@ -1,14 +1,19 @@
SECRET_KEY_BASE=
# Used to verify the integrity of signed cookies. so ensure a secure value is set
SECRET_KEY_BASE=replace_with_lengthy_secure_hex
# Replace with the URL you are planning to use for your app
FRONTEND_URL=http://0.0.0.0:3000
# Force all access to the app over SSL, default is set to false
FORCE_SSL=
FORCE_SSL=false
# This lets you control new sign ups on your chatwoot installation
# true : default option, allows sign ups
# false : disables all the end points related to sign ups
# api_only: disables the UI for signup, but you can create sign ups via the account apis
ENABLE_ACCOUNT_SIGNUP=
ENABLE_ACCOUNT_SIGNUP=true
#redis config
# Redis config
REDIS_URL=redis://redis:6379
# If you are using docker-compose, set this variable's value to be any string,
# which will be the password for the redis service running inside the docker-compose
@@ -22,18 +27,7 @@ POSTGRES_PASSWORD=
RAILS_ENV=development
RAILS_MAX_THREADS=5
#fb app
FB_VERIFY_TOKEN=
FB_APP_SECRET=
FB_APP_ID=
#twitter app
TWITTER_APP_ID=
TWITTER_CONSUMER_KEY=
TWITTER_CONSUMER_SECRET=
TWITTER_ENVIRONMENT=
#mail
# Mail outgoing
MAILER_SENDER_EMAIL=accounts@chatwoot.com
SMTP_PORT=1025
SMTP_DOMAIN=chatwoot.com
@@ -45,32 +39,76 @@ SMTP_PASSWORD=
SMTP_AUTHENTICATION=
SMTP_ENABLE_STARTTLS_AUTO=
#misc
FRONTEND_URL=http://0.0.0.0:3000
# Mail Incoming
# This is the domain set for the reply emails when conversation continuity is enabled
MAILER_INBOUND_EMAIL_DOMAIN=
# Set this to appropriate ingress channel with regards to incoming emails
# Possible values are :
# :relay for Exim, Postfix, Qmail
# :mailgun for Mailgun
# :mandrill for Mandrill
# :postmark for Postmark
# :sendgrid for Sendgrid
RAILS_INBOUND_EMAIL_SERVICE=
# Use one of the following based on the email ingress service
# Ref: https://edgeguides.rubyonrails.org/action_mailbox_basics.html
RAILS_INBOUND_EMAIL_PASSWORD=
MAILGUN_INGRESS_SIGNING_KEY=
MANDRILL_INGRESS_API_KEY=
# Storage
ACTIVE_STORAGE_SERVICE=local
#s3
# Amazon S3
# documentation: https://www.chatwoot.com/docs/configuring-s3-bucket-as-cloud-storage
S3_BUCKET_NAME=
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_REGION=
#sentry
# Sentry
SENTRY_DSN=
#Log settings
LOG_LEVEL=
LOG_SIZE=
# Log settings
# Disable if you want to write logs to a file
RAILS_LOG_TO_STDOUT=true
LOG_LEVEL=info
LOG_SIZE=500
# Credentials to access sidekiq dashboard in production
SIDEKIQ_AUTH_USERNAME=
SIDEKIQ_AUTH_PASSWORD=
### This environment variables are only required if you are setting up social media channels
#### This environment variables are only required in hosted version which has billing
ENABLE_BILLING=
# Facebook
# documentation: https://www.chatwoot.com/docs/facebook-setup
FB_VERIFY_TOKEN=
FB_APP_SECRET=
FB_APP_ID=
## chargebee settings
CHARGEBEE_API_KEY=
CHARGEBEE_SITE=
CHARGEBEE_WEBHOOK_USERNAME=
CHARGEBEE_WEBHOOK_PASSWORD=
# Twitter
# documentation: https://www.chatwoot.com/docs/twitter-app-setup
TWITTER_APP_ID=
TWITTER_CONSUMER_KEY=
TWITTER_CONSUMER_SECRET=
TWITTER_ENVIRONMENT=
#slack integration
SLACK_CLIENT_ID=
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
## Push Notification
## generate a new key value here : https://d3v.one/vapid-key-generator/
# VAPID_PUBLIC_KEY=
# VAPID_PRIVATE_KEY=
#
# for mobile apps
# FCM_SERVER_KEY=
## Bot Customizations
USE_INBOX_AVATAR_FOR_BOT=true
## Development Only Config
# if you want to use letter_opener for local emails
# LETTER_OPENER=true
+4 -3
View File
@@ -1,8 +1,8 @@
module.exports = {
extends: ['airbnb/base', 'prettier', 'plugin:vue/recommended'],
extends: ['airbnb-base/legacy', 'prettier', 'plugin:vue/recommended'],
parserOptions: {
parser: 'babel-eslint',
ecmaVersion: 2017,
ecmaVersion: 2020,
sourceType: 'module',
},
plugins: ['html', 'prettier', 'babel'],
@@ -28,7 +28,7 @@ module.exports = {
}],
'vue/html-self-closing': 'off',
"vue/no-v-html": 'off',
'import/extensions': ['never']
'import/extensions': ['off']
},
settings: {
@@ -46,5 +46,6 @@ module.exports = {
},
globals: {
__WEBPACK_ENV__: true,
bus: true,
},
};
+1
View File
@@ -1 +1,2 @@
open_collective: chatwoot
github: chatwoot
+4
View File
@@ -55,3 +55,7 @@ node_modules
package-lock.json
*.dump
# cypress
test/cypress/videos/*
+1
View File
@@ -0,0 +1 @@
12.16.1
+87 -10
View File
@@ -4,22 +4,54 @@ require:
- rubocop-rspec
inherit_from: .rubocop_todo.yml
Lint/RaiseException:
Enabled: true
Lint/StructNewOverride:
Enabled: true
Lint/DeprecatedOpenSSLConstant:
Enabled: true
Lint/MixedRegexpCaptureTypes:
Enabled: true
Layout/LineLength:
Max: 150
Layout/EmptyLinesAroundAttributeAccessor:
Enabled: true
Layout/SpaceAroundMethodCallOperator:
Enabled: true
Metrics/ClassLength:
Max: 125
Exclude:
- 'app/models/conversation.rb'
RSpec/ExampleLength:
Max: 15
Max: 25
Style/Documentation:
Enabled: false
Style/ExponentialNotation:
Enabled: false
Style/FrozenStringLiteralComment:
Enabled: false
Style/SymbolArray:
Enabled: false
Style/HashEachMethods:
Enabled: true
Style/HashTransformKeys:
Enabled: true
Style/HashTransformValues:
Enabled: true
Style/RedundantFetchBlock:
Enabled: true
Style/RedundantRegexpCharacterClass:
Enabled: true
Style/RedundantRegexpEscape:
Enabled: true
Style/SlicingWithRange:
Enabled: true
Style/GlobalVars:
Exclude:
- 'config/initializers/redis.rb'
- 'lib/redis/alfred.rb'
- 'lib/global_config.rb'
Metrics/BlockLength:
Exclude:
- spec/**/*
@@ -41,14 +73,59 @@ RSpec/NestedGroups:
Max: 4
RSpec/MessageSpies:
Enabled: false
Metrics/MethodLength:
Exclude:
- 'db/migrate/20161123131628_devise_token_auth_create_users.rb'
Rails/CreateTableWithTimestamps:
Exclude:
- 'db/migrate/20170207092002_acts_as_taggable_on_migration.acts_as_taggable_on_engine.rb'
Style/GuardClause:
Exclude:
- 'app/builders/account_builder.rb'
- 'app/models/attachment.rb'
- 'app/models/message.rb'
- 'db/migrate/20190819005836_add_missing_indexes_on_taggings.acts_as_taggable_on_engine.rb'
Metrics/AbcSize:
Exclude:
- 'db/migrate/20190819005836_add_missing_indexes_on_taggings.acts_as_taggable_on_engine.rb'
Metrics/CyclomaticComplexity:
Exclude:
- 'db/migrate/20190819005836_add_missing_indexes_on_taggings.acts_as_taggable_on_engine.rb'
Rails/ReversibleMigration:
Exclude:
- 'db/migrate/20161025070152_removechannelsfrommodels.rb'
- 'db/migrate/20161025070645_remchannel.rb'
- 'db/migrate/20161025070645_remchannel.rb'
- 'db/migrate/20161110102609_removeinboxid.rb'
- 'db/migrate/20170519091539_add_avatar_to_fb.rb'
- 'db/migrate/20191020085608_rename_old_tables.rb'
- 'db/migrate/20191126185833_update_user_invite_foreign_key.rb'
- 'db/migrate/20191130164019_add_template_type_to_messages.rb'
Rails/BulkChangeTable:
Exclude:
- 'db/migrate/20161025070152_removechannelsfrommodels.rb'
- 'db/migrate/20200121190901_create_account_users.rb'
- 'db/migrate/20170211092540_notnullableusers.rb'
- 'db/migrate/20170403095203_contactadder.rb'
- 'db/migrate/20170406104018_add_default_status_conv.rb'
- 'db/migrate/20170511134418_latlong.rb'
- 'db/migrate/20191027054756_create_contact_inboxes.rb'
- 'db/migrate/20191130164019_add_template_type_to_messages.rb'
Rails/UniqueValidationWithoutIndex:
Exclude:
- 'app/models/channel/twitter_profile.rb'
- 'app/models/webhook.rb'
RSpec/NamedSubject:
Enabled: false
AllCops:
Exclude:
- db/*
- bin/**/*
- db/**/*
- config/**/*
- public/**/*
- vendor/**/*
- node_modules/**/*
- lib/tasks/auto_annotate_models.rake
- config/environments/**/*
- 'bin/**/*'
- 'db/schema.rb'
- 'public/**/*'
- 'config/initializers/bot.rb'
- 'vendor/**/*'
- 'node_modules/**/*'
- 'lib/tasks/auto_annotate_models.rake'
- 'config/environments/**/*'
- 'tmp/**/*'
- 'storage/**/*'
-30
View File
@@ -88,7 +88,6 @@ Naming/MemoizedInstanceVariableName:
- 'app/controllers/application_controller.rb'
- 'app/models/message.rb'
- 'lib/integrations/widget/outgoing_message_builder.rb'
- 'lib/webhooks/chargebee.rb'
# Offense count: 4
# Cop supports --auto-correct.
@@ -187,7 +186,6 @@ Rails/EnumHash:
- 'app/models/attachment.rb'
- 'app/models/conversation.rb'
- 'app/models/message.rb'
- 'app/models/subscription.rb'
- 'app/models/user.rb'
# Offense count: 1
@@ -226,7 +224,6 @@ Rails/Output:
Rails/TimeZone:
Exclude:
- 'app/builders/report_builder.rb'
- 'app/models/subscription.rb'
- 'lib/reports/update_account_identity.rb'
- 'lib/reports/update_agent_identity.rb'
- 'lib/reports/update_identity.rb'
@@ -269,33 +266,6 @@ Style/CommentedKeyword:
- 'app/controllers/api/v1/conversations/labels_controller.rb'
- 'app/controllers/api/v1/labels_controller.rb'
# Offense count: 1
# Configuration parameters: EnforcedStyle.
# SupportedStyles: annotated, template, unannotated
Style/FormatStringToken:
Exclude:
- 'lib/constants/redis_keys.rb'
# Offense count: 4
# Configuration parameters: AllowedVariables.
Style/GlobalVars:
Exclude:
- 'lib/redis/alfred.rb'
# Offense count: 7
# Configuration parameters: MinBodyLength.
Style/GuardClause:
Exclude:
- 'app/builders/account_builder.rb'
- 'app/models/attachment.rb'
- 'app/models/message.rb'
- 'lib/webhooks/chargebee.rb'
# Offense count: 4
Style/IdenticalConditionalBranches:
Exclude:
- 'app/controllers/api/v1/reports_controller.rb'
# Offense count: 1
# Configuration parameters: AllowIfModifier.
Style/IfInsideElse:
+1 -1
View File
@@ -1 +1 @@
2.6.5
2.7.1
+7 -3
View File
@@ -51,7 +51,7 @@ linters:
ElsePlacement:
enabled: true
style: same_line # or 'new_line'
style: new_line
EmptyLineBetweenBlocks:
enabled: true
@@ -82,7 +82,7 @@ linters:
enabled: true
ImportantRule:
enabled: true
enabled: false
ImportPath:
enabled: true
@@ -177,6 +177,8 @@ linters:
allow_element_with_attribute: false
allow_element_with_class: false
allow_element_with_id: false
exclude:
- 'app/assets/stylesheets/administrate/components/_buttons.scss'
SelectorDepth:
enabled: true
@@ -252,7 +254,7 @@ linters:
enabled: false
UnnecessaryParentReference:
enabled: true
enabled: false
UrlFormat:
enabled: true
@@ -279,3 +281,5 @@ linters:
exclude:
- 'app/javascript/widget/assets/scss/_reset.scss'
- 'app/javascript/widget/assets/scss/sdk.css'
- 'app/assets/stylesheets/administrate/reset/_normalize.scss'
- 'app/javascript/shared/assets/stylesheets/*.scss'
+30 -16
View File
@@ -1,6 +1,6 @@
source 'https://rubygems.org'
ruby '2.6.5'
ruby '2.7.1'
##-- base gems for rails --##
gem 'rack-cors', require: 'rack/cors'
@@ -8,7 +8,7 @@ gem 'rails'
# Reduces boot times through caching; required in config/boot.rb
gem 'bootsnap', require: false
##-- rails helper gems --##
##-- rails application helper gems --##
gem 'acts-as-taggable-on'
gem 'attr_extras'
gem 'browser'
@@ -17,19 +17,27 @@ gem 'jbuilder'
gem 'kaminari'
gem 'responders'
gem 'rest-client'
gem 'telephone_number'
gem 'time_diff'
gem 'tzinfo-data'
gem 'valid_email2'
# compress javascript config.assets.js_compressor
gem 'uglifier'
##-- used for single column multiple binary flags in notification settings/feature flagging --##
gem 'flag_shih_tzu'
# Random name generator for user names
gem 'haikunator'
# Template parsing safetly
gem 'liquid'
##-- for active storage --##
gem 'aws-sdk-s3', require: false
gem 'azure-storage', require: false
gem 'azure-storage-blob', require: false
gem 'google-cloud-storage', require: false
gem 'mini_magick'
##-- gems for database --#
gem 'groupdate'
gem 'pg'
gem 'redis'
gem 'redis-namespace'
@@ -47,27 +55,24 @@ gem 'devise_token_auth'
# authorization
gem 'jwt'
gem 'pundit'
# super admin
gem 'administrate'
##--- gems for pubsub service ---##
# https://karolgalanciak.com/blog/2019/11/30/from-activerecord-callbacks-to-publish-slash-subscribe-pattern-and-event-driven-design/
gem 'wisper', '2.0.0'
##--- gems for reporting ---##
gem 'nightfury'
##--- gems for billing ---##
gem 'chargebee'
##--- gems for channels ---##
gem 'facebook-messenger'
gem 'telegram-bot-ruby'
gem 'twilio-ruby', '~> 5.32.0'
# twitty will handle subscription of twitter account events
gem 'twitty', git: 'https://github.com/chatwoot/twitty'
# gem 'twitty', git: 'https://github.com/chatwoot/twitty'
gem 'twitty'
# facebook client
gem 'koala'
# Random name generator
gem 'haikunator'
# slack client
gem 'slack-ruby-client'
##--- gems for debugging and error reporting ---##
# static analysis
@@ -78,8 +83,9 @@ gem 'sentry-raven'
##-- background job processing --##
gem 'sidekiq'
##-- used for single column multiple binary flags in notification settings/feature flagging --##
gem 'flag_shih_tzu'
##-- Push notification service --##
gem 'fcm'
gem 'webpush'
group :development do
gem 'annotate'
@@ -91,6 +97,13 @@ group :development do
gem 'json_refs', git: 'https://github.com/tzmfreedom/json_refs', ref: 'e32deb0'
end
group :test do
# Cypress in rails.
gem 'cypress-on-rails', '~> 1.0'
# fast cleaning of database
gem 'database_cleaner'
end
group :development, :test do
# locking until https://github.com/codeclimate/test-reporter/issues/418 is resolved
gem 'action-cable-testing'
@@ -99,7 +112,7 @@ group :development, :test do
gem 'factory_bot_rails'
gem 'faker'
gem 'listen'
gem 'mock_redis'
gem 'mock_redis', git: 'https://github.com/sds/mock_redis', ref: '16d00789f0341a3aac35126c0ffe97a596753ff9'
gem 'pry-rails'
gem 'rspec-rails', '~> 4.0.0.beta2'
gem 'rubocop', require: false
@@ -112,4 +125,5 @@ group :development, :test do
gem 'simplecov', '0.17.1', require: false
gem 'spring'
gem 'spring-watcher-listen'
gem 'webmock'
end
+274 -198
View File
@@ -1,9 +1,9 @@
GIT
remote: https://github.com/chatwoot/twitty
revision: c1edd557401d1e8a197b19e738f82e39507a8e2d
remote: https://github.com/sds/mock_redis
revision: 16d00789f0341a3aac35126c0ffe97a596753ff9
ref: 16d00789f0341a3aac35126c0ffe97a596753ff9
specs:
twitty (0.1.0)
oauth
mock_redis (0.22.0)
GIT
remote: https://github.com/tzmfreedom/json_refs
@@ -16,142 +16,158 @@ GIT
GEM
remote: https://rubygems.org/
specs:
action-cable-testing (0.6.0)
action-cable-testing (0.6.1)
actioncable (>= 5.0)
actioncable (6.0.2.1)
actionpack (= 6.0.2.1)
actioncable (6.0.3.2)
actionpack (= 6.0.3.2)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
actionmailbox (6.0.2.1)
actionpack (= 6.0.2.1)
activejob (= 6.0.2.1)
activerecord (= 6.0.2.1)
activestorage (= 6.0.2.1)
activesupport (= 6.0.2.1)
actionmailbox (6.0.3.2)
actionpack (= 6.0.3.2)
activejob (= 6.0.3.2)
activerecord (= 6.0.3.2)
activestorage (= 6.0.3.2)
activesupport (= 6.0.3.2)
mail (>= 2.7.1)
actionmailer (6.0.2.1)
actionpack (= 6.0.2.1)
actionview (= 6.0.2.1)
activejob (= 6.0.2.1)
actionmailer (6.0.3.2)
actionpack (= 6.0.3.2)
actionview (= 6.0.3.2)
activejob (= 6.0.3.2)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0)
actionpack (6.0.2.1)
actionview (= 6.0.2.1)
activesupport (= 6.0.2.1)
actionpack (6.0.3.2)
actionview (= 6.0.3.2)
activesupport (= 6.0.3.2)
rack (~> 2.0, >= 2.0.8)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0)
actiontext (6.0.2.1)
actionpack (= 6.0.2.1)
activerecord (= 6.0.2.1)
activestorage (= 6.0.2.1)
activesupport (= 6.0.2.1)
actiontext (6.0.3.2)
actionpack (= 6.0.3.2)
activerecord (= 6.0.3.2)
activestorage (= 6.0.3.2)
activesupport (= 6.0.3.2)
nokogiri (>= 1.8.5)
actionview (6.0.2.1)
activesupport (= 6.0.2.1)
actionview (6.0.3.2)
activesupport (= 6.0.3.2)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0)
activejob (6.0.2.1)
activesupport (= 6.0.2.1)
activejob (6.0.3.2)
activesupport (= 6.0.3.2)
globalid (>= 0.3.6)
activemodel (6.0.2.1)
activesupport (= 6.0.2.1)
activerecord (6.0.2.1)
activemodel (= 6.0.2.1)
activesupport (= 6.0.2.1)
activestorage (6.0.2.1)
actionpack (= 6.0.2.1)
activejob (= 6.0.2.1)
activerecord (= 6.0.2.1)
activemodel (6.0.3.2)
activesupport (= 6.0.3.2)
activerecord (6.0.3.2)
activemodel (= 6.0.3.2)
activesupport (= 6.0.3.2)
activestorage (6.0.3.2)
actionpack (= 6.0.3.2)
activejob (= 6.0.3.2)
activerecord (= 6.0.3.2)
marcel (~> 0.3.1)
activesupport (6.0.2.1)
activesupport (6.0.3.2)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
tzinfo (~> 1.1)
zeitwerk (~> 2.2)
zeitwerk (~> 2.2, >= 2.2.2)
acts-as-taggable-on (6.5.0)
activerecord (>= 5.0, < 6.1)
addressable (2.7.0)
public_suffix (>= 2.0.2, < 5.0)
annotate (3.0.3)
administrate (0.13.0)
actionpack (>= 4.2)
actionview (>= 4.2)
activerecord (>= 4.2)
autoprefixer-rails (>= 6.0)
datetime_picker_rails (~> 0.0.7)
jquery-rails (>= 4.0)
kaminari (>= 1.0)
momentjs-rails (~> 2.8)
sassc-rails (~> 2.1)
selectize-rails (~> 0.6)
annotate (3.1.1)
activerecord (>= 3.2, < 7.0)
rake (>= 10.4, < 14.0)
ast (2.4.0)
attr_extras (6.2.3)
aws-eventstream (1.0.3)
aws-partitions (1.269.0)
aws-sdk-core (3.89.1)
aws-eventstream (~> 1.0, >= 1.0.2)
ast (2.4.1)
attr_extras (6.2.4)
autoprefixer-rails (9.8.2)
execjs
aws-eventstream (1.1.0)
aws-partitions (1.332.0)
aws-sdk-core (3.100.0)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.239.0)
aws-sigv4 (~> 1.1)
jmespath (~> 1.0)
aws-sdk-kms (1.28.0)
aws-sdk-core (~> 3, >= 3.71.0)
aws-sdk-kms (1.34.1)
aws-sdk-core (~> 3, >= 3.99.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.60.1)
aws-sdk-core (~> 3, >= 3.83.0)
aws-sdk-s3 (1.69.1)
aws-sdk-core (~> 3, >= 3.99.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.1)
aws-sigv4 (1.1.0)
aws-eventstream (~> 1.0, >= 1.0.2)
aws-sigv4 (1.2.0)
aws-eventstream (~> 1, >= 1.0.2)
axiom-types (0.1.1)
descendants_tracker (~> 0.0.4)
ice_nine (~> 0.11.0)
thread_safe (~> 0.3, >= 0.3.1)
azure-core (0.1.15)
faraday (~> 0.9)
faraday_middleware (~> 0.10)
nokogiri (~> 1.6)
azure-storage (0.15.0.preview)
azure-core (~> 0.1)
faraday (~> 0.9)
faraday_middleware (~> 0.10)
nokogiri (~> 1.6, >= 1.6.8)
azure-storage-blob (2.0.0)
azure-storage-common (~> 2.0)
nokogiri (~> 1.10.4)
azure-storage-common (2.0.1)
faraday (~> 1.0)
faraday_middleware (~> 1.0.0.rc1)
nokogiri (~> 1.10.4)
bcrypt (3.1.13)
bindex (0.8.1)
bootsnap (1.4.5)
bootsnap (1.4.6)
msgpack (~> 1.0)
brakeman (4.7.2)
browser (3.0.3)
brakeman (4.8.2)
browser (4.2.0)
builder (3.2.4)
bullet (6.1.0)
activesupport (>= 3.0.0)
uniform_notifier (~> 1.11)
bundle-audit (0.1.0)
bundler-audit
bundler-audit (0.6.1)
bundler-audit (0.7.0.1)
bundler (>= 1.2.0, < 3)
thor (~> 0.18)
byebug (11.1.1)
chargebee (2.7.3)
json_pure (~> 2.1)
rest-client (>= 1.8, < 3.0)
coderay (1.1.2)
thor (>= 0.18, < 2)
byebug (11.1.3)
coderay (1.1.3)
coercible (1.0.0)
descendants_tracker (~> 0.0.1)
concurrent-ruby (1.1.5)
connection_pool (2.2.2)
concurrent-ruby (1.1.6)
connection_pool (2.2.3)
crack (0.4.3)
safe_yaml (~> 1.0.0)
crass (1.0.6)
cypress-on-rails (1.7.0)
rack
database_cleaner (1.8.5)
datetime_picker_rails (0.0.7)
momentjs-rails (>= 2.8.1)
declarative (0.0.10)
declarative-option (0.1.0)
descendants_tracker (0.0.4)
thread_safe (~> 0.3, >= 0.3.1)
devise (4.7.1)
devise (4.7.2)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 4.1.0)
responders
warden (~> 1.2.3)
devise_token_auth (1.1.3)
devise_token_auth (1.1.4)
bcrypt (~> 3.0)
devise (> 3.5.2, < 5)
rails (>= 4.2.0, < 6.1)
diff-lcs (1.3)
digest-crc (0.4.1)
sprockets (= 3.7.2)
diff-lcs (1.4)
digest-crc (0.5.1)
docile (1.3.2)
domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0)
@@ -162,26 +178,29 @@ GEM
equalizer (0.0.11)
erubi (1.9.0)
execjs (2.7.0)
facebook-messenger (1.4.1)
facebook-messenger (1.5.0)
httparty (~> 0.13, >= 0.13.7)
rack (>= 1.4.5)
factory_bot (5.1.1)
activesupport (>= 4.2.0)
factory_bot_rails (5.1.1)
factory_bot (~> 5.1.0)
railties (>= 4.2.0)
faker (2.10.1)
factory_bot (6.0.2)
activesupport (>= 5.0.0)
factory_bot_rails (6.0.0)
factory_bot (~> 6.0.0)
railties (>= 5.0.0)
faker (2.12.0)
i18n (>= 1.6, < 2)
faraday (0.17.3)
faraday (1.0.1)
multipart-post (>= 1.2, < 3)
faraday_middleware (0.14.0)
faraday (>= 0.7.4, < 1.0)
ffi (1.12.2)
faraday_middleware (1.0.0)
faraday (~> 1.0)
fcm (1.0.1)
faraday (~> 1.0.0)
ffi (1.13.1)
flag_shih_tzu (0.3.23)
foreman (0.87.0)
foreman (0.87.1)
gli (2.19.1)
globalid (0.4.2)
activesupport (>= 4.2.0)
google-api-client (0.36.4)
google-api-client (0.41.0)
addressable (~> 2.5, >= 2.5.1)
googleauth (~> 0.9)
httpclient (>= 2.8.1, < 3.0)
@@ -192,68 +211,75 @@ GEM
google-cloud-core (1.5.0)
google-cloud-env (~> 1.0)
google-cloud-errors (~> 1.0)
google-cloud-env (1.3.0)
faraday (~> 0.11)
google-cloud-errors (1.0.0)
google-cloud-storage (1.25.1)
google-cloud-env (1.3.2)
faraday (>= 0.17.3, < 2.0)
google-cloud-errors (1.0.1)
google-cloud-storage (1.26.2)
addressable (~> 2.5)
digest-crc (~> 0.4)
google-api-client (~> 0.33)
google-cloud-core (~> 1.2)
googleauth (~> 0.9)
mini_mime (~> 1.0)
googleauth (0.10.0)
faraday (~> 0.12)
googleauth (0.13.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.12)
signet (~> 0.14)
groupdate (5.0.0)
activesupport (>= 5)
haikunator (1.1.0)
hana (1.3.5)
hana (1.3.6)
hashdiff (1.0.1)
hashie (4.1.0)
hkdf (0.3.0)
http-accept (1.7.0)
http-cookie (1.0.3)
domain_name (~> 0.5)
httparty (0.17.3)
httparty (0.18.1)
mime-types (~> 3.0)
multi_xml (>= 0.5.2)
httpclient (2.8.3)
i18n (1.8.2)
i18n (1.8.3)
concurrent-ruby (~> 1.0)
ice_nine (0.11.2)
inflecto (0.0.2)
jaro_winkler (1.5.4)
jbuilder (2.9.1)
activesupport (>= 4.2.0)
jbuilder (2.10.0)
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.0)
json_pure (2.2.0)
jwt (2.2.1)
kaminari (1.2.0)
kaminari (1.2.1)
activesupport (>= 4.1.0)
kaminari-actionview (= 1.2.0)
kaminari-activerecord (= 1.2.0)
kaminari-core (= 1.2.0)
kaminari-actionview (1.2.0)
kaminari-actionview (= 1.2.1)
kaminari-activerecord (= 1.2.1)
kaminari-core (= 1.2.1)
kaminari-actionview (1.2.1)
actionview
kaminari-core (= 1.2.0)
kaminari-activerecord (1.2.0)
kaminari-core (= 1.2.1)
kaminari-activerecord (1.2.1)
activerecord
kaminari-core (= 1.2.0)
kaminari-core (1.2.0)
kaminari-core (= 1.2.1)
kaminari-core (1.2.1)
koala (3.0.0)
addressable
faraday
json (>= 1.8)
launchy (2.4.3)
addressable (~> 2.3)
launchy (2.5.0)
addressable (~> 2.7)
letter_opener (1.7.0)
launchy (~> 2.2)
liquid (4.0.3)
listen (3.2.1)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
loofah (2.4.0)
loofah (2.6.0)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
mail (2.7.1)
@@ -261,44 +287,44 @@ GEM
marcel (0.3.3)
mimemagic (~> 0.3.2)
memoist (0.16.2)
method_source (0.9.2)
method_source (1.0.0)
mime-types (3.3.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2019.1009)
mimemagic (0.3.4)
mime-types-data (3.2020.0512)
mimemagic (0.3.5)
mini_magick (4.10.1)
mini_mime (1.0.2)
mini_portile2 (2.4.0)
minitest (5.14.0)
mock_redis (0.22.0)
msgpack (1.3.1)
minitest (5.14.1)
momentjs-rails (2.20.1)
railties (>= 3.1)
msgpack (1.3.3)
multi_json (1.14.1)
multi_xml (0.6.0)
multipart-post (2.1.1)
netrc (0.11.0)
nightfury (1.0.1)
nio4r (2.5.2)
nokogiri (1.10.8)
nokogiri (1.10.9)
mini_portile2 (~> 2.4.0)
oauth (0.5.4)
orm_adapter (0.5.0)
os (1.0.1)
parallel (1.19.1)
parser (2.7.0.2)
ast (~> 2.4.0)
pg (1.2.2)
pry (0.12.2)
coderay (~> 1.1.0)
method_source (~> 0.9.0)
os (1.1.0)
parallel (1.19.2)
parser (2.7.1.4)
ast (~> 2.4.1)
pg (1.2.3)
pry (0.13.1)
coderay (~> 1.1)
method_source (~> 1.0)
pry-rails (0.3.9)
pry (>= 0.10.4)
public_suffix (4.0.3)
puma (4.3.3)
public_suffix (4.0.5)
puma (4.3.5)
nio4r (~> 2.0)
pundit (2.1.0)
activesupport (>= 3.0.0)
rack (2.1.2)
rack-cache (1.11.0)
rack (2.2.3)
rack-cache (1.12.0)
rack (>= 0.4)
rack-cors (1.1.1)
rack (>= 2.0.0)
@@ -308,50 +334,51 @@ GEM
rack
rack-test (1.1.0)
rack (>= 1.0, < 3)
rails (6.0.2.1)
actioncable (= 6.0.2.1)
actionmailbox (= 6.0.2.1)
actionmailer (= 6.0.2.1)
actionpack (= 6.0.2.1)
actiontext (= 6.0.2.1)
actionview (= 6.0.2.1)
activejob (= 6.0.2.1)
activemodel (= 6.0.2.1)
activerecord (= 6.0.2.1)
activestorage (= 6.0.2.1)
activesupport (= 6.0.2.1)
rails (6.0.3.2)
actioncable (= 6.0.3.2)
actionmailbox (= 6.0.3.2)
actionmailer (= 6.0.3.2)
actionpack (= 6.0.3.2)
actiontext (= 6.0.3.2)
actionview (= 6.0.3.2)
activejob (= 6.0.3.2)
activemodel (= 6.0.3.2)
activerecord (= 6.0.3.2)
activestorage (= 6.0.3.2)
activesupport (= 6.0.3.2)
bundler (>= 1.3.0)
railties (= 6.0.2.1)
railties (= 6.0.3.2)
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.2.1)
actionpack (= 6.0.2.1)
activesupport (= 6.0.2.1)
railties (6.0.3.2)
actionpack (= 6.0.3.2)
activesupport (= 6.0.3.2)
method_source
rake (>= 0.8.7)
thor (>= 0.20.3, < 2.0)
rainbow (3.0.0)
rake (13.0.1)
rb-fsevent (0.10.3)
rb-fsevent (0.10.4)
rb-inotify (0.10.1)
ffi (~> 1.0)
redis (4.1.3)
redis (4.2.1)
redis-namespace (1.7.0)
redis (>= 3.0.4)
redis-rack-cache (2.2.1)
rack-cache (>= 1.10, < 2)
redis-store (>= 1.6, < 2)
redis-store (1.8.1)
redis-store (1.9.0)
redis (>= 4, < 5)
regexp_parser (1.7.1)
representable (3.0.4)
declarative (< 0.1.0)
declarative-option (< 0.2.0)
uber (< 0.2.0)
responders (3.0.0)
responders (3.0.1)
actionpack (>= 5.0)
railties (>= 5.0)
rest-client (2.1.0)
@@ -360,15 +387,16 @@ GEM
mime-types (>= 1.16, < 4.0)
netrc (~> 0.8)
retriable (3.1.2)
rspec-core (3.9.1)
rspec-support (~> 3.9.1)
rspec-expectations (3.9.0)
rexml (3.2.4)
rspec-core (3.9.2)
rspec-support (~> 3.9.3)
rspec-expectations (3.9.2)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.9.0)
rspec-mocks (3.9.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.9.0)
rspec-rails (4.0.0.beta4)
rspec-rails (4.0.1)
actionpack (>= 4.2)
activesupport (>= 4.2)
railties (>= 4.2)
@@ -376,46 +404,62 @@ GEM
rspec-expectations (~> 3.9)
rspec-mocks (~> 3.9)
rspec-support (~> 3.9)
rspec-support (3.9.2)
rubocop (0.79.0)
jaro_winkler (~> 1.5.1)
rspec-support (3.9.3)
rubocop (0.86.0)
parallel (~> 1.10)
parser (>= 2.7.0.1)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.7)
rexml
rubocop-ast (>= 0.0.3, < 1.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 1.7)
rubocop-performance (1.5.2)
unicode-display_width (>= 1.4.0, < 2.0)
rubocop-ast (0.0.3)
parser (>= 2.7.0.1)
rubocop-performance (1.6.1)
rubocop (>= 0.71.0)
rubocop-rails (2.4.2)
rubocop-rails (2.6.0)
activesupport (>= 4.2.0)
rack (>= 1.1)
rubocop (>= 0.72.0)
rubocop-rspec (1.37.1)
rubocop (>= 0.82.0)
rubocop-rspec (1.40.0)
rubocop (>= 0.68.1)
ruby-progressbar (1.10.1)
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)
scout_apm (2.6.6)
sassc (2.4.0)
ffi (~> 1.9)
sassc-rails (2.1.2)
railties (>= 4.0.0)
sassc (>= 2.0)
sprockets (> 3.0)
sprockets-rails
tilt
scout_apm (2.6.7)
parser
scss_lint (0.59.0)
sass (~> 3.5, >= 3.5.5)
seed_dump (3.3.1)
activerecord (>= 4)
activesupport (>= 4)
sentry-raven (2.13.0)
faraday (>= 0.7.6, < 1.0)
shoulda-matchers (4.2.0)
selectize-rails (0.12.6)
semantic_range (2.3.0)
sentry-raven (3.0.0)
faraday (>= 1.0)
shoulda-matchers (4.3.0)
activesupport (>= 4.2.0)
sidekiq (6.0.4)
sidekiq (6.0.7)
connection_pool (>= 2.2.2)
rack (>= 2.0.0)
rack (~> 2.0)
rack-protection (>= 2.0.0)
redis (>= 4.1.0)
signet (0.12.0)
signet (0.14.0)
addressable (~> 2.3)
faraday (~> 0.9)
faraday (>= 0.17.3, < 2.0)
jwt (>= 1.5, < 3.0)
multi_json (~> 1.10)
simplecov (0.17.1)
@@ -423,11 +467,18 @@ GEM
json (>= 1.8, < 3)
simplecov-html (~> 0.10.0)
simplecov-html (0.10.2)
slack-ruby-client (0.14.6)
activesupport
faraday (>= 0.9)
faraday_middleware
gli
hashie
websocket-driver
spring (2.1.0)
spring-watcher-listen (2.0.1)
listen (>= 2.7, < 4.0)
spring (>= 1.2, < 3.0)
sprockets (4.0.0)
sprockets (3.7.2)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-rails (3.2.1)
@@ -438,24 +489,32 @@ GEM
faraday
inflecto
virtus
thor (0.20.3)
telephone_number (1.4.7)
thor (1.0.1)
thread_safe (0.3.6)
tilt (2.0.10)
time_diff (0.3.0)
activesupport
i18n
tzinfo (1.2.6)
twilio-ruby (5.32.0)
faraday (~> 1.0.0)
jwt (>= 1.5, <= 2.5)
nokogiri (>= 1.6, < 2.0)
twitty (0.1.1)
oauth
tzinfo (1.2.7)
thread_safe (~> 0.1)
tzinfo-data (1.2019.3)
tzinfo-data (1.2020.1)
tzinfo (>= 1.0.0)
uber (0.1.0)
uglifier (4.2.0)
execjs (>= 0.3.0, < 3)
unf (0.1.4)
unf_ext
unf_ext (0.0.7.6)
unicode-display_width (1.6.1)
unf_ext (0.0.7.7)
unicode-display_width (1.7.0)
uniform_notifier (1.13.0)
valid_email2 (3.1.3)
valid_email2 (3.2.2)
activemodel (>= 3.2)
mail (~> 2.5)
virtus (1.0.5)
@@ -465,20 +524,28 @@ GEM
equalizer (~> 0.0, >= 0.0.9)
warden (1.2.8)
rack (>= 2.0.6)
web-console (4.0.1)
web-console (4.0.3)
actionview (>= 6.0.0)
activemodel (>= 6.0.0)
bindex (>= 0.4.0)
railties (>= 6.0.0)
webpacker (4.2.2)
activesupport (>= 4.2)
webmock (3.8.3)
addressable (>= 2.3.6)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
webpacker (5.1.1)
activesupport (>= 5.2)
rack-proxy (>= 0.6.1)
railties (>= 4.2)
websocket-driver (0.7.1)
railties (>= 5.2)
semantic_range (>= 2.3.0)
webpush (1.0.0)
hkdf (~> 0.2)
jwt (~> 2.0)
websocket-driver (0.7.2)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.4)
websocket-extensions (0.1.5)
wisper (2.0.0)
zeitwerk (2.2.2)
zeitwerk (2.3.0)
PLATFORMS
ruby
@@ -486,26 +553,30 @@ PLATFORMS
DEPENDENCIES
action-cable-testing
acts-as-taggable-on
administrate
annotate
attr_extras
aws-sdk-s3
azure-storage
azure-storage-blob
bootsnap
brakeman
browser
bullet
bundle-audit
byebug
chargebee
cypress-on-rails (~> 1.0)
database_cleaner
devise
devise_token_auth
dotenv-rails
facebook-messenger
factory_bot_rails
faker
fcm
flag_shih_tzu
foreman
google-cloud-storage
groupdate
haikunator
hashie
jbuilder
@@ -514,10 +585,10 @@ DEPENDENCIES
kaminari
koala
letter_opener
liquid
listen
mini_magick
mock_redis
nightfury
mock_redis!
pg
pry-rails
puma
@@ -541,20 +612,25 @@ DEPENDENCIES
shoulda-matchers
sidekiq
simplecov (= 0.17.1)
slack-ruby-client
spring
spring-watcher-listen
telegram-bot-ruby
telephone_number
time_diff
twitty!
twilio-ruby (~> 5.32.0)
twitty
tzinfo-data
uglifier
valid_email2
web-console
webmock
webpacker
webpush
wisper (= 2.0.0)
RUBY VERSION
ruby 2.6.5p114
ruby 2.7.1p83
BUNDLED WITH
2.0.2
2.1.4
+2 -1
View File
@@ -1,2 +1,3 @@
release: bundle exec rails db:prepare
web: bin/rails server -p $PORT -e $RAILS_ENV
worker: bundle exec sidekiq -C config/sidekiq.yml
worker: bundle exec sidekiq -C config/sidekiq.yml
+3
View File
@@ -0,0 +1,3 @@
backend: RAILS_ENV=test bin/rails s -p 5050
frontend: bin/webpack-dev-server
worker: RAILS_ENV=test bundle exec sidekiq -C config/sidekiq.yml
+9 -9
View File
@@ -1,5 +1,5 @@
<p align="center">
<img src="https://storage.googleapis.com/chatwoot-assets/woot-logo.svg" alt="Woot-logo" width="240">
<img src="https://s3.us-west-2.amazonaws.com/gh-assets.chatwoot.com/brand.svg" alt="Woot-logo" width="240">
<div align="center">A simple and elegant live chat software</div>
<div align="center">An opensource alternative to Intercom, Zendesk, Drift, Crisp etc.</div>
@@ -19,17 +19,17 @@ ___
<a href="https://hub.docker.com/r/chatwoot/chatwoot/"><img src="https://img.shields.io/docker/cloud/build/chatwoot/chatwoot" alt="Docker Build Badge"></a>
<img src="https://img.shields.io/github/license/chatwoot/chatwoot" alt="License">
<img src="https://img.shields.io/github/commit-activity/m/chatwoot/chatwoot" alt="Commits-per-month">
<img src="https://img.shields.io/discord/647412545203994635" alt="Discord">
<a href="https://discord.gg/cJXdrwS"><img src="https://img.shields.io/badge/chat-Discord-violet?logo=discord" alt="Chat on Discord"></a>
<a title="Crowdin" target="_self" href="https://chatwoot.crowdin.com/chatwoot"><img src="https://badges.crowdin.net/e/37ced7eba411064bd792feb3b7a28b16/localized.svg"></a>
<a href="https://discord.gg/cJXdrwS"><img src="https://img.shields.io/discord/647412545203994635" alt="Discord"></a>
</p>
![ChatUI progess](https://storage.googleapis.com/chatwoot-assets/dashboard-screen.png)
![ChatUI progess](https://s3.us-west-2.amazonaws.com/gh-assets.chatwoot.com/chatwoot-dashboard-assets.png)
## Background
Chatwoot is a customer support tool for instant messaging channels which can help businesses to provide exceptional customer support. The development of Chatwoot started in 2016 and it failed to succeed as a business and eventually shut the shop in 2017. During 2019 #Hacktoberfest, the maintainers decided to make it opensource 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.
Chatwoot is a customer support tool for instant messaging channels which can help businesses provide exceptional customer support. 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 opensource, 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 we are building it in the open. Thanks to the ideas and contributions 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.
## Documentation
@@ -39,12 +39,12 @@ You can find the quick setup docs [here](https://www.chatwoot.com/docs/quick-set
## Branching model
We use [git-flow](https://nvie.com/posts/a-successful-git-branching-model/) branching model. The base branch is `develop`.
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`.
## Heroku one-click deploy
Deploying chatwoot to heroku, it's a breeze. It's as simple as clicking this button.
Deploying chatwoot to heroku is a breeze. It's as simple as clicking this button:
[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/chatwoot/chatwoot/tree/master)
@@ -54,7 +54,7 @@ Follow this [link](https://www.chatwoot.com/docs/environment-variables) to under
Follow our [docker development guide](https://www.chatwoot.com/docs/installation-guide-docker) to develop and debug the application using `docker-compose`.
Follow our [environment variables](https://www.chatwoot.com/docs/environment-variables/) guide to setup environment for Docker.
Follow our [environment variables](https://www.chatwoot.com/docs/environment-variables/) guide to setup the environment for Docker.
## Contributors ✨
+6 -15
View File
File diff suppressed because one or more lines are too long
+47
View File
@@ -0,0 +1,47 @@
class ContactIdentifyAction
pattr_initialize [:contact!, :params!]
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)
update_contact
end
@contact
end
private
def account
@account ||= @contact.account
end
def existing_identified_contact
return if params[:identifier].blank?
@existing_identified_contact ||= Contact.where(account_id: account.id).find_by(identifier: params[:identifier])
end
def existing_email_contact
return if params[:email].blank?
@existing_email_contact ||= Contact.where(account_id: account.id).find_by(email: params[:email])
end
def merge_contacts?(existing_contact, _contact)
existing_contact && existing_contact.id != @contact.id
end
def update_contact
@contact.update!(params.slice(:name, :email, :identifier))
ContactAvatarJob.perform_later(@contact, params[:avatar_url]) if params[:avatar_url].present?
end
def merge_contact(base_contact, merge_contact)
ContactMergeAction.new(
account: account,
base_contact: base_contact,
mergee_contact: merge_contact
).perform
end
end
+7 -1
View File
@@ -5,9 +5,11 @@ class ContactMergeAction
ActiveRecord::Base.transaction do
validate_contacts
merge_conversations
merge_messages
merge_contact_inboxes
remove_mergee_contact
end
@base_contact
end
private
@@ -15,7 +17,7 @@ class ContactMergeAction
def validate_contacts
return if belongs_to_account?(@base_contact) && belongs_to_account?(@mergee_contact)
raise Exception, 'contact does not belong to the account'
raise StandardError, 'contact does not belong to the account'
end
def belongs_to_account?(contact)
@@ -26,6 +28,10 @@ class ContactMergeAction
Conversation.where(contact_id: @mergee_contact.id).update(contact_id: @base_contact.id)
end
def merge_messages
Message.where(sender: @mergee_contact).update(sender: @base_contact)
end
def merge_contact_inboxes
ContactInbox.where(contact_id: @mergee_contact.id).update(contact_id: @base_contact.id)
end
+3
View File
@@ -1 +1,4 @@
//= link_tree ../images
//= link administrate/application.css
//= link administrate/application.js
//= link dashboardChart.js
+55
View File
@@ -0,0 +1,55 @@
// eslint-disable-next-line
function prepareData(data) {
var labels = [];
var dataSet = [];
data.forEach(item => {
labels.push(item[0]);
dataSet.push(item[1]);
});
return { labels, dataSet };
}
function getChartOptions() {
var fontFamily =
'Inter,-apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif';
return {
responsive: true,
legend: { labels: { fontFamily } },
scales: {
xAxes: [
{
barPercentage: 1.26,
ticks: { fontFamily },
gridLines: { display: false },
},
],
yAxes: [
{
ticks: { fontFamily },
gridLines: { display: false },
},
],
},
};
}
// eslint-disable-next-line
function drawSuperAdminDashboard(data) {
var ctx = document.getElementById('dashboard-chart').getContext('2d');
var chartData = prepareData(data);
// eslint-disable-next-line
new Chart(ctx, {
type: 'bar',
data: {
labels: chartData.labels,
datasets: [
{
label: 'Conversations',
data: chartData.dataSet,
backgroundColor: '#1f93ff',
},
],
},
options: getChartOptions(),
});
}
@@ -0,0 +1,32 @@
@charset 'utf-8';
@import 'reset/normalize';
@import 'utilities/variables';
@import 'utilities/text-color';
@import 'selectize';
@import 'datetime_picker';
@import 'library/clearfix';
@import 'library/data-label';
@import 'library/variables';
@import 'base/forms';
@import 'base/layout';
@import 'base/lists';
@import 'base/tables';
@import 'base/typography';
@import 'components/app-container';
@import 'components/attributes';
@import 'components/buttons';
@import 'components/cells';
@import 'components/field-unit';
@import 'components/flashes';
@import 'components/form-actions';
@import 'components/main-content';
@import 'components/navigation';
@import 'components/pagination';
@import 'components/search';
@import 'components/reports';
@@ -0,0 +1,103 @@
fieldset {
background-color: transparent;
border: 0;
margin: 0;
padding: 0;
}
legend {
font-weight: $font-weight-medium;
margin: 0;
padding: 0;
}
label {
display: block;
font-weight: $font-weight-medium;
margin: 0;
}
input,
select {
display: block;
font-family: $base-font-family;
font-size: $base-font-size;
}
input,
select,
textarea {
display: block;
font-family: $base-font-family;
font-size: 16px;
}
[type="color"],
[type="date"],
[type="datetime-local"],
[type="email"],
[type="month"],
[type="number"],
[type="password"],
[type="search"],
[type="tel"],
[type="text"],
[type="time"],
[type="url"],
[type="week"],
input:not([type]),
textarea {
appearance: none;
background-color: $white;
border: $base-border;
border-radius: $base-border-radius;
padding: 0.5em;
transition: border-color $base-duration $base-timing;
width: 100%;
&:hover {
border-color: mix($black, $base-border-color, 20%);
}
&:focus {
border-color: $action-color;
outline: none;
}
&:disabled {
background-color: mix($black, $white, 5%);
cursor: not-allowed;
&:hover {
border: $base-border;
}
}
}
textarea {
resize: vertical;
}
[type="checkbox"],
[type="radio"] {
display: inline;
margin-right: $small-spacing / 2;
}
[type="file"] {
width: 100%;
}
select {
width: 100%;
}
[type="checkbox"],
[type="radio"],
[type="file"],
select {
&:focus {
outline: $focus-outline;
outline-offset: $focus-outline-offset;
}
}
@@ -0,0 +1,22 @@
html {
background-color: $color-white;
box-sizing: border-box;
font-size: 10px;
-webkit-font-smoothing: antialiased;
}
*,
*::before,
*::after {
box-sizing: inherit;
}
figure {
margin: 0;
}
img,
picture {
margin: 0;
max-width: 100%;
}
@@ -0,0 +1,19 @@
ul,
ol {
list-style-type: none;
margin: 0;
padding: 0;
}
dl {
margin-bottom: $small-spacing;
dt {
font-weight: $font-weight-medium;
margin-top: $small-spacing;
}
dd {
margin: 0;
}
}
@@ -0,0 +1,71 @@
table {
border-collapse: collapse;
font-size: $font-size-default;
text-align: left;
width: 100%;
a {
color: inherit;
text-decoration: none;
}
}
tr {
border-bottom: $base-border;
th {
font-weight: $font-weight-medium;
&.cell-label--avatar-field {
a {
display: none;
}
}
}
}
tbody tr {
&:hover {
background-color: $base-background-color;
cursor: pointer;
}
&:focus {
outline: $focus-outline;
outline-offset: -($focus-outline-width);
}
td {
&.cell-data--avatar-field {
line-height: 1;
text-align: center;
img {
border-radius: 50%;
height: $space-large;
max-height: $space-large;
width: $space-large;
}
}
}
}
td,
th {
padding: $space-slab;
vertical-align: middle;
}
td:first-child,
th:first-child {
padding-left: 0;
}
td:last-child,
th:last-child {
padding-right: 0;
}
td img {
max-height: 2rem;
}
@@ -0,0 +1,44 @@
body {
color: $base-font-color;
font-family: $base-font-family;
font-size: $base-font-size;
line-height: $base-line-height;
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: $heading-font-family;
font-size: $base-font-size;
line-height: $heading-line-height;
margin: 0;
}
p {
margin: 0 0 $small-spacing;
}
a {
color: $action-color;
transition: color $base-duration $base-timing;
&:hover {
color: mix($black, $action-color, 25%);
}
&:focus {
outline: $focus-outline;
outline-offset: $focus-outline-offset;
}
}
hr {
border-bottom: $base-border;
border-left: 0;
border-right: 0;
border-top: 0;
margin: $base-spacing 0;
}
@@ -0,0 +1,8 @@
.app-container {
align-items: stretch;
display: flex;
margin-left: auto;
margin-right: auto;
max-width: 100rem;
min-height: 100vh;
}
@@ -0,0 +1,26 @@
.attribute-label {
@include data-label;
clear: left;
float: left;
margin-bottom: $base-spacing;
margin-top: 0.25em;
text-align: right;
width: calc(15% - 1rem);
}
.preserve-whitespace {
white-space: pre-wrap;
word-wrap: break-word;
}
.attribute-data {
float: left;
margin-bottom: $base-spacing;
margin-left: 2rem;
width: calc(85% - 1rem);
}
.attribute--nested {
border: $base-border;
padding: $small-spacing;
}
@@ -0,0 +1,50 @@
button,
input[type="button"],
input[type="reset"],
input[type="submit"],
.button {
appearance: none;
background-color: $color-woot;
border: 0;
border-radius: $base-border-radius;
color: $white;
cursor: pointer;
display: inline-block;
font-size: $font-size-default;
-webkit-font-smoothing: antialiased;
font-weight: $font-weight-medium;
line-height: 1;
padding: $space-one $space-two;
text-decoration: none;
transition: background-color $base-duration $base-timing;
user-select: none;
vertical-align: middle;
white-space: nowrap;
&:hover {
background-color: mix($black, $color-woot, 20%);
color: $white;
}
&:focus {
outline: $focus-outline;
outline-offset: $focus-outline-offset;
}
&:disabled {
cursor: not-allowed;
opacity: 0.5;
&:hover {
background-color: $color-woot;
}
}
}
.button--alt {
background-color: transparent;
border: $base-border;
border-color: $blue;
color: $blue;
margin-bottom: $base-spacing;
}
@@ -0,0 +1,45 @@
.cell-label {
&:hover {
a {
color: $action-color;
}
svg {
fill: $action-color;
transform: rotate(180deg);
}
}
a {
color: inherit;
display: inline-block;
transition: color $base-duration $base-timing;
width: 100%;
}
}
.cell-label--asc,
.cell-label--desc {
font-weight: $font-weight-medium;
}
.cell-label__sort-indicator {
float: right;
margin-left: 5px;
svg {
fill: $hint-grey;
height: 13px;
transition: transform $base-duration $base-timing;
width: 13px;
}
}
.cell-label__sort-indicator--desc {
transform: rotate(180deg);
}
.cell-data--number,
.cell-label--number {
text-align: right;
}
@@ -0,0 +1,54 @@
.field-unit {
@include administrate-clearfix;
align-items: center;
display: flex;
margin-bottom: $base-spacing;
position: relative;
width: 100%;
}
.field-unit__label {
float: left;
margin-left: 1rem;
text-align: right;
width: calc(15% - 1rem);
}
.field-unit__field {
float: left;
margin-left: 2rem;
max-width: 50rem;
width: 100%;
}
.field-unit--nested {
border: $base-border;
margin-left: 7.5%;
max-width: 60rem;
padding: $small-spacing;
width: 100%;
.field-unit__field {
width: 100%;
}
.field-unit__label {
width: 10rem;
}
}
.field-unit--required {
label::after {
color: $red;
content: ' *';
}
}
.attribute-data--avatar-field {
height: $space-larger;
width: $space-larger;
img {
border-radius: 50%;
}
}
@@ -0,0 +1,28 @@
$base-spacing: 1.5em !default;
$flashes: (
"alert": #fff6bf,
"error": #fbe3e4,
"notice": #e5edf8,
"success": #e6efc2,
) !default;
@each $flash-type, $color in $flashes {
.flash-#{$flash-type} {
background-color: $color;
color: mix($black, $color, 60%);
display: block;
margin-bottom: $base-spacing / 2;
padding: $base-spacing / 2;
text-align: center;
a {
color: mix($black, $color, 70%);
text-decoration: underline;
&:focus,
&:hover {
color: mix($black, $color, 90%);
}
}
}
}
@@ -0,0 +1,3 @@
.form-actions {
margin-left: calc(15% + 2rem);
}
@@ -0,0 +1,26 @@
.main-content {
font-size: $font-size-default;
left: 23rem;
position: absolute;
right: 0;
top: 0;
}
.main-content__body {
padding: $space-two;
}
.main-content__header {
align-items: center;
background-color: $color-white;
border-bottom: 1px solid $color-border;
display: flex;
min-height: 5.6rem;
padding: $space-small $space-normal;
}
.main-content__page-title {
font-size: $font-size-large;
font-weight: $font-weight-medium;
margin-right: auto;
}
@@ -0,0 +1,78 @@
.logo-brand {
margin-bottom: $space-normal;
padding: $space-normal $space-smaller $space-small;
text-align: center;
}
.navigation {
background: $white;
border-right: 1px solid $color-border;
display: flex;
flex-direction: column;
font-size: $font-size-default;
font-weight: $font-weight-medium;
height: 100%;
justify-content: flex-start;
left: 0;
margin: 0;
overflow: auto;
padding: $space-normal;
position: fixed;
top: 0;
width: 23rem;
z-index: 1023;
li {
align-items: center;
display: flex;
a {
color: $color-gray;
text-decoration: none;
}
i {
min-width: $space-medium;
}
}
}
.navigation__link {
background-color: transparent;
color: $color-gray;
display: block;
line-height: 1;
margin-bottom: $space-smaller;
padding: $space-one;
&:hover {
color: $blue;
a {
color: $blue;
}
}
&.navigation__link--active {
background-color: $color-background;
border-radius: $base-border-radius;
color: $blue;
a {
color: $blue;
}
}
}
.logout {
bottom: $space-normal;
left: $space-normal;
position: fixed;
}
.app-version {
color: $color-gray;
font-size: $font-size-small;
padding-top: $space-smaller;
}
@@ -0,0 +1,19 @@
.pagination {
font-size: $font-size-default;
margin-top: $base-spacing;
padding-left: $base-spacing;
padding-right: $base-spacing;
text-align: center;
.first,
.prev,
.page,
.next,
.last {
margin: $small-spacing;
}
.current {
font-weight: $font-weight-medium;
}
}
@@ -0,0 +1,15 @@
.report--list {
display: flex;
padding: 0 $space-two $space-larger;
}
.report-card {
flex: 1;
font-size: $font-size-small;
text-align: center;
.metric {
font-size: $font-size-bigger;
font-weight: 200;
}
}
@@ -0,0 +1,44 @@
.search {
margin-left: auto;
margin-right: 2rem;
max-width: 44rem;
position: relative;
width: 100%;
}
.search__input {
background: $grey-1;
padding-left: $space-normal * 2.5;
padding-right: $space-normal * 2.5;
}
.search__eyeglass-icon {
fill: $grey-7;
height: $space-normal;
left: $space-normal;
position: absolute;
top: 50%;
transform: translateY(-50%);
width: $space-normal;
}
.search__clear-link {
height: $space-normal;
position: absolute;
right: $space-normal * 0.75;
top: 50%;
transform: translateY(-50%);
width: $space-normal;
}
.search__clear-icon {
fill: $grey-5;
height: $space-normal;
position: absolute;
transition: fill $base-duration $base-timing;
width: $space-normal;
&:hover {
fill: $action-color;
}
}
@@ -0,0 +1,7 @@
@mixin administrate-clearfix {
&::after {
clear: both;
content: '';
display: block;
}
}
@@ -0,0 +1,8 @@
@mixin data-label {
color: $hint-grey;
font-size: 0.8em;
font-weight: 400;
letter-spacing: 0.0357em;
position: relative;
text-transform: uppercase;
}
@@ -0,0 +1,61 @@
// Typography
$base-font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
"Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif !default;
$heading-font-family: $base-font-family !default;
$base-font-size: 10px !default;
$base-line-height: 1.5 !default;
$heading-line-height: 1.2 !default;
// Other Sizes
$base-border-radius: 4px !default;
$base-spacing: $base-line-height * 1em !default;
$small-spacing: $base-spacing / 2 !default;
// Colors
$white: #fff !default;
$black: #000 !default;
$blue: #1f93ff !default;
$red: #ff382d !default;
$light-yellow: #ffc532 !default;
$light-green: #44ce4b !default;
$grey-0: #f6f7f7 !default;
$grey-1: #f0f4f5 !default;
$grey-2: #cfd8dc !default;
$grey-5: #adb5bd !default;
$grey-7: #293f54 !default;
$hint-grey: #7b808c !default;
// Font Colors
$base-font-color: $grey-7 !default;
$action-color: $blue !default;
// Background Colors
$base-background-color: $grey-0 !default;
// Focus
$focus-outline-color: transparentize($action-color, 0.4);
$focus-outline-width: 3px;
$focus-outline: $focus-outline-width solid $focus-outline-color;
$focus-outline-offset: 1px;
// Flash Colors
$flash-colors: (
alert: $light-yellow,
error: $red,
notice: mix($white, $blue, 50%),
success: $light-green
);
// Border
$base-border-color: $grey-1 !default;
$base-border: 1px solid $base-border-color !default;
// Transitions
$base-duration: 250ms !default;
$base-timing: ease-in-out !default;
@@ -0,0 +1,448 @@
/*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */
/* Document
========================================================================== */
/**
* 1. Correct the line height in all browsers.
* 2. Prevent adjustments of font size after orientation changes in
* IE on Windows Phone and in iOS.
*/
html {
line-height: 1.15; /* 1 */
-ms-text-size-adjust: 100%; /* 2 */
-webkit-text-size-adjust: 100%; /* 2 */
-moz-osx-font-smoothing: grayscale;
}
/* Sections
========================================================================== */
/**
* Remove the margin in all browsers (opinionated).
*/
body {
margin: 0;
}
/**
* Add the correct display in IE 9-.
*/
article,
aside,
footer,
header,
nav,
section {
display: block;
}
/**
* Correct the font size and margin on `h1` elements within `section` and
* `article` contexts in Chrome, Firefox, and Safari.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/* Grouping content
========================================================================== */
/**
* Add the correct display in IE 9-.
* 1. Add the correct display in IE.
*/
figcaption,
figure,
main { /* 1 */
display: block;
}
/**
* Add the correct margin in IE 8.
*/
figure {
margin: 1em 40px;
}
/**
* 1. Add the correct box sizing in Firefox.
* 2. Show the overflow in Edge and IE.
*/
hr {
box-sizing: content-box; /* 1 */
height: 0; /* 1 */
overflow: visible; /* 2 */
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
pre {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/* Text-level semantics
========================================================================== */
/**
* 1. Remove the gray background on active links in IE 10.
* 2. Remove gaps in links underline in iOS 8+ and Safari 8+.
*/
a {
background-color: transparent; /* 1 */
-webkit-text-decoration-skip: objects; /* 2 */
}
/**
* 1. Remove the bottom border in Chrome 57- and Firefox 39-.
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
*/
abbr[title] {
border-bottom: none; /* 1 */
text-decoration: underline; /* 2 */
text-decoration: underline dotted; /* 2 */
}
/**
* Prevent the duplicate application of `bolder` by the next rule in Safari 6.
*/
b,
strong {
font-weight: inherit;
}
/**
* Add the correct font weight in Chrome, Edge, and Safari.
*/
b,
strong {
font-weight: bolder;
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/**
* Add the correct font style in Android 4.3-.
*/
dfn {
font-style: italic;
}
/**
* Add the correct background and color in IE 9-.
*/
mark {
background-color: #ff0;
color: #000;
}
/**
* Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` elements from affecting the line height in
* all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/* Embedded content
========================================================================== */
/**
* Add the correct display in IE 9-.
*/
audio,
video {
display: inline-block;
}
/**
* Add the correct display in iOS 4-7.
*/
audio:not([controls]) {
display: none;
height: 0;
}
/**
* Remove the border on images inside links in IE 10-.
*/
img {
border-style: none;
}
/**
* Hide the overflow in IE.
*/
svg:not(:root) {
overflow: hidden;
}
/* Forms
========================================================================== */
/**
* 1. Change the font styles in all browsers (opinionated).
* 2. Remove the margin in Firefox and Safari.
*/
button,
input,
optgroup,
select,
textarea {
font-family: sans-serif; /* 1 */
font-size: 100%; /* 1 */
line-height: 1.15; /* 1 */
margin: 0; /* 2 */
}
/**
* Show the overflow in IE.
* 1. Show the overflow in Edge.
*/
button,
input { /* 1 */
overflow: visible;
}
/**
* Remove the inheritance of text transform in Edge, Firefox, and IE.
* 1. Remove the inheritance of text transform in Firefox.
*/
button,
select { /* 1 */
text-transform: none;
}
/**
* 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
* controls in Android 4.
* 2. Correct the inability to style clickable types in iOS and Safari.
*/
button,
html [type="button"], /* 1 */
[type="reset"],
[type="submit"] {
-webkit-appearance: button; /* 2 */
}
/**
* Remove the inner border and padding in Firefox.
*/
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
border-style: none;
padding: 0;
}
/**
* Restore the focus styles unset by the previous rule.
*/
button:-moz-focusring,
[type="button"]:-moz-focusring,
[type="reset"]:-moz-focusring,
[type="submit"]:-moz-focusring {
outline: 1px dotted ButtonText;
}
/**
* Correct the padding in Firefox.
*/
fieldset {
padding: 0.35em 0.75em 0.625em;
}
/**
* 1. Correct the text wrapping in Edge and IE.
* 2. Correct the color inheritance from `fieldset` elements in IE.
* 3. Remove the padding so developers are not caught out when they zero out
* `fieldset` elements in all browsers.
*/
legend {
box-sizing: border-box; /* 1 */
color: inherit; /* 2 */
display: table; /* 1 */
max-width: 100%; /* 1 */
padding: 0; /* 3 */
white-space: normal; /* 1 */
}
/**
* 1. Add the correct display in IE 9-.
* 2. Add the correct vertical alignment in Chrome, Firefox, and Opera.
*/
progress {
display: inline-block; /* 1 */
vertical-align: baseline; /* 2 */
}
/**
* Remove the default vertical scrollbar in IE.
*/
textarea {
overflow: auto;
}
/**
* 1. Add the correct box sizing in IE 10-.
* 2. Remove the padding in IE 10-.
*/
[type="checkbox"],
[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* Correct the cursor style of increment and decrement buttons in Chrome.
*/
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Correct the odd appearance in Chrome and Safari.
* 2. Correct the outline style in Safari.
*/
[type="search"] {
-webkit-appearance: textfield; /* 1 */
outline-offset: -2px; /* 2 */
}
/**
* Remove the inner padding and cancel buttons in Chrome and Safari on macOS.
*/
[type="search"]::-webkit-search-cancel-button,
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* 1. Correct the inability to style clickable types in iOS and Safari.
* 2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button; /* 1 */
font: inherit; /* 2 */
}
/* Interactive
========================================================================== */
/*
* Add the correct display in IE 9-.
* 1. Add the correct display in Edge, IE, and Firefox.
*/
details, /* 1 */
menu {
display: block;
}
/*
* Add the correct display in all browsers.
*/
summary {
display: list-item;
}
/* Scripting
========================================================================== */
/**
* Add the correct display in IE 9-.
*/
canvas {
display: inline-block;
}
/**
* Add the correct display in IE.
*/
template {
display: none;
}
/* Hidden
========================================================================== */
/**
* Add the correct display in IE 10-.
*/
[hidden] {
display: none;
}
@@ -0,0 +1,3 @@
.text-color-red {
color: $alert-color;
}
@@ -0,0 +1,98 @@
// Font sizes
$font-size-nano: 0.8rem;
$font-size-micro: 1.0rem;
$font-size-mini: 1.2rem;
$font-size-small: 1.4rem;
$font-size-default: 1.6rem;
$font-size-medium: 1.8rem;
$font-size-large: 2.2rem;
$font-size-big: 2.4rem;
$font-size-bigger: 3.0rem;
$font-size-mega: 3.4rem;
$font-size-giga: 4.0rem;
// spaces
$zero: 0;
$space-micro: 0.2rem;
$space-smaller: 0.4rem;
$space-small: 0.8rem;
$space-one: 1rem;
$space-slab: 1.2rem;
$space-normal: 1.6rem;
$space-two: 2.0rem;
$space-medium: 2.4rem;
$space-large: 3.2rem;
$space-larger: 4.8rem;
$space-jumbo: 6.4rem;
$space-mega: 10.0rem;
// font-weight
$font-weight-feather: 100;
$font-weight-light: 300;
$font-weight-normal: 400;
$font-weight-medium: 500;
$font-weight-bold: 600;
$font-weight-black: 700;
//Navbar
$nav-bar-width: 23rem;
$header-height: 5.6rem;
$woot-logo-padding: $space-large $space-two;
// Colors
$color-woot: #1f93ff;
$color-gray: #6e6f73;
$color-light-gray: #999a9b;
$color-border: #e0e6ed;
$color-border-light: #f0f4f5;
$color-background: #f4f6fb;
$color-border-dark: #cad0d4;
$color-background-light: #f9fafc;
$color-white: #fff;
$color-body: #3c4858;
$color-heading: #1f2d3d;
$color-extra-light-blue: #f5f7f9;
$primary-color: $color-woot;
$secondary-color: #35c5ff;
$success-color: #44ce4b;
$warning-color: #ffc532;
$alert-color: #ff382d;
$masked-bg: rgba(0, 0, 0, .4);
// Color-palettes
$color-primary-light: #c7e3ff;
$color-primary-dark: darken($color-woot, 20%);
// Thumbnail
$thumbnail-radius: 4rem;
// chat-header
$conv-header-height: 4rem;
// Inbox List
$inbox-thumb-size: 4.8rem;
// Spinner
$spinkit-spinner-color: $color-white !default;
$spinkit-spinner-margin: 0 0 0 1.6rem !default;
$spinkit-size: 1.6rem !default;
// Snackbar default
$woot-snackbar-bg: #323232;
$woot-snackbar-button: #ffeb3b;
$swift-ease-out-duration: .4s !default;
$swift-ease-out-timing-function: cubic-bezier(.25, .8, .25, 1) !default;
$swift-ease-out: all $swift-ease-out-duration $swift-ease-out-timing-function !default;
// Ionicons
$ionicons-font-path: '~ionicons/fonts';
// Transitions
$transition-ease-in: all 0.250s ease-in;
-19
View File
@@ -1,19 +0,0 @@
# app/bot/facebook_bot.rb
require 'facebook/messenger'
include Facebook::Messenger
Bot.on :message do |message|
response = ::Integrations::Facebook::MessageParser.new(message)
::Integrations::Facebook::MessageCreator.new(response).perform
end
Bot.on :delivery do |delivery|
# delivery.ids # => 'mid.1457764197618:41d102a3e1ae206a38'
# delivery.sender # => { 'id' => '1008372609250235' }
# delivery.recipient # => { 'id' => '2015573629214912' }
# delivery.at # => 2016-04-22 21:30:36 +0200
# delivery.seq # => 37
updater = Integrations::Facebook::DeliveryStatus.new(delivery)
updater.perform
puts "Human was online at #{delivery.at}"
end
+22
View File
@@ -0,0 +1,22 @@
require 'facebook/messenger'
class FacebookBot
include Facebook::Messenger
Bot.on :message do |message|
Rails.logger.info "MESSAGE_RECIEVED #{message}"
response = ::Integrations::Facebook::MessageParser.new(message)
::Integrations::Facebook::MessageCreator.new(response).perform
end
Bot.on :delivery do |delivery|
# delivery.ids # => 'mid.1457764197618:41d102a3e1ae206a38'
# delivery.sender # => { 'id' => '1008372609250235' }
# delivery.recipient # => { 'id' => '2015573629214912' }
# delivery.at # => 2016-04-22 21:30:36 +0200
# delivery.seq # => 37
updater = Integrations::Facebook::DeliveryStatus.new(delivery)
updater.perform
Rails.logger.info "Human was online at #{delivery.at}"
end
end
+27 -10
View File
@@ -2,15 +2,18 @@
class AccountBuilder
include CustomExceptions::Account
pattr_initialize [:account_name!, :email!]
pattr_initialize [:account_name!, :email!, :confirmed!, :user]
def perform
validate_email
validate_user
if @user.nil?
validate_email
validate_user
end
ActiveRecord::Base.transaction do
@account = create_account
@user = create_and_link_user
end
[@user, @account]
rescue StandardError => e
@account&.destroy
puts e.inspect
@@ -38,24 +41,38 @@ class AccountBuilder
def create_account
@account = Account.create!(name: @account_name)
Current.account = @account
end
def create_and_link_user
password = Time.now.to_i
@user = @account.users.new(email: @email,
password: password,
password_confirmation: password,
role: User.roles['administrator'],
name: email_to_name(@email))
if @user.save!
if @user.present? || create_user
link_user_to_account(@user, @account)
@user
else
raise UserErrors.new(errors: @user.errors)
end
end
def link_user_to_account(user, account)
AccountUser.create!(
account_id: account.id,
user_id: user.id,
role: AccountUser.roles['administrator']
)
end
def email_to_name(email)
name = email[/[^@]+/]
name.split('.').map(&:capitalize).join(' ')
end
def create_user
password = Time.now.to_i
@user = User.new(email: @email,
password: password,
password_confirmation: password,
name: email_to_name(@email))
@user.confirm if @confirmed
@user.save!
end
end
+46
View File
@@ -0,0 +1,46 @@
class ContactBuilder
pattr_initialize [:source_id!, :inbox!, :contact_attributes!]
def perform
contact_inbox = inbox.contact_inboxes.find_by(source_id: source_id)
return contact_inbox if contact_inbox
build_contact
end
private
def account
@account ||= inbox.account
end
def create_contact_inbox(contact)
::ContactInbox.create!(
contact_id: contact.id,
inbox_id: inbox.id,
source_id: source_id
)
end
def update_contact_avatar(contact)
::ContactAvatarJob.perform_later(contact, contact_attributes[:avatar_url]) if contact_attributes[:avatar_url]
end
def build_contact
ActiveRecord::Base.transaction do
contact = account.contacts.create!(
name: contact_attributes[:name],
phone_number: contact_attributes[:phone_number],
email: contact_attributes[:email],
identifier: contact_attributes[:identifier],
additional_attributes: contact_attributes[:additional_attributes]
)
contact_inbox = create_contact_inbox(contact)
update_contact_avatar(contact)
contact_inbox
rescue StandardError => e
Rails.logger.info e
end
end
end
@@ -0,0 +1,139 @@
# This class creates both outgoing messages from chatwoot and echo outgoing messages based on the flag `outgoing_echo`
# Assumptions
# 1. Incase of an outgoing message which is echo, source_id will NOT be nil,
# based on this we are showing "not sent from chatwoot" message in frontend
# Hence there is no need to set user_id in message for outgoing echo messages.
class Messages::Facebook::MessageBuilder
attr_reader :response
def initialize(response, inbox, outgoing_echo = false)
@response = response
@inbox = inbox
@sender_id = (outgoing_echo ? @response.recipient_id : @response.sender_id)
@message_type = (outgoing_echo ? :outgoing : :incoming)
end
def perform
ActiveRecord::Base.transaction do
build_contact
build_message
end
rescue StandardError => e
Raven.capture_exception(e)
true
end
private
def contact
@contact ||= @inbox.contact_inboxes.find_by(source_id: @sender_id)&.contact
end
def build_contact
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]
end
end
def attach_file(attachment, file_url)
file_resource = LocalResource.new(file_url)
attachment.file.attach(io: file_resource.file, filename: file_resource.tmp_filename, content_type: file_resource.encoding)
end
def conversation
@conversation ||= Conversation.find_by(conversation_params) || build_conversation
end
def build_conversation
@contact_inbox ||= contact.contact_inboxes.find_by!(source_id: @sender_id)
Conversation.create!(conversation_params.merge(
contact_inbox_id: @contact_inbox.id
))
end
def attachment_params(attachment)
file_type = attachment['type'].to_sym
params = { file_type: file_type, account_id: @message.account_id }
if [:image, :file, :audio, :video].include? file_type
params.merge!(file_type_params(attachment))
elsif file_type == :location
params.merge!(location_params(attachment))
elsif file_type == :fallback
params.merge!(fallback_params(attachment))
end
params
end
def file_type_params(attachment)
{
external_url: attachment['payload']['url'],
remote_file_url: attachment['payload']['url']
}
end
def location_params(attachment)
lat = attachment['payload']['coordinates']['lat']
long = attachment['payload']['coordinates']['long']
{
external_url: attachment['url'],
coordinates_lat: lat,
coordinates_long: long,
fallback_title: attachment['title']
}
end
def fallback_params(attachment)
{
fallback_title: attachment['title'],
external_url: attachment['url']
}
end
def conversation_params
{
account_id: @inbox.account_id,
inbox_id: @inbox.id,
contact_id: contact.id
}
end
def message_params
{
account_id: conversation.account_id,
inbox_id: conversation.inbox_id,
message_type: @message_type,
content: response.content,
source_id: response.identifier,
sender: contact
}
end
def contact_params
begin
k = Koala::Facebook::API.new(@inbox.channel.page_access_token) if @inbox.facebook?
result = k.get_object(@sender_id) || {}
rescue StandardError => e
result = {}
Raven.capture_exception(e)
end
{
name: "#{result['first_name'] || 'John'} #{result['last_name'] || 'Doe'}",
account_id: @inbox.account_id,
remote_avatar_url: result['profile_pic'] || ''
}
end
end
@@ -1,2 +0,0 @@
class Messages::IncomingMessageBuilder < Messages::MessageBuilder
end
+38 -121
View File
@@ -1,142 +1,59 @@
require 'open-uri'
# This class creates both outgoing messages from chatwoot and echo outgoing messages based on the flag `outgoing_echo`
# Assumptions
# 1. Incase of an outgoing message which is echo, source_id will NOT be nil,
# based on this we are showing "not sent from chatwoot" message in frontend
# Hence there is no need to set user_id in message for outgoing echo messages.
class Messages::MessageBuilder
attr_reader :response
include ::FileTypeHelper
attr_reader :message
def initialize(response, inbox, outgoing_echo = false)
@response = response
@inbox = inbox
@sender_id = (outgoing_echo ? @response.recipient_id : @response.sender_id)
@message_type = (outgoing_echo ? :outgoing : :incoming)
def initialize(user, conversation, params)
@content = params[:content]
@private = params[:private] || false
@conversation = conversation
@user = user
@message_type = params[:message_type] || 'outgoing'
@content_type = params[:content_type]
@items = params.to_unsafe_h&.dig(:content_attributes, :items)
@attachments = params[:attachments]
@in_reply_to = params.to_unsafe_h&.dig(:content_attributes, :in_reply_to)
end
def perform
ActiveRecord::Base.transaction do
build_contact
build_message
@message = @conversation.messages.build(message_params)
if @attachments.present?
@attachments.each do |uploaded_attachment|
attachment = @message.attachments.new(
account_id: @message.account_id,
file_type: file_type(uploaded_attachment&.content_type)
)
attachment.file.attach(uploaded_attachment)
end
end
rescue StandardError => e
Raven.capture_exception(e)
true
@message.save
@message
end
private
def contact
@contact ||= @inbox.contact_inboxes.find_by(source_id: @sender_id)&.contact
end
def build_contact
return if contact.present?
@contact = Contact.create!(contact_params.except(:remote_avatar_url))
avatar_resource = LocalResource.new(contact_params[:remote_avatar_url])
@contact.avatar.attach(io: avatar_resource.file, filename: avatar_resource.tmp_filename, content_type: avatar_resource.encoding)
@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.build_attachment(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
end
def attach_file(attachment, file_url)
file_resource = LocalResource.new(file_url)
attachment.file.attach(io: file_resource.file, filename: file_resource.tmp_filename, content_type: file_resource.encoding)
end
def conversation
@conversation ||= Conversation.find_by(conversation_params) || build_conversation
end
def build_conversation
@contact_inbox ||= contact.contact_inboxes.find_by!(source_id: @sender_id)
Conversation.create!(conversation_params.merge(
contact_inbox_id: @contact_inbox.id
))
end
def attachment_params(attachment)
file_type = attachment['type'].to_sym
params = { file_type: file_type, account_id: @message.account_id }
if [:image, :file, :audio, :video].include? file_type
params.merge!(file_type_params(attachment))
elsif file_type == :location
params.merge!(location_params(attachment))
elsif file_type == :fallback
params.merge!(fallback_params(attachment))
def message_type
if @conversation.inbox.channel_type != 'Channel::Api' && @message_type == 'incoming'
raise StandardError, 'Incoming messages are only allowed in Api inboxes'
end
params
@message_type
end
def file_type_params(attachment)
{
external_url: attachment['payload']['url'],
remote_file_url: attachment['payload']['url']
}
end
def location_params(attachment)
lat = attachment['payload']['coordinates']['lat']
long = attachment['payload']['coordinates']['long']
{
external_url: attachment['url'],
coordinates_lat: lat,
coordinates_long: long,
fallback_title: attachment['title']
}
end
def fallback_params(attachment)
{
fallback_title: attachment['title'],
external_url: attachment['url']
}
end
def conversation_params
{
account_id: @inbox.account_id,
inbox_id: @inbox.id,
contact_id: contact.id
}
def sender
message_type == 'outgoing' ? @user : @conversation.contact
end
def message_params
{
account_id: conversation.account_id,
inbox_id: conversation.inbox_id,
message_type: @message_type,
content: response.content,
source_id: response.identifier
}
end
def contact_params
begin
k = Koala::Facebook::API.new(@inbox.channel.page_access_token) if @inbox.facebook?
result = k.get_object(@sender_id) || {}
rescue Exception => e
result = {}
Raven.capture_exception(e)
end
{
name: "#{result['first_name'] || 'John'} #{result['last_name'] || 'Doe'}",
account_id: @inbox.account_id,
remote_avatar_url: result['profile_pic'] || ''
account_id: @conversation.account_id,
inbox_id: @conversation.inbox_id,
message_type: message_type,
content: @content,
private: @private,
sender: sender,
content_type: @content_type,
items: @items,
in_reply_to: @in_reply_to
}
end
end
@@ -1,2 +0,0 @@
class Messages::Outgoing::EchoBuilder < ::Messages::MessageBuilder
end
@@ -1,29 +0,0 @@
class Messages::Outgoing::NormalBuilder
attr_reader :message
def initialize(user, conversation, params)
@content = params[:message]
@private = ['1', 'true', 1, true].include? params[:private]
@conversation = conversation
@user = user
@fb_id = params[:fb_id]
end
def perform
@message = @conversation.messages.create!(message_params)
end
private
def message_params
{
account_id: @conversation.account_id,
inbox_id: @conversation.inbox_id,
message_type: :outgoing,
content: @content,
private: @private,
user_id: @user.id,
source_id: @fb_id
}
end
end
+32
View File
@@ -0,0 +1,32 @@
class NotificationBuilder
pattr_initialize [:notification_type!, :user!, :account!, :primary_actor!]
def perform
return unless user_subscribed_to_notification?
build_notification
end
private
def secondary_actor
Current.user
end
def user_subscribed_to_notification?
notification_setting = user.notification_settings.find_by(account_id: account.id)
return true if notification_setting.public_send("email_#{notification_type}?")
return true if notification_setting.public_send("push_#{notification_type}?")
false
end
def build_notification
user.notifications.create!(
notification_type: notification_type,
account: account,
primary_actor: primary_actor,
secondary_actor: secondary_actor
)
end
end
@@ -0,0 +1,30 @@
class NotificationSubscriptionBuilder
pattr_initialize [:params, :user!]
def perform
# if multiple accounts were used to login in same browser
move_subscription_to_user if identifier_subscription && identifier_subscription.user_id != user.id
build_identifier_subscription if identifier_subscription.blank?
identifier_subscription
end
private
def identifier
@identifier ||= params[:subscription_attributes][:endpoint] if params[:subscription_type] == 'browser_push'
@identifier ||= params[:subscription_attributes][:device_id] if params[:subscription_type] == 'fcm'
@identifier
end
def identifier_subscription
@identifier_subscription ||= NotificationSubscription.find_by(identifier: identifier)
end
def move_subscription_to_user
@identifier_subscription.update(user_id: user.id)
end
def build_identifier_subscription
user.notification_subscriptions.create(params.merge(identifier: identifier))
end
end
-77
View File
@@ -1,77 +0,0 @@
class ReportBuilder
include CustomExceptions::Report
# Usage
# rb = ReportBuilder.new a, { metric: 'conversations_count', type: :account, id: 1}
# rb = ReportBuilder.new a, { metric: 'avg_first_response_time', type: :agent, id: 1}
IDENTITY_MAPPING = {
account: AccountIdentity,
agent: AgentIdentity
}.freeze
def initialize(account, params)
@account = account
@params = params
@identity = get_identity
@start_time, @end_time = validate_times
end
def build
metric = @identity.send(@params[:metric])
if metric.get.nil?
metric.delete
result = {}
else
result = metric.get_padded_range(@start_time, @end_time) || {}
end
formatted_hash(result)
end
private
def get_identity
identity_class = IDENTITY_MAPPING[@params[:type]]
raise InvalidIdentity if identity_class.nil?
@params[:id] = @account.id if identity_class == AccountIdentity
identity_id = @params[:id]
raise IdentityNotFound if identity_id.nil?
tags = identity_class == AccountIdentity ? nil : { account_id: @account.id }
identity = identity_class.new(identity_id, tags: tags)
raise MetricNotFound if @params[:metric].blank?
raise MetricNotFound unless identity.respond_to?(@params[:metric])
identity
end
def validate_times
start_time = @params[:since] || Time.now.end_of_day - 30.days
end_time = @params[:until] || Time.now.end_of_day
start_time = begin
parse_date_time(start_time)
rescue StandardError
raise(InvalidStartTime)
end
end_time = begin
parse_date_time(end_time)
rescue StandardError
raise(InvalidEndTime)
end
[start_time, end_time]
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
def formatted_hash(hash)
hash.each_with_object([]) do |p, arr|
arr << { value: p[1], timestamp: p[0] }
end
end
end
+111
View File
@@ -0,0 +1,111 @@
class V2::ReportBuilder
attr_reader :account, :params
def initialize(account, params)
@account = account
@params = params
end
def timeseries
send(params[:metric])
end
# For backward compatible with old report
def build
timeseries.each_with_object([]) do |p, arr|
arr << { value: p[1], timestamp: p[0].to_time.to_i }
end
end
def summary
{
conversations_count: conversations_count.values.sum,
incoming_messages_count: incoming_messages_count.values.sum,
outgoing_messages_count: outgoing_messages_count.values.sum,
avg_first_response_time: avg_first_response_time_summary,
avg_resolution_time: avg_resolution_time_summary,
resolutions_count: resolutions_count.values.sum
}
end
private
def scope
return account if params[:type].match?('account')
return inbox if params[:type].match?('inbox')
return user if params[:type].match?('agent')
end
def inbox
@inbox ||= account.inboxes.where(id: params[:id]).first
end
def user
@user ||= account.users.where(id: params[:id]).first
end
def conversations_count
scope.conversations
.group_by_day(:created_at, range: range, default_value: 0)
.count
end
# unscoped removes all scopes added to a model previously
def incoming_messages_count
scope.messages.unscoped.where(account_id: account.id).incoming
.group_by_day(:created_at, range: range, default_value: 0)
.count
end
def outgoing_messages_count
scope.messages.unscoped.where(account_id: account.id).outgoing
.group_by_day(:created_at, range: range, default_value: 0)
.count
end
def resolutions_count
scope.conversations
.resolved
.group_by_day(:created_at, range: range, default_value: 0)
.count
end
def avg_first_response_time
scope.events
.where(name: 'first_response')
.group_by_day(:created_at, range: range, default_value: 0)
.average(:value)
end
def avg_resolution_time
scope.events.where(name: 'conversation_resolved')
.group_by_day(:created_at, range: range, default_value: 0)
.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
def avg_resolution_time_summary
return 0 if avg_resolution_time.values.empty?
(avg_resolution_time.values.sum / avg_resolution_time.values.length)
end
def avg_first_response_time_summary
return 0 if avg_first_response_time.values.empty?
(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
+41 -4
View File
@@ -1,10 +1,47 @@
class RoomChannel < ApplicationCable::Channel
def subscribed
stream_from params[:pubsub_token]
::OnlineStatusTracker.add_subscription(params[:pubsub_token])
ensure_stream
current_user
current_account
update_subscription
broadcast_presence
end
def unsubscribed
::OnlineStatusTracker.remove_subscription(params[:pubsub_token])
def update_presence
update_subscription
broadcast_presence
end
private
def broadcast_presence
data = { account_id: @current_account.id, users: ::OnlineStatusTracker.get_available_users(@current_account.id) }
data[:contacts] = ::OnlineStatusTracker.get_available_contacts(@current_account.id) if @current_user.is_a? User
ActionCable.server.broadcast(@pubsub_token, { event: 'presence.update', data: data })
end
def ensure_stream
@pubsub_token = params[:pubsub_token]
stream_from @pubsub_token
end
def update_subscription
::OnlineStatusTracker.update_presence(@current_account.id, @current_user.class.name, @current_user.id)
end
def current_user
@current_user ||= if params[:user_id].blank?
Contact.find_by!(pubsub_token: @pubsub_token)
else
User.find_by!(pubsub_token: @pubsub_token, id: params[:user_id])
end
end
def current_account
@current_account ||= if @current_user.is_a? Contact
@current_user.account
else
@current_user.accounts.find(params[:account_id])
end
end
end
+6 -7
View File
@@ -1,14 +1,13 @@
class Api::BaseController < ApplicationController
include AccessTokenAuthHelper
respond_to :json
before_action :authenticate_user!
before_action :authenticate_access_token!, if: :authenticate_by_access_token?
before_action :validate_bot_access_token!, if: :authenticate_by_access_token?
before_action :authenticate_user!, unless: :authenticate_by_access_token?
private
def set_conversation
@conversation ||= current_account.conversations.find_by(display_id: params[:conversation_id])
end
def check_billing_enabled
raise ActionController::RoutingError, 'Not Found' unless ENV['BILLING_ENABLED']
def authenticate_by_access_token?
request.headers[:api_access_token].present? || request.headers[:HTTP_API_ACCESS_TOKEN].present?
end
end
@@ -1,10 +1,10 @@
class Api::V1::Actions::ContactMergesController < Api::BaseController
class Api::V1::Accounts::Actions::ContactMergesController < Api::V1::Accounts::BaseController
before_action :set_base_contact, only: [:create]
before_action :set_mergee_contact, only: [:create]
def create
contact_merge_action = ContactMergeAction.new(
account: current_account,
account: Current.account,
base_contact: @base_contact,
mergee_contact: @mergee_contact
)
@@ -23,6 +23,6 @@ class Api::V1::Actions::ContactMergesController < Api::BaseController
end
def contacts
@contacts ||= current_account.contacts
@contacts ||= Current.account.contacts
end
end
@@ -0,0 +1,69 @@
class Api::V1::Accounts::AgentsController < Api::V1::Accounts::BaseController
before_action :fetch_agent, except: [:create, :index]
before_action :check_authorization
before_action :find_user, only: [:create]
before_action :create_user, only: [:create]
before_action :save_account_user, only: [:create]
def index
@agents = agents
end
def destroy
@agent.current_account_user.destroy
head :ok
end
def update
@agent.update!(agent_params.except(:role))
@agent.current_account_user.update!(role: agent_params[:role]) if agent_params[:role]
render partial: 'api/v1/models/agent.json.jbuilder', locals: { resource: @agent }
end
def create
render partial: 'api/v1/models/agent.json.jbuilder', locals: { resource: @user }
end
private
def check_authorization
authorize(User)
end
def fetch_agent
@agent = agents.find(params[:id])
end
def find_user
@user = User.find_by(email: new_agent_params[:email])
end
def create_user
return if @user
@user = User.create!(new_agent_params.slice(:email, :name, :password, :password_confirmation))
end
def save_account_user
AccountUser.create!(
account_id: Current.account.id,
user_id: @user.id,
role: new_agent_params[:role],
inviter_id: current_user.id
)
end
def agent_params
params.require(:agent).permit(:email, :name, :role)
end
def new_agent_params
time = Time.now.to_i
params.require(:agent).permit(:email, :name, :role)
.merge!(password: time, password_confirmation: time, inviter: current_user)
end
def agents
@agents ||= Current.account.users
end
end
@@ -0,0 +1,31 @@
class Api::V1::Accounts::BaseController < Api::BaseController
before_action :current_account
private
def current_account
@current_account ||= ensure_current_account
Current.account = @current_account
end
def ensure_current_account
account = Account.find(params[:account_id])
if current_user
account_accessible_for_user?(account)
elsif @resource&.is_a?(AgentBot)
account_accessible_for_bot?(account)
end
switch_locale account
account
end
def account_accessible_for_user?(account)
@current_account_user = account.account_users.find_by(user_id: current_user.id)
Current.account_user = @current_account_user
render_unauthorized('You are not authorized to access this account') unless @current_account_user
end
def account_accessible_for_bot?(account)
render_unauthorized('You are not authorized to access this account') unless @resource.agent_bot_inboxes.find_by(account_id: account.id)
end
end
@@ -1,28 +1,28 @@
require 'rest-client'
require 'telegram/bot'
class Api::V1::CallbacksController < Api::BaseController
class Api::V1::Accounts::CallbacksController < Api::V1::Accounts::BaseController
before_action :inbox, only: [:reauthorize_page]
def register_facebook_page
user_access_token = params[:user_access_token]
page_access_token = params[:page_access_token]
page_name = params[:page_name]
page_id = params[:page_id]
inbox_name = params[:inbox_name]
facebook_channel = current_account.facebook_pages.create!(
name: page_name, page_id: page_id, user_access_token: user_access_token,
page_access_token: page_access_token
)
set_avatar(facebook_channel, page_id)
inbox = current_account.inboxes.create!(name: inbox_name, channel: facebook_channel)
render json: inbox
ActiveRecord::Base.transaction do
facebook_channel = Current.account.facebook_pages.create!(
page_id: page_id, user_access_token: user_access_token,
page_access_token: page_access_token
)
@facebook_inbox = Current.account.inboxes.create!(name: inbox_name, channel: facebook_channel)
set_avatar(@facebook_inbox, page_id)
rescue StandardError => e
Rails.logger.info e
end
end
def get_facebook_pages
def facebook_pages
@page_details = mark_already_existing_facebook_pages(fb_object.get_connections('me', 'accounts'))
end
# get params[:inbox_id], current_account, params[:omniauth_token]
# get params[:inbox_id], current_account. params[:omniauth_token]
def reauthorize_page
if @inbox&.facebook?
fb_page_id = @inbox.channel.page_id
@@ -40,7 +40,7 @@ class Api::V1::CallbacksController < Api::BaseController
private
def inbox
@inbox = current_account.inboxes.find_by(id: params[:inbox_id])
@inbox = Current.account.inboxes.find_by(id: params[:inbox_id])
end
def update_fb_page(fb_page_id, access_token)
@@ -50,7 +50,7 @@ class Api::V1::CallbacksController < Api::BaseController
end
def get_fb_page(fb_page_id)
current_account.facebook_pages.find_by(page_id: fb_page_id)
Current.account.facebook_pages.find_by(page_id: fb_page_id)
end
def fb_object
@@ -61,24 +61,26 @@ class Api::V1::CallbacksController < Api::BaseController
def long_lived_token(omniauth_token)
koala = Koala::Facebook::OAuth.new(ENV['FB_APP_ID'], ENV['FB_APP_SECRET'])
koala.exchange_access_token_info(omniauth_token)['access_token']
rescue StandardError => e
Rails.logger.info e
end
def mark_already_existing_facebook_pages(data)
return [] if data.empty?
data.inject([]) do |result, page_detail|
current_account.facebook_pages.exists?(page_id: page_detail['id']) ? page_detail.merge!(exists: true) : page_detail.merge!(exists: false)
page_detail[:exists] = Current.account.facebook_pages.exists?(page_id: page_detail['id']) ? true : false
result << page_detail
end
end
def set_avatar(facebook_channel, page_id)
def set_avatar(facebook_inbox, page_id)
uri = get_avatar_url(page_id)
return unless uri
avatar_resource = LocalResource.new(uri)
facebook_channel.avatar.attach(io: avatar_resource.file, filename: avatar_resource.tmp_filename, content_type: avatar_resource.encoding)
facebook_inbox.avatar.attach(io: avatar_resource.file, filename: avatar_resource.tmp_filename, content_type: avatar_resource.encoding)
end
def get_avatar_url(page_id)
@@ -90,11 +92,12 @@ class Api::V1::CallbacksController < Api::BaseController
response = uri.open(redirect: false)
rescue OpenURI::HTTPRedirect => e
uri = e.uri # assigned from the "Location" response header
retry if (tries -= 1) > 0
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
@@ -1,4 +1,4 @@
class Api::V1::CannedResponsesController < Api::BaseController
class Api::V1::Accounts::CannedResponsesController < Api::V1::Accounts::BaseController
before_action :fetch_canned_response, only: [:update, :destroy]
def index
@@ -6,7 +6,7 @@ class Api::V1::CannedResponsesController < Api::BaseController
end
def create
@canned_response = current_account.canned_responses.new(canned_response_params)
@canned_response = Current.account.canned_responses.new(canned_response_params)
@canned_response.save!
render json: @canned_response
end
@@ -24,7 +24,7 @@ class Api::V1::CannedResponsesController < Api::BaseController
private
def fetch_canned_response
@canned_response = current_account.canned_responses.find(params[:id])
@canned_response = Current.account.canned_responses.find(params[:id])
end
def canned_response_params
@@ -33,9 +33,9 @@ class Api::V1::CannedResponsesController < Api::BaseController
def canned_responses
if params[:search]
current_account.canned_responses.where('short_code ILIKE ?', "#{params[:search]}%")
Current.account.canned_responses.where('short_code ILIKE ?', "#{params[:search]}%")
else
current_account.canned_responses
Current.account.canned_responses
end
end
end
@@ -0,0 +1,57 @@
class Api::V1::Accounts::Channels::TwilioChannelsController < Api::V1::Accounts::BaseController
before_action :authorize_request
def create
ActiveRecord::Base.transaction do
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
render_could_not_create_error(e.message)
end
end
private
def authorize_request
authorize ::Inbox
end
def authenticate_twilio
client = Twilio::REST::Client.new(permitted_params[:account_sid], permitted_params[:auth_token])
client.messages.list(limit: 1)
end
def setup_webhooks
::Twilio::WebhookSetupService.new(inbox: @inbox).perform
end
def phone_number
medium == 'sms' ? permitted_params[:phone_number] : "whatsapp:#{permitted_params[:phone_number]}"
end
def medium
permitted_params[:medium]
end
def build_inbox
@twilio_channel = Current.account.twilio_sms.create!(
account_sid: permitted_params[:account_sid],
auth_token: permitted_params[:auth_token],
phone_number: phone_number,
medium: medium
)
@inbox = Current.account.inboxes.create(
name: permitted_params[:name],
channel: @twilio_channel
)
end
def permitted_params
params.require(:twilio_channel).permit(
:account_id, :phone_number, :account_sid, :auth_token, :name, :medium
)
end
end
@@ -0,0 +1,26 @@
class Api::V1::Accounts::Contacts::ContactInboxesController < Api::V1::Accounts::BaseController
before_action :ensure_contact
before_action :ensure_inbox, only: [:create]
before_action :validate_channel_type
def create
source_id = params[:source_id] || SecureRandom.uuid
@contact_inbox = ContactInbox.create(contact: @contact, inbox: @inbox, source_id: source_id)
end
private
def validate_channel_type
return if @inbox.channel_type == 'Channel::Api'
render json: { error: 'Contact Inbox creation is only allowed in API inboxes' }, status: :unprocessable_entity
end
def ensure_inbox
@inbox = Current.account.inboxes.find(params[:inbox_id])
end
def ensure_contact
@contact = Current.account.contacts.find(params[:contact_id])
end
end
@@ -1,6 +1,6 @@
class Api::V1::Contacts::ConversationsController < Api::BaseController
class Api::V1::Accounts::Contacts::ConversationsController < Api::V1::Accounts::BaseController
def index
@conversations = current_account.conversations.includes(
@conversations = Current.account.conversations.includes(
:assignee, :contact, :inbox
).where(inbox_id: inbox_ids, contact_id: permitted_params[:contact_id])
end
@@ -9,7 +9,7 @@ class Api::V1::Contacts::ConversationsController < Api::BaseController
def inbox_ids
if current_user.administrator?
current_account.inboxes.pluck(:id)
Current.account.inboxes.pluck(:id)
elsif current_user.agent?
current_user.assigned_inboxes.pluck(:id)
else
@@ -0,0 +1,56 @@
class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController
protect_from_forgery with: :null_session
before_action :check_authorization
before_action :fetch_contact, only: [:show, :update]
def index
@contacts = Current.account.contacts
end
def show; end
def create
ActiveRecord::Base.transaction do
@contact = Current.account.contacts.new(contact_create_params)
@contact.save!
@contact_inbox = build_contact_inbox
end
end
def update
@contact.update!(contact_params)
end
def search
render json: { error: 'Specify search string with parameter q' }, status: :unprocessable_entity if params[:q].blank? && return
@contacts = Current.account.contacts.where('name LIKE :search OR email LIKE :search', search: "%#{params[:q]}%")
end
private
def check_authorization
authorize(Contact)
end
def build_contact_inbox
return if params[:inbox_id].blank?
inbox = Current.account.inboxes.find(params[:inbox_id])
source_id = params[:source_id] || SecureRandom.uuid
ContactInbox.create(contact: @contact, inbox: inbox, source_id: source_id)
end
def contact_params
params.require(:contact).permit(:name, :email, :phone_number)
end
def fetch_contact
@contact = Current.account.contacts.find(params[:id])
end
def contact_create_params
params.require(:contact).permit(:name, :email, :phone_number)
end
end
@@ -0,0 +1,9 @@
class Api::V1::Accounts::Conversations::AssignmentsController < Api::V1::Accounts::Conversations::BaseController
# assign agent to a conversation
def create
# if params[:assignee_id] is not a valid id, it will set to nil, hence unassigning the conversation
assignee = Current.account.users.find_by(id: params[:assignee_id])
@conversation.update_assignee(assignee)
render json: assignee
end
end
@@ -0,0 +1,9 @@
class Api::V1::Accounts::Conversations::BaseController < Api::V1::Accounts::BaseController
before_action :conversation
private
def conversation
@conversation ||= Current.account.conversations.find_by(display_id: params[:conversation_id])
end
end
@@ -0,0 +1,11 @@
class Api::V1::Accounts::Conversations::LabelsController < Api::V1::Accounts::Conversations::BaseController
def create
@conversation.update_labels(params[:labels])
@labels = @conversation.label_list
end
# all labels of the current conversation
def index
@labels = @conversation.label_list
end
end
@@ -0,0 +1,19 @@
class Api::V1::Accounts::Conversations::MessagesController < Api::V1::Accounts::Conversations::BaseController
def index
@messages = message_finder.perform
end
def create
user = current_user || @resource
mb = Messages::MessageBuilder.new(user, @conversation, params)
@message = mb.perform
rescue StandardError => e
render_could_not_create_error(e.message)
end
private
def message_finder
@message_finder ||= MessageFinder.new(@conversation, params)
end
end
@@ -0,0 +1,78 @@
class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseController
include Events::Types
before_action :conversation, except: [:index]
before_action :contact_inbox, only: [:create]
def index
result = conversation_finder.perform
@conversations = result[:conversations]
@conversations_count = result[:count]
end
def meta
result = conversation_finder.perform
@conversations_count = result[:count]
end
def create
@conversation = ::Conversation.create!(conversation_params)
end
def show; end
def mute
@conversation.mute!
head :ok
end
def toggle_status
if params[:status]
@conversation.status = params[:status]
@status = @conversation.save
else
@status = @conversation.toggle_status
end
end
def toggle_typing_status
if params[:typing_status] == 'on'
trigger_typing_event(CONVERSATION_TYPING_ON)
elsif params[:typing_status] == 'off'
trigger_typing_event(CONVERSATION_TYPING_OFF)
end
head :ok
end
def update_last_seen
@conversation.agent_last_seen_at = DateTime.now.utc
@conversation.save!
end
private
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])
end
def contact_inbox
@contact_inbox ||= ::ContactInbox.find_by!(source_id: params[:source_id])
end
def conversation_params
{
account_id: Current.account.id,
inbox_id: @contact_inbox.inbox_id,
contact_id: @contact_inbox.contact_id,
contact_inbox_id: @contact_inbox.id
}
end
def conversation_finder
@conversation_finder ||= ConversationFinder.new(current_user, params)
end
end
@@ -1,4 +1,4 @@
class Api::V1::FacebookIndicatorsController < Api::BaseController
class Api::V1::Accounts::FacebookIndicatorsController < Api::V1::Accounts::BaseController
before_action :set_access_token
around_action :handle_with_exception
@@ -26,6 +26,7 @@ class Api::V1::FacebookIndicatorsController < Api::BaseController
def handle_with_exception
yield
rescue Facebook::Messenger::Error => e
Rails.logger.debug "Rescued: #{e.inspect}"
true
end
@@ -37,7 +38,7 @@ class Api::V1::FacebookIndicatorsController < Api::BaseController
end
def inbox
@inbox ||= current_account.inboxes.find(permitted_params[:inbox_id])
@inbox ||= Current.account.inboxes.find(permitted_params[:inbox_id])
end
def set_access_token
@@ -1,4 +1,4 @@
class Api::V1::InboxMembersController < Api::BaseController
class Api::V1::Accounts::InboxMembersController < Api::V1::Accounts::BaseController
before_action :fetch_inbox, only: [:create, :show]
before_action :current_agents_ids, only: [:create]
@@ -12,7 +12,7 @@ class Api::V1::InboxMembersController < Api::BaseController
end
def show
@agents = current_account.users.where(id: @inbox.members.pluck(:user_id))
@agents = Current.account.users.where(id: @inbox.members.pluck(:user_id))
end
private
@@ -40,6 +40,6 @@ class Api::V1::InboxMembersController < Api::BaseController
end
def fetch_inbox
@inbox = current_account.inboxes.find(params[:inbox_id])
@inbox = Current.account.inboxes.find(params[:inbox_id])
end
end
@@ -0,0 +1,97 @@
class Api::V1::Accounts::InboxesController < Api::V1::Accounts::BaseController
before_action :fetch_inbox, except: [:index, :create]
before_action :fetch_agent_bot, only: [:set_agent_bot]
before_action :check_authorization
def index
@inboxes = policy_scope(Current.account.inboxes.order_by_id.includes(:channel, :avatar_attachment))
end
def create
ActiveRecord::Base.transaction do
channel = create_channel
@inbox = Current.account.inboxes.build(
name: permitted_params[:name],
greeting_message: permitted_params[:greeting_message],
greeting_enabled: permitted_params[:greeting_enabled],
channel: channel
)
@inbox.avatar.attach(permitted_params[:avatar])
@inbox.save!
end
end
def update
@inbox.update(inbox_update_params.except(:channel))
return unless @inbox.channel.is_a?(Channel::WebWidget) && inbox_update_params[:channel].present?
@inbox.channel.update!(inbox_update_params[:channel])
update_channel_feature_flags
end
def set_agent_bot
if @agent_bot
agent_bot_inbox = @inbox.agent_bot_inbox || AgentBotInbox.new(inbox: @inbox)
agent_bot_inbox.agent_bot = @agent_bot
agent_bot_inbox.save!
elsif @inbox.agent_bot_inbox.present?
@inbox.agent_bot_inbox.destroy!
end
head :ok
end
def destroy
@inbox.destroy
head :ok
end
private
def fetch_inbox
@inbox = Current.account.inboxes.find(params[:id])
end
def fetch_agent_bot
@agent_bot = AgentBot.find(params[:agent_bot]) if params[:agent_bot]
end
def check_authorization
authorize(Inbox)
end
def create_channel
case permitted_params[:channel][:type]
when 'web_widget'
Current.account.web_widgets.create!(permitted_params[:channel].except(:type))
when 'api'
Current.account.api_channels.create!(permitted_params[:channel].except(:type))
when 'email'
Current.account.email_channels.create!(permitted_params[:channel].except(:type))
end
end
def update_channel_feature_flags
return unless inbox_update_params[:channel].key? :selected_feature_flags
@inbox.channel.selected_feature_flags = inbox_update_params[:channel][:selected_feature_flags]
@inbox.channel.save!
end
def permitted_params
params.permit(:id, :avatar, :name, :greeting_message, :greeting_enabled, channel:
[:type, :website_url, :widget_color, :welcome_title, :welcome_tagline, :webhook_url, :email])
end
def inbox_update_params
params.permit(:enable_auto_assignment, :name, :avatar, :greeting_message, :greeting_enabled,
channel: [
:website_url,
:widget_color,
:welcome_title,
:welcome_tagline,
:webhook_url,
:email,
selected_feature_flags: []
])
end
end
@@ -0,0 +1,18 @@
class Api::V1::Accounts::Integrations::AppsController < Api::V1::Accounts::BaseController
before_action :fetch_apps, only: [:index]
before_action :fetch_app, only: [:show]
def index; end
def show; end
private
def fetch_apps
@apps = Integrations::App.all.select(&:active?)
end
def fetch_app
@app = Integrations::App.find(id: params[:id])
end
end
@@ -0,0 +1,38 @@
class Api::V1::Accounts::Integrations::SlackController < Api::V1::Accounts::BaseController
before_action :fetch_hook, only: [:update, :destroy]
def create
builder = Integrations::Slack::HookBuilder.new(
account: current_account,
code: params[:code],
inbox_id: params[:inbox_id]
)
@hook = builder.perform
create_chatwoot_slack_channel
end
def update
create_chatwoot_slack_channel
render json: @hook
end
def destroy
@hook.destroy
head :ok
end
private
def fetch_hook
@hook = Integrations::Hook.find_by(app_id: 'slack')
end
def create_chatwoot_slack_channel
channel = params[:channel] || 'customer-conversations'
builder = Integrations::Slack::ChannelBuilder.new(
hook: @hook, channel: channel
)
builder.perform
end
end
@@ -0,0 +1,38 @@
class Api::V1::Accounts::LabelsController < Api::V1::Accounts::BaseController
before_action :current_account
before_action :fetch_label, except: [:index, :create]
before_action :check_authorization
def index
@labels = policy_scope(Current.account.labels)
end
def show; end
def create
@label = Current.account.labels.create!(permitted_params)
end
def update
@label.update!(permitted_params)
end
def destroy
@label.destroy
head :ok
end
private
def fetch_label
@label = Current.account.labels.find(params[:id])
end
def check_authorization
authorize(Label)
end
def permitted_params
params.require(:label).permit(:title, :description, :color, :show_on_sidebar)
end
end
@@ -1,4 +1,4 @@
class Api::V1::User::NotificationSettingsController < Api::BaseController
class Api::V1::Accounts::NotificationSettingsController < Api::V1::Accounts::BaseController
before_action :set_user, :load_notification_setting
def show; end
@@ -16,14 +16,15 @@ class Api::V1::User::NotificationSettingsController < Api::BaseController
end
def load_notification_setting
@notification_setting = @user.notification_settings.find_by(account_id: current_account.id)
@notification_setting = @user.notification_settings.find_by(account_id: Current.account.id)
end
def notification_setting_params
params.require(:notification_settings).permit(selected_email_flags: [])
params.require(:notification_settings).permit(selected_email_flags: [], selected_push_flags: [])
end
def update_flags
@notification_setting.selected_email_flags = notification_setting_params[:selected_email_flags]
@notification_setting.selected_push_flags = notification_setting_params[:selected_push_flags]
end
end
@@ -0,0 +1,40 @@
class Api::V1::Accounts::NotificationsController < Api::V1::Accounts::BaseController
protect_from_forgery with: :null_session
before_action :fetch_notification, only: [:update]
before_action :set_primary_actor, only: [:read_all]
def index
@unread_count = current_user.notifications.where(account_id: current_account.id, read_at: nil).count
@notifications = current_user.notifications.where(account_id: current_account.id).page params[:page]
end
def read_all
if @primary_actor
current_user.notifications.where(account_id: current_account.id, primary_actor: @primary_actor, read_at: nil)
.update(read_at: DateTime.now.utc)
else
current_user.notifications.where(account_id: current_account.id, read_at: nil).update(read_at: DateTime.now.utc)
end
head :ok
end
def update
@notification.update(read_at: DateTime.now.utc)
render json: @notification
end
private
def set_primary_actor
return unless params[:primary_actor_type]
return unless Notification::PRIMARY_ACTORS.include?(params[:primary_actor_type])
@primary_actor = params[:primary_actor_type].safe_constantize.find_by(id: params[:primary_actor_id])
end
def fetch_notification
@notification = current_user.notifications.find(params[:id])
end
end
@@ -1,13 +1,13 @@
class Api::V1::Account::WebhooksController < Api::BaseController
class Api::V1::Accounts::WebhooksController < Api::V1::Accounts::BaseController
before_action :check_authorization
before_action :fetch_webhook, only: [:update, :destroy]
def index
@webhooks = current_account.webhooks
@webhooks = Current.account.webhooks
end
def create
@webhook = current_account.webhooks.new(webhook_params)
@webhook = Current.account.webhooks.new(webhook_params)
@webhook.save!
end
@@ -27,7 +27,7 @@ class Api::V1::Account::WebhooksController < Api::BaseController
end
def fetch_webhook
@webhook = current_account.webhooks.find(params[:id])
@webhook = Current.account.webhooks.find(params[:id])
end
def check_authorization
+45 -8
View File
@@ -2,9 +2,11 @@ class Api::V1::AccountsController < Api::BaseController
include AuthHelper
skip_before_action :verify_authenticity_token, only: [:create]
skip_before_action :authenticate_user!, :set_current_user, :check_subscription, :handle_with_exception,
skip_before_action :authenticate_user!, :set_current_user, :handle_with_exception,
only: [:create], raise: false
before_action :check_signup_enabled
before_action :check_signup_enabled, only: [:create]
before_action :fetch_account, except: [:create]
before_action :check_authorization, except: [:create]
rescue_from CustomExceptions::Account::InvalidEmail,
CustomExceptions::Account::UserExists,
@@ -12,27 +14,62 @@ class Api::V1::AccountsController < Api::BaseController
with: :render_error_response
def create
@user = AccountBuilder.new(
@user, @account = AccountBuilder.new(
account_name: account_params[:account_name],
email: account_params[:email]
email: account_params[:email],
confirmed: confirmed?,
user: current_user
).perform
if @user
send_auth_headers(@user)
render json: {
data: @user.token_validation_response
}
render 'api/v1/accounts/create.json', locals: { resource: @user }
else
render_error_response(CustomExceptions::Account::SignupFailed.new({}))
end
end
def show
render 'api/v1/accounts/show.json'
end
def update
@account.update!(account_params.slice(:name, :locale, :domain, :support_email))
end
def update_active_at
@current_account_user.active_at = Time.now.utc
@current_account_user.save!
head :ok
end
private
def check_authorization
authorize(Account)
end
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)
params.permit(:account_name, :email, :name, :locale, :domain, :support_email)
end
def check_signup_enabled
raise ActionController::RoutingError, 'Not Found' if ENV.fetch('ENABLE_ACCOUNT_SIGNUP', true) == 'false'
end
def pundit_user
{
user: current_user,
account: @account,
account_user: @current_account_user
}
end
end
@@ -0,0 +1,7 @@
class Api::V1::AgentBotsController < Api::BaseController
skip_before_action :authenticate_user!
def index
render json: AgentBot.all
end
end
@@ -1,52 +0,0 @@
class Api::V1::AgentsController < Api::BaseController
before_action :fetch_agent, except: [:create, :index]
before_action :check_authorization
before_action :build_agent, only: [:create]
def index
@agents = agents
end
def destroy
@agent.destroy
head :ok
end
def update
@agent.update!(agent_params)
render json: @agent
end
def create
@agent.save!
render json: @agent
end
private
def check_authorization
authorize(User)
end
def fetch_agent
@agent = agents.find(params[:id])
end
def build_agent
@agent = agents.new(new_agent_params)
end
def agent_params
params.require(:agent).permit(:email, :name, :role)
end
def new_agent_params
time = Time.now.to_i
params.require(:agent).permit(:email, :name, :role)
.merge!(password: time, password_confirmation: time, inviter: current_user)
end
def agents
@agents ||= current_account.users
end
end
@@ -1,45 +0,0 @@
class Api::V1::ContactsController < Api::BaseController
protect_from_forgery with: :null_session
before_action :check_authorization
before_action :fetch_contact, only: [:show, :update]
skip_before_action :authenticate_user!, only: [:create]
skip_before_action :set_current_user, only: [:create]
skip_before_action :check_subscription, only: [:create]
skip_around_action :handle_with_exception, only: [:create]
def index
@contacts = current_account.contacts
end
def show; end
def create
@contact = Contact.new(contact_create_params)
@contact.save!
render json: @contact
end
def update
@contact.update!(contact_params)
end
private
def check_authorization
authorize(Contact)
end
def contact_params
params.require(:contact).permit(:name, :email, :phone_number)
end
def fetch_contact
@contact = current_account.contacts.find(params[:id])
end
def contact_create_params
params.require(:contact).permit(:account_id, :inbox_id).merge!(name: SecureRandom.hex)
end
end
@@ -1,10 +0,0 @@
class Api::V1::Conversations::AssignmentsController < Api::BaseController
before_action :set_conversation, only: [:create]
def create # assign agent to a conversation
# if params[:assignee_id] is not a valid id, it will set to nil, hence unassigning the conversation
assignee = current_account.users.find_by(id: params[:assignee_id])
@conversation.update_assignee(assignee)
render json: assignee
end
end
@@ -1,12 +0,0 @@
class Api::V1::Conversations::LabelsController < Api::BaseController
before_action :set_conversation, only: [:create, :index]
def create
@conversation.update_labels(params[:labels])
@labels = @conversation.label_list
end
def index # all labels of the current conversation
@labels = @conversation.label_list
end
end
@@ -1,18 +0,0 @@
class Api::V1::Conversations::MessagesController < Api::BaseController
before_action :set_conversation, only: [:index, :create]
def index
@messages = message_finder.perform
end
def create
mb = Messages::Outgoing::NormalBuilder.new(current_user, @conversation, params)
@message = mb.perform
end
private
def message_finder
@message_finder ||= MessageFinder.new(@conversation, params)
end
end
@@ -1,35 +0,0 @@
class Api::V1::ConversationsController < Api::BaseController
before_action :set_conversation, except: [:index]
def index
result = conversation_finder.perform
@conversations = result[:conversations]
@conversations_count = result[:count]
end
def show; end
def toggle_status
@status = @conversation.toggle_status
end
def update_last_seen
@conversation.agent_last_seen_at = parsed_last_seen_at
@conversation.save!
head :ok
end
private
def parsed_last_seen_at
DateTime.strptime(params[:agent_last_seen_at].to_s, '%s')
end
def set_conversation
@conversation ||= current_account.conversations.find_by(display_id: params[:id])
end
def conversation_finder
@conversation_finder ||= ConversationFinder.new(current_user, params)
end
end
@@ -1,31 +0,0 @@
class Api::V1::InboxesController < Api::BaseController
before_action :check_authorization
before_action :fetch_inbox, only: [:destroy, :update]
def index
@inboxes = policy_scope(current_account.inboxes)
end
def destroy
@inbox.destroy
head :ok
end
def update
@inbox.update(inbox_update_params)
end
private
def fetch_inbox
@inbox = current_account.inboxes.find(params[:id])
end
def check_authorization
authorize(Inbox)
end
def inbox_update_params
params.require(:inbox).permit(:enable_auto_assignment)
end
end
@@ -0,0 +1,7 @@
class Api::V1::Integrations::WebhooksController < ApplicationController
def create
builder = Integrations::Slack::IncomingMessageBuilder.new(params)
response = builder.perform
render json: response
end
end
@@ -1,9 +0,0 @@
class Api::V1::LabelsController < Api::BaseController
def index # list all labels in account
@labels = current_account.all_conversation_tags
end
def most_used
@labels = ActsAsTaggableOn::Tag.most_used(params[:count] || 10)
end
end
@@ -0,0 +1,19 @@
class Api::V1::NotificationSubscriptionsController < Api::BaseController
before_action :set_user
def create
notification_subscription = NotificationSubscriptionBuilder.new(user: @user, params: notification_subscription_params).perform
render json: notification_subscription
end
private
def set_user
@user = current_user
end
def notification_subscription_params
params.require(:notification_subscription).permit(:subscription_type, subscription_attributes: {})
end
end
+10 -2
View File
@@ -2,7 +2,7 @@ class Api::V1::ProfilesController < Api::BaseController
before_action :set_user
def show
render json: @user
render partial: 'api/v1/models/user.json.jbuilder', locals: { resource: @user }
end
def update
@@ -16,6 +16,14 @@ class Api::V1::ProfilesController < Api::BaseController
end
def profile_params
params.require(:profile).permit(:email, :name, :password, :password_confirmation, :avatar)
params.require(:profile).permit(
:email,
:name,
:display_name,
:password,
:password_confirmation,
:avatar,
:availability
)
end
end
@@ -1,103 +0,0 @@
class Api::V1::ReportsController < Api::BaseController
include CustomExceptions::Report
include Constants::Report
around_action :report_exception
def account
builder = ReportBuilder.new(current_account, account_report_params)
data = builder.build
render json: data
end
def agent
builder = ReportBuilder.new(current_account, agent_report_params)
data = builder.build
render json: data
end
def account_summary
render json: account_summary_metrics
end
def agent_summary
render json: agent_summary_metrics
end
private
def report_exception
yield
rescue InvalidIdentity, IdentityNotFound, MetricNotFound, InvalidStartTime, InvalidEndTime => e
render_error_response(e)
end
def current_account
current_user.account
end
def agent
@agent ||= current_account.users.find(params[:agent_id])
end
def account_summary_metrics
summary_metrics(ACCOUNT_METRICS, :account_summary_params, AVG_ACCOUNT_METRICS)
end
def agent_summary_metrics
summary_metrics(AGENT_METRICS, :agent_summary_params, AVG_AGENT_METRICS)
end
def summary_metrics(metrics, calc_function, avg_metrics)
metrics.each_with_object({}) do |metric, result|
data = ReportBuilder.new(current_account, send(calc_function, metric)).build
if avg_metrics.include?(metric)
sum = data.inject(0) { |sum, hash| sum + hash[:value].to_i }
sum /= data.length unless sum.zero?
else
sum = data.inject(0) { |sum, hash| sum + hash[:value].to_i }
end
result[metric] = sum
end
end
def account_summary_params(metric)
{
metric: metric.to_s,
type: :account,
since: params[:since],
until: params[:until]
}
end
def agent_summary_params(metric)
{
metric: metric.to_s,
type: :agent,
since: params[:since],
until: params[:until],
id: params[:id]
}
end
def account_report_params
{
metric: params[:metric],
type: :account,
since: params[:since],
until: params[:until]
}
end
def agent_report_params
{
metric: params[:metric],
type: :agent,
id: params[:id],
since: params[:since],
until: params[:until]
}
end
end
@@ -1,13 +0,0 @@
class Api::V1::SubscriptionsController < Api::BaseController
skip_before_action :check_subscription
before_action :check_billing_enabled
def index
render json: current_account.subscription_data
end
def status
render json: current_account.subscription.summary
end
end
@@ -1,17 +1,6 @@
class Api::V1::WebhooksController < ApplicationController
skip_before_action :authenticate_user!, raise: false
skip_before_action :set_current_user
skip_before_action :check_subscription
before_action :login_from_basic_auth, only: [:chargebee]
before_action :check_billing_enabled, only: [:chargebee]
def chargebee
chargebee_consumer.consume
head :ok
rescue StandardError => e
Raven.capture_exception(e)
head :ok
end
def twitter_crc
render json: { response_token: "sha256=#{twitter_client.generate_crc(params[:crc_token])}" }
@@ -33,16 +22,6 @@ class Api::V1::WebhooksController < ApplicationController
end
end
def login_from_basic_auth
authenticate_or_request_with_http_basic do |username, password|
username == ENV['CHARGEBEE_WEBHOOK_USERNAME'] && password == ENV['CHARGEBEE_WEBHOOK_PASSWORD']
end
end
def chargebee_consumer
@chargebee_consumer ||= ::Webhooks::Chargebee.new(params)
end
def twitter_consumer
@twitter_consumer ||= ::Webhooks::Twitter.new(params)
end
@@ -1,10 +1,13 @@
class Api::V1::Widget::BaseController < ApplicationController
before_action :set_web_widget
before_action :set_contact
private
def conversation
@conversation ||= @contact_inbox.conversations.find_by(
@conversation ||= @contact_inbox.conversations.where(
inbox_id: auth_token_params[:inbox_id]
)
).last
end
def auth_token_params
@@ -18,6 +21,7 @@ class Api::V1::Widget::BaseController < ApplicationController
def set_web_widget
@web_widget = ::Channel::WebWidget.find_by!(website_token: permitted_params[:website_token])
@account = @web_widget.account
switch_locale @account
end
def set_contact
@@ -26,4 +30,14 @@ class Api::V1::Widget::BaseController < ApplicationController
)
@contact = @contact_inbox.contact
end
def browser_params
{
browser_name: browser.name,
browser_version: browser.full_version,
device_name: browser.device.name,
platform_name: browser.platform.name,
platform_version: browser.platform.version
}
end
end

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