├── .gitignore ├── LICENSE.md ├── README.md ├── README_NoMySQL.md ├── TROUBLESHOOT.md ├── config-default.php ├── cwslack-activities.php ├── cwslack-configs.php ├── cwslack-contacts.php ├── cwslack-dbmanage.php ├── cwslack-firmalerts.php ├── cwslack-follow.php ├── cwslack-incoming.php ├── cwslack-lunch-cron.php ├── cwslack-lunch.php ├── cwslack-notes.php ├── cwslack-priorityalerts.php ├── cwslack-stats.php ├── cwslack-tasks.php ├── cwslack-time.php ├── cwslack-timealerts.php ├── cwslack.php ├── functions.php ├── install.php └── updates ├── update-2.0-to-2.1.php ├── update-2.1-to-2.2.php ├── update-2.2-to-2.3.php └── update-2.3-2.4-to-2.5.php /.gitignore: -------------------------------------------------------------------------------- 1 | cwslacktest.php 2 | .idea/ 3 | internal/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CWSlack-SlashCommands 2 | 3 | This script, when hosted on a PHP supported web server, will act as a bridge between the JSON requests of Slack and the JSON responses of the ConnectWise REST API. 4 | 5 | cwslack.php, cwslack-incoming.php, cwslack-activities.php, cwslack-configs.php, and cwslack-contacts.php were designed to be independent, but all rely on the config.php and functions.php files. This allows you to pick and choose what you want and for different Slack commands instead of one universal /cw tickets 249123 and /cw contact john doe it can be /t 249123 and /c john doe. 6 | 7 | #### Note: This project is currently being maintained for bugs only, all further new feature development is being done on the hosted successor at https://mspic.io 8 | 9 | #### Note: As of July 2019, you need a Client ID to use any public integrations with the ConnectWise APIs. You'll need to sign up for a developer account at https://developer.connectwise.com and you can then generate a Client ID which goes into config.php as $cwClientId. This does not apply to hosted integrations such as mspIC, as that is handled by the host. Generate your ID here: https://developer.connectwise.com/ClientID 10 | 11 | #### Usage 12 | 13 | * cwslack.php: Pull ticket information, create new tickets, change status, and change priority. 14 | * cwslack-activities.php: Pull activity information 15 | * cwslack-contacts.php: Pull contact information 16 | * cwslack-configs.php: Pull configuration record information 17 | * cwslack-tasks.php: View and update tasks on tickets 18 | * cwslack-notes.php: Post notes to tickets 19 | * cwslack-time.php: Post time entries to tickets and report on time sheets. 20 | * cwslack-incoming.php: Receive ticket creation/update notices in Slack 21 | * cwslack-follow.php: Follow a ticket to be direct messaged when updated 22 | * cwslack-firmalerts.php: Receive notifications when firm appointments are coming up 23 | * cwslack-dbmanage.php: Manage the MySQL user database within Slack. 24 | 25 | # Installation Instructions 26 | 27 | This script set and all modules require PHP version 5 and the cURL extension, and many require a MySQL or MariaDB server. 28 | 29 | For unsupported non-MySQL installation instructions, please see README_NoMySQL.md 30 | 31 | **See TROUBLESHOOT.md first if you have any issues. Otherwise, contact info below.** 32 | 33 | You can reach me on the r/msp Discord, LabTechGeek Slack, or via reddit at /u/jundis if you need basic support. 34 | 35 | You can also reach me at joey(at)und.is should you need more intense support, custom modifications, or want your install done by me. 36 | 37 | #### Update Instructions 38 | 39 | Use the scripts found in the updates folder to upgrade from an older version to current. This will automatically update the config.php file with necessary values and create any new MySQL tables as well. You can also manually update by comparing the config file from this repository to your active one. 40 | 41 | ## cwslack.php, activities, contacts, notes, configs, tasks, time, and dbmanage 42 | 43 | 1. Download the respective php file, functions.php, install.php, and config-default.php files. 44 | 2. Place on a compatible web server 45 | 3. **Rename config-default.php to config.php** 46 | 3. Create a new slack slash command integration at https://SLACK TEAM.slack.com/apps/A0F82E8CA-slash-commands 47 | 4. Set command to reflect the task necessary. E.x. /t for tickets, /act for activities, /note for notes. 48 | 5. Set the URL to https://domain.tld/cwslack.php (or other php file) 49 | 6. Set Method to GET 50 | 7. Copy the token 51 | 8. Set a name, icon, and auto complete text if wanted. 52 | 9. Run install.php and proceed through database setup. This will also verify you have the required PHP and cURL versions. 53 | 10. Complete the install.php configuration, or manually modify the config.php file with the necessary values. Full configuration info found in config.php comments. 54 | 11. Test it in Slack! 55 | 56 | ## cwslack-incoming.php 57 | 58 | 1. Download the cwslack-incoming.php, functions.php, install.php, and config-default.php files. 59 | 2. Place on a compatible web server 60 | 3. **Rename config-default.php to config.php** 61 | 3. Create a new slack incoming web hook integration at https://my.slack.com/services/new/incoming-webhook/ 62 | 4. Set a name, icon, and if wanted. 63 | 5. Set channel that you want to post to and copy the Web hook URL 64 | 6. Create a new integrator login in ConnectWise: 65 | - Go to System > Setup Tables in the client 66 | - Type "int" in the table field and select Integrator Login 67 | - Create a new login with whatever username/password, we don't need this. 68 | - Set Access Level to "All Records" 69 | - Enable "Service Ticket API" and select the board(s) you want this to run on. 70 | - Enter https://domain.tld/cwslack-incoming.php?id= for the callback URL (do not enable legacy format) 71 | 7. Modify the config.php file with your companies values and timezone, make sure to set the value for $webhookurl to the value copied in step 5. 72 | 8. Change the $postupdated and $postadded to what you prefer. Enabling $postupdated can get spammy. 73 | 9. Test it in Slack by creating a new ticket on the board you selected in step 6! 74 | 75 | #### Different boards to different channels 76 | 77 | As of version 2.4 the implementation has been changed from using callback URLs to using the $boardmapping variable in config.php 78 | 79 | ## cwslack-firmalerts.php AND cwslack-priorityalerts.php 80 | 81 | **(Requires some variables from cwslack-incoming.php to function if you don't use that)** 82 | 83 | Priority alerts uses the same instructions, same cron line just different file name. 84 | 85 | 1. Download the cwslack-firmalerts.php, functions.php, install.php, and config-default.php files. 86 | 2. Place on a compatible web server. 87 | 3. **Rename config-default.php to config.php** 88 | 3. Run install.php and proceed through database setup. This will also verify you have the required PHP and cURL versions. 89 | 4. Change $posttousers or $posttochan to 0 in config.php if you don't want it posting to one or the other. 90 | 5. Setup a cron job or scheduled task on your server to run this PHP file **every minute.** 91 | ```Cron: * * * * * /usr/bin/php /var/www/cwslack-firmalerts.php >/dev/null 2>&1``` 92 | 6. Set a firm appointment and test 93 | 94 | ## cwslack-timealerts.php 95 | 96 | **(Requires some variables from cwslack-firmalerts.php and cwslack-time.php to function if you don't use those)** 97 | 98 | 1. Download the cwslack-timealerts.php, functions.php, install.php, and config-default.php files. 99 | 2. Place on a compatible web server. 100 | 3. **Rename config-default.php to config.php** 101 | 3. Run install.php and proceed through database setup. This will also verify you have the required PHP and cURL versions. 102 | 4. Change $posttousers or $posttochan to 0 in config.php if you don't want it posting to one or the other. 103 | 5. Setup a cron job or scheduled task on your server to run this PHP file **every 30 minutes.** 104 | ```Cron: */30 * * * 1-5 /usr/bin/php /var/www/cwslack-timealerts.php >/dev/null 2>&1``` 105 | 6. Fail to enter time and test! It will alert after 2 hours of time is lacking. 106 | 107 | ## cwslack-lunch.php 108 | 109 | This module requires a cron job or other scheduled task to clear the Lunch database table at midnight each night. 110 | 111 | Schedule using the following line in crontab. 112 | 113 | ```0 0 * * * /usr/bin/php /var/www/cwslack-lunch-cron.php >/dev/null 2>&1``` 114 | 115 | ## cwslack-follow.php 116 | 117 | **(Also requires cwslack-incoming.php to function)** 118 | 119 | 1. Download the cwslack-follow.php, functions.php, install.php, and config-default.php files. 120 | 2. Place on a compatible web server 121 | 3. **Rename config-default.php to config.php** 122 | 3. Create a new slack slash command integration at https://SLACK TEAM.slack.com/apps/A0F82E8CA-slash-commands 123 | 4. Set command to /follow (or other if you prefer) 124 | 5. Set the URL to https://domain.tld/cwslack-follow.php 125 | 6. Set Method to GET 126 | 7. Copy the token 127 | 8. Set a name, icon, and auto complete text if wanted. 128 | 9. Run install.php and proceed through database setup. This will also verify you have the required PHP and cURL versions. 129 | 10. Modify the config.php file with your companies values and timezone. Full configuration info found in config.php comments. 130 | 11. Test it in Slack! 131 | 132 | To enable ConnectWise link to follow and unfollow a ticket: 133 | 134 | 1. Open Setup Tables in ConnectWise. 135 | 2. Open the "Links" table. 136 | 3. Create a new Link referencing "Service" 137 | 4. Set the Link Name to "Slack Follow" 138 | 5. Set the Link Definition to https://yourdomain.tld/cwslack-follow.php?memberid=[memberid]&srnumber=[srnumber]&method=follow 139 | 6. Create a new Link referencing "Service" 140 | 7. Set the Link Name to "Slack Unfollow" 141 | 8. Set the Link Definition to https://yourdomain.tld/cwslack-follow.php?memberid=[memberid]&srnumber=[srnumber]&method=unfollow 142 | 9. Change the "method" on these links to whatever you set your $followtoken and $unfollowtoken to in config.php. 143 | 10. Test the links! 144 | 145 | 146 | # API Key Setup 147 | 148 | 1. Login to ConnectWise 149 | 2. In the top right, click on your name 150 | 3. Go to "My Account" 151 | 4. Select the "API Keys" tab 152 | 5. Click the Plus icon to create a new key 153 | 6. Provide a description and click the Save icon. 154 | 7. Save this information, you cannot retrieve the private key ever again so if lost you will need to create new ones. 155 | 156 | # Command Usage 157 | 158 | \* denotes required 159 | 160 | ## cwslack.php 161 | 162 | /t [ticket number or new]* [command] [option3] 163 | 164 | ### status 165 | 166 | option3 should be a valid status for your ticket. This can be partial: e.x. /t 592139 status need, can set the status to Need To Schedule if that is the only status with the word "need" in it. 167 | 168 | ### priority 169 | 170 | option3 should be a valid priority for your ticket. Accepts partial like status above. 171 | 172 | ### full (or) notes (or) all 173 | 174 | If $posttext=1 in config.php, shows you the latest note and the initial note. This displays to you only to avoid spam. 175 | 176 | ### initial (or) note (or) first 177 | 178 | If $posttext=1 in config.php, shows you the initial note of the ticket. This displays to you only to avoid spam. 179 | 180 | ### new [board name]|[company name]|[ticket summary] 181 | 182 | Use new instead of a ticket number to create a new ticket. Pipe symbols are required between [board name], [company name], and [ticket summary], but brackets are not used in final command. 183 | 184 | Only include [board name] if you have $useboards set to 1 in config.php. 185 | 186 | ### scheduleme [time] 187 | 188 | Schedules you for the specified ticket at the specified time. Accepts most reasonable forms of time (e.x. 4:00PM, Tomorrow 4:00PM, 1/4/2017 4:00PM, Wednesday 4:00PM) 189 | 190 | ### schedule [user] [time] 191 | 192 | Schedules the specified user for the ticket at specified time. Accepts most reasonable forms of time like above. 193 | 194 | ## cwslack-activities.php 195 | 196 | /act new\*|[activity title]\*|[assigned to]* 197 | 198 | All are required for activities. New will be replaced with more commands in the future. 199 | 200 | ## cwslack-follow.php 201 | 202 | /follow [ticket number]* (unfollow) 203 | 204 | Add unfollow to the end of the command to stop following a ticket. 205 | 206 | ## cwslack-contacts.php 207 | 208 | /contact [last name]* OR [first name] [last name] 209 | 210 | Either option works, but you cannot search by first name only. 211 | 212 | ## cwslack-notes.php 213 | 214 | /note [ticket number]* [internal OR external OR externalemail]* [ticket note]* 215 | 216 | This does allow spaces for the ticket note so do not wrap in quotation marks or anything. Using "externalemail" as the option will trigger notifications according to boxes checked on ticket "Send Notes as Email" 217 | 218 | ## cwslack-configs.php 219 | 220 | /config [company name]\*|[config name]* 221 | 222 | Requires pipe symbol between the two, will return details on config that matches search. 223 | 224 | ## cwslack-tasks.php 225 | 226 | /tasks [ticket number]\* [command]\* [task number] [note] 227 | 228 | Commands: 229 | 230 | * list : List all tasks on [ticket number] 231 | * open/reopen : Mark a task as open, removing the done flag, requires [task number]. 232 | * close/complete/done/completed : Mark a task as done, requires [task number]. Can also add [note] for a resolution note. 233 | * update/change/note : Change the note on a task, requires [task number] and [note]. 234 | * new/add : Add a new task to the end of the priority list, requires [note] but do not include [task number]. 235 | 236 | ## cwslack-time.php 237 | 238 | /times [ticket number]\* [type]\* [time]\* [note]\* 239 | 240 | * [ticket number] = A valid ticket number 241 | * [type] = Eitehr detailed, internal, or resolution. Also accepts d/i/r instead 242 | * [time] = Shorthand time, use digits then h or m to designate units. E.x. 1.5h, 35m, 80m. NOT 1.5 hours, 35 minutes, etc 243 | * [note] = Any sentence to be used as the ticket note. 244 | 245 | /times report [user] 246 | 247 | * Accepts a username (direct CW or Slack mapped) and outputs their daily time information 248 | * If no user name is specified, it uses your Slack username as the target 249 | 250 | /times reportall 251 | 252 | * No input, just outputs a list of users who have entered time and their time information. 253 | 254 | ## cwslack-dbmanage.php 255 | 256 | /dbm [command]* [options] 257 | 258 | Commands available: 259 | * help - Display this help text 260 | * listmap - List all username mappings between CW and Slack 261 | * addmap [slackname]* [cwname]* - Associate the two names 262 | * removemap [slackname]* - Remove a mapping 263 | * clearfollow confirm* - Clears the follow database 264 | -------------------------------------------------------------------------------- /README_NoMySQL.md: -------------------------------------------------------------------------------- 1 | # CWSlack-SlashCommands 2 | 3 | See README.md for full configuration details and other information. 4 | 5 | This file should only be used if you cannot use a MySQL or Maria DB server for this script. Note that any new updates are not supported without MySQL and the Follow functionality will not work without MySQL past version 2.2. 6 | 7 | Please use this release if intending on working without MySQL: https://github.com/jundis/CWSlack-SlashCommands/releases/tag/2.2 8 | 9 | 10 | # Installation Instructions 11 | 12 | ## cwslack.php, activities, contacts, notes, and configs 13 | 14 | 1. Download the respective php file, functions.php, and config.php files. 15 | 2. Place on a compatible web server 16 | 3. Create a new slack slash command integration at https://SLACK TEAM.slack.com/apps/A0F82E8CA-slash-commands 17 | 4. Set command to reflect the task necessary. E.x. /t for tickets, /act for activities, /note for notes. 18 | 5. Set the URL to https://domain.tld/cwslack.php (or other php file) 19 | 6. Set Method to GET 20 | 7. Copy the token 21 | 8. Set a name, icon, and auto complete text if wanted. 22 | 9. Modify the config.php file with your companies values and timezone. Full configuration info below. 23 | 10. Test it in Slack! 24 | 25 | ## cwslack-incoming.php 26 | 27 | 1. Download the cwslack-incoming.php, functions.php, and config.php files. 28 | 2. Place on a compatible web server 29 | 3. Create a new slack incoming web hook integration at https://my.slack.com/services/new/incoming-webhook/ 30 | 4. Set a name, icon, and if wanted. 31 | 5. Set channel that you want to post to and copy the Web hook URL 32 | 6. Create a new integrator login in ConnectWise: 33 | - Go to System > Setup Tables in the client 34 | - Type "int" in the table field and select Integrator Login 35 | - Create a new login with whatever username/password, we don't need this. 36 | - Set Access Level to "All Records" 37 | - Enable "Service Ticket API" and select the board(s) you want this to run on. 38 | - Enter https://domain.tld/cwslack-incoming.php?id= for the callback URL (do not enable legacy format) 39 | 7. Modify the config.php file with your companies values and timezone, make sure to set the value for $webhookurl to the value copied in step 5. 40 | 8. Change the $postupdated and $postadded to what you prefer. Enabling $postupdated can get spammy. 41 | 9. Test it in Slack by creating a new ticket on the board you selected in step 6! 42 | 43 | ## cwslack-firmalerts.php 44 | 45 | **(Requires some variables from cwslack-incoming.php to function if you don't use that)** 46 | 47 | 1. Download the cwslack-firmalerts.php, functions.php, and config.php files. 48 | 2. Place on a compatible web server. 49 | 3. Change $posttousers or $posttochan to 0 in config.php if you don't want it posting to one or the other. 50 | 4. Setup a cron job or scheduled task on your server to run this PHP file **every minute.** 51 | ```Cron: * * * * * /usr/bin/php /var/www/cwslack-firmalerts.php >/dev/null 2>&1``` 52 | 5. Set a firm appointment and test 53 | 54 | ## cwslack-follow.php 55 | 56 | **(Also requires cwslack-incoming.php to function)** 57 | 58 | 1. Download the cwslack-follow.php, functions.php, and config.php files. 59 | 2. Place on a compatible web server 60 | 3. Create a new slack slash command integration at https://SLACK TEAM.slack.com/apps/A0F82E8CA-slash-commands 61 | 4. Set command to /follow (or other if you prefer) 62 | 5. Set the URL to https://domain.tld/cwslack-follow.php 63 | 6. Set Method to GET 64 | 7. Copy the token 65 | 8. Set a name, icon, and auto complete text if wanted. 66 | 9. Modify the config.php file with your companies values, Full configuration info below. 67 | 10. Test it in Slack! 68 | 69 | To enable ConnectWise link to follow and unfollow a ticket: 70 | 71 | 1. Open Setup Tables in ConnectWise. 72 | 2. Open the "Links" table. 73 | 3. Create a new Link referencing "Service" 74 | 4. Set the Link Name to "Slack Follow" 75 | 5. Set the Link Definition to https://yourdomain.tld/cwslack-follow.php?memberid=[memberid]&srnumber=[srnumber]&method=follow 76 | 6. Create a new Link referencing "Service" 77 | 7. Set the Link Name to "Slack Unfollow" 78 | 8. Set the Link Definition to https://yourdomain.tld/cwslack-follow.php?memberid=[memberid]&srnumber=[srnumber]&method=unfollow 79 | 9. Change the "method" on these links to whatever you set your $followtoken and $unfollowtoken to in config.php. 80 | 10. Test the links! 81 | 82 | 83 | # API Key Setup 84 | 85 | 1. Login to ConnectWise 86 | 2. In the top right, click on your name 87 | 3. Go to "My Account" 88 | 4. Select the "API Keys" tab 89 | 5. Click the Plus icon to create a new key 90 | 6. Provide a description and click the Save icon. 91 | 7. Save this information, you cannot retrieve the private key ever again so if lost you will need to create new ones. -------------------------------------------------------------------------------- /TROUBLESHOOT.md: -------------------------------------------------------------------------------- 1 | # CWSlack-SlashCommands Troubleshooting 2 | 3 | ### Issue - Error Security: SSL is required OR Error 401: Unauthorized 4 | 5 | If you are using hosted ConnectWise, you need to set your $connectwise variable in config.php to `https://api-na.myconnectwise.net` instead of `https://na.myconnectwise.net` 6 | 7 | Change the country code to whichever you have when you visit the site to login. 8 | 9 | ### Issue - Peer's Certificate issuer is not recognized. 10 | 11 | See the StackOverflow link below, the second answer fixes this. You need to attach the CA cert to cURL since it does not come stock with them on most Windows machines. The `cacert.pem` file includes the CA for connectwise as well as most other major sites including Slack. 12 | 13 | http://stackoverflow.com/questions/6400300/https-and-ssl3-get-server-certificatecertificate-verify-failed-ca-is-ok 14 | 15 | ### Issue - cURL not enabled 16 | 17 | Ensure that CURL is installed and enabled in PHP. 18 | 19 | #### Linux 20 | 21 | Install CURL: `sudo apt-get install php5-curl` 22 | 23 | Modify your PHP .ini to include `extension=curl.so` and restart Apache 24 | 25 | You can find your .ini file by running `php --ini` in a terminal 26 | 27 | #### Windows 28 | 29 | See here: https://www.codeooze.com/coding/php-curl-on-windows/ 30 | 31 | Or google "enable curl windows" and use your best judgement on what site to choose. 32 | 33 | ### Issue - Other? 34 | 35 | You can reach me on the r/msp Discord, LabTechGeek Slack, or via reddit at /u/jundis if you have a bug or run into any issues, but support will be limited as I turn my focus to developing the hosted solution. 36 | 37 | You can also reach me at joey(at)und.is should you need more intense support, custom modifications, or want your install done by me. 38 | -------------------------------------------------------------------------------- /config-default.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | 21 | //SET THESE VARIABLES 22 | // 23 | 24 | //General configuration, Required for all PHP scripts to function!! 25 | $connectwise = "https://cw.domain.com"; //Set your Connectwise URL 26 | $connectwisebranch = "v4_6_release"; //Set to the portion of your CW URL shown here: https://cw.domain.com/**v4_6_release**/ConnectWise.aspx 27 | $companyname = "MyCompany"; //Set your company name from Connectwise. This is the company name field from login. 28 | $apipublickey = "Key"; //Public API key 29 | $apiprivatekey = "Key"; //Private API key 30 | $timezone = "America/Chicago"; //Set your timezone here. 31 | $timeoutfix = false; //Enable to fix any 3000ms response from Slack. 32 | $sendtimeoutwait = false; //Set to true to send a please wait message with every command. Only does something when $timeoutfix is set to true. 33 | $cwClientId=""; //This is required for all ConnectWise Manage API calls as of v2019.3 34 | 35 | // Database Configuration, required for if you want to use MySQL/Maria DB features. 36 | $usedatabase = 0; // Set to 0 by default, set to 1 if you want to enable MySQL. 37 | $dbhost = "127.0.0.1"; //Your MySQL DB 38 | $dbusername = "username"; //Your MySQL DB Username 39 | $dbpassword = "password"; //Your MySQL DB Password 40 | $dbdatabase = "cwslack"; //Change if you have an existing database you want to use, otherwise leave as default. 41 | 42 | //E-mail configuration, required for lunch module mail functions 43 | $smtpserver = "smtp.domain.com"; //Set your SMTP server her 44 | $smtpport = 25; //Set your SMTP port. Defaults: 25 (No security), 465 (SSL), 587 (TLS) 45 | $smtpfrom = "notifications@domain.com"; //Set to the address all mail should be sent from 46 | $smtpname = "Company Notifications"; //Set to what you want e-mails to appear as coming from. E.x. Company Notifications 47 | 48 | //cwslack.php 49 | $slacktoken = "Slack Token Here"; //Set token from the Slack slash command screen. 50 | $useboards = 1; //Use the board function in new tickets. /t new company|summary vs /t new board|company|summary 51 | $schedulestatus = ""; //Set to the name of your status (e.x. "Scheduled") if you want the [/t # schedule] functions to update the status 52 | 53 | //cwslack-activities.php 54 | $slackactivitiestoken = "Slack Token Here"; //Set your token for the activities slash command 55 | 56 | //cwslack-contacts.php 57 | $slackcontactstoken = "Slack Token Here"; //Set your token for the contacts slash command 58 | $inactivecontacts = false; //Set to true to return inactive contacts 59 | 60 | //cwlsack-notes.php 61 | $slacknotestoken = "Slack Token Here"; //Set your token for the notes slash command 62 | $usecwname = 1; //If set to 1, it will create tickets using the user's slack name. Command will fail if their slack name is not the same as connectwise name. 63 | $defaultnotetype = ""; //Set to internal, external, or externalemail and this will be used if they do not specify a type. Leave blank to have no default and return an error if they don't specify. 64 | 65 | //cwslack-configs.php 66 | $slackconfigstoken = "Slack Token Here"; //Set your token for the configs slash command 67 | $hidepasswords = 0; //Set to 1 if you want to hide passwords. 68 | 69 | //cwslack-tasks.php 70 | $slacktaskstoken = "Slack Token Here"; //Set your token for the tasks slash command 71 | 72 | //cwslack-time.php 73 | $slacktimetoken = "Slack Token Here"; //Set your token for the time slash command 74 | $timedetailworktype = "Remote Support"; //Set to the worktype name you want to use when a note is posted to detailed 75 | $timeinternalworktype = "Admin"; //Set to the worktype name you want to use when a note is posted to internal 76 | $timeresolutionworktype = "Remote Support"; //Set to the worktype name you want to use when a note is posted to resolution 77 | $timebusinessstart = "8:00AM"; //Set to when your business opens in your timezone 78 | $timebusinessclose = "5:00PM"; //Set to when your business closes in your timezone 79 | 80 | //cwslack-incoming.php 81 | $webhookurl = "https://hooks.slack.com/services/tokens"; //Change this to the URL retrieved from incoming webhook setup for Slack. 82 | $postadded = 1; //Set this to post new tickets to slack. 83 | $postupdated = 0; //Set this to post updated tickets to slack. Defaults to off to avoid spam 84 | $posttext = 1; //Set to 1 if you want it to post the latest note from the ticket into chat whenever a ticket is created or updated. 85 | $postcompany = 1; //Set to 1 if you want the Company to be posted in the clear text of the post (general what will be seen on IRC/XMPP) 86 | $timeenabled = 0; //Set to 1 if you want to post all tickets past $timepast to a specific channel, $timechan 87 | $timepast = 1.0; //Set to a time in hours where once reached all updates will post to #dispatch. 88 | $timechan = "#ticketstime"; //Set to a channel to post to for $timeenabled 89 | //"Bad" variables to block certain things from coming through in cwslack-incoming.php. Separate by a pipe symbol | to have multiple. 90 | $badboard = "Alerts"; //Set to any board name you want to fail, to avoid ticket creation/updates from this board posting to Slack. 91 | $badstatus = "Closed|Canceled"; //Set to any status name you want to fail, to avoid ticket creation/updates with this status from posting to Slack. 92 | $badcompany = "CatchAll (for email connector)"; //Set to any company name you want to fail, to avoid ticket creation for catchall from posting to Slack. 93 | //Example $boardmapping = "Alerts|alerts,Customer Support|support,Incoming|dispatch"; This would send Alerts to #alerts, Customer Support to #support, Incoming to #dispatch, and Orders to the channel specified on Slack's webhook page. 94 | $boardmapping = ""; //Put board to channel mappings in here. Formatted as "Board Name|channel,Board Name|channel". Any board not covered will go to the default channel for the webhook, filter boards using $badboard. Example above 95 | 96 | //cwslack-firmalerts.php 97 | //This uses the variables $webhookurl and $timechan from cwslack-incoming.php above. 98 | $posttousers = 1; //When set, will post to the user whenever the appointment reminder is reached. 99 | $posttochan = 1; //When set, will post to $timechan whenever the firm appointment starts. 100 | $usetimechan = 1; //When set, this will use the $timechan variable instead of the one below. 101 | $firmalertchan = "#dispatch"; //When you want to split time alerts and firm alerts into their own channels. 102 | 103 | //cwslack-timealerts.php 104 | //This uses all four variables above 105 | $notimeusers = "user1|user2"; //Usernames of users who should not be alerted on. Useful if you have techs who occasionally enter time and you don't want it pinging them every day. Separate with pipe | 106 | $specialtimeusers = "user1,7:00am-4:00pm|user2,9:00am-6:00pm"; //Usernames of users who should be alerted on, but who have special hours different from default start-close. Seperate user and time with comma, seperate different users with pipe |. No spaces 107 | 108 | //cwslack-priorityalerts.php 109 | //This uses all the variables from firmalerts as well, adhering to it for whether to post to users/channel and which channel 110 | $prioritylist = "High|Critical"; // Name of the priority(ies) to look out for. Separate by pipe if more than one needed. 111 | $prioritystatus = "Scheduled|Scheduled -Notify"; // Status(es), seperated by pipe | symbol, which the priority alerts will check for and send alerts on. 112 | $prioritywait = 30; // Number of minutes to wait after a high-priority event before alerting the technician. Maximum 119 minutes. 113 | 114 | //cwslack-follow.php 115 | //Requires cwslack-incoming.php to function. 116 | $slackfollowtoken = "Slack Token Here"; //Set your token for the follow slash command 117 | $followenabled = 0; //When set to 1, follow commands and the follow scripts will be enabled. 118 | $followtoken = "follow"; //Change to random text to be used in your CW follow link if you use it. Defaults to follow which is fine for testing. 119 | $unfollowtoken = "unfollow"; //Change to random text to be used in your CW unfollow link if you use it. Defaults to unfollow which is fine for testing. 120 | 121 | //cwslack-lunch.php 122 | //E-mail functionality requires you to setup your system for PHP mail(), info below 123 | //Windows: https://stackoverflow.com/questions/4652566/php-mail-setup-in-xampp 124 | //Linux: http://lukepeters.me/blog/getting-the-php-mail-function-to-work-on-ubuntu 125 | $slacklunchtoken = "test"; // Set your token for the lunch slash command 126 | $lunchchargecode = 41; // Set to your "Break" charge code that lunches should be put under 127 | $lunchtime = 60; // Expected number of MINUTES that a user is on lunch 128 | $lunchmax = 120; // Number of minutes to allow before cancelling the lunch entry, does not submit time 129 | $lunchsendslack = true; // Send messages to a slack channel when a user goes on/off lunch 130 | $lunchsendemail = false; // Send messages to an e-mail address when a user goes on/off lunch 131 | $lunchsendonoff = 1; // Key: 0 = No notifications, 1 = Send notifications when a user goes on lunch, 2 = Send when a user goes off lunch, 3 = Send when a user goes on lunch OR off lunch 132 | $lunchslackchannel = "general"; // Channel to send Slack messages to 133 | $lunchemailto = "allstaff@domain.com"; // E-mail address to send messages to 134 | $lunchcreatesched = true; // Should the script create a schedule entry on the users board 135 | $lunchsavetime = true; // Should the script submit a time entry for the user for their lunch duration 136 | 137 | //cwslack-dbmanage.php 138 | $slackdbmantoken = "Slack Token Here"; //Set your token for the database management slash command 139 | $adminlist = "admin1|admin2"; //Separate by pipe symbol as seen in example if you need multiple people to have access. 140 | 141 | //cwslack-stats.php 142 | $collectstats = false; // By default this is turned off. Turn it on if you want stats collected to the stats table in MySQL. No slack based access to these stats. 143 | 144 | //Change optional 145 | $helpurl = "https://github.com/jundis/CWSlack-SlashCommands"; //Set your help article URL here. 146 | 147 | // Variable below used for advanced diagnostics. $timeoutfix will be set to false automatically when this is turned on. 148 | $debugmode = false; 149 | 150 | // 151 | //Don't modify below unless you know what you're doing! 152 | // 153 | 154 | //Timezone Setting to be used for all files. 155 | date_default_timezone_set($timezone); 156 | 157 | // Stats collection 158 | if($collectstats && array_key_exists("token",$_REQUEST)) 159 | { 160 | require_once 'cwslack-stats.php'; 161 | } 162 | 163 | //Debug mode 164 | if($debugmode) //If debug mode is on.. 165 | { 166 | $timeoutfix = false; //Set timeoutfix to false so that all data is returned properly as ephemeral messages. 167 | } 168 | 169 | 170 | ?> 171 | -------------------------------------------------------------------------------- /cwslack-activities.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | 21 | ini_set('display_errors', 1); //Display errors in case something occurs 22 | header('Content-Type: application/json'); //Set the header to return JSON, required by Slack 23 | require_once 'config.php'; 24 | require_once 'functions.php'; 25 | 26 | // Authorization array. Auto encodes API key for auhtorization above. 27 | $header_data = postHeader($companyname, $apipublickey, $apiprivatekey); 28 | 29 | if(empty($_REQUEST['token']) || ($_REQUEST['token'] != $slackactivitiestoken)) die("Slack token invalid."); //If Slack token is not correct, kill the connection. This allows only Slack to access the page for security purposes. 30 | if(empty($_REQUEST['text'])) die("No text provided."); //If there is no text added, kill the connection. 31 | $exploded = explode("|",$_REQUEST['text']); //Explode the string attached to the slash command for use in variables. 32 | 33 | //Check to see if the first command in the text array is actually help, if so redirect to help webpage detailing slash command use. 34 | if ($exploded[0]=="help") { 35 | die(json_encode(array("parse" => "full", "response_type" => "in_channel","text" => "Please visit " . $helpurl . " for more help information","mrkdwn"=>true))); //Encode a JSON response with a help URL. 36 | } 37 | 38 | //Timeout Fix Block 39 | if($timeoutfix == true) 40 | { 41 | ob_end_clean(); 42 | header("Connection: close"); 43 | ob_start(); 44 | echo ('{"response_type": "in_channel"}'); 45 | $size = ob_get_length(); 46 | header("Content-Length: $size"); 47 | ob_end_flush(); 48 | flush(); 49 | session_write_close(); 50 | if($sendtimeoutwait==true) { 51 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral", "text" => "Please wait...")); 52 | } 53 | } 54 | //End timeout fix block 55 | 56 | $urlactivities = $connectwise . "/$connectwisebranch/apis/3.0/sales/activities/"; 57 | $activityurl = $connectwise . '/$connectwisebranch/ConnectWise.aspx?fullscreen=false&locale=en_US#startscreen=activity_detail&state={"p":"activity_detail", "s":{"p":{"pid":3, "rd":'; 58 | $activityurl2 = ' ,"compId":0, "contId":0, "oppid":0}}}'; 59 | 60 | $command=NULL; //Create a command variable and set it to Null 61 | if (array_key_exists(0,$exploded)) //If a string exists in the slash command array, make it the command. 62 | { 63 | $command = $exploded[0]; 64 | } 65 | 66 | //Need to create array before hand to ensure no errors occur. 67 | $dataResponse = array(); 68 | 69 | if($command=="new") { 70 | $dataResponse = cURLPost( 71 | $urlactivities, 72 | $header_data, 73 | "POST", 74 | array("name"=>$exploded[1],"status"=>array("id"=>1),"assignTo"=>array("identifier"=>$exploded[2]))); 75 | } 76 | 77 | $return="Unknown command."; 78 | if($command == "new") //If command is new. 79 | { 80 | $return =array( 81 | "parse" => "full", //Parse all text. 82 | "response_type" => "in_channel", //Send the response in the channel 83 | "attachments"=>array(array( 84 | "fallback" => "New Activity Created: " . $dataResponse->name, //Fallback for notifications 85 | "title" => "New Activity Created: " . $dataResponse->name, //Set bolded title text 86 | "pretext" => "Activity #" . $dataResponse->id . " has been created and assigned to " . $exploded[2], //Set pretext 87 | "text" => "Click <" . $activityurl . $dataResponse -> id . $activityurl2 . "|here> to open the activity.", //Set text to be returned 88 | "mrkdwn_in" => array( //Set markdown values 89 | "text", 90 | "pretext" 91 | ) 92 | )) 93 | ); 94 | } 95 | else 96 | { 97 | if ($timeoutfix == true) { 98 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral","text" => $return)); 99 | } else { 100 | die($return); //Post to slack 101 | } 102 | die(); 103 | } 104 | if ($timeoutfix == true) { 105 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", $return); 106 | } else { 107 | die(json_encode($return, JSON_PRETTY_PRINT)); //Return properly encoded arrays in JSON for Slack parsing. 108 | } 109 | ?> -------------------------------------------------------------------------------- /cwslack-configs.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | 21 | ini_set('display_errors', 1); //Display errors in case something occurs 22 | header('Content-Type: application/json'); //Set the header to return JSON, required by Slack 23 | require_once 'config.php'; //Require config 24 | require_once 'functions.php'; //Require functions 25 | 26 | if(empty($_REQUEST['token']) || ($_REQUEST['token'] != $slackconfigstoken)) die("Slack token invalid."); //If Slack token is not correct, kill the connection. This allows only Slack to access the page for security purposes. 27 | if(empty($_REQUEST['text'])) die("No text provided."); //If there is no text added, kill the connection. 28 | 29 | $exploded = explode("|",$_REQUEST['text']); //Explode the string attached to the slash command for use in variables. 30 | 31 | //Check to see if the first command in the text array is actually help, if so redirect to help webpage detailing slash command use. 32 | if ($exploded[0]=="help") { 33 | die(json_encode(array("parse" => "full", "response_type" => "in_channel","text" => "Please visit " . $helpurl . " for more help information","mrkdwn"=>true))); //Encode a JSON response with a help URL. 34 | } 35 | 36 | //Timeout Fix Block 37 | if($timeoutfix == true) 38 | { 39 | ob_end_clean(); 40 | header("Connection: close"); 41 | ob_start(); 42 | echo ('{"response_type": "in_channel"}'); 43 | $size = ob_get_length(); 44 | header("Content-Length: $size"); 45 | ob_end_flush(); 46 | flush(); 47 | session_write_close(); 48 | if($sendtimeoutwait==true) { 49 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral", "text" => "Please wait...")); 50 | } 51 | } 52 | //End timeout fix block 53 | 54 | $company=NULL; //Just in case 55 | $config=NULL; //Just in case 56 | 57 | if (array_key_exists(1,$exploded)) //If two parts of the array exists 58 | { 59 | $company = $exploded[0]; //Set the first portion to company name 60 | $config = $exploded[1]; //Set the second portion to config name 61 | 62 | $url = $connectwise . "/$connectwisebranch/apis/3.0/company/configurations?conditions=status/name=%27active%27%20and%20company/name%20contains%20%27" . $company . "%27%20and%20name%20contains%20%27" . $config . "%27&pagesize=1"; 63 | } 64 | else //If 2 parts don't exist 65 | { 66 | $config=$exploded[0]; 67 | 68 | $url = $connectwise . "/$connectwisebranch/apis/3.0/company/configurations?conditions=status/name=%27active%27%20and%20name%20contains%20%27" . $config . "%27&pagesize=1"; 69 | } 70 | 71 | 72 | $url = str_replace(' ', '%20', $url); //Encode URL to prevent errors with spaces. 73 | 74 | // Authorization array. Auto encodes API key for auhtorization. 75 | $header_data = authHeader($companyname, $apipublickey, $apiprivatekey); 76 | 77 | //Need to create array before hand to ensure no errors occur. 78 | $dataTData = array(); 79 | 80 | //- 81 | //cURL connection to ConnectWise to pull Company API. 82 | //- 83 | $dataTData = cURL($url, $header_data); // Get the JSON returned by the CW API. 84 | 85 | //Error handling 86 | if($dataTData==NULL) //If no contact is returned or your API URL is incorrect. 87 | { 88 | if ($timeoutfix == true) { 89 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral","text" => "No configuration found.")); 90 | } else { 91 | die("No configuration found."); //Return properly encoded arrays in JSON for Slack parsing. 92 | } 93 | die(); 94 | } 95 | 96 | $return="Nothing!"; //Create return value and set to a basic message just in case. 97 | $conf = $dataTData[0]; //Shortcut to item. 98 | $notes = "None"; //Just in case 99 | $vendornotes = "None"; //Just in case 100 | $answers = ""; //Nothing just in case 101 | 102 | if(array_key_exists("questions",$conf)) 103 | { 104 | $questions = $conf->questions; //Array of questions 105 | if($questions!=NULL) 106 | { 107 | foreach($questions as $q) //For each item in the Question array 108 | { 109 | if($q->answer!=NULL) //If the answer exists and is not just blank and useless. 110 | { 111 | if(strpos($q->question,"Password") !== false && $hidepasswords == 1) //If question contains "Password". 112 | { 113 | 114 | if (strpos($q->question, ":") !== false) //If question contains a colon. 115 | { 116 | $answers = $answers . $q->question . " Hidden, please view in CW\n"; //Return the question, answer, and new line. 117 | } 118 | else //Else, add a colon. 119 | { 120 | $answers = $answers . $q->question . ": Hidden, please view in CW\n"; //Return the question, answer, and new line. 121 | } 122 | } 123 | else 124 | { 125 | if (strpos($q->question, ":") !== false) //If question contains a colon. 126 | { 127 | $answers = $answers . $q->question . " " . $q->answer . "\n"; //Return the question, answer, and new line. 128 | } 129 | else //Else, add a colon. 130 | { 131 | $answers = $answers . $q->question . ": " . $q->answer . "\n"; //Return the question, answer, and new line. 132 | } 133 | } 134 | } 135 | } 136 | } 137 | } 138 | else if(array_key_exists("customFields",$conf)) 139 | { 140 | $questions = $conf->customFields; //Array of questions 141 | if($questions!=NULL) 142 | { 143 | foreach($questions as $q) //For each item in the Question array 144 | { 145 | if(array_key_exists("value",$q) && $q->value!=NULL) //If the answer exists and is not just blank and useless. 146 | { 147 | if(strpos($q->caption,"Password") !== false && $hidepasswords == 1) //If question contains "Password". 148 | { 149 | 150 | if (strpos($q->caption, ":") !== false) //If question contains a colon. 151 | { 152 | $answers = $answers . $q->caption . " Hidden, please view in CW\n"; //Return the question, answer, and new line. 153 | } 154 | else //Else, add a colon. 155 | { 156 | $answers = $answers . $q->caption . ": Hidden, please view in CW\n"; //Return the question, answer, and new line. 157 | } 158 | } 159 | else 160 | { 161 | if (strpos($q->caption, ":") !== false) //If question contains a colon. 162 | { 163 | $answers = $answers . $q->caption . " " . $q->value . "\n"; //Return the question, answer, and new line. 164 | } 165 | else //Else, add a colon. 166 | { 167 | $answers = $answers . $q->caption . ": " . $q->value . "\n"; //Return the question, answer, and new line. 168 | } 169 | } 170 | } 171 | } 172 | } 173 | } 174 | else 175 | { 176 | $answers="None"; 177 | } 178 | 179 | if(array_key_exists("notes",$conf) && $conf->notes!=NULL) //If notes are not null 180 | { 181 | $notes = $conf->notes; //Set $notes to the config notes 182 | } 183 | if(array_key_exists("vendorNotes",$conf) && $conf->vendorNotes!=NULL) //If vendornotes are not null 184 | { 185 | $vendornotes = $conf->vendorNotes; //Set $vendornotes to the config vendor notes. 186 | } 187 | 188 | $return =array( 189 | "parse" => "full", //Parse all text. 190 | "response_type" => "in_channel", //Send the response in the channel 191 | "attachments"=>array(array( 192 | "fallback" => "Configuration Info for " . $conf->company->identifier . "\\" . $conf->name, //Fallback for notifications 193 | "title" => "Configuration: <". $connectwise . "/$connectwisebranch/services/system_io/router/openrecord.rails?locale=en_US&recordType=ConfigFv&recid=" . $conf->id . "|" . $conf->name . ">", //Set bolded title text 194 | "pretext" => "Configuration for: " . $conf->company->identifier, //Set pretext 195 | "text" => "*_Notes_*:\n" . $notes . "\n*_Vendor Notes_*:\n" . $vendornotes . "\n*_Questions_*:\n" . $answers,//Set text to be returned 196 | "mrkdwn_in" => array( //Set markdown values 197 | "text", 198 | "pretext" 199 | ) 200 | )) 201 | ); 202 | 203 | if ($timeoutfix == true) { 204 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", $return); 205 | } else { 206 | die(json_encode($return, JSON_PRETTY_PRINT)); //Return properly encoded arrays in JSON for Slack parsing. 207 | } 208 | 209 | ?> -------------------------------------------------------------------------------- /cwslack-contacts.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | 21 | ini_set('display_errors', 1); //Display errors in case something occurs 22 | header('Content-Type: application/json'); //Set the header to return JSON, required by Slack 23 | require_once 'config.php'; 24 | require_once 'functions.php'; 25 | 26 | if(empty($_REQUEST['token']) || ($_REQUEST['token'] != $slackcontactstoken)) die("Slack token invalid."); //If Slack token is not correct, kill the connection. This allows only Slack to access the page for security purposes. 27 | if(empty($_REQUEST['text'])) die("No text provided."); //If there is no text added, kill the connection. 28 | 29 | $exploded = explode(" ",$_REQUEST['text']); //Explode the string attached to the slash command for use in variables. 30 | 31 | //Check to see if the first command in the text array is actually help, if so redirect to help webpage detailing slash command use. 32 | if ($exploded[0]=="help") { 33 | die(json_encode(array("parse" => "full", "response_type" => "in_channel","text" => "Please visit " . $helpurl . " for more help information","mrkdwn"=>true))); //Encode a JSON response with a help URL. 34 | } 35 | 36 | //Timeout Fix Block 37 | if($timeoutfix == true) 38 | { 39 | ob_end_clean(); 40 | header("Connection: close"); 41 | ob_start(); 42 | echo ('{"response_type": "in_channel"}'); 43 | $size = ob_get_length(); 44 | header("Content-Length: $size"); 45 | ob_end_flush(); 46 | flush(); 47 | session_write_close(); 48 | if($sendtimeoutwait==true) { 49 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral", "text" => "Please wait...")); 50 | } 51 | } 52 | //End timeout fix block 53 | 54 | $firstname=NULL; //Create a first name variable and set it to Null 55 | $lastname=NULL; //Create a last name variable and set it to Null 56 | $url=NULL; //Create a URL variable and set it to Null. 57 | 58 | if (array_key_exists(0,$exploded)) //If the first part of the array exists (always will) 59 | { 60 | $lastname = $exploded[0]; 61 | $url = $connectwise . "/$connectwisebranch/apis/3.0/company/contacts?conditions=inactiveFlag=" . ($inactivecontacts ? "True" : "False") . "%20and%20lastName%20contains%20%27" . $lastname . "%27"; //Set contact API url 62 | } 63 | if (array_key_exists(1,$exploded)) //If two parts of the array exists 64 | { 65 | $lastname = $exploded[1]; //Set the second portion to last name 66 | $firstname = $exploded[0]; //Set the first portion to first name 67 | 68 | $url = $connectwise . "/$connectwisebranch/apis/3.0/company/contacts?conditions=inactiveFlag=" . ($inactivecontacts ? "True" : "False") . "%20and%20lastName%20contains%20%27" . $lastname . "%27%20and%20firstName%20contains%20%27" . $firstname . "%27"; //Set contact API url to include first and last name. 69 | } 70 | 71 | $utc = time(); //Get the time. 72 | // Authorization array. Auto encodes API key for auhtorization above. 73 | $header_data = authHeader($companyname, $apipublickey, $apiprivatekey); 74 | 75 | 76 | //Need to create array before hand to ensure no errors occur. 77 | $dataTData = array(); 78 | 79 | //- 80 | //cURL connection to ConnectWise to pull Company API. 81 | //- 82 | $dataTData = cURL($url, $header_data); 83 | 84 | if($dataTData==NULL) //If no contact is returned or your API URL is incorrect. 85 | { 86 | if ($timeoutfix == true) { 87 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral","text" => "No contact found or your API URL is incorrect.")); 88 | } else { 89 | die("No contact found or your API URL is incorrect."); //Post to slack 90 | } 91 | die(); 92 | } 93 | 94 | $return="Nothing!"; //Create return value and set to a basic message just in case. 95 | $company=$dataTData[0]->company; //Set company array for easier reference later on. 96 | $compurl=$company->_info; 97 | 98 | //Company phone # 99 | $dataCData = cURL($compurl->company_href, $header_data); //Decode the JSON returned by the CW API. 100 | $cphone = NULL; // Just in case. 101 | 102 | if ($dataCData->phoneNumber != NULL && $dataCData->phoneNumber != NULL) 103 | { 104 | $cphone = preg_replace('~.*(\d{3})[^\d]{0,7}(\d{3})[^\d]{0,7}(\d{4}).*~', '($1) $2-$3', $dataCData->phoneNumber); 105 | } 106 | 107 | 108 | $sphone = NULL; // Just in case. 109 | 110 | if($dataTData[0]->site!=NULL) { 111 | $site = $dataTData[0]->site; 112 | 113 | $siteurl = $site->_info; 114 | //Company phone # 115 | $dataSData = cURL($siteurl->site_href, $header_data); //Decode the JSON returned by the CW API. 116 | 117 | if ($dataSData->phoneNumber != NULL && $dataSData->phoneNumber != NULL) 118 | { 119 | $sphone = preg_replace('~.*(\d{3})[^\d]{0,7}(\d{3})[^\d]{0,7}(\d{4}).*~', '($1) $2-$3', $dataSData->phoneNumber); 120 | } 121 | } 122 | 123 | $text="No contact info found."; //Set catch error "just in case" 124 | 125 | if(array_key_exists("communicationItems",$dataTData[0]) && $dataTData[0]->communicationItems != NULL) 126 | { 127 | $comms=$dataTData[0]->communicationItems; //Set communications array for iteration. 128 | $text=""; //Set blank text varaible "just in case" 129 | if($cphone != NULL || $sphone != NULL) //Check if one is not null and has data 130 | { 131 | $text = ""; //Set text to blank for proper processing 132 | } 133 | if($cphone != NULL) //Check if cphone is null 134 | { 135 | $text=$text . "Company Phone: " . $cphone . "\n"; //If not, set text and add new line 136 | } 137 | if($sphone != NULL && $sphone != $cphone) //Check if sphone is null AND if sphone is the same as cphone, skip if both are true. 138 | { 139 | $text=$text . "Site Phone: " . $sphone . "\n"; 140 | } 141 | //Iteration block to search through all contact types on the user. 142 | foreach($comms as $item) { 143 | $type = $item->type; //Set the type variable to whatever the contact type is, this would be Email or Direct or whatever you have it set to in CW. 144 | $formatted = preg_replace('~.*(\d{3})[^\d]{0,7}(\d{3})[^\d]{0,7}(\d{4}).*~', '($1) $2-$3', $item->value); //Format phone numbers 145 | if(array_key_exists("extension",$item) && $item->extension!=NULL) 146 | { 147 | $text = $text . $type->name . ": " . $formatted . " x" . $item->extension . "\n"; //Create a new line for each iteration, 148 | } 149 | else 150 | { 151 | $text = $text . $type->name . ": " . $formatted . "\n"; //Create a new line for each iteration, 152 | } 153 | } 154 | } 155 | else 156 | { 157 | if($cphone != NULL || $sphone != NULL) //Check if one is not null and has data 158 | { 159 | $text = ""; //Set text to blank for proper processing 160 | } 161 | if($cphone != NULL) //Check if cphone is null 162 | { 163 | $text=$text . "Company Phone: " . $cphone . "\n"; //If not, set text and add new line 164 | } 165 | if($sphone != NULL && $sphone != $cphone) //Check if sphone is null AND if sphone is the same as cphone, skip if both are true. 166 | { 167 | $text=$text . "Site Phone: " . $sphone . "\n"; 168 | } 169 | } 170 | 171 | 172 | $return =array( 173 | "parse" => "full", //Parse all text. 174 | "response_type" => "in_channel", //Send the response in the channel 175 | "attachments"=>array(array( 176 | "fallback" => "Contact Info for " . $dataTData[0]->firstName . " " . $dataTData[0]->lastName, //Fallback for notifications 177 | "title" => "Company: " . $company->name, //Set bolded title text 178 | "pretext" => "Contact Info for " . $dataTData[0]->firstName . " " . $dataTData[0]->lastName, //Set pretext 179 | "text" => $text, //Set text to be returned 180 | "mrkdwn_in" => array( //Set markdown values 181 | "text", 182 | "pretext" 183 | ) 184 | )) 185 | ); 186 | 187 | if ($timeoutfix == true) { 188 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", $return); 189 | } else { 190 | die(json_encode($return, JSON_PRETTY_PRINT)); //Return properly encoded arrays in JSON for Slack parsing. 191 | } 192 | ?> -------------------------------------------------------------------------------- /cwslack-dbmanage.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | ini_set('display_errors', 1); //Display errors in case something occurs 21 | header('Content-Type: application/json'); //Set the header to return JSON, required by Slack 22 | require_once 'config.php'; 23 | require_once 'functions.php'; 24 | 25 | if(empty($_REQUEST['token']) || ($_REQUEST['token'] != $slackdbmantoken)) die("Slack token invalid."); //If Slack token is not correct, kill the connection. This allows only Slack to access the page for security purposes. 26 | if(empty($_REQUEST['text'])) die("No text provided."); //If there is no text added, kill the connection. 27 | $exploded = explode(" ",$_REQUEST['text']); //Explode the string attached to the slash command for use in variables. 28 | 29 | $explodeadmins = explode("|", $adminlist); //Explode list of acceptable admins. 30 | if(!in_array($_REQUEST["user_name"],$explodeadmins)) 31 | { 32 | die("You are not authorized to access this command. Only the following users can: " . implode(", ",$explodeadmins)); 33 | } 34 | 35 | //Check to see if the first command in the text array is actually help, if so redirect to help webpage detailing slash command use. 36 | if ($exploded[0]=="help") { 37 | die("The following commands are available:\nlistmap - List all username mappings between CW and Slack\naddmap (slackname) (cwname) - Associate the two names\nremovemap (slackname) - Remove a mapping\nclearfollow confirm - Clears the follow database"); 38 | } 39 | 40 | $mysql = mysqli_connect($dbhost, $dbusername, $dbpassword, $dbdatabase); //Connect MySQL 41 | 42 | if (!$mysql) //Check for errors 43 | { 44 | die("Connection Error: " . mysqli_connect_error()); 45 | } 46 | 47 | if ($exploded[0]=="listmap") 48 | { 49 | $sql = "SELECT * FROM `usermap`"; //SQL Query to select all users 50 | 51 | $result = mysqli_query($mysql, $sql); //Run result 52 | $output = "List of username mappings:\n"; 53 | if(mysqli_num_rows($result) > 0) //If there were too many rows matching query 54 | { 55 | while($row = mysqli_fetch_assoc($result)) 56 | { 57 | $output = $output . "Slack: " . $row["slackuser"] . " | ConnectWise: " . $row["cwname"] . "\n"; 58 | } 59 | die($output); 60 | } 61 | else 62 | { 63 | die("No user mappings found in database."); 64 | } 65 | } 66 | else if ($exploded[0]=="addmap") 67 | { 68 | if (!array_key_exists(2,$exploded)) 69 | { 70 | die("Error: Please ensure you're entering the following: addmap (slack name) (connectwise username)"); 71 | } 72 | 73 | $val1 = mysqli_real_escape_string($mysql,$exploded[1]); 74 | $val2 = mysqli_real_escape_string($mysql,$exploded[2]); 75 | $sql = "INSERT INTO `usermap` (`slackuser`, `cwname`) VALUES ('" . $val1 . "', '" . $val2 . "');"; //SQL Query to insert new map 76 | 77 | if(mysqli_query($mysql,$sql)) 78 | { 79 | die("Successfully added mapping for Slack User " . $exploded[1] . " to ConnectWise User " . $exploded[2]); 80 | } 81 | else 82 | { 83 | die("MySQL Error: " . mysqli_error($mysql)); 84 | } 85 | } 86 | else if ($exploded[0]=="removemap") 87 | { 88 | if (!array_key_exists(1,$exploded)) 89 | { 90 | die("Error: Please ensure you're entering the following: removemap (slack name)"); 91 | } 92 | 93 | $val1 = mysqli_real_escape_string($mysql,$exploded[1]); 94 | $sql = "DELETE FROM .`usermap` WHERE `usermap`.`slackuser` = '" . $val1 . "';"; //SQL Query to remove map 95 | 96 | if(mysqli_query($mysql,$sql)) 97 | { 98 | die("Successfully removed mapping for Slack User " . $exploded[1]); 99 | } 100 | else 101 | { 102 | die("MySQL Error: " . mysqli_error($mysql)); 103 | } 104 | } 105 | else if ($exploded[0]=="clearfollow") 106 | { 107 | if (!array_key_exists(1,$exploded) || $exploded[1]!="confirm") 108 | { 109 | die("Error: Please ensure you're confirming the command by entering: clearfollow confirm"); 110 | } 111 | $sql = "TRUNCATE follow"; //SQL Query to remove map 112 | 113 | if(mysqli_query($mysql,$sql)) 114 | { 115 | die("Successfully cleared follow table."); 116 | } 117 | else 118 | { 119 | die("MySQL Error: " . mysqli_error($mysql)); 120 | } 121 | } 122 | 123 | ?> -------------------------------------------------------------------------------- /cwslack-firmalerts.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | //Receive connector for Connectwise Callbacks 21 | ini_set('display_errors', 1); //Display errors in case something occurs 22 | header('Content-Type: application/json'); //Set the header to return JSON, required by Slack 23 | require_once 'config.php'; //Require the config file. 24 | require_once 'functions.php'; 25 | 26 | //Dates required for URL to function 27 | $datenow = gmdate("Y-m-d\TH:i", strtotime("-10 minutes")); //Date set to 10 minutes prior to now, to catch for tickets happening right now. 28 | $date2hours = gmdate("Y-m-d\TH:i", strtotime("+2 hours")); //Date set to 2 hours out so reminders up to 2 hours function. 29 | 30 | $url = $connectwise. "/$connectwisebranch/apis/3.0/schedule/entries?conditions=status/Name=%27Firm%27%20and%20dateStart%20%3E%20[" . $datenow . "]%20and%20dateStart%20%3C%20[". $date2hours . "]&orderBy=dateStart%20desc"; //URL to access the schedule API 31 | $ticketurl = $connectwise . "/$connectwisebranch/services/system_io/Service/fv_sr100_request.rails?service_recid="; //Set the URL required for ticket links. 32 | 33 | 34 | //Set headers for cURL requests. $header_data covers API authentication while $header_data2 covers the Slack output. 35 | $header_data = authHeader($companyname, $apipublickey, $apiprivatekey); 36 | 37 | $header_data2 =array( 38 | "Content-Type: application/json" 39 | ); 40 | 41 | // Pre-connect mysql if it will be needed in the loop. 42 | if($usedatabase==1) { 43 | $mysql = mysqli_connect($dbhost, $dbusername, $dbpassword, $dbdatabase); //Connect MySQL 44 | 45 | if (!$mysql) //Check for errors 46 | { 47 | die("Connection Error: " . mysqli_connect_error()); //Return error 48 | } 49 | } 50 | 51 | $dataTData = cURL($url, $header_data); //Decode the JSON returned by the CW API. 52 | 53 | foreach($dataTData as $entry) //For each schedule entry returned 54 | { 55 | $reminder = $entry->reminder->name; //Set the reminder type 56 | $user = $entry->member->identifier; //Set the user's ConnectWise username (e.g. jdoe) 57 | $username = $entry->member->name; //Set the user's name (e.g. John Doe) 58 | $namearray = explode(" - ",$entry->name); //Explode the summary field 59 | $companyarray = explode(" / ",$namearray[0]); //Explode that into company/ticket number array. 60 | $company = $companyarray[0]; //Set company to first part of second explode. 61 | $summary = $namearray[1]; //Set the ticket summary to second part of first explode. 62 | $datenow = date("Y-m-d\TH:i"); //Reusing datenow as non-GMT based time. 63 | $datestart = date("Y-m-d\TH:i",strtotime($entry->dateStart)); //Start time of the ticket. 64 | 65 | //Username mapping code 66 | if($usedatabase==1) 67 | { 68 | $val1 = mysqli_real_escape_string($mysql,$user); 69 | $sql = "SELECT * FROM `usermap` WHERE `cwname`=\"" . $val1 . "\""; //SQL Query to select all ticket number entries 70 | 71 | $result = mysqli_query($mysql, $sql); //Run result 72 | $rowcount = mysqli_num_rows($result); 73 | if($rowcount > 1) //If there were too many rows matching query 74 | { 75 | die("Error: too many users somehow?"); //This should NEVER happen. 76 | } 77 | else if ($rowcount == 1) //If exactly 1 row was found. 78 | { 79 | $row = mysqli_fetch_assoc($result); //Row association. 80 | 81 | $user = $row["slackuser"]; //Return the slack username portion of the found row as the $user variable to be used as part of the notification. 82 | } 83 | //If no rows are found here, then it just uses whatever if found as $user previously from the ticket. 84 | } 85 | 86 | if($reminder != "0 minutes" && $posttousers == 1) //If reminder is not 0 minutes, proceed. Pointless to have 0 minute reminder as that is handled below. 87 | { 88 | $datereminder = date("Y-m-d\TH:i",strtotime($entry->dateStart . " -" . $reminder)); //Set the reminder date to a readable comparable format. 89 | 90 | if($datenow==$datereminder) //If datenow and datereminder are the same.. 91 | { 92 | //Setup the slack return text 93 | $postfieldspre = array( 94 | "channel"=>"@".$user, //Send to user 95 | "attachments"=>array(array( 96 | "fallback" => "Firm with " . $company . " in " . $reminder . ".", //Notification, since there's only attachment and no text it will always use fallback. 97 | "title" => "<" . $ticketurl . $entry->objectId . "&companyName=" . $companyname . "|#" . $entry->objectId . ">: " . $summary, //Title in bold 98 | "pretext" => "Firm starting in " . $reminder, //Text before title. 99 | "text" => "You have a firm ticket with " . $company . " coming up. Please wrap up work on current ticket.", //Reminder text. 100 | "mrkdwn_in" => array( 101 | "text", 102 | "pretext" 103 | ) 104 | )) 105 | ); 106 | 107 | cURLPost($webhookurl, $header_data2, "POST", $postfieldspre); 108 | } 109 | } 110 | 111 | if($datenow == $datestart) //If the start of the ticket is right now.. 112 | { 113 | if($posttousers==1) //And user post is on. 114 | { 115 | $postfieldspre = array( 116 | "channel"=>"@".$user, 117 | "attachments"=>array(array( 118 | "fallback" => "Firm with " . $company . " starting now.", 119 | "title" => "<" . $ticketurl . $entry->objectId . "&companyName=" . $companyname . "|#" . $entry->objectId . ">: " . $summary, 120 | "pretext" => "Firm starting now.", 121 | "text" => "You have a firm ticket with " . $company . " starting now. You should be calling the client now.", 122 | "mrkdwn_in" => array( 123 | "text", 124 | "pretext" 125 | ) 126 | )) 127 | ); 128 | 129 | cURLPost($webhookurl, $header_data2, "POST", $postfieldspre); 130 | } 131 | if($posttochan==1) //If channel post is on 132 | { 133 | if($usetimechan==1) 134 | { 135 | $postfieldspre = array( 136 | "channel"=>$timechan, //Post to channel set in config.php 137 | "attachments"=>array(array( 138 | "fallback" => "Firm for " . $username . " with " . $company . " now.", 139 | "title" => "<" . $ticketurl . $entry->objectId . "&companyName=" . $companyname . "|#" . $entry->objectId . ">: " . $summary, 140 | "pretext" => $username . " has a firm starting now.", 141 | "text" => "Please remind the technician of this appointment with " . $company . ". They should be on the phone with them or calling them shortly.", 142 | "mrkdwn_in" => array( 143 | "text", 144 | "pretext" 145 | ) 146 | )) 147 | ); 148 | } 149 | else 150 | { 151 | $postfieldspre = array( 152 | "channel"=>$firmalertchan, //Post to channel set in config.php 153 | "attachments"=>array(array( 154 | "fallback" => "Firm for " . $username . " with " . $company . " now.", 155 | "title" => "<" . $ticketurl . $entry->objectId . "&companyName=" . $companyname . "|#" . $entry->objectId . ">: " . $summary, 156 | "pretext" => $username . " has a firm starting now.", 157 | "text" => "Please remind the technician of this appointment with " . $company . ". They should be on the phone with them or calling them shortly.", 158 | "mrkdwn_in" => array( 159 | "text", 160 | "pretext" 161 | ) 162 | )) 163 | ); 164 | } 165 | 166 | cURLPost($webhookurl, $header_data2, "POST", $postfieldspre); 167 | } 168 | } 169 | } 170 | ?> -------------------------------------------------------------------------------- /cwslack-follow.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | ini_set('display_errors', 1); //Display errors in case something occurs 21 | header('Content-Type: application/json'); //Set the header to return JSON, required by Slack 22 | require_once 'config.php'; 23 | require_once 'functions.php'; 24 | 25 | $link=0; 26 | 27 | if(empty($_REQUEST['method']) || ($_REQUEST['method'] != $followtoken && $_REQUEST['method'] != $unfollowtoken)){ 28 | if(empty($_REQUEST['token']) || $_REQUEST['token'] != $slackfollowtoken) die("Slack token invalid."); //If Slack token is not correct, kill the connection. This allows only Slack to access the page for security purposes. 29 | if(empty($_REQUEST['text'])) die("No text provided."); //If there is no text added, kill the connection. 30 | 31 | $exploded = explode(" ",$_REQUEST['text']); //Explode the string attached to the slash command for use in variables. 32 | } else { 33 | $link=1; 34 | } 35 | 36 | $command=NULL; //Set a null command variable, so it has something set no matter what. 37 | 38 | //Check for command errors. 39 | if($link==0 && !is_numeric($exploded[0])) { 40 | //Check to see if the first command in the text array is actually help, if so redirect to help webpage detailing slash command use. 41 | if ($exploded[0]=="help") { 42 | $test=json_encode(array("parse" => "full", "response_type" => "in_channel","text" => "Please visit " . $helpurl . " for more help information","mrkdwn"=>true)); 43 | echo $test; 44 | return; 45 | } 46 | else //Else close the connection. 47 | { 48 | echo "Unknown entry for ticket number."; 49 | return; 50 | } 51 | } 52 | 53 | 54 | if($link==0){ 55 | $ticketnumber = $exploded[0]; //Read ticket number to variable for convenience. 56 | $username = $_REQUEST['user_name']; //Read Slack username to variable for convenience. 57 | 58 | if (array_key_exists(1,$exploded)) //If a second string exists in the slash command array, make it the command. 59 | { 60 | $command = $exploded[1]; 61 | } 62 | } 63 | else 64 | { 65 | $ticketnumber = $_REQUEST['srnumber']; 66 | $mysql = mysqli_connect($dbhost, $dbusername, $dbpassword, $dbdatabase); 67 | if (!$mysql) //Check for errors 68 | { 69 | die("Connection Error: " . mysqli_connect_error()); 70 | } 71 | 72 | $val1 = mysqli_real_escape_string($mysql,$_REQUEST['memberid']); 73 | $sql = "SELECT slackuser FROM usermap where cwname = '".$val1."'"; 74 | 75 | $result = mysqli_query($mysql, $sql); //Run result 76 | // Check for mapping, otherwise use Connectwise 77 | if(mysqli_num_rows($result) > 0) 78 | { 79 | $user = mysqli_fetch_assoc($result); 80 | $username = $user['slackuser']; 81 | } 82 | else 83 | { 84 | $username = $_REQUEST['memberid']; 85 | } 86 | mysqli_close($mysql); 87 | 88 | if($_REQUEST['method']==$followtoken) 89 | { 90 | //For future use. 91 | } 92 | else if ($_REQUEST['method']==$unfollowtoken) 93 | { 94 | $command="unfollow"; //Set command to unfollow if it matches the CW unfollowtoken 95 | } 96 | else 97 | { 98 | die("Method does not match follow or unfollow tokens."); //If matches neither token, die. 99 | } 100 | } 101 | 102 | if($usedatabase==1) 103 | { 104 | $mysql = mysqli_connect($dbhost, $dbusername, $dbpassword, $dbdatabase); 105 | if (!$mysql) 106 | { 107 | die("Connection Error: " . mysqli_connect_error()); 108 | } 109 | 110 | if ($command == "unfollow") 111 | { 112 | $val1 = mysqli_real_escape_string($mysql,$ticketnumber); 113 | $val2 = mysqli_real_escape_string($mysql,$username); 114 | $sql = "DELETE FROM `follow` WHERE `ticketnumber`=\"" . $val1 . "\" AND `slackuser`=\"" . $val2 . "\""; 115 | 116 | if(mysqli_query($mysql,$sql)) 117 | { 118 | die("Successfully unfollowed ticket #".$ticketnumber); 119 | } 120 | else 121 | { 122 | die("MySQL Error: " . mysqli_error($mysql)); 123 | } 124 | } 125 | else 126 | { 127 | $val1 = mysqli_real_escape_string($mysql,$ticketnumber); 128 | $val2 = mysqli_real_escape_string($mysql,$username); 129 | $sql = "INSERT INTO `follow` (`id`, `ticketnumber`, `slackuser`) VALUES (NULL, '" . $val1 . "', '" . $val2 . "');"; 130 | if(mysqli_query($mysql,$sql)) 131 | { 132 | die("Successfully followed ticket #".$ticketnumber); 133 | } 134 | else 135 | { 136 | die("MySQL Error: " . mysqli_error($mysql)); 137 | } 138 | } 139 | } 140 | else 141 | { 142 | die("Follow module requires MySQL to function."); 143 | } 144 | 145 | 146 | 147 | ?> 148 | -------------------------------------------------------------------------------- /cwslack-incoming.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | //Receive connector for Connectwise Callbacks 21 | ini_set('display_errors', 1); //Display errors in case something occurs 22 | header('Content-Type: application/json'); //Set the header to return JSON, required by Slack 23 | require_once 'config.php'; //Require the config file. 24 | require_once 'functions.php'; 25 | 26 | $data = json_decode(file_get_contents('php://input')); //Decode incoming body from connectwise callback. 27 | if($data==NULL) 28 | { 29 | die("No ticket data was submitted. This is expected behavior if you are just browsing to this page with a web browser."); 30 | } 31 | $info = json_decode(stripslashes($data->Entity)); //Decode the entity field which contains the JSON data we want. 32 | 33 | //Connection kill blocks. Stops things from running if certain conditions are met. 34 | if(empty($_REQUEST['id']) || empty($_REQUEST['action']) || empty($info)) die; //If anything we need doesn't exist, kill connection. 35 | 36 | if($_REQUEST['action'] == "updated" && $_REQUEST['srDetailRecId']==0 && $_REQUEST['timeRecId']==0) die; //Kill connection if the update is not a note, and is something like a status change. This will prevent duplicate entries. 37 | 38 | if($_REQUEST['isProblemDescription']=="False" && $_REQUEST['isInternalAnalysis']=="False" && $_REQUEST['isResolution']=="False") die; //Die if no actual update. 39 | 40 | $badboards = explode("|",$badboard); //Explode with pipe seperator. 41 | $badstatuses = explode("|",$badstatus); //Explode with pipe seperator. 42 | $badcompanies = explode("|",$badcompany); //Explode with pipe seperator. 43 | if (in_array($info->BoardName,$badboards)) die; 44 | if (in_array($info->StatusName,$badstatuses)) die; 45 | if (in_array($info->CompanyName,$badcompanies)) die; 46 | 47 | $channel = NULL; //Set channel to NULL for future use. 48 | 49 | if (!empty($boardmapping)) 50 | { 51 | $explode = explode(",",$boardmapping); 52 | foreach($explode as $item) { 53 | $temp = explode("|",$item); 54 | if(strcasecmp($temp[0],$info->BoardName) == 0) { 55 | $channel = $temp[1]; 56 | } 57 | } 58 | } 59 | else if (!empty($_REQUEST['board'])) 60 | { 61 | if(strpos($_REQUEST['board'], "-") !== false) 62 | { 63 | $tempboards = explode("-", $_REQUEST['board']); 64 | if(!in_array($info->BoardName, $tempboards)) 65 | { 66 | die("Incorrect board"); 67 | } 68 | } 69 | else if($_REQUEST['board'] != $info->BoardName) 70 | { 71 | die("Incorrect board"); 72 | } 73 | 74 | if(!empty($_REQUEST['channel'])) //If using channels in URL is set, and channel is not empty.. 75 | { 76 | $channel = $_REQUEST['channel']; //Set $channel to the channel. 77 | } 78 | } 79 | 80 | //URL creation 81 | $ticketurl = $connectwise . "/$connectwisebranch/services/system_io/Service/fv_sr100_request.rails?service_recid="; //Set the URL required for ticket links. 82 | $noteurl = $connectwise . "/$connectwisebranch/apis/3.0/service/tickets/" . $_REQUEST['id'] . "/notes?orderBy=id%20desc"; //Set the URL required for cURL requests to ticket note API. 83 | $timeurl = $connectwise . "/$connectwisebranch/apis/3.0/time/entries?conditions=chargeToId=" . $_REQUEST['id'] . "&chargeToType=%27ServiceTicket%27&orderBy=dateEntered%20desc"; //Set the URL required for cURL requests to the time entry API. 84 | 85 | $dataTData = array(); //Blank array. 86 | $dataTimeData = array(); //Blank array. 87 | 88 | //Set headers for cURL requests. $header_data covers API authentication while $header_data2 covers the Slack output. 89 | $header_data = authHeader($companyname, $apipublickey, $apiprivatekey); // Authorization array. Auto encodes API key for auhtorization. 90 | $header_data2 =array( 91 | "Content-Type: application/json" 92 | ); 93 | 94 | $skip = 0; //Create variable to skip posting to Slack channel while also allowing follow posts. 95 | $date=strtotime($info->EnteredDateUTC); //Convert date entered JSON result to time. 96 | $dateformat=date('m-d-Y g:i:sa',$date); //Convert previously converted time to a better time string. 97 | $ticket=$_REQUEST['id']; 98 | $usetime = 0; //For posttext internal vs external flag. 99 | $dataarray = NULL; //For internal vs external flag. 100 | $dateformat = "None"; //Just in case! 101 | 102 | if($posttext==1) //Block for curl to get latest note 103 | { 104 | $createdby = "Error"; //Create with error just in case. 105 | $notetext = "Error"; //Create with error just in case. 106 | 107 | $dataTData = cURL($noteurl, $header_data); //Decode the JSON returned by the CW API. 108 | 109 | if($posttext==1) //Verifies no curl error occurred. If one has, ignore $posttext. 110 | { 111 | $dataTimeData = cURL($timeurl, $header_data); //Decode the JSON returned by the CW API. 112 | 113 | if($dataTData == NULL && $dataTimeData == NULL) 114 | { 115 | $posttext = 0; 116 | } 117 | 118 | if($posttext==1 && ($dataTData[0]->text != NULL || $dataTimeData[0]->text != NULL)) //Verified no curl error occurred as well as makes sure that if both text values == null, then there is no text to post. 119 | { 120 | $createdby = $dataTData[0]->createdBy; //Set $createdby to the ticket note creator. 121 | $text = $dataTData[0]->text; //Set $text to the ticket text. 122 | if (array_key_exists(0, $dataTData) && array_key_exists(0, $dataTimeData)) //Check if arrays exist properly. 123 | { 124 | $timetime = new DateTime($dataTimeData[0]->dateEntered); //Create new time object based on time entry note. 125 | $notetime = new DateTime($dataTData[0]->dateCreated); //Create new datetime object based on ticketnote note. 126 | 127 | if ($timetime > $notetime) //If the time entry is newer than latest ticket note. 128 | { 129 | $createdby = $dataTimeData[0]->enteredBy; //Set $createdby to the time entry creator. 130 | $text = $dataTimeData[0]->notes; // 131 | $usetime = 1; //Set time flag. 132 | } 133 | } 134 | } 135 | else 136 | { 137 | $posttext=0; //If text is null, ensure posttext = 0. 138 | } 139 | 140 | if ($usetime == 1) 141 | { 142 | $dataarray = $dataTimeData[0]; 143 | $notedate = $dataTimeData[0]->dateEntered; 144 | $dateformat2=date('m-d-Y g:i:sa',strtotime($notedate)); 145 | } 146 | else 147 | { 148 | $dataarray = $dataTData[0]; 149 | $notedate = $dataTData[0]->dateCreated; 150 | $dateformat2=date('m-d-Y g:i:sa',strtotime($notedate)); 151 | } 152 | 153 | } 154 | } 155 | 156 | if($_REQUEST['action'] == "added" && $postadded == 1) 157 | { 158 | if($posttext==0) 159 | { 160 | $postfieldspre = array( 161 | "channel" => ($channel!=NULL ? "#" . $channel : NULL), 162 | "attachments"=>array(array( 163 | "fallback" => (strtolower($_REQUEST['memberId'])=="zadmin" ? $info->ContactName : $info->UpdatedBy) ." created #" . $ticket . " - " . ($postcompany ? "(" . $info->CompanyName . ") " : "") . $info->Summary, 164 | "title" => "<" . $ticketurl . $ticket . "&companyName=" . $companyname . "|#" . $ticket . ">: ". $info->Summary, 165 | "pretext" => "Ticket #" . $ticket . " has been created by " . (strtolower($_REQUEST['memberId'])=="zadmin" ? $info->ContactName : $info->UpdatedBy) . ".", 166 | "text" => $info->CompanyName . " | " . $info->ContactName . //Return "Company / Contact" string 167 | "\n" . "Priority: " . $info->Priority . " | " . $info->StatusName . //Return "Prority / Status" string 168 | "\n" . $info->Resources, //Return assigned resources 169 | "mrkdwn_in" => array( 170 | "text", 171 | "pretext", 172 | "title" 173 | ) 174 | )) 175 | ); 176 | } 177 | else 178 | { 179 | $postfieldspre = array( 180 | "channel" => ($channel!=NULL ? "#" . $channel : NULL), 181 | "attachments"=>array(array( 182 | "fallback" => (strtolower($_REQUEST['memberId'])=="zadmin" ? $info->ContactName : $info->UpdatedBy) ." created #" . $ticket . " - " . ($postcompany ? "(" . $info->CompanyName . ") " : "") . $info->Summary, 183 | "title" => "<" . $ticketurl . $ticket . "&companyName=" . $companyname . "|#" . $ticket . ">: ". $info->Summary, 184 | "pretext" => "Ticket #" . $ticket . " has been created by " . (strtolower($_REQUEST['memberId'])=="zadmin" ? $info->ContactName : $info->UpdatedBy) . ".", 185 | "text" => $info->CompanyName . " | " . $info->ContactName . //Return "Company / Contact" string 186 | "\n" . "Priority: " . $info->Priority . " | " . $info->StatusName . //Return "Prority / Status" string 187 | "\n" . $info->Resources, //Return assigned resources 188 | "mrkdwn_in" => array( 189 | "text", 190 | "pretext", 191 | "title" 192 | ) 193 | ), 194 | array( 195 | "pretext" => "Latest " . ($dataarray->internalAnalysisFlag == "true" ? "Internal" : "External") . " Note (" . $dateformat2 . ") from: " . $createdby, 196 | "text" => $text, 197 | "mrkdwn_in" => array( 198 | "text", 199 | "pretext", 200 | "title" 201 | ) 202 | )) 203 | ); 204 | } 205 | } 206 | else if($_REQUEST['action'] == "updated" && $postupdated == 1) 207 | { 208 | if($posttext==0) 209 | { 210 | $postfieldspre = array( 211 | "channel" => ($channel!=NULL ? "#" . $channel : NULL), 212 | "attachments"=>array(array( 213 | "fallback" => $info->UpdatedBy . " updated #" . $ticket . " - " . ($postcompany ? "(" . $info->CompanyName . ") " : "") . $info->Summary, 214 | "title" => "<" . $ticketurl . $ticket . "&companyName=" . $companyname . "|#" . $ticket . ">: ". $info->Summary, 215 | "pretext" => "Ticket #" . $ticket . " has been updated by " . $info->UpdatedBy . ".", 216 | "text" => $info->CompanyName . " | " . $info->ContactName . //Return "Company / Contact" string 217 | "\n" . $dateformat . " | " . $info->StatusName . //Return "Date Entered / Status" string 218 | "\n" . $info->Resources, //Return assigned resources 219 | "mrkdwn_in" => array( 220 | "text", 221 | "pretext" 222 | ) 223 | )) 224 | ); 225 | } 226 | else 227 | { 228 | $postfieldspre = array( 229 | "channel" => ($channel!=NULL ? "#" . $channel : NULL), 230 | "attachments"=>array(array( 231 | "fallback" => $info->UpdatedBy . " updated #" . $ticket . " - " . ($postcompany ? "(" . $info->CompanyName . ") " : "") . $info->Summary, 232 | "title" => "<" . $ticketurl . $ticket . "&companyName=" . $companyname . "|#" . $ticket . ">: ". $info->Summary, 233 | "pretext" => "Ticket #" . $ticket . " has been updated by " . $info->UpdatedBy . ".", 234 | "text" => $info->CompanyName . " | " . $info->ContactName . //Return "Company / Contact" string 235 | "\n" . $dateformat . " | " . $info->StatusName . //Return "Date Entered / Status" string 236 | "\n" . $info->Resources, //Return assigned resources 237 | "mrkdwn_in" => array( 238 | "text", 239 | "pretext" 240 | ) 241 | ), 242 | array( 243 | "pretext" => "Latest " . ($dataarray->internalAnalysisFlag == "true" ? "Internal" : "External") . " Note (" . $dateformat2 . ") from: " . $createdby, 244 | "text" => $text, 245 | "mrkdwn_in" => array( 246 | "text", 247 | "pretext", 248 | "title" 249 | ) 250 | )) 251 | ); 252 | } 253 | } 254 | else 255 | { 256 | $skip=1; 257 | } 258 | 259 | if($skip==0) 260 | { 261 | cURLPost($webhookurl, $header_data2, "POST", $postfieldspre); 262 | } 263 | 264 | if($followenabled==1) 265 | { 266 | $alerts = array(); //Create a blank array. 267 | 268 | if($usedatabase==1) 269 | { 270 | $mysql = mysqli_connect($dbhost, $dbusername, $dbpassword, $dbdatabase); //Connect MySQL 271 | if (!$mysql) //Check for errors 272 | { 273 | die("Connection Error: " . mysqli_connect_error()); //Die with error if error found 274 | } 275 | 276 | $val1 = mysqli_real_escape_string($mysql,$ticket); 277 | $sql = "SELECT * FROM `follow` WHERE `ticketnumber`=\"" . $val1 . "\""; //SQL Query to select all ticket number entries 278 | 279 | $result = mysqli_query($mysql, $sql); //Run result 280 | 281 | if(mysqli_num_rows($result) > 0) //If there were rows matching query 282 | { 283 | while($row = mysqli_fetch_assoc($result)) //While we still have rows to work with 284 | { 285 | $alerts[]=$row["slackuser"]; //Add user to alerts array. 286 | } 287 | } 288 | } 289 | else 290 | { 291 | die(); 292 | } 293 | 294 | if(!empty($alerts)) { 295 | foreach ($alerts as $username) //For each user in alerts array, set $postfieldspre to the follow message. 296 | { 297 | if ($_REQUEST['action'] == "added") 298 | { 299 | if ($posttext == 0) 300 | { 301 | $postfieldspre = array( 302 | "channel" => "@" . $username, 303 | "attachments" => array(array( 304 | "fallback" => (strtolower($_REQUEST['memberId'])=="zadmin" ? $info->ContactName : $info->UpdatedBy) ." created #" . $ticket . " - " . ($postcompany ? "(" . $info->CompanyName . ") " : "") . $info->Summary, 305 | "title" => "<" . $ticketurl . $ticket . "&companyName=" . $companyname . "|#" . $ticket . ">: " . $info->Summary, 306 | "pretext" => "Ticket #" . $ticket . " has been created by " . (strtolower($_REQUEST['memberId'])=="zadmin" ? $info->ContactName : $info->UpdatedBy) . ".", 307 | "text" => $info->CompanyName . " | " . $info->ContactName . //Return "Company / Contact" string 308 | "\n" . "Priority: " . $info->Priority . " | " . $info->StatusName . //Return "Prority / Status" string 309 | "\n" . $info->Resources, //Return assigned resources 310 | "mrkdwn_in" => array( 311 | "text", 312 | "pretext", 313 | "title" 314 | ) 315 | )) 316 | ); 317 | } else { 318 | $postfieldspre = array( 319 | "channel" => "@" . $username, 320 | "attachments" => array(array( 321 | "fallback" => (strtolower($_REQUEST['memberId'])=="zadmin" ? $info->ContactName : $info->UpdatedBy) ." created #" . $ticket . " - " . ($postcompany ? "(" . $info->CompanyName . ") " : "") . $info->Summary, 322 | "title" => "<" . $ticketurl . $ticket . "&companyName=" . $companyname . "|#" . $ticket . ">: " . $info->Summary, 323 | "pretext" => "Ticket #" . $ticket . " has been created by " . (strtolower($_REQUEST['memberId'])=="zadmin" ? $info->ContactName : $info->UpdatedBy) . ".", 324 | "text" => $info->CompanyName . " | " . $info->ContactName . //Return "Company / Contact" string 325 | "\n" . "Priority: " . $info->Priority . " | " . $info->StatusName . //Return "Prority / Status" string 326 | "\n" . $info->Resources, //Return assigned resources 327 | "mrkdwn_in" => array( 328 | "text", 329 | "pretext", 330 | "title" 331 | ) 332 | ), 333 | array( 334 | "pretext" => "Latest " . ($dataarray->internalAnalysisFlag == "true" ? "Internal" : "External") . " Note (" . $dateformat2 . ") from: " . $createdby, 335 | "text" => $text, 336 | "mrkdwn_in" => array( 337 | "text", 338 | "pretext", 339 | "title" 340 | ) 341 | )) 342 | ); 343 | } 344 | } else if ($_REQUEST['action'] == "updated") { 345 | if ($posttext == 0) { 346 | $postfieldspre = array( 347 | "channel" => "@" . $username, 348 | "attachments" => array(array( 349 | "fallback" => $info->UpdatedBy . " updated #" . $ticket . " - " . ($postcompany ? "(" . $info->CompanyName . ") " : "") . $info->Summary, 350 | "title" => "<" . $ticketurl . $ticket . "&companyName=" . $companyname . "|#" . $ticket . ">: " . $info->Summary, 351 | "pretext" => "Ticket #" . $ticket . " has been updated by " . $info->UpdatedBy . ".", 352 | "text" => $info->CompanyName . " | " . $info->ContactName . //Return "Company / Contact" string 353 | "\n" . $dateformat . " | " . $info->StatusName . //Return "Date Entered / Status" string 354 | "\n" . $info->Resources, //Return assigned resources 355 | "mrkdwn_in" => array( 356 | "text", 357 | "pretext" 358 | ) 359 | )) 360 | ); 361 | } else { 362 | $postfieldspre = array( 363 | "channel" => "@" . $username, 364 | "attachments" => array(array( 365 | "fallback" => $info->UpdatedBy . " updated #" . $ticket . " - " . ($postcompany ? "(" . $info->CompanyName . ") " : "") . $info->Summary, 366 | "title" => "<" . $ticketurl . $ticket . "&companyName=" . $companyname . "|#" . $ticket . ">: " . $info->Summary, 367 | "pretext" => "Ticket #" . $ticket . " has been updated by " . $info->UpdatedBy . ".", 368 | "text" => $info->CompanyName . " | " . $info->ContactName . //Return "Company / Contact" string 369 | "\n" . $dateformat . " | " . $info->StatusName . //Return "Date Entered / Status" string 370 | "\n" . $info->Resources, //Return assigned resources 371 | "mrkdwn_in" => array( 372 | "text", 373 | "pretext" 374 | ) 375 | ), 376 | array( 377 | "pretext" => "Latest " . ($dataarray->internalAnalysisFlag == "true" ? "Internal" : "External") . " Note (" . $dateformat2 . ") from: " . $createdby, 378 | "text" => $text, 379 | "mrkdwn_in" => array( 380 | "text", 381 | "pretext", 382 | "title" 383 | ) 384 | ) 385 | ) 386 | ); 387 | } 388 | } 389 | 390 | cURLPost($webhookurl, $header_data2, "POST", $postfieldspre); 391 | } 392 | } 393 | } 394 | 395 | //Block for if ticket time reaches past X value 396 | if($timeenabled==1 && $info->ActualHours>$timepast) 397 | { 398 | if($_REQUEST['action'] == "added") 399 | { 400 | if($posttext==0) 401 | { 402 | $postfieldspre = array( 403 | "channel"=>$timechan, 404 | "attachments"=>array(array( 405 | "fallback" => (strtolower($_REQUEST['memberId'])=="zadmin" ? $info->ContactName : $info->UpdatedBy) ." created #" . $ticket . " - " . ($postcompany ? "(" . $info->CompanyName . ") " : "") . $info->Summary, 406 | "title" => "<" . $ticketurl . $ticket . "&companyName=" . $companyname . "|#" . $ticket . ">: ". $info->Summary, 407 | "color" => "#F0E68C", 408 | "pretext" => "Ticket #" . $ticket . " has been created by " . (strtolower($_REQUEST['memberId'])=="zadmin" ? $info->ContactName : $info->UpdatedBy) . ".", 409 | "text" => $info->CompanyName . " | " . $info->ContactName . //Return "Company / Contact" string 410 | "\n" . "Priority: " . $info->Priority . " | " . $info->StatusName . //Return "Prority / Status" string 411 | "\n" . $info->Resources . " | Total Hours: *" . $info->ActualHours . "*", //Return assigned resources 412 | "mrkdwn_in" => array( 413 | "text", 414 | "pretext", 415 | "title" 416 | ) 417 | )) 418 | ); 419 | } 420 | else 421 | { 422 | $postfieldspre = array( 423 | "channel"=>$timechan, 424 | "attachments"=>array(array( 425 | "fallback" => (strtolower($_REQUEST['memberId'])=="zadmin" ? $info->ContactName : $info->UpdatedBy) ." created #" . $ticket . " - " . ($postcompany ? "(" . $info->CompanyName . ") " : "") . $info->Summary, 426 | "title" => "<" . $ticketurl . $ticket . "&companyName=" . $companyname . "|#" . $ticket . ">: ". $info->Summary, 427 | "color" => "#F0E68C", 428 | "pretext" => "Ticket #" . $ticket . " has been created by " . (strtolower($_REQUEST['memberId'])=="zadmin" ? $info->ContactName : $info->UpdatedBy) . ".", 429 | "text" => $info->CompanyName . " | " . $info->ContactName . //Return "Company / Contact" string 430 | "\n" . "Priority: " . $info->Priority . " | " . $info->StatusName . //Return "Prority / Status" string 431 | "\n" . $info->Resources . " | Total Hours: *" . $info->ActualHours . "*", //Return assigned resources 432 | "mrkdwn_in" => array( 433 | "text", 434 | "pretext", 435 | "title" 436 | ) 437 | ), 438 | array( 439 | "pretext" => "Latest " . ($dataarray->internalAnalysisFlag == "true" ? "Internal" : "External") . " Note (" . $dateformat2 . ") from: " . $createdby, 440 | "text" => $text, 441 | "mrkdwn_in" => array( 442 | "text", 443 | "pretext", 444 | "title" 445 | ) 446 | )) 447 | ); 448 | } 449 | } 450 | else if($_REQUEST['action'] == "updated") 451 | { 452 | if ($posttext == 0) { 453 | $postfieldspre = array( 454 | "channel" => $timechan, 455 | "attachments" => array(array( 456 | "fallback" => $info->UpdatedBy . " updated #" . $ticket . " - " . ($postcompany ? "(" . $info->CompanyName . ") " : "") . $info->Summary, 457 | "title" => "<" . $ticketurl . $ticket . "&companyName=" . $companyname . "|#" . $ticket . ">: " . $info->Summary, 458 | "color" => "#F0E68C", 459 | "pretext" => "Ticket #" . $ticket . " has been updated by " . $info->UpdatedBy . ".", 460 | "text" => $info->CompanyName . " | " . $info->ContactName . //Return "Company / Contact" string 461 | "\n" . $dateformat . " | " . $info->StatusName . //Return "Date Entered / Status" string 462 | "\n" . $info->Resources . " | Total Hours: *" . $info->ActualHours . "*", //Return assigned resources 463 | "mrkdwn_in" => array( 464 | "text", 465 | "pretext" 466 | ) 467 | )) 468 | ); 469 | } else { 470 | $postfieldspre = array( 471 | "channel" => $timechan, 472 | "attachments" => array(array( 473 | "fallback" => $info->UpdatedBy . " updated #" . $ticket . " - " . ($postcompany ? "(" . $info->CompanyName . ") " : "") . $info->Summary, 474 | "title" => "<" . $ticketurl . $ticket . "&companyName=" . $companyname . "|#" . $ticket . ">: " . $info->Summary, 475 | "color" => "#F0E68C", 476 | "pretext" => "Ticket #" . $ticket . " has been updated by " . $info->UpdatedBy . ".", 477 | "text" => $info->CompanyName . " | " . $info->ContactName . //Return "Company / Contact" string 478 | "\n" . $dateformat . " | " . $info->StatusName . //Return "Date Entered / Status" string 479 | "\n" . $info->Resources . " | Total Hours: *" . $info->ActualHours . "*", //Return assigned resources 480 | "mrkdwn_in" => array( 481 | "text", 482 | "pretext" 483 | ) 484 | ), 485 | array( 486 | "pretext" => "Latest " . ($dataarray->internalAnalysisFlag == "true" ? "Internal" : "External") . " Note (" . $dateformat2 . ") from: " . $createdby, 487 | "text" => $text, 488 | "mrkdwn_in" => array( 489 | "text", 490 | "pretext", 491 | "title" 492 | ) 493 | ) 494 | ) 495 | ); 496 | } 497 | } 498 | 499 | cURLPost($webhookurl, $header_data2, "POST", $postfieldspre); 500 | } 501 | 502 | 503 | ?> 504 | -------------------------------------------------------------------------------- /cwslack-lunch-cron.php: -------------------------------------------------------------------------------- 1 | /dev/null 2>&1 5 | // May have to adjust locations of php and this file if they are different on your system 6 | 7 | $mysql = mysqli_connect($dbhost, $dbusername, $dbpassword, $dbdatabase); //Connect MySQL 8 | 9 | if (!$mysql) //Check for errors 10 | { 11 | die("Connection Error: " . mysqli_connect_error()); //Return properly encoded arrays in JSON for Slack parsing. 12 | } 13 | 14 | $sql = "UPDATE lunch SET lunchtoday = 0, lunchon = 0, lunchend = NULL, lunchstart = NULL"; 15 | if (mysqli_query($mysql, $sql)) { 16 | //Table cleared successfully 17 | } else { 18 | die("lunch Table Clear Error: " . mysqli_error($mysql)); 19 | } -------------------------------------------------------------------------------- /cwslack-lunch.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | ini_set('display_errors', 1); //Display errors in case something occurs 21 | header('Content-Type: application/json'); //Set the header to return JSON, required by Slack 22 | require_once 'config.php'; 23 | require_once 'functions.php'; 24 | 25 | if($usedatabase==0) die("Unable to run this module without a MySQL database"); // Warning if you don't have MySQL enabled 26 | if(empty($_REQUEST['token']) || ($_REQUEST['token'] != $slacklunchtoken)) die("Slack token invalid."); //If Slack token is not correct, kill the connection. This allows only Slack to access the page for security purposes. 27 | if(empty($_REQUEST['text'])) 28 | { 29 | $blanktext = true; 30 | } 31 | else 32 | { 33 | $blanktext = false; 34 | } 35 | 36 | $exploded = explode(" ",$_REQUEST['text']); //Explode the string attached to the slash command for use in variables. 37 | if ($exploded[0]=="help") { 38 | die(json_encode(array("parse" => "full", "response_type" => "in_channel","text" => "Please visit " . $helpurl . " for more help information","mrkdwn"=>true))); 39 | } 40 | 41 | //Timeout Fix Block 42 | if($timeoutfix == true) 43 | { 44 | ob_end_clean(); 45 | header("Connection: close"); 46 | ob_start(); 47 | //echo ('{"response_type": "in_channel"}'); Uncomment to show /lunch command in text 48 | $size = ob_get_length(); 49 | header("Content-Length: $size"); 50 | ob_end_flush(); 51 | flush(); 52 | session_write_close(); 53 | if($sendtimeoutwait==true) { 54 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral", "text" => "Please wait...")); 55 | } 56 | } 57 | //End timeout fix block 58 | 59 | // Authorization array. Auto encodes API key for auhtorization above. 60 | $header_data = authHeader($companyname, $apipublickey, $apiprivatekey); 61 | // Authorization array, with extra json content-type used in patch commands to change tickets. 62 | $header_data2 = postHeader($companyname, $apipublickey, $apiprivatekey); 63 | 64 | $timeurl = $connectwise . "/$connectwisebranch/apis/3.0/time/entries"; 65 | $schedurl = $connectwise . "/$connectwisebranch/apis/3.0/schedule/entries"; 66 | 67 | $slackname = $_REQUEST["user_name"]; 68 | 69 | $mysql = mysqli_connect($dbhost, $dbusername, $dbpassword, $dbdatabase); //Connect MySQL 70 | 71 | if (!$mysql) //Check for errors 72 | { 73 | if ($timeoutfix == true) { 74 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral","text" => "Connection Error: " . mysqli_connect_error())); 75 | } else { 76 | die("Connection Error: " . mysqli_connect_error()); //Return properly encoded arrays in JSON for Slack parsing. 77 | } 78 | die(); 79 | } 80 | 81 | $sql = "CREATE TABLE IF NOT EXISTS lunch (slackuser VARCHAR(25) PRIMARY KEY, lunchstart DATETIME, lunchend DATETIME, lunchtoday BOOLEAN NOT NULL DEFAULT 0, lunchon BOOLEAN NOT NULL DEFAULT 0)"; 82 | if (mysqli_query($mysql, $sql)) { 83 | //Table created successfully 84 | } else { 85 | if ($timeoutfix == true) { 86 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral","text" => "lunch Table Creation Error: " . mysqli_error($mysql))); 87 | } else { 88 | die("lunch Table Creation Error: " . mysqli_error($mysql)); 89 | } 90 | die(); 91 | } 92 | 93 | 94 | if($_REQUEST['text'] == "list" || $_REQUEST['text'] == "status") 95 | { 96 | $sql = "SELECT * FROM `lunch`"; 97 | 98 | $result = mysqli_query($mysql, $sql); //Run result 99 | $output = "```User | Status | Lunch Start Today\n"; 100 | if(mysqli_num_rows($result) > 0) //If there were too many rows matching query 101 | { 102 | while($row = mysqli_fetch_assoc($result)) 103 | { 104 | $formatuser = ""; 105 | $formatstatus = ""; 106 | $formatstart = ""; 107 | 108 | if(strlen($row["slackuser"]) > 14) 109 | { 110 | $formatuser = substr($row["slackuser"],0,11) . "... "; 111 | } 112 | else if(strlen($row["slackuser"]) == 14) 113 | { 114 | $formatuser = $row["slackuser"] . " "; 115 | } 116 | else 117 | { 118 | $formatuser = str_pad($row["slackuser"], 15); 119 | } 120 | 121 | if($row["lunchon"] == 1) 122 | { 123 | $formatstatus = " On Lunch "; 124 | } 125 | else if($row["lunchon"] == 0 && $row["lunchtoday"] == 1) 126 | { 127 | $formatstatus = " Off Lunch "; 128 | } 129 | else 130 | { 131 | $formatstatus = " Not Taken "; 132 | } 133 | 134 | if($row["lunchstart"] == NULL) 135 | { 136 | $formatstart = " Not yet started"; 137 | } 138 | else 139 | { 140 | $formatstart = " " . date("m/d/y g:ia", strtotime($row["lunchstart"])); 141 | } 142 | 143 | $output = $output . $formatuser . "|" . $formatstatus . "|" . $formatstart . "\n"; 144 | } 145 | $output = $output . "```"; 146 | if ($timeoutfix == true) { 147 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "in_channel","fallback" => "Lunch Status List","title"=>"Lunch Status List","text" => $output)); 148 | } else { 149 | die($output); 150 | } 151 | die(); 152 | } 153 | else 154 | { 155 | die("No user mappings found in database."); 156 | } 157 | } 158 | 159 | 160 | $val1 = mysqli_real_escape_string($mysql,$slackname); 161 | $sql = "SELECT * FROM `usermap` WHERE `slackuser`=\"" . $val1 . "\""; 162 | 163 | $result = mysqli_query($mysql, $sql); //Run result 164 | $rowcount = mysqli_num_rows($result); 165 | if($rowcount > 1) //If there were too many rows matching query 166 | { 167 | die("Error: too many users somehow?"); //This should NEVER happen. 168 | } 169 | else if ($rowcount == 1) //If exactly 1 row is found. 170 | { 171 | $row = mysqli_fetch_assoc($result); //Row association. 172 | 173 | $cwname = $row["cwname"]; //Return the connectwise name of the row found as the CW member name. 174 | } 175 | else //If no rows are found 176 | { 177 | if($usecwname==1) //If variable enabled 178 | { 179 | $cwname = $_REQUEST['user_name']; 180 | } 181 | else 182 | { 183 | // Die if unable to get cwname set 184 | if ($timeoutfix == true) { 185 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral","text" => "Unable to find user to match to in Connectwise. Please have your admin map your username in Slack or enable the usecwname setting")); 186 | } else { 187 | die("Unable to find user to match to in Connectwise. Please have your admin map your username in Slack or enable the usecwname setting"); 188 | } 189 | die(); 190 | } 191 | } 192 | 193 | //Parse no-text 194 | $golunchon = NULL; 195 | if($blanktext) 196 | { 197 | $val1 = mysqli_real_escape_string($mysql,$slackname); 198 | $sql = "SELECT * FROM `lunch` WHERE `slackuser`=\"" . $val1 . "\""; 199 | 200 | $result = mysqli_query($mysql, $sql); //Run result 201 | $rowcount = mysqli_num_rows($result); 202 | if($rowcount > 1) //If there were too many rows matching query 203 | { 204 | die("Error: too many users somehow?"); //This should NEVER happen. 205 | } 206 | else if ($rowcount == 1) //If exactly 1 row is found. 207 | { 208 | $row = mysqli_fetch_assoc($result); //Row association. 209 | 210 | if($row["lunchstart"] == NULL || !$row["lunchon"]) 211 | { 212 | $golunchon = true; 213 | } 214 | else 215 | { 216 | $golunchon = false; 217 | } 218 | } 219 | else 220 | { 221 | $val1 = mysqli_real_escape_string($mysql,$slackname); 222 | $sql = "INSERT INTO `lunch` (`slackuser`) VALUES ('" . $val1 . "');"; //SQL Query to insert new map 223 | 224 | if(mysqli_query($mysql,$sql)) 225 | { 226 | $golunchon = true; 227 | } 228 | else 229 | { 230 | die("MySQL Error: " . mysqli_error($mysql)); 231 | } 232 | } 233 | } 234 | 235 | //Lunch on block 236 | if($golunchon || $exploded[0]=="on" || $exploded[0]=="go" || $exploded[0]=="start") 237 | { 238 | $val1 = mysqli_real_escape_string($mysql,$slackname); 239 | $sql = "SELECT * FROM `lunch` WHERE `slackuser`=\"" . $val1 . "\""; // 240 | 241 | $result = mysqli_query($mysql, $sql); //Run result 242 | $userdata = mysqli_fetch_assoc($result); //Row association. 243 | 244 | if($userdata["lunchon"]) 245 | { 246 | if ($timeoutfix == true) { 247 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral","text" => "You are already on lunch. Please use /lunch off to go off lunch")); 248 | } else { 249 | die("You are already on lunch. Please use /lunch off to go off lunch"); 250 | } 251 | die(); 252 | } 253 | 254 | if($userdata["lunchtoday"]) 255 | { 256 | if ($timeoutfix == true) { 257 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral","text" => "You have already taken lunch today. Please use /lunch split if you are taking additional lunch time.")); 258 | } else { 259 | die("You have already taken lunch today. Please use /lunch split if you are taking additional lunch time."); 260 | } 261 | die(); 262 | } 263 | 264 | //Schedule entry block 265 | if($lunchcreatesched) 266 | { 267 | $datestart = gmdate("Y-m-d\TH:i:s\Z"); //Start time of the ticket. 268 | $dateend = gmdate("Y-m-d\TH:i:s\Z",strtotime("+1 hour")); //Start time of the ticket. 269 | $postfieldspre = array("member"=>array("identifier"=>$cwname), "type"=>array("id"=>13), "dateStart" => $datestart, "dateEnd" => $dateend, "allowScheduleConflictsFlag"=>true, "name"=>"Lunch [Slack]"); 270 | $dataTNotes = cURLPost($schedurl, $header_data2, "POST", $postfieldspre); 271 | } 272 | 273 | //Slack notifications block 274 | if($lunchsendslack) 275 | { 276 | if($lunchsendonoff == 1 || $lunchsendonoff == 3) 277 | { 278 | $offlunchat = date("g:ia", strtotime("+1 hour")); 279 | 280 | $postfieldspre = array( 281 | "channel"=>$lunchslackchannel, 282 | "text"=>"$cwname has taken their lunch and they will return at $offlunchat." 283 | ); 284 | 285 | cURLPost($webhookurl, $header_data2, "POST", $postfieldspre); 286 | } 287 | } 288 | 289 | //Email notifications block 290 | if($lunchsendemail) 291 | { 292 | if($lunchsendonoff == 1 || $lunchsendonoff == 3) 293 | { 294 | ini_set("SMTP", $smtpserver); 295 | ini_set("smtp_port", $smtpport); 296 | 297 | $headers = 'From: ' . $smtpname . '<' . $smtpfrom . ">\r\n" . 298 | 'Reply-To: ' . $smtpfrom . "\r\n" . 299 | 'X-Mailer: PHP/' . phpversion(); 300 | $subject = "$cwname on lunch"; 301 | $body = "$cwname has taken their lunch and should return at $offlunchat."; 302 | 303 | mail($lunchemailto, $subject, $body, $headers); 304 | } 305 | } 306 | 307 | $starttime = date("Y-m-d H:i") . ":00"; 308 | $val1 = mysqli_real_escape_string($mysql,$slackname); 309 | $sql = "UPDATE `lunch` SET `lunchstart` = '" . $starttime . "', `lunchon` = 1 WHERE `slackuser`=\"" . $val1 . "\""; // 310 | 311 | $result = mysqli_query($mysql, $sql); //Run result 312 | 313 | if ($timeoutfix == true) { 314 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral","text" => "You have gone on lunch. Please return by $offlunchat")); 315 | } else { 316 | die("You have gone on lunch. Please return by $offlunchat"); //Post to slack 317 | } 318 | die(); //End of section 319 | } 320 | 321 | //Lunch on block 322 | if($exploded[0]=="split") 323 | { 324 | $val1 = mysqli_real_escape_string($mysql,$slackname); 325 | $sql = "SELECT * FROM `lunch` WHERE `slackuser`=\"" . $val1 . "\""; // 326 | 327 | $result = mysqli_query($mysql, $sql); //Run result 328 | $userdata = mysqli_fetch_assoc($result); //Row association. 329 | 330 | if($userdata["lunchon"]) 331 | { 332 | if ($timeoutfix == true) { 333 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral","text" => "You are already on lunch. Please use /lunch off to go off lunch")); 334 | } else { 335 | die("You are already on lunch. Please use /lunch off to go off lunch"); 336 | } 337 | die(); 338 | } 339 | 340 | //Slack notifications block 341 | if($lunchsendslack) 342 | { 343 | if($lunchsendonoff == 1 || $lunchsendonoff == 3) 344 | { 345 | $postfieldspre = array( 346 | "channel"=>$lunchslackchannel, 347 | "text"=>"$cwname has gone back on a split lunch, and will return soon. Please message the tech for exact timing." 348 | ); 349 | 350 | cURLPost($webhookurl, $header_data2, "POST", $postfieldspre); 351 | } 352 | } 353 | 354 | //Email notifications block 355 | if($lunchsendemail) 356 | { 357 | if($lunchsendonoff == 1 || $lunchsendonoff == 3) 358 | { 359 | ini_set("SMTP", $smtpserver); 360 | ini_set("smtp_port", $smtpport); 361 | 362 | $headers = 'From: ' . $smtpname . '<' . $smtpfrom . ">\r\n" . 363 | 'Reply-To: ' . $smtpfrom . "\r\n" . 364 | 'X-Mailer: PHP/' . phpversion(); 365 | $subject = "$cwname on lunch"; 366 | $body = "$cwname has gone back on a split lunch, and will return soon. Please message the tech for exact timing."; 367 | 368 | mail($lunchemailto, $subject, $body, $headers); 369 | } 370 | } 371 | 372 | $starttime = date("Y-m-d H:i") . ":00"; 373 | $val1 = mysqli_real_escape_string($mysql,$slackname); 374 | $sql = "UPDATE `lunch` SET `lunchstart` = '" . $starttime . "', `lunchend` = NULL, `lunchon` = 1 WHERE `slackuser`=\"" . $val1 . "\""; // 375 | 376 | $result = mysqli_query($mysql, $sql); //Run result 377 | 378 | if ($timeoutfix == true) { 379 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral","text" => "You have gone on lunch. As this is a split lunch, please return within your remaining lunch time.")); 380 | } else { 381 | die("You have gone on lunch. As this is a split lunch, please return within your remaining lunch time."); //Post to slack 382 | } 383 | die(); //End of section 384 | } 385 | 386 | //Lunch off block 387 | if(!$golunchon || $exploded[0]=="off" || $exploded[0]=="back" || $exploded[0]=="stop" || $exploded[0]=="end") 388 | { 389 | $val1 = mysqli_real_escape_string($mysql,$slackname); 390 | $sql = "SELECT * FROM `lunch` WHERE `slackuser`=\"" . $val1 . "\""; // 391 | 392 | $result = mysqli_query($mysql, $sql); //Run result 393 | $userdata = mysqli_fetch_assoc($result); //Row association. 394 | 395 | if(!$userdata["lunchon"]) 396 | { 397 | if ($timeoutfix == true) { 398 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral","text" => "You are already off lunch. Please use /lunch on to go on lunch")); 399 | } else { 400 | die("You are already off lunch. Please use /lunch on to go on lunch"); 401 | } 402 | die(); 403 | } 404 | 405 | //Time block 406 | if($lunchsavetime) 407 | { 408 | $datestart = gmdate("Y-m-d\TH:i",strtotime($userdata["lunchstart"])) . ":00Z"; //Start time of the time entry. 409 | $dateend = gmdate("Y-m-d\TH:i") . ":00Z"; //End time of the time entry. 410 | 411 | $postfieldspre = array( 412 | "notes" => "Lunch via Slack", 413 | "chargeToType" => "ChargeCode", 414 | "chargeToId" => "$lunchchargecode", 415 | "timeStart" => $datestart, 416 | "timeEnd" => $dateend, 417 | "member" => array("identifier"=>$cwname) 418 | ); //Post time as user 419 | 420 | $dataTNotes = cURLPost($timeurl, $header_data2, "POST", $postfieldspre); 421 | 422 | if(array_key_exists("errors",$dataTNotes)) //If connectwise returned an error. 423 | { 424 | $errors = $dataTNotes->errors; //Make array easier to access. 425 | 426 | if ($timeoutfix == true) { 427 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral","text" => "ConnectWise Error: " . $errors[0]->message)); 428 | } else { 429 | die("ConnectWise Error: " . $errors[0]->message); //Post to slack 430 | } 431 | die(); //Return CW error 432 | } 433 | } 434 | 435 | //Slack notifications block 436 | if($lunchsendslack) 437 | { 438 | if($lunchsendonoff == 2 || $lunchsendonoff == 3) 439 | { 440 | $dateobj = DateTime::createFromFormat("Y-m-d H:i:s",$userdata["lunchstart"]); 441 | $datetime = date_format($dateobj, "g:ia"); 442 | 443 | $postfieldspre = array( 444 | "channel"=>$lunchslackchannel, 445 | "text"=>"$cwname has returned from lunch. They went on lunch at " . $datetime 446 | ); 447 | 448 | cURLPost($webhookurl, $header_data2, "POST", $postfieldspre); 449 | } 450 | } 451 | 452 | //Email notifications block 453 | if($lunchsendemail) 454 | { 455 | if($lunchsendonoff == 2 || $lunchsendonoff == 3) 456 | { 457 | ini_set("SMTP", $smtpserver); 458 | ini_set("smtp_port", $smtpport); 459 | 460 | $headers = 'From: ' . $smtpname . '<' . $smtpfrom . ">\r\n" . 461 | 'Reply-To: ' . $smtpfrom . "\r\n" . 462 | 'X-Mailer: PHP/' . phpversion(); 463 | $subject = "$cwname off lunch"; 464 | $body = "$cwname has returned from lunch."; 465 | 466 | mail($lunchemailto, $subject, $body, $headers); 467 | } 468 | } 469 | 470 | $endtime = date("Y-m-d H:i" . ":00"); 471 | $val1 = mysqli_real_escape_string($mysql,$slackname); 472 | $sql = "UPDATE `lunch` SET `lunchend` = '" . $endtime . "', `lunchon` = 0, `lunchtoday` = 1 WHERE `slackuser`=\"" . $val1 . "\""; // 473 | 474 | $result = mysqli_query($mysql, $sql); //Run result 475 | 476 | if ($timeoutfix == true) { 477 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral","text" => "You have gone off lunch and a time entry has been added.")); 478 | } else { 479 | die("You have gone off lunch and a time entry has been added."); //Post to slack 480 | } 481 | die(); //End of section 482 | } 483 | 484 | 485 | ?> -------------------------------------------------------------------------------- /cwslack-notes.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | 21 | ini_set('display_errors', 1); //Display errors in case something occurs 22 | header('Content-Type: application/json'); //Set the header to return JSON, required by Slack 23 | require_once 'config.php'; 24 | require_once 'functions.php'; 25 | 26 | if(empty($_REQUEST['token']) || ($_REQUEST['token'] != $slacknotestoken)) die("Slack token invalid."); //If Slack token is not correct, kill the connection. This allows only Slack to access the page for security purposes. 27 | if(empty($_REQUEST['text'])) die("No text provided."); //If there is no text added, kill the connection. 28 | 29 | $exploded = explode(" ",$_REQUEST['text']); //Explode the string attached to the slash command for use in variables. 30 | 31 | //This section checks if the ticket number is not equal to 6 digits (our tickets are in the hundreds of thousands but not near a million yet) and kills the connection if it's not. 32 | if(!is_numeric($exploded[0])) { 33 | //Check to see if the first command in the text array is actually help, if so redirect to help webpage detailing slash command use. 34 | if ($exploded[0]=="help") { 35 | die(json_encode(array("parse" => "full", "response_type" => "in_channel","text" => "Please visit " . $helpurl . " for more help information","mrkdwn"=>true))); //Encode a JSON response with a help URL. 36 | } 37 | else //Else close the connection. 38 | { 39 | echo "Unknown entry for ticket number."; 40 | return; 41 | } 42 | } 43 | 44 | //Timeout Fix Block 45 | if($timeoutfix == true) 46 | { 47 | ob_end_clean(); 48 | header("Connection: close"); 49 | ob_start(); 50 | echo ('{"response_type": "in_channel"}'); 51 | $size = ob_get_length(); 52 | header("Content-Length: $size"); 53 | ob_end_flush(); 54 | flush(); 55 | session_write_close(); 56 | if($sendtimeoutwait==true) { 57 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral", "text" => "Please wait...")); 58 | } 59 | } 60 | //End timeout fix block 61 | 62 | $ticketnumber = $exploded[0]; //Set the ticket number to the first string 63 | $command=NULL; //Create a command variable and set it to Null 64 | $sentence=NULL; //Create a option variable and set it to Null 65 | $urlticketdata = $connectwise . "/$connectwisebranch/apis/3.0/service/tickets/" . $ticketnumber; //Set ticket API url 66 | $notype=false; // For use if they do not specify a type. 67 | 68 | //Set URL 69 | $noteurl = $connectwise . "/$connectwisebranch/apis/3.0/service/tickets/" . $ticketnumber . "/notes"; 70 | 71 | 72 | if (array_key_exists(1, $exploded)) //If a second string exists in the slash command array, make it the command. 73 | { 74 | $command = $exploded[1]; 75 | if (array_key_exists(2, $exploded)) //If a third string exists in the slash command array, make it the option for the command. 76 | { 77 | unset($exploded[0]); 78 | unset($exploded[1]); 79 | $sentence = implode(" ", $exploded); 80 | } 81 | } 82 | 83 | // Authorization array, with extra json content-type used in patch commands to change tickets. 84 | $header_data = postHeader($companyname, $apipublickey, $apiprivatekey); 85 | 86 | //Need to create array before hand to ensure no errors occur. 87 | $dataTNotes = array(); 88 | 89 | $ch = curl_init(); 90 | $postfieldspre = NULL; //avoid errors. 91 | if($command == "internal") //If second part of text is internal 92 | { 93 | $postfieldspre = array("internalAnalysisFlag" => "True", "text" => $sentence); //Post ticket as API user 94 | } 95 | else if ($command == "external")//If second part of text is external 96 | { 97 | $postfieldspre = array("detailDescriptionFlag" => "True", "text" => $sentence); 98 | } 99 | else if ($command == "externalemail" || $command == "emailexternal")//If second part of text is external 100 | { 101 | $postfieldspre = array("detailDescriptionFlag" => "True", "processNotifications" => "True", "text" => $sentence); 102 | } 103 | else //If second part of text is neither external or internal 104 | { 105 | $notype = true; 106 | 107 | $exploded = explode(" ",$_REQUEST['text']); // Recreate exploded so we reset the unsets. 108 | if (array_key_exists(1, $exploded)) // Assuming any second word exists 109 | { 110 | 111 | unset($exploded[0]); 112 | $sentence = implode(" ", $exploded); 113 | } 114 | 115 | if($defaultnotetype=="internal") 116 | { 117 | $postfieldspre = array("internalAnalysisFlag" => "True", "text" => $sentence); //Post ticket as API user 118 | } 119 | elseif($defaultnotetype=="external") 120 | { 121 | $postfieldspre = array("detailDescriptionFlag" => "True", "text" => $sentence); 122 | } 123 | elseif($defaultnotetype=="externalemail") 124 | { 125 | $postfieldspre = array("detailDescriptionFlag" => "True", "processNotifications" => "True", "text" => $sentence); 126 | } 127 | else 128 | { 129 | if ($timeoutfix == true) { 130 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral","text" => "Second part of text must be either internal or external.")); 131 | } else { 132 | die("Second part of text must be either internal or external."); //Return error text. 133 | } 134 | die(); 135 | } 136 | } 137 | 138 | //Username mapping code 139 | if($usedatabase==1) 140 | { 141 | $mysql = mysqli_connect($dbhost, $dbusername, $dbpassword, $dbdatabase); //Connect MySQL 142 | 143 | if (!$mysql) //Check for errors 144 | { 145 | if ($timeoutfix == true) { //This should NEVER happen. 146 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral","text" => "Connection Error: " . mysqli_connect_error())); 147 | } else { 148 | die("Connection Error: " . mysqli_connect_error()); //Post to slack 149 | } 150 | die(); 151 | } 152 | 153 | $val1 = mysqli_real_escape_string($mysql,$_REQUEST["user_name"]); 154 | $sql = "SELECT * FROM `usermap` WHERE `slackuser`=\"" . $val1 . "\""; //SQL Query to select all ticket number entries 155 | 156 | $result = mysqli_query($mysql, $sql); //Run result 157 | $rowcount = mysqli_num_rows($result); 158 | if($rowcount > 1) //If there were too many rows matching query 159 | { 160 | if ($timeoutfix == true) { //This should NEVER happen. 161 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral","text" => "Error: too many users somehow?")); 162 | } else { 163 | die("Error: too many users somehow?"); //Post to slack 164 | } 165 | die(); 166 | } 167 | else if ($rowcount == 1) //If exactly 1 row is found. 168 | { 169 | $row = mysqli_fetch_assoc($result); //Row association. 170 | 171 | $postfieldspre["member"] = array("identifier"=>$row["cwname"]); //Return the connectwise name of the row found as the CW member name. 172 | } 173 | else //If no rows are found 174 | { 175 | if($usecwname==1) //If variable enabled 176 | { 177 | $postfieldspre["member"] = array("identifier"=>$_REQUEST['user_name']); //Return the slack username as the user for the ticket note. If the user does not exist in CW, it will use the API username. 178 | } 179 | } 180 | } 181 | else 182 | { 183 | if($usecwname==1) 184 | { 185 | $postfieldspre["member"] = array("identifier"=>$_REQUEST['user_name']); 186 | } 187 | } 188 | 189 | $dataTData = cURL($urlticketdata, $header_data); //Decode the JSON returned by the CW API. 190 | 191 | if($dataTData==NULL) 192 | { 193 | if ($timeoutfix == true) { 194 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral","text" => "Array not returned. Please check your connectwise URL variable in config.php and ensure it is accessible via the web at " . $urlticketdata)); 195 | } else { 196 | die("Array not returned. Please check your connectwise URL variable in config.php and ensure it is accessible via the web at " . $urlticketdata); //Return properly encoded arrays in JSON for Slack parsing. 197 | } 198 | die(); 199 | } 200 | 201 | $dataTNotes = cURLPost($noteurl, $header_data, "POST", $postfieldspre); 202 | 203 | if(array_key_exists("errors",$dataTNotes)) //If connectwise returned an error. 204 | { 205 | $errors = $dataTNotes->errors; //Make array easier to access. 206 | 207 | if ($timeoutfix == true) { //Return CW error 208 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral","text" => "ConnectWise Error: " . $errors[0]->message)); 209 | } else { 210 | die("ConnectWise Error: " . $errors[0]->message); //Post to slack 211 | } 212 | die(); 213 | } 214 | else //No error 215 | { 216 | if($notype) 217 | { 218 | $command = $defaultnotetype; 219 | } 220 | if ($timeoutfix == true) { 221 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "in_channel","text" => "New " . $command . " note created on #" . $ticketnumber . ": " . $dataTData->company->identifier . " / " . $dataTData->summary . "\n\"" . $sentence . "\"")); 222 | } else { 223 | echo json_encode(array("parse" => "full", "response_type" => "in_channel","text" => "New " . $command . " note created on #" . $ticketnumber . ": " . $dataTData->company->identifier . " / " . $dataTData->summary . "\n\"" . $sentence . "\"")); //Post to slack 224 | } 225 | } 226 | 227 | ?> -------------------------------------------------------------------------------- /cwslack-priorityalerts.php: -------------------------------------------------------------------------------- 1 | . 19 | */ 20 | 21 | //Receive connector for Connectwise Callbacks 22 | ini_set('display_errors', 1); //Display errors in case something occurs 23 | header('Content-Type: application/json'); //Set the header to return JSON, required by Slack 24 | require_once 'config.php'; //Require the config file. 25 | require_once 'functions.php'; 26 | 27 | //Dates required for URL to function 28 | $datenow = gmdate("Y-m-d\TH:i", strtotime("-" . $prioritywait . " minutes")); //Date set to 10 minutes prior to now, to catch for tickets happening right now. 29 | $date2hours = gmdate("Y-m-d\TH:i", strtotime("-2 hours")); //Date set to 2 hours out so reminders up to 2 hours function. 30 | 31 | $url = $connectwise. "/$connectwisebranch/apis/3.0/schedule/entries?conditions=dateStart%20%3E%20[" . $date2hours . "]%20and%20dateStart%20%3C%20[". $datenow . "]&orderBy=dateStart%20desc"; //URL to access the schedule API 32 | $ticketurl = $connectwise . "/$connectwisebranch/services/system_io/Service/fv_sr100_request.rails?service_recid="; //Set the URL required for ticket links. 33 | 34 | //Set headers for cURL requests. $header_data covers API authentication while $header_data2 covers the Slack output. 35 | $header_data = authHeader($companyname, $apipublickey, $apiprivatekey); 36 | 37 | $header_data2 =array( 38 | "Content-Type: application/json" 39 | ); 40 | 41 | // Pre-connect mysql if it will be needed in the loop. 42 | if($usedatabase==1) { 43 | $mysql = mysqli_connect($dbhost, $dbusername, $dbpassword, $dbdatabase); //Connect MySQL 44 | 45 | if (!$mysql) //Check for errors 46 | { 47 | die("Connection Error: " . mysqli_connect_error()); //Return error 48 | } 49 | } 50 | 51 | $dataTData = cURL($url, $header_data); //Decode the JSON returned by the CW API. 52 | 53 | $prioritystatuses = explode("|",$prioritystatus); 54 | $priorities = explode("|",$prioritylist); 55 | 56 | foreach($dataTData as $entry) 57 | { 58 | $user = $entry->member->identifier; 59 | $username = $entry->member->name; 60 | $ticketnumber = $entry->objectId; 61 | 62 | $urlticketdata = $connectwise . "/$connectwisebranch/apis/3.0/service/tickets/" . $ticketnumber; //Set ticket API url 63 | $entryTData = cURL($urlticketdata, $header_data); //Decode the JSON returned by the CW API. 64 | 65 | $summary = $entryTData->summary; 66 | $company = $entryTData->company->name; 67 | 68 | if($debugmode) 69 | { 70 | echo "\nDEBUG: Ticket #" . $ticketnumber . " of user " . $user; 71 | } 72 | 73 | if(!in_array($entryTData->priority->name,$priorities)) 74 | { 75 | // Priority of found ticket is not one of the ones we're looking for, exit loop 76 | if($debugmode) 77 | { 78 | echo "\nDEBUG: Breaking at line 73, priority (" . $entryTData->priority->name . ") is not in the list: " . implode(", ", $priorities); 79 | } 80 | continue; 81 | } 82 | 83 | if(!in_array($entryTData->status->name,$prioritystatuses)) 84 | { 85 | // Status of found ticket is not one of the ones we're looking for, exit loop 86 | if($debugmode) 87 | { 88 | echo "\nDEBUG: Breaking at line 73, status (" . $entryTData->status->name . ") is not in the list: " . implode(", ", $prioritystatuses); 89 | } 90 | continue; 91 | } 92 | 93 | if($debugmode) 94 | { 95 | echo "\nDEBUG: Passed status and priority validation"; 96 | } 97 | 98 | //Username mapping code 99 | if($usedatabase==1) 100 | { 101 | $val1 = mysqli_real_escape_string($mysql,$user); 102 | $sql = "SELECT * FROM `usermap` WHERE `cwname`=\"" . $val1 . "\""; //SQL Query to select all ticket number entries 103 | 104 | $result = mysqli_query($mysql, $sql); //Run result 105 | $rowcount = mysqli_num_rows($result); 106 | if($rowcount > 1) //If there were too many rows matching query 107 | { 108 | die("Error: too many users somehow?"); //This should NEVER happen. 109 | } 110 | else if ($rowcount == 1) //If exactly 1 row was found. 111 | { 112 | $row = mysqli_fetch_assoc($result); //Row association. 113 | 114 | $user = $row["slackuser"]; //Return the slack username portion of the found row as the $user variable to be used as part of the notification. 115 | } 116 | //If no rows are found here, then it just uses whatever if found as $user previously from the ticket. 117 | } 118 | 119 | $utc = new DateTimeZone("UTC"); 120 | $realtz = new DateTimeZone($timezone); 121 | $datenow = new DateTime("now", $utc); 122 | $dateticket = new DateTime($entry->dateStart, $utc); 123 | $dateticket->setTimezone($realtz); 124 | $dateticketformat = $dateticket->format("g:i A"); 125 | 126 | if($posttousers==1) //And user post is on. 127 | { 128 | $postfieldspre = array( 129 | "channel"=>"@".$user, 130 | "attachments"=>array(array( 131 | "fallback" => "Priority ticket with " . $company . " has been missed.", 132 | "title" => "<" . $ticketurl . $ticketnumber . "&companyName=" . $companyname . "|#" . $ticketnumber . ">: " . $summary, 133 | "pretext" => "Priority ticket missed.", 134 | "text" => "You have a " . $entryTData->priority->name . " priority ticket with " . $company . " that was scheduled for " . $dateticketformat . ". You should be calling the client now.", 135 | "mrkdwn_in" => array( 136 | "text", 137 | "pretext" 138 | ) 139 | )) 140 | ); 141 | 142 | cURLPost($webhookurl, $header_data2, "POST", $postfieldspre); 143 | } 144 | if($posttochan==1) //If channel post is on 145 | { 146 | if($usetimechan==1) 147 | { 148 | $postfieldspre = array( 149 | "channel"=>$timechan, //Post to channel set in config.php 150 | "attachments"=>array(array( 151 | "fallback" => "Priority ticket for " . $username . " with " . $company . " has been missed.", 152 | "title" => "<" . $ticketurl . $ticketnumber . "&companyName=" . $companyname . "|#" . $ticketnumber . ">: " . $summary, 153 | "pretext" => "Priority ticket missed by $username.", 154 | "text" => "Please remind the technician that they have a " . $entryTData->priority->name . " priority ticket with " . $company . " that was scheduled for " . $dateticketformat, 155 | "mrkdwn_in" => array( 156 | "text", 157 | "pretext" 158 | ) 159 | )) 160 | ); 161 | } 162 | else 163 | { 164 | $postfieldspre = array( 165 | "channel"=>$firmalertchan, //Post to channel set in config.php 166 | "attachments"=>array(array( 167 | "fallback" => "Priority ticket for " . $username . " with " . $company . " has been missed.", 168 | "title" => "<" . $ticketurl . $ticketnumber . "&companyName=" . $companyname . "|#" . $ticketnumber . ">: " . $summary, 169 | "pretext" => "Priority ticket missed by $username.", 170 | "text" => "Please remind the technician that they have a " . $entryTData->priority->name . " priority ticket with " . $company . " that was scheduled for " . $dateticketformat, 171 | "mrkdwn_in" => array( 172 | "text", 173 | "pretext" 174 | ) 175 | )) 176 | ); 177 | } 178 | 179 | cURLPost($webhookurl, $header_data2, "POST", $postfieldspre); 180 | } 181 | } -------------------------------------------------------------------------------- /cwslack-stats.php: -------------------------------------------------------------------------------- 1 | "full", "response_type" => "ephemeral","text" => "Connection Error: " . mysqli_connect_error())); 16 | } else { 17 | die("Connection Error: " . mysqli_connect_error()); //Return properly encoded arrays in JSON for Slack parsing. 18 | } 19 | die(); 20 | } 21 | 22 | $sql = "CREATE TABLE IF NOT EXISTS stats (user VARCHAR(50) NOT NULL PRIMARY KEY, lastcommand VARCHAR(10), lastused DATETIME)"; 23 | if (mysqli_query($mysql, $sql)) { 24 | //Table created successfully 25 | } else { 26 | echo "stats Table Creation Error: " . mysqli_error($mysql); 27 | } 28 | $statscommand = substr($_REQUEST['command'],1); 29 | $statsuser = $_REQUEST['user_name']; 30 | 31 | $sql = "SELECT ".$statscommand." FROM stats"; //SQL Query to insert new map 32 | 33 | if(!mysqli_query($mysql,$sql)) 34 | { 35 | 36 | $sql = "ALTER TABLE `stats` ADD COLUMN " . $statscommand . " INT DEFAULT 0"; 37 | if(mysqli_query($mysql,$sql)) 38 | { 39 | // Inserted user 40 | } 41 | else 42 | { 43 | die("MySQL Error: " . mysqli_error($mysql)); 44 | } 45 | } 46 | $statsdate = date("Y-m-d H:i:s"); 47 | $sql = "SELECT * FROM `stats` WHERE `user` = '" . $statsuser . "'"; //SQL Query to select all users 48 | 49 | $result = mysqli_query($mysql, $sql); //Run result 50 | $rowcount = mysqli_num_rows($result); 51 | if($rowcount > 1) //If there were too many rows matching query 52 | { 53 | die("Error: too many users somehow?"); //This should NEVER happen. 54 | } 55 | else if ($rowcount == 1) //If exactly 1 row is found. 56 | { 57 | $statsuserdata = mysqli_fetch_assoc($result); 58 | $commandcount = $statsuserdata[$statscommand] + 1; 59 | $sql = "UPDATE `stats` SET lastcommand = '" . $statscommand . "', lastused = '" . $statsdate . "', " . $statscommand ." = '" . $commandcount . "' WHERE `user` = '" . $statsuser . "'"; //SQL Query to insert new map 60 | if(mysqli_query($mysql,$sql)) 61 | { 62 | // Inserted user 63 | } 64 | else 65 | { 66 | die("MySQL Error: " . mysqli_error($mysql)); 67 | } 68 | } 69 | else 70 | { 71 | $sql = "INSERT INTO `stats` (`user`, `lastcommand`, `lastused`, `" . $statscommand . "`) VALUES ('" . $statsuser . "', '" . $statscommand . "', '" . $statsdate . "', 1);"; //SQL Query to insert new map 72 | if(mysqli_query($mysql,$sql)) 73 | { 74 | // Inserted user 75 | } 76 | else 77 | { 78 | die("MySQL Error: " . mysqli_error($mysql)); 79 | } 80 | } 81 | 82 | 83 | ?> -------------------------------------------------------------------------------- /cwslack-tasks.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | 21 | ini_set('display_errors', 1); //Display errors in case something occurs 22 | header('Content-Type: application/json'); //Set the header to return JSON, required by Slack 23 | require_once 'config.php'; 24 | require_once 'functions.php'; 25 | 26 | if(empty($_REQUEST['token']) || ($_REQUEST['token'] != $slacktaskstoken)) die("Slack token invalid."); //If Slack token is not correct, kill the connection. This allows only Slack to access the page for security purposes. 27 | if(empty($_REQUEST['text'])) die("No text provided."); //If there is no text added, kill the connection. 28 | 29 | $exploded = explode(" ",$_REQUEST['text']); //Explode the string attached to the slash command for use in variables. 30 | 31 | //This section checks if the ticket number is not equal to 6 digits (our tickets are in the hundreds of thousands but not near a million yet) and kills the connection if it's not. 32 | if(!is_numeric($exploded[0])) { 33 | //Check to see if the first command in the text array is actually help, if so redirect to help webpage detailing slash command use. 34 | if ($exploded[0]=="help") { 35 | die(json_encode(array("parse" => "full", "response_type" => "in_channel","text" => "Please visit " . $helpurl . " for more help information","mrkdwn"=>true))); //Encode a JSON response with a help URL. 36 | } 37 | else //Else close the connection. 38 | { 39 | die("Unknown entry for ticket number. Please use [ticket number] [list/update/complete/open/new] [task number]"); 40 | } 41 | } 42 | 43 | //Timeout Fix Block 44 | if($timeoutfix == true) 45 | { 46 | ob_end_clean(); 47 | header("Connection: close"); 48 | ob_start(); 49 | echo ('{"response_type": "in_channel"}'); 50 | $size = ob_get_length(); 51 | header("Content-Length: $size"); 52 | ob_end_flush(); 53 | flush(); 54 | session_write_close(); 55 | if($sendtimeoutwait==true) { 56 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral", "text" => "Please wait...")); 57 | } 58 | } 59 | //End timeout fix block 60 | 61 | //Set NULL Variables 62 | $ticketnumber = $exploded[0]; 63 | $command = NULL; 64 | $task = NULL; 65 | $sentence = NULL; 66 | 67 | // Authorization array. Auto encodes API key for auhtorization above. 68 | $header_data = authHeader($companyname, $apipublickey, $apiprivatekey); 69 | // Authorization array, with extra json content-type used in patch commands to change tickets. 70 | $header_data2 = postHeader($companyname, $apipublickey, $apiprivatekey); 71 | 72 | //Set URL 73 | $taskurl = $connectwise . "/$connectwisebranch/apis/3.0/service/tickets/" . $ticketnumber . "/tasks"; 74 | 75 | if (array_key_exists(1, $exploded)) //If a second string exists in the slash command array, make it the command. 76 | { 77 | $command = $exploded[1]; 78 | if ($command!="new"&&$command!="add"&&array_key_exists(2, $exploded)) //If a third string exists in the slash command array, make it the task number. 79 | { 80 | $task = $exploded[2]; 81 | } 82 | if($command=="new"||$command=="add") 83 | { 84 | if (array_key_exists(2, $exploded)) //If a third string exists in the slash command array, make it the sentence for notes. 85 | { 86 | unset($exploded[0]); 87 | unset($exploded[1]); 88 | $sentence = implode(" ", $exploded); //Set the sentence 89 | } 90 | } 91 | else 92 | { 93 | if (array_key_exists(3, $exploded)) //If a fourth string exists in the slash command array, make it the sentence for notes. 94 | { 95 | unset($exploded[0]); 96 | unset($exploded[1]); 97 | unset($exploded[2]); 98 | $sentence = implode(" ", $exploded); //Set the sentence 99 | } 100 | } 101 | } 102 | else 103 | { 104 | if ($timeoutfix == true) { 105 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral","text" => "Please use [ticket number] [list/update/complete/open/new] [task number]")); 106 | } else { 107 | die("Please use [ticket number] [list/update/complete/open/new] [task number]"); //Post to slack 108 | } 109 | die(); 110 | } 111 | 112 | if($command=="list") 113 | { 114 | $output = ""; 115 | $taskdata = cURL($taskurl, $header_data); // Get the JSON returned by the CW API for $taskurl. 116 | if(empty($taskdata)) 117 | { 118 | if ($timeoutfix == true) { 119 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral","text" => "No tasks found on ticket #".$ticketnumber)); 120 | } else { 121 | die("No tasks found on ticket #".$ticketnumber); //Post to slack 122 | } 123 | die(); 124 | } 125 | foreach($taskdata as $t) 126 | { 127 | $output = $output . "Task #" . $t->priority. " | Status: " . ($t->closedFlag ? "*Done*" : "*Open*") . ":\n" . $t->notes . "\n"; 128 | } 129 | 130 | $return =array( 131 | "parse" => "full", 132 | "response_type" => "ephemeral", 133 | "attachments"=>array(array( 134 | "fallback" => "Tasks for Ticket #" . $ticketnumber, //Fallback for notifications 135 | "title" => "Task ID | Status | Notes", 136 | "pretext" => "Tasks for Ticket #" . $ticketnumber, //Return info string with ticket number. 137 | "text" => $output, 138 | "mrkdwn_in" => array( 139 | "text", 140 | "pretext" 141 | ) 142 | )) 143 | ); 144 | 145 | if ($timeoutfix == true) { 146 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", $return); 147 | } else { 148 | die(json_encode($return, JSON_PRETTY_PRINT)); //Return properly encoded arrays in JSON for Slack parsing. 149 | } 150 | } 151 | else if ($command=="open"||$command=="reopen") 152 | { 153 | $taskid=NULL; //Set ID to NULL for later. 154 | 155 | $taskdata = cURL($taskurl, $header_data); // Get the JSON returned by the CW API for $taskurl. 156 | if(empty($taskdata)) 157 | { 158 | if ($timeoutfix == true) { 159 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral","text" => "No tasks found on ticket #".$ticketnumber)); 160 | } else { 161 | die("No tasks found on ticket #".$ticketnumber); //Post to slack 162 | } 163 | die(); 164 | } 165 | foreach($taskdata as $t) 166 | { 167 | if($t->priority==$task) 168 | { 169 | $taskid = $t->id; 170 | if($t->closedFlag==false) 171 | { 172 | if ($timeoutfix == true) { 173 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral","text" => "Task #" .$task . " is already open.")); 174 | } else { 175 | die("Task #" .$task . " is already open."); //Post to slack 176 | } 177 | die(); 178 | } 179 | } 180 | } 181 | if($taskid==NULL) 182 | { 183 | if ($timeoutfix == true) { 184 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral","text" => "Task #" . $task . " not found on Ticket #" . $ticketnumber . ".")); 185 | } else { 186 | die("Task #" . $task . " not found on Ticket #" . $ticketnumber . "."); //Post to slack 187 | } 188 | die(); 189 | } 190 | 191 | $taskpatch = $taskurl . "/" . $taskid; 192 | 193 | $dataTCmd = cURLPost( 194 | $taskpatch, 195 | $header_data2, 196 | "PATCH", 197 | array(array("op" => "replace", "path" => "/closedFlag", "value" => false)) 198 | ); 199 | 200 | if ($timeoutfix == true) { 201 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral","text" => "Task #" . $task . " has been marked open.")); 202 | } else { 203 | die("Task #" . $task . " has been marked open."); //Post to slack 204 | } 205 | die(); 206 | } 207 | else if ($command=="close"||$command=="complete"||$command=="done"||$command=="completed") 208 | { 209 | $taskid=NULL; //Set ID to NULL for later. 210 | 211 | $taskdata = cURL($taskurl, $header_data); // Get the JSON returned by the CW API for $taskurl. 212 | if(empty($taskdata)) 213 | { 214 | if ($timeoutfix == true) { 215 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral","text" => "No tasks found on ticket #".$ticketnumber)); 216 | } else { 217 | die("No tasks found on ticket #".$ticketnumber); //Post to slack 218 | } 219 | die(); 220 | } 221 | foreach($taskdata as $t) 222 | { 223 | if($t->priority==$task) 224 | { 225 | $taskid = $t->id; 226 | if($t->closedFlag==true) 227 | { 228 | if ($timeoutfix == true) { 229 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral","text" => "Task #" .$task . " is already marked done.")); 230 | } else { 231 | die("Task #" .$task . " is already marked done."); //Post to slack 232 | } 233 | die(); 234 | } 235 | } 236 | } 237 | if($taskid==NULL) 238 | { 239 | if ($timeoutfix == true) { 240 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral","text" => "Task #" . $task . " not found on Ticket #" . $ticketnumber . ".")); 241 | } else { 242 | die("Task #" . $task . " not found on Ticket #" . $ticketnumber . "."); //Post to slack 243 | } 244 | die(); 245 | } 246 | 247 | $taskpatch = $taskurl . "/" . $taskid; 248 | 249 | $dataTCmd = cURLPost( 250 | $taskpatch, 251 | $header_data2, 252 | "PATCH", 253 | array(array("op" => "replace", "path" => "/closedFlag", "value" => true)) 254 | ); 255 | if($sentence != NULL) { 256 | $dataTCmd = cURLPost( 257 | $taskpatch, 258 | $header_data2, 259 | "PATCH", 260 | array(array("op" => "replace", "path" => "/resolution", "value" => $sentence)) 261 | ); 262 | 263 | if ($timeoutfix == true) { 264 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral","text" => "Task #" . $task . " has been marked completed with resolution note: " . $sentence)); 265 | } else { 266 | die("Task #" . $task . " has been marked completed with resolution note: " . $sentence); //Post to slack 267 | } 268 | die(); 269 | } 270 | else 271 | { 272 | if ($timeoutfix == true) { 273 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral","text" => "Task #" . $task . " has been marked completed.")); 274 | } else { 275 | die("Task #" . $task . " has been marked completed."); //Post to slack 276 | } 277 | die(); 278 | } 279 | } 280 | else if ($command=="update"||$command=="change"||$command=="note") 281 | { 282 | $taskid=NULL; //Set ID to NULL for later. 283 | 284 | $taskdata = cURL($taskurl, $header_data); // Get the JSON returned by the CW API for $taskurl. 285 | if(empty($taskdata)) 286 | { 287 | if ($timeoutfix == true) { 288 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral","text" => "No tasks found on ticket #".$ticketnumber)); 289 | } else { 290 | die("No tasks found on ticket #".$ticketnumber); //Post to slack 291 | } 292 | die(); 293 | } 294 | foreach($taskdata as $t) 295 | { 296 | if($t->priority==$task) 297 | { 298 | $taskid = $t->id; 299 | if($t->closedFlag==true) 300 | { 301 | if ($timeoutfix == true) { 302 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral","text" => "Task #" .$task . " is already marked done.")); 303 | } else { 304 | die("Task #" .$task . " is already marked done."); //Post to slack 305 | } 306 | die(); 307 | } 308 | } 309 | } 310 | if($taskid==NULL) 311 | { 312 | if ($timeoutfix == true) { 313 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral","text" => "Task #" . $task . " not found on Ticket #" . $ticketnumber . ".")); 314 | } else { 315 | die("Task #" . $task . " not found on Ticket #" . $ticketnumber . "."); //Post to slack 316 | } 317 | die(); 318 | } 319 | 320 | $taskpatch = $taskurl . "/" . $taskid; 321 | 322 | if($sentence != NULL) { 323 | $dataTCmd = cURLPost( 324 | $taskpatch, 325 | $header_data2, 326 | "PATCH", 327 | array(array("op" => "replace", "path" => "/notes", "value" => $sentence)) 328 | ); 329 | 330 | if ($timeoutfix == true) { 331 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral","text" => "Task #" . $task . " has been updated with note: " . $sentence)); 332 | } else { 333 | die("Task #" . $task . " has been updated with note: " . $sentence); //Post to slack 334 | } 335 | die(); 336 | } 337 | else 338 | { 339 | if ($timeoutfix == true) { 340 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral","text" => "No note provided for update.")); 341 | } else { 342 | die("No note provided for update."); //Post to slack 343 | } 344 | die(); 345 | } 346 | } 347 | else if ($command=="new"||$command=="add") 348 | { 349 | $priority = 1; 350 | $taskdata = cURL($taskurl, $header_data); // Get the JSON returned by the CW API for $taskurl. 351 | if(empty($taskdata)) 352 | { 353 | //Do nothing. 354 | } 355 | else 356 | { 357 | $priority = sizeof($taskdata) + 1; 358 | } 359 | if($sentence != NULL) { 360 | $dataTCmd = cURLPost( 361 | $taskurl, 362 | $header_data2, 363 | "POST", 364 | array("notes"=>$sentence,"priority"=>$priority) 365 | ); 366 | 367 | if ($timeoutfix == true) { 368 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral","text" => "A new task has been created with note: " . $sentence)); 369 | } else { 370 | die("A new task has been created with note: " . $sentence); //Post to slack 371 | } 372 | die(); 373 | } 374 | else 375 | { 376 | if ($timeoutfix == true) { 377 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral","text" => "No note provided for new task.")); 378 | } else { 379 | die("No note provided for new task."); //Post to slack 380 | } 381 | die(); 382 | } 383 | } 384 | else 385 | { 386 | if ($timeoutfix == true) { 387 | cURLPost($_REQUEST["response_url"], array("Content-Type: application/json"), "POST", array("parse" => "full", "response_type" => "ephemeral","text" => "Unknown command. Please use [ticket number] [list/update/complete/open/new] [task number]")); 388 | } else { 389 | die("Unknown command. Please use [ticket number] [list/update/complete/open/new] [task number]"); //Post to slack 390 | } 391 | die(); 392 | } 393 | 394 | 395 | 396 | ?> -------------------------------------------------------------------------------- /cwslack-timealerts.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | //Receive connector for Connectwise Callbacks 21 | ini_set('display_errors', 1); //Display errors in case something occurs 22 | header('Content-Type: application/json'); //Set the header to return JSON, required by Slack 23 | require_once 'config.php'; //Require the config file. 24 | require_once 'functions.php'; 25 | 26 | //Set headers for cURL requests. $header_data covers API authentication while $header_data2 covers the Slack output. 27 | $header_data = authHeader($companyname, $apipublickey, $apiprivatekey); 28 | 29 | $header_data2 =array( 30 | "Content-Type: application/json" 31 | ); 32 | 33 | 34 | $datetoday = date("Y-m-d"); 35 | $timeurl = $connectwise . "/$connectwisebranch/apis/3.0/time/entries"; 36 | $filterurl = $timeurl . "?conditions=timeStart%20%3C%20[" . $datetoday . "T23:59:59Z]%20and%20timeStart%20%3E%20[" . $datetoday . "T00:00:00Z]&orderBy=dateEntered%20desc&pagesize=1000"; 37 | 38 | $data = cURL($filterurl, $header_data); 39 | 40 | if ($data == NULL) 41 | { 42 | die("No users have recorded time information for today."); 43 | } 44 | 45 | $timeset = array(); 46 | $users = array(); 47 | 48 | foreach($data as $entry) 49 | { 50 | $name = $entry->member->identifier; 51 | if(array_key_exists($name,$timeset)) 52 | { 53 | $timeset[$entry->member->identifier]["totaltime"] = $timeset[$entry->member->identifier]["totaltime"] + $entry->actualHours; 54 | } 55 | else 56 | { 57 | $timeset[$entry->member->identifier] = null; 58 | $timeset[$entry->member->identifier]["totaltime"] = $entry->actualHours; 59 | } 60 | } 61 | 62 | $blockedtime = explode("|",$notimeusers); 63 | $specialusers = array(); 64 | foreach(explode("|",$specialtimeusers) as $user) 65 | { 66 | $tempval = explode(",", $user); 67 | $specialusers[strtolower($tempval[0])] = $tempval[1]; 68 | } 69 | foreach($timeset as $user => $val) 70 | { 71 | 72 | if(array_key_exists(strtolower($user),$specialusers)) 73 | { 74 | $specialtimes = explode("-",$specialusers[$user]); 75 | $expectedtime = round((strtotime("now") - strtotime($specialtimes[0])) / 3600,2); 76 | if ($expectedtime > (round((strtotime($specialtimes[1]) - strtotime($specialtimes[0])) / 3600,2))) 77 | { 78 | $expectedtime = round((strtotime($specialtimes[1]) - strtotime($specialtimes[0])) / 3600,2); 79 | } 80 | } 81 | else 82 | { 83 | $expectedtime = round((strtotime("now") - strtotime($timebusinessstart)) / 3600,2); 84 | 85 | if ($expectedtime > (round((strtotime($timebusinessclose) - strtotime($timebusinessstart)) / 3600,2))) 86 | { 87 | $expectedtime = round((strtotime($timebusinessclose) - strtotime($timebusinessstart)) / 3600,2); 88 | } 89 | } 90 | 91 | if($expectedtime - $val["totaltime"] >= 2 && !in_array(strtolower($user),array_map("strtolower",$blockedtime))) 92 | { 93 | $username = $user; 94 | //Username mapping code 95 | if($usedatabase==1) 96 | { 97 | $mysql = mysqli_connect($dbhost, $dbusername, $dbpassword, $dbdatabase); //Connect MySQL 98 | 99 | if (!$mysql) //Check for errors 100 | { 101 | die("Connection Error: " . mysqli_connect_error()); //Return error 102 | } 103 | 104 | $val1 = mysqli_real_escape_string($mysql,$username); 105 | $sql = "SELECT * FROM `usermap` WHERE `cwname`=\"" . $val1 . "\""; //SQL Query to select all ticket number entries 106 | 107 | $result = mysqli_query($mysql, $sql); //Run result 108 | $rowcount = mysqli_num_rows($result); 109 | if($rowcount > 1) //If there were too many rows matching query 110 | { 111 | die("Error: too many users somehow?"); //This should NEVER happen. 112 | } 113 | else if ($rowcount == 1) //If exactly 1 row was found. 114 | { 115 | $row = mysqli_fetch_assoc($result); //Row association. 116 | 117 | $username = $row["slackuser"]; //Return the slack username portion of the found row as the $user variable to be used as part of the notification. 118 | } 119 | //If no rows are found here, then it just uses whatever if found as $user previously from the ticket. 120 | } 121 | $users[$username] = $user; 122 | } 123 | } 124 | 125 | foreach($users as $user => $val) 126 | { 127 | $ontheclock = true; 128 | 129 | if(array_key_exists(strtolower($user),$specialusers)) 130 | { 131 | $specialtimes = explode("-",$specialusers[$user]); 132 | $expectedtime = round((strtotime("now") - strtotime($specialtimes[0])) / 3600,2); 133 | 134 | if ($expectedtime > (round((strtotime($specialtimes[1]) - strtotime($specialtimes[0])) / 3600,2))) 135 | { 136 | $expectedtime = round((strtotime($specialtimes[1]) - strtotime($specialtimes[0])) / 3600,2); 137 | } 138 | 139 | if(strtotime("now") < strtotime($specialtimes[0]) || strtotime("now") > strtotime($specialtimes[1])) 140 | { 141 | $ontheclock = false; 142 | } 143 | } 144 | else 145 | { 146 | $expectedtime = round((strtotime("now") - strtotime($timebusinessstart)) / 3600,2); 147 | 148 | if ($expectedtime > (round((strtotime($timebusinessclose) - strtotime($timebusinessstart)) / 3600,2))) 149 | { 150 | $expectedtime = round((strtotime($timebusinessclose) - strtotime($timebusinessstart)) / 3600,2); 151 | } 152 | 153 | if(strtotime("now") < strtotime($timebusinessstart) || strtotime("now") > strtotime($timebusinessclose)) 154 | { 155 | $ontheclock = false; 156 | } 157 | } 158 | 159 | $missingtime = $expectedtime - $timeset[$val]["totaltime"]; 160 | 161 | if($posttousers==1) 162 | { 163 | $postfieldspre = array( 164 | "channel"=>"@".$user, 165 | "attachments"=>array(array( 166 | "fallback" => "Time is too far behind!", 167 | "title" => "Current hours: " . $timeset[$val]["totaltime"], 168 | "pretext" => "Your time is over 2 hours behind at this point", 169 | "text" => "Please update your time immediately as you have " . $missingtime . " hours to make up.", 170 | "mrkdwn_in" => array( 171 | "text", 172 | "pretext" 173 | ) 174 | )) 175 | ); 176 | 177 | if($ontheclock) 178 | { 179 | cURLPost($webhookurl, $header_data2, "POST", $postfieldspre); 180 | } 181 | } 182 | 183 | if($posttochan==1) //If channel post is on 184 | { 185 | if($usetimechan==1) 186 | { 187 | $postfieldspre = array( 188 | "channel"=>$timechan, //Post to channel set in config.php 189 | "attachments"=>array(array( 190 | "fallback" => "Time is too far behind for " . $user, 191 | "title" => "Current hours for " . $val . ": " . $timeset[$val]["totaltime"], 192 | "pretext" => "Their time is over 2 hours behind at this point", 193 | "text" => "Please have technician update their time immediately as they have " . $missingtime . " hours to make up.", 194 | "mrkdwn_in" => array( 195 | "text", 196 | "pretext" 197 | ) 198 | )) 199 | ); 200 | } 201 | else 202 | { 203 | $postfieldspre = array( 204 | "channel"=>$firmalertchan, 205 | "attachments"=>array(array( 206 | "fallback" => "Time is too far behind for " . $user, 207 | "title" => "Current hours for " . $val . ": " . $timeset[$val]["totaltime"], 208 | "pretext" => "Their time is over 2 hours behind at this point", 209 | "text" => "Please have technician update their time immediately as they have " . $missingtime . " hours to make up.", 210 | "mrkdwn_in" => array( 211 | "text", 212 | "pretext" 213 | ) 214 | )) 215 | ); 216 | } 217 | 218 | if($ontheclock) 219 | { 220 | cURLPost($webhookurl, $header_data2, "POST", $postfieldspre); 221 | } 222 | } 223 | 224 | } 225 | 226 | echo "OK"; 227 | 228 | ?> -------------------------------------------------------------------------------- /functions.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | // This file contains support functions for the CWSlack stack. 21 | // Do not modify unless you know what you're doing. 22 | 23 | /** 24 | * @param $url 25 | * @param $header 26 | * @return mixed 27 | */ 28 | 29 | $globalVersion = "2019.2"; 30 | 31 | function cURL($url, $header) 32 | { 33 | global $debugmode; //Require global variable $debugmode from config.php. 34 | $ch = curl_init(); //Initiate a curl session 35 | 36 | //Create curl array to set the API url, headers, and necessary flags. 37 | $curlOpts = array( 38 | CURLOPT_URL => $url, //URL to send the curl request to 39 | CURLOPT_RETURNTRANSFER => true, //Request data returned instead of output 40 | CURLOPT_HTTPHEADER => $header, //Header to include, mainly for authorization purposes 41 | CURLOPT_FOLLOWLOCATION => true, //Follow 301/302 redirects 42 | CURLOPT_HEADER => 1, //Use header 43 | ); 44 | curl_setopt_array($ch, $curlOpts); //Set the curl array to $curlOpts 45 | 46 | $answerTData = curl_exec($ch); //Set $answerTData to the curl response to the API. 47 | $headerLen = curl_getinfo($ch, CURLINFO_HEADER_SIZE); //Get the header length of the curl response 48 | $curlBodyTData = substr($answerTData, $headerLen); //Remove header data from the curl string. 49 | if($debugmode) //If the global $debugmode variable is set to true 50 | { 51 | var_dump($answerTData); //Dump the raw data. 52 | } 53 | // If there was an error, show it 54 | if (curl_error($ch)) { 55 | die(curl_error($ch)); 56 | } 57 | curl_close($ch); //Close the curl connection for cleanup. 58 | 59 | $jsonDecode = json_decode($curlBodyTData); //Decode the JSON returned by the CW API. 60 | 61 | if(is_array($jsonDecode) && array_key_exists("code",$jsonDecode)) { //Check if array contains error code 62 | if($jsonDecode->code == "NotFound") { //If error code is NotFound 63 | die("Connectwise record was not found."); //Report that the ticket was not found. 64 | } 65 | if($jsonDecode->code == "Unauthorized") { //If error code is an authorization error 66 | die("401 Unauthorized, check API key to ensure it is valid."); //Fail case. 67 | } 68 | else { //Else other error 69 | die("Unknown Error Occurred, check API key and other API settings. Error " . $jsonDecode->code . ": " . $jsonDecode->message); //Fail case, including the message and code output from connectwise. 70 | } 71 | } 72 | if(is_array($jsonDecode) && array_key_exists("errors",$jsonDecode)) //If connectwise returned an error. 73 | { 74 | $errors = $jsonDecode->errors; //Make array easier to access. 75 | 76 | die("ConnectWise Error: " . $errors[0]->message); //Return CW error 77 | } 78 | 79 | return $jsonDecode; //Return the decoded output. 80 | } 81 | 82 | /** 83 | * @param $url 84 | * @param $header 85 | * @param $postfieldspre 86 | * @return mixed 87 | */ 88 | function cURLPost($url, $header, $request, $postfieldspre) 89 | { 90 | global $debugmode; //Require global variable $debugmode from config.php 91 | $ch = curl_init(); //Initiate a curl session 92 | 93 | $postfields = json_encode($postfieldspre); //Format the array as JSON 94 | 95 | //Same as previous curl array but includes required information for PATCH commands. 96 | $curlOpts = array( 97 | CURLOPT_URL => $url, 98 | CURLOPT_RETURNTRANSFER => true, 99 | CURLOPT_HTTPHEADER => $header, 100 | CURLOPT_FOLLOWLOCATION => true, 101 | CURLOPT_CUSTOMREQUEST => $request, 102 | CURLOPT_POSTFIELDS => $postfields, 103 | CURLOPT_POST => 1, 104 | CURLOPT_HEADER => 1, 105 | ); 106 | curl_setopt_array($ch, $curlOpts); 107 | 108 | $answerTCmd = curl_exec($ch); 109 | $headerLen = curl_getinfo($ch, CURLINFO_HEADER_SIZE); 110 | $curlBodyTCmd = substr($answerTCmd, $headerLen); 111 | 112 | if($debugmode) 113 | { 114 | var_dump($answerTCmd); 115 | } 116 | 117 | // If there was an error, show it 118 | if (curl_error($ch)) { 119 | die(curl_error($ch)); 120 | } 121 | curl_close($ch); 122 | if($curlBodyTCmd == "ok") //Slack catch 123 | { 124 | return null; 125 | } 126 | $jsonDecode = json_decode($curlBodyTCmd); //Decode the JSON returned by the CW API. 127 | 128 | if(is_array($jsonDecode) && array_key_exists("code",$jsonDecode)) { //Check if array contains error code 129 | if($jsonDecode->code == "NotFound") { //If error code is NotFound 130 | die("Connectwise record was not found."); //Report that the ticket was not found. 131 | } 132 | else if($jsonDecode->code == "Unauthorized") { //If error code is an authorization error 133 | die("401 Unauthorized, check API key to ensure it is valid."); //Fail case. 134 | } 135 | else if($jsonDecode->code == "InvalidObject" && $jsonDecode->message == "schedule object is invalid") 136 | { 137 | die("Error: " . $jsonDecode->errors->message . " - Please check to ensure you have this user mapped in the database using /dbm, as their name most likely differs between Slack and ConnectWise"); 138 | } 139 | else if($jsonDecode->code == NULL) 140 | { 141 | //do nothing. 142 | } 143 | else { 144 | die("Unknown Error Occurred, check API key and other API settings. Error " . $jsonDecode->code . ": " . $jsonDecode->message); //Fail case. 145 | } 146 | } 147 | if(is_array($jsonDecode) && array_key_exists("errors",$jsonDecode)) //If connectwise returned an error. 148 | { 149 | $errors = $jsonDecode->errors; //Make array easier to access. 150 | 151 | die("ConnectWise Error: " . $errors[0]->message); //Return CW error 152 | } 153 | 154 | return $jsonDecode; 155 | } 156 | 157 | function authHeader($company, $publickey, $privatekey) 158 | { 159 | global $cwClientId; 160 | global $globalVersion; 161 | 162 | $apicompanyname = strtolower($company); //Company name all lower case for api auth. 163 | $authorization = base64_encode($apicompanyname . "+" . $publickey . ":" . $privatekey); //Encode the API, needed for authorization. 164 | 165 | return array("Authorization: Basic ". $authorization, 166 | "clientId: " . $cwClientId, 167 | "Accept: application/vnd.connectwise.com+json; version=" . $globalVersion); 168 | } 169 | 170 | function postHeader($company, $publickey, $privatekey) 171 | { 172 | global $cwClientId; 173 | global $globalVersion; 174 | 175 | $apicompanyname = strtolower($company); //Company name all lower case for api auth. 176 | $authorization = base64_encode($apicompanyname . "+" . $publickey . ":" . $privatekey); //Encode the API, needed for authorization. 177 | 178 | return array( 179 | "Authorization: Basic " . $authorization, 180 | "clientId: " . $cwClientId, 181 | "Accept: application/vnd.connectwise.com+json; version=" . $globalVersion, 182 | "Content-Type: application/json" 183 | ); 184 | } 185 | 186 | ?> -------------------------------------------------------------------------------- /updates/update-2.0-to-2.1.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | ini_set('display_errors', 1); //Display errors in case something occurs 21 | ?> 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | CWSlack Updater 33 | 34 | 35 |
36 |
37 |

