├── .gitignore ├── LICENSE ├── README.md ├── chatwoot ├── chatwoot.php └── hooks.php └── screenshot.jpg /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .DS_Store 3 | hooks-dev.php -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 WevrLabs Group 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Chatwoot-WHMCS 2 | This add-on module enables you to integrate Chatwoot with your WHMCS installation very easily and leverage its powerful features. 3 | 4 | # Installation: 5 | 1. Download the latest release of [`chatwoot-whmcs.zip`](https://github.com/WevrLabs-Group/Chatwoot-WHMCS/releases/latest) from this repository. 6 | 2. Upload and extract `chatwoot-whmcs.zip` to the `/modules/addons` directory of your WHMCS installation. 7 | 3. Navigate to `WHMCS Admin > Setup > Addon Modules`, then locate Chatwoot and click on `Activate`. 8 | 4. After addon activation, you will see the settings, start by ticking the `Enable Chatwoot` option, and then obtain your Website JS Code from your Chatwoot account by navigating to `Inboxes > Website` (if you have multiple websites added, click on the website you wish to add). Follow this guide for more details: https://www.chatwoot.com/docs/channels/website 9 | 10 | ![Addon settings](https://github.com/WevrLabs-Group/Chatwoot-WHMCS/blob/master/screenshot.jpg) 11 | 12 | > From there, everything else is self-explanatory ... 13 | 14 | > You can define your chat box position on the page ... 15 | 16 | > You can also assign a default Label to conversations from non-logged in users as we as a default label for conversations from logged-in clients. Note that the label must be present in your `Chatwoot Dashboard > Labels` section. 17 | -------------------------------------------------------------------------------- /chatwoot/chatwoot.php: -------------------------------------------------------------------------------- 1 | "Chatwoot", 30 | "description" => "Chatwoot is a customer support tool for instant messaging channels that can help businesses provide exceptional customer support. WHMCS module contributed by: WevrLabs Hosting", 31 | "version" => "2.0.4", 32 | "author" => "Contributed by WevrLabs Hosting", 33 | "fields" => [ 34 | 'chatwoot_enable' => [ 35 | 'FriendlyName' => 'Enable Chatwoot', 36 | 'Type' => 'yesno', 37 | 'Size' => '55', 38 | 'Default' => 'yes', 39 | 'Description' => 'Check to activate the chat box.', 40 | ], 41 | 'chatwoot_url' => [ 42 | 'FriendlyName' => 'Chatwoot URL', 43 | 'Type' => 'text', 44 | 'Rows' => '', 45 | 'Cols' => '', 46 | 'Default' => '', 47 | 'Description' => 'Enter Chatwoot URL. Example: https://www.chatwoot.com', 48 | ], 49 | 'chatwoot_token' => [ 50 | 'FriendlyName' => 'Website Widget Token', 51 | 'Type' => 'text', 52 | 'Rows' => '', 53 | 'Cols' => '', 54 | 'Default' => '', 55 | 'Description' => 'Enter your website widget Token in this field. You can obtain it from your Chatwoot Dashboard > Inboxes > Website > Settings.
For help, visit Chatwoot Docs', 56 | ], 57 | 'chatwoot_verhash' => [ 58 | 'FriendlyName' => 'Secret Key (Required)', 59 | 'Type' => 'text', 60 | 'Size' => '', 61 | 'Default' => '', 62 | 'Description' => 'To make sure the conversations between the customers and the support agents are private and to disallow impersonation, you can setup identity validation in Chatwoot.
The key used to generate HMAC hash is unique for each webwidget and you can copy it from Inboxes -> Widget Settings -> Configuration -> Identity Validation -> Copy the token shown there
To learn more about this, visit Chatwoot Docs', 63 | ], 64 | 'chatwoot_position' => [ 65 | 'FriendlyName' => 'Chat Box Position', 66 | 'Type' => 'radio', 67 | 'Options' => 'right,left', 68 | 'Default' => 'right', 69 | 'Description' => 'Set your chat box position, whether to be left or right in the page.', 70 | ], 71 | 'chatwoot_bubble' => [ 72 | 'FriendlyName' => 'Chat Box Bubble', 73 | 'Type' => 'radio', 74 | 'Options' => 'Standard,Expanded Bubble', 75 | 'Default' => 'Standard', 76 | 'Description' => 'Set the chat box bubble design. Read more at Chatwoot Docs.', 77 | ], 78 | 'chatwoot_launcherTitle' => [ 79 | 'FriendlyName' => 'Bubble Launcher Title', 80 | 'Type' => 'text', 81 | 'Size' => '', 82 | 'Default' => '', 83 | 'Description' => 'Set the chat box bubble design. Read more at Chatwoot Docs.', 84 | ], 85 | 'chatwoot_dark' => [ 86 | 'FriendlyName' => 'Enable Dark Mode on Widget', 87 | 'Type' => 'yesno', 88 | 'Size' => '55', 89 | 'Default' => 'no', 90 | 'Description' => 'Check to activate dark mode on the chat box.', 91 | ], 92 | 'chatwoot_lang' => [ 93 | 'FriendlyName' => 'Dynamic Language', 94 | 'Type' => 'yesno', 95 | 'Size' => '55', 96 | 'Default' => 'no', 97 | 'Description' => 'check this box to set the chat box language according to current WHMCS clientarea langauge preference.', 98 | ], 99 | 'chatwoot_setlabel' => [ 100 | 'FriendlyName' => 'Default non-logged In Conversation Label', 101 | 'Type' => 'text', 102 | 'Size' => '15', 103 | 'Default' => '', 104 | 'Description' => 'Set the default label for conversations for non-logged in visitors.
The Label must already be present in your Chatwoot Dashboard > Labels', 105 | ], 106 | 'chatwoot_setlabelloggedin' => [ 107 | 'FriendlyName' => 'Default Logged In Conversation Label', 108 | 'Type' => 'text', 109 | 'Size' => '15', 110 | 'Default' => '', 111 | 'Description' => 'Set the default label for conversations for logged in clients.
The Label must already be present in your Chatwoot Dashboard > Labels', 112 | ], 113 | 'chatwoot_enableonadmin' => [ 114 | 'FriendlyName' => 'Enable on Login as Client', 115 | 'Type' => 'yesno', 116 | 'Size' => '55', 117 | 'Default' => 'no', 118 | 'Description' => 'check this box to enable the chat box when admin is logged in as client (not recommended, as it may mess up real users\' sessions, so enable this option only for debugging purposes and make sure to logout of the user account to trigger session reset.', 119 | ], 120 | ], 121 | ]; 122 | } 123 | 124 | function chatwoot_activate() 125 | { 126 | return ['status' => 'success', 'description' => "Chatwoot for WHMCS has been successfully activated! Don't forget to configure the settings below!"]; 127 | } 128 | -------------------------------------------------------------------------------- /chatwoot/hooks.php: -------------------------------------------------------------------------------- 1 | select('value')->where('module', '=', 'chatwoot')->where('setting', '=', 'chatwoot_enable')->where('value', 'on')->count(); 30 | $chatwoot_url = Capsule::table('tbladdonmodules')->where('module', 'chatwoot')->where('setting', 'chatwoot_url')->value('value'); 31 | $chatwoot_token = Capsule::table('tbladdonmodules')->where('module', 'chatwoot')->where('setting', 'chatwoot_token')->value('value'); 32 | $verification_hash = Capsule::table('tbladdonmodules')->where('module', 'chatwoot')->where('setting', 'chatwoot_verhash')->value('value'); 33 | $chatwoot_position = Capsule::table('tbladdonmodules')->where('module', 'chatwoot')->where('setting', 'chatwoot_position')->value('value'); 34 | $chatwoot_bubble = Capsule::table('tbladdonmodules')->where('module', 'chatwoot')->where('setting', 'chatwoot_bubble')->value('value'); 35 | $chatwoot_launcherTitle = Capsule::table('tbladdonmodules')->where('module', 'chatwoot')->where('setting', 'chatwoot_launcherTitle')->value('value'); 36 | $chatwoot_dark = Capsule::table('tbladdonmodules')->where('module', 'chatwoot')->where('setting', 'chatwoot_dark')->value('value'); 37 | $chatwoot_lang_setting = Capsule::table('tbladdonmodules')->where('module', 'chatwoot')->where('setting', 'chatwoot_lang')->value('value'); 38 | 39 | $chatwoot_setlabel = Capsule::table('tbladdonmodules')->where('module', 'chatwoot')->where('setting', 'chatwoot_setlabel')->value('value'); 40 | $chatwoot_setlabelloggedin = Capsule::table('tbladdonmodules')->where('module', 'chatwoot')->where('setting', 'chatwoot_setlabelloggedin')->value('value'); 41 | 42 | $chatwoot_admin = Capsule::table('tbladdonmodules')->where('module', 'chatwoot')->where('setting', 'chatwoot_enableonadmin')->value('value'); 43 | 44 | # ignore if admin 45 | if (empty($chatwoot_admin) && isset($_SESSION['adminid'])) { 46 | return; 47 | } 48 | 49 | # Disable or Enable Chatwoot 50 | if (empty($isenabled)) { 51 | return; 52 | } 53 | 54 | # bubble design 55 | if ($chatwoot_bubble == 'Standard') { 56 | $chatwoot_bubble = 'standard'; 57 | } elseif ($chatwoot_bubble == 'Expanded Bubble') { 58 | $chatwoot_bubble = 'expanded_bubble'; 59 | } 60 | 61 | # dark mode 62 | if ($chatwoot_dark == 'on') { 63 | $chatwoot_dark = 'auto'; 64 | } 65 | 66 | # widget lang 67 | if ($chatwoot_lang_setting) { 68 | $chatwoot_lang = cw_langCode(ucfirst($vars['language'])); 69 | } 70 | 71 | # user basic info 72 | $currentUser = new CurrentUser; 73 | $client = $currentUser->client(); 74 | $user = $currentUser->user(); 75 | $currentpage = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]"; 76 | 77 | # Fetch labels 78 | if (!is_null($user)) { 79 | $chatwoot_label = $chatwoot_setlabelloggedin; 80 | } else { 81 | $chatwoot_label = $chatwoot_setlabel; 82 | } 83 | 84 | # Get client ID and set contact ID 85 | if ($user && $client && $user->isOwner($client)) { 86 | $ClientID = $client->id; 87 | } elseif ($user) { 88 | //$ownedClients = $user->ownedClients()->all(); 89 | //$ClientID = $ownedClients[0]['id']; 90 | $ClientID = $client->id; 91 | } 92 | 93 | if (!is_null($user)) { 94 | $clientChatID = hash_hmac("sha256", $ClientID, $verification_hash); 95 | $identifier_hash = hash_hmac("sha256", $clientChatID, $verification_hash); 96 | } 97 | 98 | # build contact info 99 | if (!is_null($user)) { 100 | 101 | $apiPostData = array('clientid' => $ClientID, 'stats' => true); 102 | $apiResults = localAPI('GetClientsDetails', $apiPostData); 103 | 104 | # Client Info 105 | $clientemail = $apiResults['client']['email']; 106 | $clientname = $apiResults['client']['fullname']; 107 | $clientphone = $apiResults['client']['phonenumberformatted']; 108 | $clientcompany = $apiResults['client']['companyname']; 109 | $clientcountry = $apiResults['client']['countryname']; 110 | $clientcity = $apiResults['client']['city']; 111 | $clientstate = $apiResults['client']['fullstate']; 112 | $clientpostcode = $apiResults['client']['postcode']; 113 | $clientlang = $apiResults['client']['language']; 114 | 115 | # Extra Meta 116 | $clienttickets = $apiResults['stats']['numactivetickets']; 117 | $clientcredit = $apiResults['stats']['creditbalance']; 118 | $clientrevenue = $apiResults['stats']['income']; 119 | $clientunpaid = $apiResults['stats']['numunpaidinvoices']; 120 | $clientunpaidtotal = $apiResults['stats']['unpaidinvoicesamount']; 121 | $clientoverdue = $apiResults['stats']['numoverdueinvoices']; 122 | $clientoverduetotal = $apiResults['stats']['overdueinvoicesbalance']; 123 | $isClientAffiliate = $apiResults["stats"]["isAffiliate"]; 124 | $clientemailstatus = $apiResults["email_verified"]; 125 | 126 | # Is Email Verified? 127 | if ($clientemailstatus) { 128 | $clientemailver = 'Verified'; 129 | } else { 130 | $clientemailver = 'Not Verified'; 131 | } 132 | 133 | # Is Client an Affiliate? 134 | if ($isClientAffiliate == 1) { 135 | $clientaffiliate = 'Yes'; 136 | } else { 137 | $clientaffiliate = 'No'; 138 | } 139 | } 140 | 141 | # prepare widget code 142 | $chatwoot_jscode = " 143 | 160 | "; 161 | 162 | # Now let's set the final output 163 | 164 | if (!is_null($user)) { 165 | 166 | $chatwoot_output = 167 | "$chatwoot_jscode 168 | "; 201 | } else { 202 | $chatwoot_output = " 203 | $chatwoot_jscode 204 | "; 213 | } 214 | return $chatwoot_output; 215 | } 216 | 217 | function hook_chatwoot_logout_output($vars) 218 | { 219 | $chatwoot_logoutJS = " 220 | "; 225 | echo $chatwoot_logoutJS; 226 | } 227 | 228 | $whmcsver = cwoot_whmcs_version(); 229 | 230 | # for WHMCS 8 and later 231 | if ($whmcsver > 7) { 232 | add_hook('UserLogout', 1, function ($vars) { 233 | $chatwoot_logoutJS = " 234 | 239 | "; 240 | $_SESSION['chatwoot_logoutJS'] = $chatwoot_logoutJS; 241 | }); 242 | 243 | add_hook('ClientAreaPageLogin', 1, function ($vars) { 244 | if ($_SESSION['chatwoot_logoutJS']) { 245 | echo $_SESSION['chatwoot_logoutJS']; 246 | unset($_SESSION['chatwoot_logoutJS']); 247 | } 248 | }); 249 | } 250 | 251 | $whmcsver = cwoot_whmcs_version(); 252 | $LogoutHook = ($whmcsver > 7) ? 'UserLogout' : 'ClientLogout'; 253 | 254 | function ViewClientSwitchAccount($vars) { 255 | 256 | $url = "https://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]"; 257 | 258 | if (str_contains($url, '/user/accounts') || str_contains($url, '/login')) { 259 | 260 | $output .= << 262 | 267 | 268 | HTML; 269 | 270 | return $output; 271 | } 272 | } 273 | 274 | add_hook("ClientAreaHeaderOutput", 1, "ViewClientSwitchAccount"); 275 | add_hook('ClientAreaFooterOutput', 1, 'hook_chatwoot_output'); 276 | add_hook($LogoutHook, 1, 'hook_chatwoot_logout_output'); 277 | 278 | # meta 279 | function cwoot_whmcs_version() { 280 | $whmcsversion = Capsule::table('tblconfiguration')->where('setting', 'Version')->value('value'); 281 | return substr($whmcsversion, 0, 1); 282 | } 283 | 284 | # to deal with langs 285 | function cw_langCode($name) 286 | { 287 | $languageCodes = array( 288 | "aa" => "Afar", 289 | "ab" => "Abkhazian", 290 | "ae" => "Avestan", 291 | "af" => "Afrikaans", 292 | "ak" => "Akan", 293 | "am" => "Amharic", 294 | "an" => "Aragonese", 295 | "ar" => "Arabic", 296 | "as" => "Assamese", 297 | "av" => "Avaric", 298 | "ay" => "Aymara", 299 | "az" => "Azerbaijani", 300 | "ba" => "Bashkir", 301 | "be" => "Belarusian", 302 | "bg" => "Bulgarian", 303 | "bh" => "Bihari", 304 | "bi" => "Bislama", 305 | "bm" => "Bambara", 306 | "bn" => "Bengali", 307 | "bo" => "Tibetan", 308 | "br" => "Breton", 309 | "bs" => "Bosnian", 310 | "ca" => "Catalan", 311 | "ce" => "Chechen", 312 | "ch" => "Chamorro", 313 | "co" => "Corsican", 314 | "cr" => "Cree", 315 | "cs" => "Czech", 316 | "cu" => "Church Slavic", 317 | "cv" => "Chuvash", 318 | "cy" => "Welsh", 319 | "da" => "Danish", 320 | "de" => "German", 321 | "dv" => "Divehi", 322 | "dz" => "Dzongkha", 323 | "ee" => "Ewe", 324 | "el" => "Greek", 325 | "en" => "English", 326 | "eo" => "Esperanto", 327 | "es" => "Spanish", 328 | "et" => "Estonian", 329 | "eu" => "Basque", 330 | "fa" => "Persian", 331 | "ff" => "Fulah", 332 | "fi" => "Finnish", 333 | "fj" => "Fijian", 334 | "fo" => "Faroese", 335 | "fr" => "French", 336 | "fy" => "Western Frisian", 337 | "ga" => "Irish", 338 | "gd" => "Scottish Gaelic", 339 | "gl" => "Galician", 340 | "gn" => "Guarani", 341 | "gu" => "Gujarati", 342 | "gv" => "Manx", 343 | "ha" => "Hausa", 344 | "he" => "Hebrew", 345 | "hi" => "Hindi", 346 | "ho" => "Hiri Motu", 347 | "hr" => "Croatian", 348 | "ht" => "Haitian", 349 | "hu" => "Hungarian", 350 | "hy" => "Armenian", 351 | "hz" => "Herero", 352 | "ia" => "Interlingua (International Auxiliary Language Association)", 353 | "id" => "Indonesian", 354 | "ie" => "Interlingue", 355 | "ig" => "Igbo", 356 | "ii" => "Sichuan Yi", 357 | "ik" => "Inupiaq", 358 | "io" => "Ido", 359 | "is" => "Icelandic", 360 | "it" => "Italian", 361 | "iu" => "Inuktitut", 362 | "ja" => "Japanese", 363 | "jv" => "Javanese", 364 | "ka" => "Georgian", 365 | "kg" => "Kongo", 366 | "ki" => "Kikuyu", 367 | "kj" => "Kwanyama", 368 | "kk" => "Kazakh", 369 | "kl" => "Kalaallisut", 370 | "km" => "Khmer", 371 | "kn" => "Kannada", 372 | "ko" => "Korean", 373 | "kr" => "Kanuri", 374 | "ks" => "Kashmiri", 375 | "ku" => "Kurdish", 376 | "kv" => "Komi", 377 | "kw" => "Cornish", 378 | "ky" => "Kirghiz", 379 | "la" => "Latin", 380 | "lb" => "Luxembourgish", 381 | "lg" => "Ganda", 382 | "li" => "Limburgish", 383 | "ln" => "Lingala", 384 | "lo" => "Lao", 385 | "lt" => "Lithuanian", 386 | "lu" => "Luba-Katanga", 387 | "lv" => "Latvian", 388 | "mg" => "Malagasy", 389 | "mh" => "Marshallese", 390 | "mi" => "Maori", 391 | "mk" => "Macedonian", 392 | "ml" => "Malayalam", 393 | "mn" => "Mongolian", 394 | "mr" => "Marathi", 395 | "ms" => "Malay", 396 | "mt" => "Maltese", 397 | "my" => "Burmese", 398 | "na" => "Nauru", 399 | "nb" => "Norwegian Bokmal", 400 | "nd" => "North Ndebele", 401 | "ne" => "Nepali", 402 | "ng" => "Ndonga", 403 | "nl" => "Dutch", 404 | "nn" => "Norwegian Nynorsk", 405 | "no" => "Norwegian", 406 | "nr" => "South Ndebele", 407 | "nv" => "Navajo", 408 | "ny" => "Chichewa", 409 | "oc" => "Occitan", 410 | "oj" => "Ojibwa", 411 | "om" => "Oromo", 412 | "or" => "Oriya", 413 | "os" => "Ossetian", 414 | "pa" => "Panjabi", 415 | "pi" => "Pali", 416 | "pl" => "Polish", 417 | "ps" => "Pashto", 418 | "pt" => "Portuguese", 419 | "qu" => "Quechua", 420 | "rm" => "Raeto-Romance", 421 | "rn" => "Kirundi", 422 | "ro" => "Romanian", 423 | "ru" => "Russian", 424 | "rw" => "Kinyarwanda", 425 | "sa" => "Sanskrit", 426 | "sc" => "Sardinian", 427 | "sd" => "Sindhi", 428 | "se" => "Northern Sami", 429 | "sg" => "Sango", 430 | "si" => "Sinhala", 431 | "sk" => "Slovak", 432 | "sl" => "Slovenian", 433 | "sm" => "Samoan", 434 | "sn" => "Shona", 435 | "so" => "Somali", 436 | "sq" => "Albanian", 437 | "sr" => "Serbian", 438 | "ss" => "Swati", 439 | "st" => "Southern Sotho", 440 | "su" => "Sundanese", 441 | "sv" => "Swedish", 442 | "sw" => "Swahili", 443 | "ta" => "Tamil", 444 | "te" => "Telugu", 445 | "tg" => "Tajik", 446 | "th" => "Thai", 447 | "ti" => "Tigrinya", 448 | "tk" => "Turkmen", 449 | "tl" => "Tagalog", 450 | "tn" => "Tswana", 451 | "to" => "Tonga", 452 | "tr" => "Turkish", 453 | "ts" => "Tsonga", 454 | "tt" => "Tatar", 455 | "tw" => "Twi", 456 | "ty" => "Tahitian", 457 | "ug" => "Uighur", 458 | "uk" => "Ukrainian", 459 | "ur" => "Urdu", 460 | "uz" => "Uzbek", 461 | "ve" => "Venda", 462 | "vi" => "Vietnamese", 463 | "vo" => "Volapuk", 464 | "wa" => "Walloon", 465 | "wo" => "Wolof", 466 | "xh" => "Xhosa", 467 | "yi" => "Yiddish", 468 | "yo" => "Yoruba", 469 | "za" => "Zhuang", 470 | "zh" => "Chinese", 471 | "zu" => "Zulu", 472 | ); 473 | return array_search($name, $languageCodes); 474 | } 475 | -------------------------------------------------------------------------------- /screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WevrLabs-Group/Chatwoot-WHMCS/2fb87cd387015fbe0d7c0303f04d0394afd7bff4/screenshot.jpg --------------------------------------------------------------------------------