PluginProbe
Fluent Support – Helpdesk & Customer Support Ticket System / 2.2.1
Fluent Support – Helpdesk & Customer Support Ticket System v2.2.1
2.4.5 2.4.0 2.3.2 2.3.1 2.3.0 2.2.1 2.2.0 trunk 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.10.5 1.4.0 1.4.1 1.4.2 1.4.5 1.4.6 1.4.7 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 All 69 releases
fluent-support / app / Services / Tickets / Importer / HelpScoutTickets.php

HelpScoutTickets.php in Fluent Support – Helpdesk & Customer Support Ticket System 2.2.1, at app/Services/Tickets/Importer/HelpScoutTickets.php

316 lines 11.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace FluentSupport\App\Services\Tickets\Importer;
3 use FluentSupport\App\Models\Person;
4 use FluentSupport\App\Models\Meta;
5 use FluentSupport\App\Services\Helper;
6
7 class HelpScoutTickets extends BaseImporter
8 {
9 protected $handler = 'helpscout';
10 public $accessToken;
11 public $mailbox_id;
12 private $apiUrl = 'https://api.helpscout.net/v2/';
13 protected $limit = 25;
14 private $totalPage;
15 private $currentPage;
16 private $totalTickets;
17 private $originId;
18
19 public function stats()
20 {
21 $metadata = Meta::where('object_type', '_fs_helpscout_migration_info')->first();
22 $previouslyImported = Helper::safeUnserialize($metadata->value ?? []);
23 if (isset($metadata->key)) {
24 $previouslyImported['mailbox_id'] = $metadata->key;
25 }
26
27 return [
28 'name' => esc_html('Help Scout'),
29 'handler' => $this->handler,
30 'type' => 'sass',
31 'last_migrated' => get_option('_fs_migrate_helpscout'),
32 'previously_imported' => $previouslyImported,
33 ];
34 }
35
36 public function doMigration($page, $handler)
37 {
38 $this->handler = $handler;
39
40 $tickets = $this->getTickets($page);
41 $results = $this->migrateTickets($tickets);
42
43 $hasMore = $this->currentPage < $this->totalPage;
44 $completedNow = isset($results['inserts']) ? count($results['inserts']) : 0;
45 $completedTickets = $completedNow + (($this->currentPage - 1) * 25);
46 $remainingTickets = $this->totalTickets - $completedTickets;
47 $completed = intval(($completedTickets / $this->totalTickets) * 100);
48
49 $response = [
50 'handler' => $this->handler,
51 'insert_ids' => $results['inserts'],
52 'skips' => count($results['skips']),
53 'has_more' => $hasMore,
54 'completed' => $completed,
55 'imported_page' => $page,
56 'total_pages' => $this->totalPage,
57 'next_page' => $page + 1,
58 'total_tickets' => $this->totalTickets,
59 'remaining' => $remainingTickets,
60 ];
61
62 if ($hasMore) {
63 $previousValue = Meta::where('object_type', '_fs_helpscout_migration_info')->first();
64 if ($previousValue) {
65 Meta::where('object_type', '_fs_helpscout_migration_info')->update([
66 'key' => $this->mailbox_id,
67 'value' => maybe_serialize($response)
68 ]);
69 } else {
70 Meta::insert([
71 'object_type' => '_fs_helpscout_migration_info',
72 'key' => $this->mailbox_id,
73 'value' => maybe_serialize($response)
74 ]);
75 }
76 return $response;
77 }
78
79 Meta::where('object_type', '_fs_helpscout_migration_info')->delete();
80 $response['message'] = __('All tickets have been imported successfully', 'fluent-support');
81 update_option('_fs_migrate_helpscout', current_time('mysql'), 'no');
82 return $response;
83 }
84
85 // This method will get all tickets from Help Scout
86 private function getTickets($page)
87 {
88 $request = wp_remote_get(
89 $this->apiUrl . 'conversations?mailbox=' . $this->mailbox_id . '&page=' . $page . '&status=all',
90 [
91 'headers' => [
92 'Authorization' => 'Bearer ' . $this->accessToken
93 ],
94 'timeout' => 60
95 ]
96 );
97
98 $response = wp_remote_retrieve_body($request);
99 $response = json_decode($response, true);
100 $tickets = $response['_embedded']['conversations'];
101
102 $this->totalPage = (int) $response['page']['totalPages'];
103 $this->currentPage = (int) $response['page']['number'];
104 $this->totalTickets = (int) $response['page']['totalElements'];
105
106 $formattedTickets = [];
107 foreach ($tickets as $ticket) {
108 $formattedTickets[] = $this->bindOrginalTicketAndReplies($ticket['id']);
109 }
110
111 return $formattedTickets;
112 }
113
114 // This method will make ticket data with associated replies and attachments
115 private function bindOrginalTicketAndReplies($ticketId)
116 {
117 try{
118 $request = wp_remote_get(
119 $this->apiUrl. 'conversations/' . $ticketId . '?embed=threads',
120 [
121 'headers' => [
122 'Authorization' => 'Bearer ' . $this->accessToken
123 ],
124 'timeout' => 60
125 ]
126 );
127
128 $response = wp_remote_retrieve_body($request);
129 $response = json_decode($response, true);
130
131 $formattedReplies = [];
132 $customerData = $response['primaryCustomer'] ?: [];
133
134 if ($replies = $response['_embedded']['threads']) {
135 $formattedCustomerData = $this->formatPersonData($customerData);
136 $customer = $this->fetchPerson($formattedCustomerData);
137 $this->originId = intval($response['id']);
138
139 if (isset(end($replies)['body'])) {
140 $content = wp_kses_post(end($replies)['body']);
141 } else {
142 $content = wp_kses_post($response['preview']);
143 }
144
145
146 $waitingTime = $response['customerWaitingSince']['time'] ?? $response['createdAt'] ?? null;
147 $waitingSince = $waitingTime ? gmdate('Y-m-d h:i:s', strtotime($waitingTime)) : null;
148 $createdAt = $response['createdAt'] ?? null;
149
150 $ticketData = [
151 'origin_id' => $this->originId,
152 'source' => 'helpscout',
153 'title' => isset($response['subject']) ? $response['subject'] : $response['preview'],
154 'content' => $content,
155 'customer' => $customer,
156 'response_count' => intval($response['threads']),
157 'created_at' => $createdAt ? gmdate('Y-m-d h:i:s', strtotime($createdAt)) : null,
158 'updated_at' => $createdAt ? gmdate('Y-m-d h:i:s', strtotime($createdAt)) : null,
159 'waiting_since' => $waitingSince,
160 'last_customer_response' => NULL,
161 'last_agent_response' => NULL,
162 ];
163
164 if($response['threads'] == 0 && $response['status'] == 'active'){
165 $ticketData['status'] = 'closed';
166 } elseif ($response['status'] == 'closed') {
167 $ticketData['status'] = 'closed';
168 } else {
169 $ticketData['status'] = 'active';
170 }
171
172 if ($attachments = end($replies)['_embedded']['attachments']){
173 $ticketData['attachments'] = $this->download($attachments);
174 }
175
176 array_pop($replies);
177 $replies = array_reverse($replies);
178
179 foreach ($replies as $reply) {
180 $repliedBy = $reply['createdBy'] ?? [];
181 $isLineItem = $reply['type'] == 'lineitem';
182 $user = ($customer->email === ($repliedBy['email'] ?? null)) ? $customer : $this->fetchPerson($this->formatPersonData($repliedBy), 'agent');
183 $replyCreatedAt = $reply['createdAt'] ?? null;
184
185 $formattedReplies[] = [
186 'content' => $isLineItem ? wp_kses_post($reply['action']['text']) : wp_kses_post($reply['body']),
187 'conversation_type' => $isLineItem ? 'internal_info' : 'response',
188 'created_at' => $replyCreatedAt ? gmdate('Y-m-d H:i:s', strtotime($replyCreatedAt)) : null,
189 'updated_at' => $replyCreatedAt ? gmdate('Y-m-d H:i:s', strtotime($replyCreatedAt)) : null,
190 'is_customer_reply' => $isLineItem ? false : ($customer->email === $repliedBy['email']),
191 'user' => $user,
192 'attachments' => $this->download($reply['_embedded']['attachments']),
193 ];
194 }
195 $ticketData['replies'] = $formattedReplies;
196 }
197
198 return $ticketData;
199 } catch (\Exception $e) {
200 throw new \Exception(esc_html($e->getMessage()));
201 }
202
203 }
204
205
206 private function fetchPerson($personData, $type='customer')
207 {
208 $person = Person::updateOrCreate(
209 [
210 'email' => $personData['email']
211 ],
212 [
213 'first_name' => $personData['first_name'],
214 'last_name' => $personData['last_name'],
215 'email' => $personData['email'],
216 'type' => $type
217 ]
218 );
219
220 return $person;
221 }
222
223 private function formatPersonData($personData)
224 {
225 return[
226 'first_name' => $personData['first'],
227 'last_name' => $personData['last'],
228 'email' => isset($personData['email']) ? $personData['email'] : null,
229 ];
230 }
231
232 // This method will download attachments from Help Scout
233 // It will create a new folder for each ticket and store attachments in it with their original name
234 // Example folder name: helpscout-ticket-{helpscout_ticket_id}
235 private function download($attachments)
236 {
237 $formattedAttachments = [];
238
239 if (empty($attachments) || !is_array($attachments) || count($attachments) < 1) {
240 return $formattedAttachments;
241 }
242
243 $wpUploadDir = wp_upload_dir();
244 $baseDir = $wpUploadDir['basedir'] . '/fluent-support/helpscout-ticket-'. $this->originId . '/';
245
246 foreach ($attachments as $attachment){
247 $remoteUrl = $attachment['_links']['web']['href'];
248 $fileName = basename($remoteUrl);
249
250 $filePath = $this->downloadFile($remoteUrl, $baseDir);
251
252 if ($filePath) {
253 $fileInfo = wp_check_filetype($filePath);
254 $fileUrl = $wpUploadDir['baseurl'] . '/fluent-support/helpscout-ticket-'. $this->originId . '/' . $fileName;
255
256 $formattedAttachments[] = [
257 'full_url' => $fileUrl,
258 'title' => $fileName,
259 'file_path' => $filePath,
260 'driver' => 'local',
261 'status' => 'active',
262 'file_type' => (!empty($fileInfo['type'])) ? $fileInfo['type'] : ''
263 ];
264 }
265 }
266 return $formattedAttachments;
267 }
268
269 public function setAccessToken($accessToken)
270 {
271 $this->accessToken = $accessToken;
272 }
273
274 public function setMailboxId($mailbox_id)
275 {
276 $this->mailbox_id = $mailbox_id;
277 }
278
279 public function deleteTickets($page)
280 {
281 return;
282 }
283
284 // Download a file from a remote URL and create a new directory for this if not exists
285 // Then save the file to the new directory and move this directory to a new given directory
286 private function downloadFile($remoteUrl, $baseDir)
287 {
288 $fileName = basename($remoteUrl);
289 $filePath = $baseDir . $fileName;
290
291 if (!file_exists($baseDir)) {
292 mkdir($baseDir, 0777, true);
293 }
294
295 if (!file_exists($filePath)) {
296 $response = wp_remote_get($remoteUrl, [
297 'timeout' => 60,
298 'stream' => false
299 ]);
300
301 if (is_wp_error($response)) {
302 return false;
303 }
304
305 $file_contents = wp_remote_retrieve_body($response);
306 if (empty($file_contents)) {
307 return false;
308 }
309
310 file_put_contents($filePath, $file_contents);
311 }
312
313 return $filePath;
314 }
315 }
316