CWSlack-SlashCommands Updater

38 | Settings Configuration

"; 42 | echo "
Any field left blank will not change the setting, however yes/no questions must be updated any time you save this.
"; 43 | echo "
44 |

General

45 |

Tokens

46 |
Set each of these to the respective Slack Token that you've setup. Leave blank if you do not need them.
47 |

48 |

49 |

Time Module

50 |
51 |
52 |
53 | 54 |

"; 55 | echo "
"; 56 | //Template for future use 57 | //
Yes No
58 | //
59 | die(); 60 | } 61 | if ($_GET["page"] == "Save Settings") { 62 | $filedata = file('config.php'); 63 | $newdata = array(); 64 | $line1 = false; //For later use 65 | 66 | foreach ($filedata as $data) { 67 | if (stristr($data, '$slacktimetoken =')) { 68 | if (!empty($_POST["slacktimetoken"])) { 69 | $newdata[] = '$slacktimetoken = "' . $_POST["slacktimetoken"] . '"; //Set your token for the time slash command' . PHP_EOL; 70 | $line1 = true; 71 | } else { 72 | $newdata[] = $data; 73 | } 74 | } else if (stristr($data, '$slacktaskstoken =')) { 75 | if (!empty($_POST["slacktaskstoken"])) { 76 | $newdata[] = '$slacktaskstoken = "' . $_POST["slacktaskstoken"] . '"; //Set your token for the tasks slash command' . PHP_EOL; 77 | } else { 78 | $newdata[] = $data; 79 | } 80 | }else if (stristr($data, '$timedetailworktype =')) { 81 | if (!empty($_POST["timedetailworktype"])) { 82 | $newdata[] = '$timedetailworktype = "' . $_POST["timedetailworktype"] . '"; //Set to the worktype name you want it to change tickets to when a note is posted to detailed' . PHP_EOL; 83 | } else { 84 | $newdata[] = $data; 85 | } 86 | } else if (stristr($data, '$timeinternalworktype =')) { 87 | if (!empty($_POST["timeinternalworktype"])) { 88 | $newdata[] = '$timeinternalworktype = "' . $_POST["timeinternalworktype"] . '"; //Set to the worktype name you want it to change tickets to when a note is posted to internal' . PHP_EOL; 89 | } else { 90 | $newdata[] = $data; 91 | } 92 | } else if (stristr($data, '$timeresolutionworktype =')) { 93 | if (!empty($_POST["timeresolutionworktype"])) { 94 | $newdata[] = '$timeresolutionworktype = "' . $_POST["timeresolutionworktype"] . '"; //Set to the worktype name you want it to change tickets to when a note is posted to resolution' . PHP_EOL; 95 | } else { 96 | $newdata[] = $data; 97 | } 98 | } else if (stristr($data, '//cwslack-incoming.php') && !$line1) { 99 | $newdata[] = '//cwslack-tasks.php' . PHP_EOL; 100 | $newdata[] = '$slacktaskstoken = "'.$_POST["slacktaskstoken"].'"; //Set your token for the tasks slash command' . PHP_EOL; 101 | $newdata[] = '//cwslack-time.php' . PHP_EOL; 102 | $newdata[] = '$slacktimetoken = "' . $_POST["slacktimetoken"] . '"; //Set your token for the time slash command' . PHP_EOL; 103 | $newdata[] = '$timedetailworktype = "' . $_POST["timedetailworktype"] . '"; //Set to the worktype name you want it to change tickets to when a note is posted to detailed.' . PHP_EOL; 104 | $newdata[] = '$timeinternalworktype = "' . $_POST["timeinternalworktype"] . '"; //Set to the worktype name you want it to change tickets to when a note is posted to internal.' . PHP_EOL; 105 | $newdata[] = '$timeresolutionworktype = "' . $_POST["timeresolutionworktype"] . '"; //Set to the worktype name you want it to change tickets to when a note is posted to resolution.' . PHP_EOL; 106 | $newdata[] = PHP_EOL . $data; 107 | 108 | } else { 109 | $newdata[] = $data; 110 | } 111 | } 112 | 113 | 114 | file_put_contents('config.php', implode('', $newdata)); 115 | echo "
"; 116 | echo "Successfully configured the config.php file! Please test out your commands in Slack and submit any issues you have to GitHub!"; 117 | echo "
Please remove update-2.0-to-2.1.php to avoid people accessing it externally. You can re-add it anytime to configure database or settings again, or just manually edit config.php.
"; 118 | echo ""; 119 | die(); 120 | } 121 | } 122 | 123 | $php_version=phpversion(); 124 | preg_match("#^\d.\d#", phpversion(), $match); 125 | if($match[0]<5) 126 | { 127 | $php_error="Error: PHP version is ".phpversion().", Version 5 or newer is required."; 128 | } 129 | if($match[0]>6) 130 | { 131 | $php_warning="Warning: PHP version is ".phpversion().", Script tested only on Version 5."; 132 | } 133 | 134 | // declare function 135 | function find_SQL_Version() { 136 | $output = shell_exec('mysql -V'); 137 | preg_match('@[0-9]+\.[0-9]+\.[0-9]+@', $output, $version); 138 | return @$version[0]?$version[0]:-1; 139 | } 140 | 141 | $mysql_version=find_SQL_Version(); 142 | if($mysql_version<5) 143 | { 144 | $mysql_error="Error: MySQL version is $mysql_version. Version 5 or newer is required."; 145 | } 146 | 147 | if(!function_exists('curl_exec')) 148 | { 149 | $curl_error="Error: PHP CURL function is not enabled!"; 150 | } 151 | ?> 152 | 153 | 154 |

Checking versions...

155 | 156 | Success: PHP Version $php_version - OK!

"; 158 | else if (empty($php_error)) echo "$php_warning

"; 159 | else echo "$php_error

"; 160 | 161 | if(empty($mysql_error)) echo "Success: MySQL Version $mysql_version - OK!

"; 162 | else echo "$mysql_error

"; 163 | 164 | if(empty($curl_error)) echo "Success: cURL Enabled - OK!

"; 165 | else echo "$curl_error

"; 166 | 167 | if(empty($curl_error) && empty($mysql_error) && empty($php_error)) 168 | { 169 | echo "
170 | 171 |
"; 172 | } 173 | else 174 | { 175 | echo ""; 176 | } 177 | ?> 178 | 179 | 180 | 181 | -------------------------------------------------------------------------------- /updates/update-2.1-to-2.2.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | ini_set('display_errors', 1); //Display errors in case something occurs 21 | ?> 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | CWSlack Updater 33 | 34 | 35 |
36 |
37 |

CWSlack-SlashCommands Updater

38 | Settings Configuration

"; 42 | echo "
Any field left blank will not change the setting, however yes/no questions must be updated any time you save this.
"; 43 | echo "
44 |

Tickets Module

45 |
Yes No
46 |

Configs Module

47 |
Yes No
48 |

"; 49 | echo "
"; 50 | //Template for future use 51 | //
Yes No
52 | //
53 | die(); 54 | } 55 | if ($_GET["page"] == "Save Settings") { 56 | $filedata = file('config.php'); 57 | $newdata = array(); 58 | $line1 = false; //For later use 59 | 60 | foreach ($filedata as $data) { 61 | if (stristr($data, '$useboards =')) { 62 | if ($_POST["useboards"] == "yes") { 63 | $newdata[] = '$useboards = 1; //Use the board function in new tickets. /t new company|summary vs /t new board|company|summary' . PHP_EOL; 64 | $line1 = true; 65 | } else { 66 | $newdata[] = '$useboards = 0; //Use the board function in new tickets. /t new company|summary vs /t new board|company|summary' . PHP_EOL; 67 | } 68 | } else if (stristr($data, '$hidepasswords =')) { 69 | if ($_POST["hidepasswords"] == "yes") { 70 | $newdata[] = '$hidepasswords = 1; //Set to 1 if you want to hide passwords.' . PHP_EOL; 71 | } else { 72 | $newdata[] = '$hidepasswords = 0; //Set to 1 if you want to hide passwords.' . PHP_EOL; 73 | } 74 | } else if (stristr($data, '//cwslack-activities.php') && !$line1) { 75 | array_pop($newdata); 76 | if ($_POST["useboards"] == "yes") { 77 | $newdata[] = '$useboards = 1; //Use the board function in new tickets. /t new company|summary vs /t new board|company|summary' . PHP_EOL; 78 | } else { 79 | $newdata[] = '$useboards = 0; //Use the board function in new tickets. /t new company|summary vs /t new board|company|summary' . PHP_EOL; 80 | } 81 | $newdata[] = PHP_EOL . $data; 82 | } else if (stristr($data, '//cwslack-tasks.php') && !$line1) { 83 | array_pop($newdata); 84 | if ($_POST["hidepasswords"] == "yes") { 85 | $newdata[] = '$hidepasswords = 1; //Set to 1 if you want to hide passwords.' . PHP_EOL; 86 | } else { 87 | $newdata[] = '$hidepasswords = 0; //Set to 1 if you want to hide passwords.' . PHP_EOL; 88 | } 89 | $newdata[] = PHP_EOL . $data; 90 | } else { 91 | $newdata[] = $data; 92 | } 93 | } 94 | 95 | 96 | file_put_contents('config.php', implode('', $newdata)); 97 | echo "
"; 98 | echo "Successfully configured the config.php file! Please test out your commands in Slack and submit any issues you have to GitHub!"; 99 | echo "
Please remove update-2.1-to-2.2.php to avoid people accessing it externally. You can re-add it anytime to configure database or settings again, or just manually edit config.php.
"; 100 | echo ""; 101 | die(); 102 | } 103 | } 104 | 105 | $php_version=phpversion(); 106 | preg_match("#^\d.\d#", phpversion(), $match); 107 | if($match[0]<5) 108 | { 109 | $php_error="Error: PHP version is ".phpversion().", Version 5 or newer is required."; 110 | } 111 | if($match[0]>6) 112 | { 113 | $php_warning="Warning: PHP version is ".phpversion().", Script tested only on Version 5."; 114 | } 115 | 116 | // declare function 117 | function find_SQL_Version() { 118 | $output = shell_exec('mysql -V'); 119 | preg_match('@[0-9]+\.[0-9]+\.[0-9]+@', $output, $version); 120 | return @$version[0]?$version[0]:-1; 121 | } 122 | 123 | $mysql_version=find_SQL_Version(); 124 | if($mysql_version<5) 125 | { 126 | $mysql_error="Error: MySQL version is $mysql_version. Version 5 or newer is required."; 127 | } 128 | 129 | if(!function_exists('curl_exec')) 130 | { 131 | $curl_error="Error: PHP CURL function is not enabled!"; 132 | } 133 | ?> 134 | 135 | 136 |

Checking versions...

137 | 138 | Success: PHP Version $php_version - OK!

"; 140 | else if (empty($php_error)) echo "$php_warning

"; 141 | else echo "$php_error

"; 142 | 143 | if(empty($mysql_error)) echo "Success: MySQL Version $mysql_version - OK!

"; 144 | else echo "$mysql_error

"; 145 | 146 | if(empty($curl_error)) echo "Success: cURL Enabled - OK!

"; 147 | else echo "$curl_error

"; 148 | 149 | if(empty($curl_error) && empty($mysql_error) && empty($php_error)) 150 | { 151 | echo "
152 | 153 |
"; 154 | } 155 | else 156 | { 157 | echo ""; 158 | } 159 | ?> 160 | 161 | 162 | 163 | -------------------------------------------------------------------------------- /updates/update-2.2-to-2.3.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | ini_set('display_errors', 1); //Display errors in case something occurs 21 | ?> 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | CWSlack Updater 33 | 34 | 35 |
36 |
37 |

CWSlack-SlashCommands Updater

38 | Settings Configuration

"; 42 | echo "
Any field left blank will not change the setting, however yes/no questions must be updated any time you save this.
"; 43 | echo "
44 |

General

45 |
Set below variable to True if you know your ConnectWise is slow. This will slow down the Slack integration a bit but prevent the 3000ms slack error.
46 |
Yes No
47 |

TimeAlerts Module

48 |
49 |

Time Module

50 |
51 |
52 |

"; 53 | echo "
"; 54 | //Template for future use 55 | //
Yes No
56 | //
57 | die(); 58 | } 59 | if ($_GET["page"] == "Save Settings") { 60 | $filedata = file('config.php'); 61 | $newdata = array(); 62 | $line1 = false; //For later use 63 | 64 | foreach ($filedata as $data) { 65 | if (stristr($data, '$timeoutfix =')) { 66 | if ($_POST["timeoutfix"] == "yes") { 67 | $newdata[] = '$timeoutfix = true; //Enable to fix any 3000ms response from Slack.' . PHP_EOL; 68 | $line1=true; 69 | } else { 70 | $newdata[] = '$timeoutfix = false; //Enable to fix any 3000ms response from Slack.' . PHP_EOL; 71 | } 72 | } else if (stristr($data, '$timebusinessstart =')) { 73 | if (!empty($_POST["timebusinessstart"])) { 74 | $newdata[] = '$timebusinessstart = ' . $_POST["timebusinessstart"] . '; //Set to when your business opens in your timezone' . PHP_EOL; 75 | } else { 76 | $newdata[] = $data; 77 | } 78 | } else if (stristr($data, '$timebusinessclose =')) { 79 | if (!empty($_POST["timebusinessclose"])) { 80 | $newdata[] = '$timebusinessclose = ' . $_POST["timebusinessclose"] . '; //Set to when your business closes in your timezone' . PHP_EOL; 81 | } else { 82 | $newdata[] = $data; 83 | } 84 | } else if (stristr($data, '$notimeusers =')) { 85 | if (!empty($_POST["notimeusers"])) { 86 | $newdata[] = '$notimeusers = ' . $_POST["notimeusers"] . '; //Usernames of users who should not be alerted on. Useful if you have techs who occasionally enter time and you don\'t want it pinging them every day. Separate with pipe |' . PHP_EOL; 87 | } else { 88 | $newdata[] = $data; 89 | } 90 | } else if (stristr($data, '//cwslack-incoming.php') && !$line1) { 91 | array_pop($newdata); 92 | if (!empty($_POST["timebusinessstart"])) { 93 | $newdata[] = '$timebusinessstart = "' . $_POST["timebusinessstart"] . '""; //Set to when your business opens in your timezone' . PHP_EOL; 94 | } else { 95 | $newdata[] = $data; 96 | } 97 | if (!empty($_POST["timebusinessstart"])) { 98 | $newdata[] = '$timebusinessstart = "' . $_POST["timebusinessstart"] . '""; //Set to when your business opens in your timezone' . PHP_EOL; 99 | } else { 100 | $newdata[] = $data; 101 | } 102 | $newdata[] = PHP_EOL . $data; 103 | } else if (stristr($data, '// Database Configuration') && !$line1) { 104 | array_pop($newdata); 105 | if ($_POST["timeoutfix"] == "yes") { 106 | $newdata[] = '$timeoutfix = true; //Enable to fix any 3000ms response from Slack.' . PHP_EOL; 107 | } else { 108 | $newdata[] = '$timeoutfix = false; //Enable to fix any 3000ms response from Slack.' . PHP_EOL; 109 | } 110 | $newdata[] = PHP_EOL . $data; 111 | } else if (stristr($data, '//cwslack-follow.php') && !$line1) { 112 | array_pop($newdata); 113 | if (!empty($_POST["notimeusers"])) { 114 | $newdata[] = '//cwslack-timealerts.php'; 115 | $newdata[] = '//This uses all four variables above'; 116 | $newdata[] = '$notimeusers = ' . $_POST["notimeusers"] . '; //Usernames of users who should not be alerted on. Useful if you have techs who occasionally enter time and you don\'t want it pinging them every day. Separate with pipe |' . PHP_EOL; 117 | } else { 118 | $newdata[] = $data; 119 | } 120 | $newdata[] = PHP_EOL . $data; 121 | } else { 122 | $newdata[] = $data; 123 | } 124 | } 125 | 126 | 127 | file_put_contents('config.php', implode('', $newdata)); 128 | echo "
"; 129 | echo "Successfully configured the config.php file! Please test out your commands in Slack and submit any issues you have to GitHub!"; 130 | echo "
Please remove update-2.2-to-2.3.php to avoid people accessing it externally. You can re-add it anytime to configure database or settings again, or just manually edit config.php.
"; 131 | echo ""; 132 | die(); 133 | } 134 | } 135 | 136 | $php_version=phpversion(); 137 | preg_match("#^\d.\d#", phpversion(), $match); 138 | if($match[0]<5) 139 | { 140 | $php_error="Error: PHP version is ".phpversion().", Version 5 or newer is required."; 141 | } 142 | if($match[0]>6) 143 | { 144 | $php_warning="Warning: PHP version is ".phpversion().", Script tested only on Version 5."; 145 | } 146 | 147 | // declare function 148 | function find_SQL_Version() { 149 | $output = shell_exec('mysql -V'); 150 | preg_match('@[0-9]+\.[0-9]+\.[0-9]+@', $output, $version); 151 | return @$version[0]?$version[0]:-1; 152 | } 153 | 154 | $mysql_version=find_SQL_Version(); 155 | if($mysql_version<5) 156 | { 157 | $mysql_error="Error: MySQL version is $mysql_version. Version 5 or newer is required."; 158 | } 159 | 160 | if(!function_exists('curl_exec')) 161 | { 162 | $curl_error="Error: PHP CURL function is not enabled!"; 163 | } 164 | ?> 165 | 166 | 167 |

Checking versions...

168 | 169 | Success: PHP Version $php_version - OK!

"; 171 | else if (empty($php_error)) echo "$php_warning

"; 172 | else echo "$php_error

"; 173 | 174 | if(empty($mysql_error)) echo "Success: MySQL Version $mysql_version - OK!

"; 175 | else echo "$mysql_error

"; 176 | 177 | if(empty($curl_error)) echo "Success: cURL Enabled - OK!

"; 178 | else echo "$curl_error

"; 179 | 180 | if(empty($curl_error) && empty($mysql_error) && empty($php_error)) 181 | { 182 | echo "
183 | 184 |
"; 185 | } 186 | else 187 | { 188 | echo ""; 189 | } 190 | ?> 191 | 192 | 193 | 194 | -------------------------------------------------------------------------------- /updates/update-2.3-2.4-to-2.5.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | ini_set('display_errors', 1); //Display errors in case something occurs 21 | ?> 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | CWSlack Updater 33 | 34 | 35 |
36 |
37 |

CWSlack-SlashCommands Updater

38 | Settings Configuration

"; 42 | echo "
Any field left blank will not change the setting, however yes/no questions must be updated any time you save this.
"; 43 | echo "
44 |

General

45 |
46 |

Notes Module

47 |
48 |

TimeAlerts Module

49 |
50 |

PriorityAlerts Module

51 |
52 |
53 |
54 |

Lunch Module

55 |
56 |
57 |
58 |
59 |
60 |
Yes No
61 |
Yes No
62 |
Yes No
63 |
Yes No
64 |

"; 65 | echo "
"; 66 | //Template for future use 67 | //
Yes No
68 | //
69 | die(); 70 | } 71 | if ($_GET["page"] == "Save Settings") { 72 | $filedata = file('config.php'); 73 | $newdata = array(); 74 | $line1 = false; //For later use 75 | 76 | foreach ($filedata as $data) { 77 | if (stristr($data, '$schedulestatus =')) { 78 | if (!empty($_POST["schedulestatus"])) { 79 | $newdata[] = '$schedulestatus = "' . $_POST["schedulestatus"] . '"; //Set to the name of your status (e.x. "Scheduled") if you want the [/t # schedule] functions to update the status' . PHP_EOL; 80 | $line1=true; 81 | } else { 82 | $newdata[] = $data; 83 | } 84 | } else if (stristr($data, '$defaultnotetype =')) { 85 | if (!empty($_POST["defaultnotetype"])) { 86 | $newdata[] = '$defaultnotetype = "' . $_POST["defaultnotetype"] . '"; //Set to internal, external, or externalemail and this will be used if they do not specify a type. Leave blank to have no default and return an error if they don\'t specify.' . PHP_EOL; 87 | } else { 88 | $newdata[] = $data; 89 | } 90 | } else if (stristr($data, '$specialtimeusers =')) { 91 | if (!empty($_POST["specialtimeusers"])) { 92 | $newdata[] = '$specialtimeusers = "' . $_POST["specialtimeusers"] . '"; //Usernames of users who should be alerted on, but who have special hours different from default start-close. Seperate user and time with comma, seperate different users with pipe |. No spaces' . PHP_EOL; 93 | } else { 94 | $newdata[] = $data; 95 | } 96 | } else if (stristr($data, '$prioritylist =')) { 97 | if (!empty($_POST["prioritylist"])) { 98 | $newdata[] = '$prioritylist = "' . $_POST["prioritylist"] . '"; // Name of the priority(ies) to look out for. Separate by pipe if more than one needed.' . PHP_EOL; 99 | } else { 100 | $newdata[] = $data; 101 | } 102 | } else if (stristr($data, '$prioritystatus =')) { 103 | if (!empty($_POST["prioritystatus"])) { 104 | $newdata[] = '$prioritystatus = "' . $_POST["prioritystatus"] . '"; // Status(es), seperated by pipe | symbol, which the priority alerts will check for and send alerts on.' . PHP_EOL; 105 | } else { 106 | $newdata[] = $data; 107 | } 108 | } else if (stristr($data, '$prioritywait =')) { 109 | if (!empty($_POST["prioritywait"])) { 110 | $newdata[] = '$prioritywait = ' . $_POST["prioritywait"] . '; // Number of minutes to wait after a high-priority event before alerting the technician. Maximum 119 minutes.' . PHP_EOL; 111 | } else { 112 | $newdata[] = $data; 113 | } 114 | } else if (stristr($data, '$slacklunchtoken =')) { 115 | if (!empty($_POST["slacklunchtoken"])) { 116 | $newdata[] = '$slacklunchtoken = "' . $_POST["slacklunchtoken"] . '"; // Set your token for the lunch slash command' . PHP_EOL; 117 | } else { 118 | $newdata[] = $data; 119 | } 120 | } else if (stristr($data, '$lunchchargecode =')) { 121 | if (!empty($_POST["lunchchargecode"])) { 122 | $newdata[] = '$lunchchargecode = ' . $_POST["lunchchargecode"] . '; // Set to your "Break" charge code that lunches should be put under' . PHP_EOL; 123 | } else { 124 | $newdata[] = $data; 125 | } 126 | } else if (stristr($data, '$lunchtime =')) { 127 | if (!empty($_POST["lunchtime"])) { 128 | $newdata[] = '$lunchtime = ' . $_POST["lunchtime"] . '; // Expected number of MINUTES that a user is on lunch' . PHP_EOL; 129 | } else { 130 | $newdata[] = $data; 131 | } 132 | } else if (stristr($data, '$lunchmax =')) { 133 | if (!empty($_POST["lunchmax"])) { 134 | $newdata[] = '$lunchmax = ' . $_POST["lunchmax"] . '; // Number of minutes to allow before cancelling the lunch entry, does not submit time' . PHP_EOL; 135 | } else { 136 | $newdata[] = $data; 137 | } 138 | } else if (stristr($data, '$lunchslackchannel =')) { 139 | if (!empty($_POST["lunchslackchannel"])) { 140 | $newdata[] = '$lunchslackchannel = "' . $_POST["lunchslackchannel"] . '"; // Channel to send Slack messages to' . PHP_EOL; 141 | } else { 142 | $newdata[] = $data; 143 | } 144 | } else if (stristr($data, '$lunchsendslack =')) { 145 | if ($_POST["lunchsendslack"] == "yes") { 146 | $newdata[] = '$lunchsendslack = true; // Send messages to a slack channel when a user goes on/off lunch' . PHP_EOL; 147 | } else { 148 | $newdata[] = '$lunchsendslack = false; // Send messages to a slack channel when a user goes on/off lunch' . PHP_EOL; 149 | } 150 | } else if (stristr($data, '$lunchsendonoff =')) { 151 | if ($_POST["lunchsendonoff"] == "yes") { 152 | $newdata[] = '$lunchsendonoff = 1; // Key: 0 = No notifications, 1 = Send notifications when a user goes on lunch, 2 = Send when a user goes off lunch, 3 = Send when a user goes on lunch OR off lunch' . PHP_EOL; 153 | } else { 154 | $newdata[] = '$lunchsendonoff = 0; // Key: 0 = No notifications, 1 = Send notifications when a user goes on lunch, 2 = Send when a user goes off lunch, 3 = Send when a user goes on lunch OR off lunch' . PHP_EOL; 155 | } 156 | } else if (stristr($data, '$lunchcreatesched =')) { 157 | if ($_POST["lunchcreatesched"] == "yes") { 158 | $newdata[] = '$lunchcreatesched = true; // Should the script create a schedule entry on the users board' . PHP_EOL; 159 | } else { 160 | $newdata[] = '$lunchcreatesched = false; // Should the script create a schedule entry on the users board' . PHP_EOL; 161 | } 162 | } else if (stristr($data, '$lunchsavetime =')) { 163 | if ($_POST["lunchsavetime"] == "yes") { 164 | $newdata[] = '$lunchsavetime = true; // Should the script submit a time entry for the user for their lunch duration' . PHP_EOL; 165 | } else { 166 | $newdata[] = '$lunchsavetime = false; // Should the script submit a time entry for the user for their lunch duration' . PHP_EOL; 167 | } 168 | } else if (stristr($data, '//cwslack-activities.php') && !$line1) { 169 | array_pop($newdata); 170 | if (!empty($_POST["schedulestatus"])) { 171 | $newdata[] = '$schedulestatus = "' . $_POST["schedulestatus"] . '"; //Set to the name of your status (e.x. "Scheduled") if you want the [/t # schedule] functions to update the status' . PHP_EOL; 172 | } else { 173 | $newdata[] = $data; 174 | } 175 | $newdata[] = PHP_EOL . $data; 176 | } else if (stristr($data, '//cwslack-configs.php') && !$line1) { 177 | array_pop($newdata); 178 | if (!empty($_POST["defaultnotetype"])) { 179 | $newdata[] = '$defaultnotetype = "' . $_POST["defaultnotetype"] . '"; //Set to internal, external, or externalemail and this will be used if they do not specify a type. Leave blank to have no default and return an error if they don\'t specify.' . PHP_EOL; 180 | } else { 181 | $newdata[] = $data; 182 | } 183 | $newdata[] = PHP_EOL . $data; 184 | } else if (stristr($data, '//cwslack-follow.php') && !$line1) { 185 | array_pop($newdata); 186 | if (!empty($_POST["specialtimeusers"])) { 187 | $newdata[] = '$specialtimeusers = "' . $_POST["specialtimeusers"] . '"; //Usernames of users who should be alerted on, but who have special hours different from default start-close. Seperate user and time with comma, seperate different users with pipe |. No spaces' . PHP_EOL; 188 | } else { 189 | $newdata[] = $data; 190 | } 191 | $newdata[] = PHP_EOL; 192 | $newdata[] = "//cwslack-priorityalerts.php" . PHP_EOL; 193 | $newdata[] = "//This uses all the variables from firmalerts as well, adhering to it for whether to post to users/channel and which channel" . PHP_EOL; 194 | if (!empty($_POST["prioritylist"])) { 195 | $newdata[] = '$prioritylist = "' . $_POST["prioritylist"] . '"; // Name of the priority(ies) to look out for. Separate by pipe if more than one needed.' . PHP_EOL; 196 | } else { 197 | $newdata[] = $data; 198 | } 199 | if (!empty($_POST["prioritystatus"])) { 200 | $newdata[] = '$prioritystatus = "' . $_POST["prioritystatus"] . '"; // Status(es), seperated by pipe | symbol, which the priority alerts will check for and send alerts on.' . PHP_EOL; 201 | } else { 202 | $newdata[] = $data; 203 | } 204 | if (!empty($_POST["prioritywait"])) { 205 | $newdata[] = '$prioritywait = ' . $_POST["prioritywait"] . '; // Number of minutes to wait after a high-priority event before alerting the technician. Maximum 119 minutes.' . PHP_EOL; 206 | } else { 207 | $newdata[] = $data; 208 | } 209 | $newdata[] = PHP_EOL . $data; 210 | } else if (stristr($data, '//cwslack-dbmanage.php') && !$line1) { 211 | array_pop($newdata); 212 | $newdata[] = PHP_EOL; 213 | $newdata[] = "//cwslack-lunch.php" . PHP_EOL; 214 | if (!empty($_POST["slacklunchtoken"])) { 215 | $newdata[] = '$slacklunchtoken = "' . $_POST["slacklunchtoken"] . '"; // Set your token for the lunch slash command' . PHP_EOL; 216 | } else { 217 | $newdata[] = $data; 218 | } 219 | if (!empty($_POST["lunchchargecode"])) { 220 | $newdata[] = '$lunchchargecode = ' . $_POST["lunchchargecode"] . '; // Set to your "Break" charge code that lunches should be put under' . PHP_EOL; 221 | } else { 222 | $newdata[] = $data; 223 | } 224 | if (!empty($_POST["lunchtime"])) { 225 | $newdata[] = '$lunchtime = ' . $_POST["lunchtime"] . '; // Expected number of MINUTES that a user is on lunch' . PHP_EOL; 226 | } else { 227 | $newdata[] = $data; 228 | } 229 | if (!empty($_POST["lunchmax"])) { 230 | $newdata[] = '$lunchmax = ' . $_POST["lunchmax"] . '; // Number of minutes to allow before cancelling the lunch entry, does not submit time' . PHP_EOL; 231 | } else { 232 | $newdata[] = $data; 233 | } 234 | if (!empty($_POST["lunchslackchannel"])) { 235 | $newdata[] = '$lunchslackchannel = "' . $_POST["lunchslackchannel"] . '"; // Channel to send Slack messages to' . PHP_EOL; 236 | } else { 237 | $newdata[] = $data; 238 | } 239 | if ($_POST["lunchsendslack"] == "yes") { 240 | $newdata[] = '$lunchsendslack = true; // Send messages to a slack channel when a user goes on/off lunch' . PHP_EOL; 241 | } else { 242 | $newdata[] = '$lunchsendslack = false; // Send messages to a slack channel when a user goes on/off lunch' . PHP_EOL; 243 | } 244 | if ($_POST["lunchsendonoff"] == "yes") { 245 | $newdata[] = '$lunchsendonoff = 1; // Key: 0 = No notifications, 1 = Send notifications when a user goes on lunch, 2 = Send when a user goes off lunch, 3 = Send when a user goes on lunch OR off lunch' . PHP_EOL; 246 | } else { 247 | $newdata[] = '$lunchsendonoff = 0; // Key: 0 = No notifications, 1 = Send notifications when a user goes on lunch, 2 = Send when a user goes off lunch, 3 = Send when a user goes on lunch OR off lunch' . PHP_EOL; 248 | } 249 | if ($_POST["lunchcreatesched"] == "yes") { 250 | $newdata[] = '$lunchcreatesched = true; // Should the script create a schedule entry on the users board' . PHP_EOL; 251 | } else { 252 | $newdata[] = '$lunchcreatesched = false; // Should the script create a schedule entry on the users board' . PHP_EOL; 253 | } 254 | if ($_POST["lunchsavetime"] == "yes") { 255 | $newdata[] = '$lunchsavetime = true; // Should the script submit a time entry for the user for their lunch duration' . PHP_EOL; 256 | } else { 257 | $newdata[] = '$lunchsavetime = false; // Should the script submit a time entry for the user for their lunch duration' . PHP_EOL; 258 | } 259 | $newdata[] = PHP_EOL . $data; 260 | } else { 261 | $newdata[] = $data; 262 | } 263 | } 264 | 265 | 266 | file_put_contents('config.php', implode('', $newdata)); 267 | echo "
"; 268 | echo "Successfully configured the config.php file! Please test out your commands in Slack and submit any issues you have to GitHub!"; 269 | echo "
Please remove update-2.3-2.4-to-2.5.php to avoid people accessing it externally. You can re-add it anytime to configure database or settings again, or just manually edit config.php.
"; 270 | echo ""; 271 | die(); 272 | } 273 | } 274 | 275 | $php_version=phpversion(); 276 | preg_match("#^\d.\d#", phpversion(), $match); 277 | if($match[0]<5) 278 | { 279 | $php_error="Error: PHP version is ".phpversion().", Version 5 or newer is required."; 280 | } 281 | if($match[0]>6) 282 | { 283 | $php_warning="Warning: PHP version is ".phpversion().", Script tested only on Version 5."; 284 | } 285 | 286 | // declare function 287 | function find_SQL_Version() { 288 | $output = shell_exec('mysql -V'); 289 | preg_match('@[0-9]+\.[0-9]+\.[0-9]+@', $output, $version); 290 | return @$version[0]?$version[0]:-1; 291 | } 292 | 293 | $mysql_version=find_SQL_Version(); 294 | if($mysql_version<5) 295 | { 296 | $mysql_error="Error: MySQL version is $mysql_version. Version 5 or newer is required."; 297 | } 298 | 299 | if(!function_exists('curl_exec')) 300 | { 301 | $curl_error="Error: PHP CURL function is not enabled!"; 302 | } 303 | ?> 304 | 305 | 306 |

Checking versions...

307 | 308 | Success: PHP Version $php_version - OK!

"; 310 | else if (empty($php_error)) echo "$php_warning

"; 311 | else echo "$php_error

"; 312 | 313 | if(empty($mysql_error)) echo "Success: MySQL Version $mysql_version - OK!

"; 314 | else echo "$mysql_error

"; 315 | 316 | if(empty($curl_error)) echo "Success: cURL Enabled - OK!

"; 317 | else echo "$curl_error

"; 318 | 319 | if(empty($curl_error) && empty($mysql_error) && empty($php_error)) 320 | { 321 | echo "
322 | 323 |
"; 324 | } 325 | else 326 | { 327 | echo ""; 328 | } 329 | ?> 330 | 331 | 332 | 333 | --------------------------------------------------------------------------------