livechat 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +11 -0
- data/MIT-LICENSE +20 -0
- data/README.md +160 -0
- data/Rakefile +11 -0
- data/app/controllers/livechat/application_controller.rb +76 -0
- data/app/controllers/livechat/conversations_controller.rb +57 -0
- data/app/controllers/livechat/messages_controller.rb +21 -0
- data/app/controllers/livechat/visitor_controller.rb +108 -0
- data/app/controllers/livechat/widgets_controller.rb +36 -0
- data/app/helpers/livechat/widget_helper.rb +37 -0
- data/app/mailers/livechat/mailer.rb +51 -0
- data/app/models/livechat/application_record.rb +7 -0
- data/app/models/livechat/conversation.rb +108 -0
- data/app/models/livechat/message.rb +53 -0
- data/app/views/layouts/livechat/application.html.erb +97 -0
- data/app/views/livechat/conversations/index.html.erb +62 -0
- data/app/views/livechat/conversations/show.html.erb +73 -0
- data/app/views/livechat/mailer/new_agent_reply.text.erb +7 -0
- data/app/views/livechat/mailer/new_visitor_message.text.erb +7 -0
- data/config/locales/livechat.ar.yml +44 -0
- data/config/locales/livechat.bg.yml +44 -0
- data/config/locales/livechat.bn.yml +44 -0
- data/config/locales/livechat.de.yml +44 -0
- data/config/locales/livechat.el.yml +44 -0
- data/config/locales/livechat.en.yml +44 -0
- data/config/locales/livechat.es.yml +44 -0
- data/config/locales/livechat.fr.yml +44 -0
- data/config/locales/livechat.hi.yml +44 -0
- data/config/locales/livechat.hr.yml +44 -0
- data/config/locales/livechat.id.yml +44 -0
- data/config/locales/livechat.it.yml +44 -0
- data/config/locales/livechat.ja.yml +44 -0
- data/config/locales/livechat.ko.yml +44 -0
- data/config/locales/livechat.lb.yml +44 -0
- data/config/locales/livechat.nl.yml +44 -0
- data/config/locales/livechat.pl.yml +44 -0
- data/config/locales/livechat.pt.yml +44 -0
- data/config/locales/livechat.ro.yml +44 -0
- data/config/locales/livechat.ru.yml +44 -0
- data/config/locales/livechat.th.yml +44 -0
- data/config/locales/livechat.tr.yml +44 -0
- data/config/locales/livechat.uk.yml +44 -0
- data/config/locales/livechat.ur.yml +44 -0
- data/config/locales/livechat.vi.yml +44 -0
- data/config/locales/livechat.zh-CN.yml +44 -0
- data/config/routes.rb +27 -0
- data/lib/generators/livechat/install/install_generator.rb +42 -0
- data/lib/generators/livechat/install/templates/create_livechat_tables.rb.tt +36 -0
- data/lib/generators/livechat/install/templates/initializer.rb +59 -0
- data/lib/livechat/configuration.rb +103 -0
- data/lib/livechat/dashboard.js +58 -0
- data/lib/livechat/engine.rb +13 -0
- data/lib/livechat/notifications.rb +41 -0
- data/lib/livechat/version.rb +5 -0
- data/lib/livechat/widget.js +563 -0
- data/lib/livechat/widget.rb +115 -0
- data/lib/livechat.rb +56 -0
- metadata +125 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'digest'
|
|
5
|
+
|
|
6
|
+
module Livechat
|
|
7
|
+
# Serves the self-contained browser widget. The JavaScript is plain ES (no
|
|
8
|
+
# framework, no build step) and styles itself inline, so it drops into any
|
|
9
|
+
# Rails app regardless of its CSS or JS setup. It lives under lib/ (not
|
|
10
|
+
# app/assets/) so a host that runs an asset pipeline never ingests it.
|
|
11
|
+
module Widget
|
|
12
|
+
SOURCE = File.expand_path('widget.js', __dir__)
|
|
13
|
+
DASHBOARD_SOURCE = File.expand_path('dashboard.js', __dir__)
|
|
14
|
+
|
|
15
|
+
# Right-to-left scripts, so the chat renders mirrored for those locales.
|
|
16
|
+
# Matched on the language subtag, so region variants ("ar-EG") count too.
|
|
17
|
+
RTL_LANGUAGES = %w[ar arc ckb dv fa ha he ks ku ps sd ug ur yi].freeze
|
|
18
|
+
|
|
19
|
+
class << self
|
|
20
|
+
def javascript
|
|
21
|
+
@javascript ||= File.read(SOURCE)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def dashboard_javascript
|
|
25
|
+
@dashboard_javascript ||= File.read(DASHBOARD_SOURCE)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Content fingerprints for cache-busting script URLs: a changed file is
|
|
29
|
+
# a changed URL, so no browser can ever run stale widget code — Safari
|
|
30
|
+
# has been caught ignoring must-revalidate on same-URL scripts.
|
|
31
|
+
def fingerprint
|
|
32
|
+
@fingerprint ||= Digest::MD5.hexdigest(javascript)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def dashboard_fingerprint
|
|
36
|
+
@dashboard_fingerprint ||= Digest::MD5.hexdigest(dashboard_javascript)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# The two <script> tags the helper renders.
|
|
40
|
+
#
|
|
41
|
+
# The config rides in a `type="application/json"` block: it is *data*,
|
|
42
|
+
# not code, so the browser never executes it and Turbo never tries to
|
|
43
|
+
# re-run it on a soft visit — which means it needs no CSP nonce and the
|
|
44
|
+
# widget can re-read the *current* page's config on every `turbo:load`.
|
|
45
|
+
#
|
|
46
|
+
# The code is a same-origin `src` script served by the engine — NOT
|
|
47
|
+
# inlined. Under a nonce-based CSP, Turbo Drive body swaps re-run body
|
|
48
|
+
# scripts against the *original* page's CSP header, so a fresh inline
|
|
49
|
+
# nonce gets refused; a same-origin src is covered by `'self'` on every
|
|
50
|
+
# visit. `nonce:` is still stamped for hosts whose script-src has no
|
|
51
|
+
# 'self'; pass nil when the app has no nonce.
|
|
52
|
+
def snippet(locale:, authenticated:, nonce: nil)
|
|
53
|
+
json = config_json(locale:, authenticated:)
|
|
54
|
+
nonce_attr = nonce ? %( nonce="#{nonce}") : ''
|
|
55
|
+
src = "#{Livechat.config.mount_path.chomp('/')}/widget.js?v=#{fingerprint}"
|
|
56
|
+
|
|
57
|
+
%(<script type="application/json" data-livechat-config>#{json}</script>) +
|
|
58
|
+
%(<script src="#{src}" defer#{nonce_attr} data-livechat-widget></script>)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def config_json(locale:, authenticated:)
|
|
62
|
+
config = Livechat.config
|
|
63
|
+
payload = {
|
|
64
|
+
endpoint: config.widget_endpoint,
|
|
65
|
+
locale: locale.to_s,
|
|
66
|
+
rtl: rtl?(locale),
|
|
67
|
+
authenticated: authenticated ? true : false,
|
|
68
|
+
launcher: config.show_launcher ? true : false,
|
|
69
|
+
appName: Livechat.app_name,
|
|
70
|
+
accentColor: config.accent_color,
|
|
71
|
+
labels: labels
|
|
72
|
+
}
|
|
73
|
+
# Escape "</" so a value can't close the <script> block early.
|
|
74
|
+
payload.to_json.gsub('</', '<\/')
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
# Every user-facing string in the widget, resolved through Rails I18n so
|
|
80
|
+
# the chat follows the app's current locale. Each lookup carries an
|
|
81
|
+
# English default, so the widget stays fully worded even when a key is
|
|
82
|
+
# missing for the active locale.
|
|
83
|
+
def labels
|
|
84
|
+
config = Livechat.config
|
|
85
|
+
{
|
|
86
|
+
launcher: config.launcher_label.presence || t(:launcher, 'Chat with us'),
|
|
87
|
+
greeting: config.greeting.presence || t(:greeting, 'Hi! How can we help?'),
|
|
88
|
+
replyTime: config.reply_time_text.presence ||
|
|
89
|
+
t(:reply_time, 'We usually reply within a few hours.'),
|
|
90
|
+
placeholder: t(:placeholder, 'Write a message…'),
|
|
91
|
+
send: t(:send, 'Send'),
|
|
92
|
+
close: t(:close, 'Close'),
|
|
93
|
+
you: t(:you, 'You'),
|
|
94
|
+
team: t(:team, 'Support'),
|
|
95
|
+
emailPrompt: t(:email_prompt, 'Get a copy of our reply by email:'),
|
|
96
|
+
emailPlaceholder: t(:email_placeholder, 'you@example.com'),
|
|
97
|
+
emailSave: t(:email_save, 'Save'),
|
|
98
|
+
emailSaved: t(:email_saved, 'We will also reply by email.'),
|
|
99
|
+
eventResolved: t(:event_resolved, 'Conversation resolved'),
|
|
100
|
+
eventReopened: t(:event_reopened, 'Conversation reopened'),
|
|
101
|
+
errorSend: t(:error_send, 'Could not send. Please try again.'),
|
|
102
|
+
unreadAria: t(:unread_aria, 'unread messages')
|
|
103
|
+
}
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def t(key, default, **args)
|
|
107
|
+
I18n.t(key, scope: :livechat, default: default, **args)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def rtl?(locale)
|
|
111
|
+
RTL_LANGUAGES.include?(locale.to_s.downcase.split(/[-_]/).first)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
data/lib/livechat.rb
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'livechat/version'
|
|
4
|
+
require 'livechat/configuration'
|
|
5
|
+
require 'livechat/widget'
|
|
6
|
+
require 'livechat/notifications'
|
|
7
|
+
require 'livechat/engine'
|
|
8
|
+
|
|
9
|
+
# Live chat for Rails. A floating widget lets visitors — signed-in or
|
|
10
|
+
# anonymous — message your team; your team answers from a built-in inbox at
|
|
11
|
+
# the mount path, every reply signed with its author. Conversations live in
|
|
12
|
+
# your own database. When nobody is around, visitors leave an email and the
|
|
13
|
+
# thread continues there. No third-party script, no separate service.
|
|
14
|
+
module Livechat
|
|
15
|
+
class << self
|
|
16
|
+
def config
|
|
17
|
+
@config ||= Configuration.new
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def configure
|
|
21
|
+
yield config
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Can this request see the widget and write messages? Checked on the
|
|
25
|
+
# server for every widget endpoint and by the helper before rendering.
|
|
26
|
+
def enabled?(request)
|
|
27
|
+
!!config.enabled.call(request)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Can this request work the inbox? Checked by every inbox action.
|
|
31
|
+
def agent?(request)
|
|
32
|
+
!!config.authorize_agent.call(request)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def app_name
|
|
36
|
+
config.app_name.presence || rails_app_name
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# The public face of an agent message. Blank display names fall back to
|
|
40
|
+
# a localized "Support", so visitors always see a sender.
|
|
41
|
+
def display_name_for(agent_label)
|
|
42
|
+
name = config.agent_display_name.call(agent_label).presence
|
|
43
|
+
name || I18n.t(:team, scope: :livechat, default: 'Support')
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
# The application's module name, verbatim ("EthicsPortal", "SupeRails") —
|
|
49
|
+
# no inflection games. Set config.app_name for anything fancier.
|
|
50
|
+
def rails_app_name
|
|
51
|
+
Rails.application.class.module_parent_name
|
|
52
|
+
rescue StandardError
|
|
53
|
+
'this app'
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: livechat
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Yaroslav Shmarov
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rails
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '7.1'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '7.1'
|
|
26
|
+
description: |
|
|
27
|
+
A mountable Rails engine that adds support chat to your app — the thing
|
|
28
|
+
you'd otherwise pay Crisp or Intercom for, or deploy Chatwoot to get. A
|
|
29
|
+
floating widget lets visitors (signed-in or anonymous) message your team;
|
|
30
|
+
your team answers from a built-in inbox where several teammates can work
|
|
31
|
+
the same thread, every reply signed with its author. When nobody is
|
|
32
|
+
around, visitors leave an email and the conversation continues there.
|
|
33
|
+
Conversations never leave your database. Framework-agnostic: no CSS or JS
|
|
34
|
+
framework required, works with Turbo Drive and strict CSPs.
|
|
35
|
+
email:
|
|
36
|
+
- yaroslav.shmarov@clickfunnels.com
|
|
37
|
+
executables: []
|
|
38
|
+
extensions: []
|
|
39
|
+
extra_rdoc_files: []
|
|
40
|
+
files:
|
|
41
|
+
- CHANGELOG.md
|
|
42
|
+
- MIT-LICENSE
|
|
43
|
+
- README.md
|
|
44
|
+
- Rakefile
|
|
45
|
+
- app/controllers/livechat/application_controller.rb
|
|
46
|
+
- app/controllers/livechat/conversations_controller.rb
|
|
47
|
+
- app/controllers/livechat/messages_controller.rb
|
|
48
|
+
- app/controllers/livechat/visitor_controller.rb
|
|
49
|
+
- app/controllers/livechat/widgets_controller.rb
|
|
50
|
+
- app/helpers/livechat/widget_helper.rb
|
|
51
|
+
- app/mailers/livechat/mailer.rb
|
|
52
|
+
- app/models/livechat/application_record.rb
|
|
53
|
+
- app/models/livechat/conversation.rb
|
|
54
|
+
- app/models/livechat/message.rb
|
|
55
|
+
- app/views/layouts/livechat/application.html.erb
|
|
56
|
+
- app/views/livechat/conversations/index.html.erb
|
|
57
|
+
- app/views/livechat/conversations/show.html.erb
|
|
58
|
+
- app/views/livechat/mailer/new_agent_reply.text.erb
|
|
59
|
+
- app/views/livechat/mailer/new_visitor_message.text.erb
|
|
60
|
+
- config/locales/livechat.ar.yml
|
|
61
|
+
- config/locales/livechat.bg.yml
|
|
62
|
+
- config/locales/livechat.bn.yml
|
|
63
|
+
- config/locales/livechat.de.yml
|
|
64
|
+
- config/locales/livechat.el.yml
|
|
65
|
+
- config/locales/livechat.en.yml
|
|
66
|
+
- config/locales/livechat.es.yml
|
|
67
|
+
- config/locales/livechat.fr.yml
|
|
68
|
+
- config/locales/livechat.hi.yml
|
|
69
|
+
- config/locales/livechat.hr.yml
|
|
70
|
+
- config/locales/livechat.id.yml
|
|
71
|
+
- config/locales/livechat.it.yml
|
|
72
|
+
- config/locales/livechat.ja.yml
|
|
73
|
+
- config/locales/livechat.ko.yml
|
|
74
|
+
- config/locales/livechat.lb.yml
|
|
75
|
+
- config/locales/livechat.nl.yml
|
|
76
|
+
- config/locales/livechat.pl.yml
|
|
77
|
+
- config/locales/livechat.pt.yml
|
|
78
|
+
- config/locales/livechat.ro.yml
|
|
79
|
+
- config/locales/livechat.ru.yml
|
|
80
|
+
- config/locales/livechat.th.yml
|
|
81
|
+
- config/locales/livechat.tr.yml
|
|
82
|
+
- config/locales/livechat.uk.yml
|
|
83
|
+
- config/locales/livechat.ur.yml
|
|
84
|
+
- config/locales/livechat.vi.yml
|
|
85
|
+
- config/locales/livechat.zh-CN.yml
|
|
86
|
+
- config/routes.rb
|
|
87
|
+
- lib/generators/livechat/install/install_generator.rb
|
|
88
|
+
- lib/generators/livechat/install/templates/create_livechat_tables.rb.tt
|
|
89
|
+
- lib/generators/livechat/install/templates/initializer.rb
|
|
90
|
+
- lib/livechat.rb
|
|
91
|
+
- lib/livechat/configuration.rb
|
|
92
|
+
- lib/livechat/dashboard.js
|
|
93
|
+
- lib/livechat/engine.rb
|
|
94
|
+
- lib/livechat/notifications.rb
|
|
95
|
+
- lib/livechat/version.rb
|
|
96
|
+
- lib/livechat/widget.js
|
|
97
|
+
- lib/livechat/widget.rb
|
|
98
|
+
homepage: https://github.com/yshmarov/livechat
|
|
99
|
+
licenses:
|
|
100
|
+
- MIT
|
|
101
|
+
metadata:
|
|
102
|
+
homepage_uri: https://github.com/yshmarov/livechat
|
|
103
|
+
source_code_uri: https://github.com/yshmarov/livechat
|
|
104
|
+
changelog_uri: https://github.com/yshmarov/livechat/blob/main/CHANGELOG.md
|
|
105
|
+
bug_tracker_uri: https://github.com/yshmarov/livechat/issues
|
|
106
|
+
rubygems_mfa_required: 'true'
|
|
107
|
+
rdoc_options: []
|
|
108
|
+
require_paths:
|
|
109
|
+
- lib
|
|
110
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
111
|
+
requirements:
|
|
112
|
+
- - ">="
|
|
113
|
+
- !ruby/object:Gem::Version
|
|
114
|
+
version: '3.2'
|
|
115
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
116
|
+
requirements:
|
|
117
|
+
- - ">="
|
|
118
|
+
- !ruby/object:Gem::Version
|
|
119
|
+
version: '0'
|
|
120
|
+
requirements: []
|
|
121
|
+
rubygems_version: 4.0.6
|
|
122
|
+
specification_version: 4
|
|
123
|
+
summary: 'Open-source live chat for Rails: a drop-in support widget plus a team inbox,
|
|
124
|
+
in your own database.'
|
|
125
|
+
test_files: []
|