PluginProbe
Fluent Support – Helpdesk & Customer Support Ticket System / 1.10.2
Fluent Support – Helpdesk & Customer Support Ticket System v1.10.2
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 1.5.5 All 68 releases
fluent-support / app / Services / Tickets / Importer / HelpScoutTickets.php

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

315 lines 11.5 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 $waitingSince = isset($response['customerWaitingSince']['time']) ?
147 date('Y-m-d h:i:s', strtotime($response['customerWaitingSince']['time'])) :
148 date('Y-m-d h:i:s', strtotime($response['createdAt']));
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' => date('Y-m-d h:i:s', strtotime($response['createdAt'])),
158 'updated_at' => date('Y-m-d h:i:s', strtotime($response['createdAt'])),
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
184 $formattedReplies[] = [
185 'content' => $isLineItem ? wp_kses_post($reply['action']['text']) : wp_kses_post($reply['body']),
186 'conversation_type' => $isLineItem ? 'internal_info' : 'response',
187 'created_at' => date('Y-m-d H:i:s', strtotime($reply['createdAt'])),
188 'updated_at' => date('Y-m-d H:i:s', strtotime($reply['createdAt'])),
189 'is_customer_reply' => $isLineItem ? false : ($customer->email === $repliedBy['email']),
190 'user' => $user,
191 'attachments' => $this->download($reply['_embedded']['attachments']),
192 ];
193 }
194 $ticketData['replies'] = $formattedReplies;
195 }
196
197 return $ticketData;
198 } catch (\Exception $e) {
199 throw new \Exception(esc_html($e->getMessage()));
200 }
201
202 }
203
204
205 private function fetchPerson($personData, $type='customer')
206 {
207 $person = Person::updateOrCreate(
208 [
209 'email' => $personData['email']
210 ],
211 [
212 'first_name' => $personData['first_name'],
213 'last_name' => $personData['last_name'],
214 'email' => $personData['email'],
215 'type' => $type
216 ]
217 );
218
219 return $person;
220 }
221
222 private function formatPersonData($personData)
223 {
224 return[
225 'first_name' => $personData['first'],
226 'last_name' => $personData['last'],
227 'email' => isset($personData['email']) ? $personData['email'] : null,
228 ];
229 }
230
231 // This method will download attachments from Help Scout
232 // It will create a new folder for each ticket and store attachments in it with their original name
233 // Example folder name: helpscout-ticket-{helpscout_ticket_id}
234 private function download($attachments)
235 {
236 $formattedAttachments = [];
237
238 if (count($attachments) < 1) {
239 return $formattedAttachments;
240 }
241
242 $wpUploadDir = wp_upload_dir();
243 $baseDir = $wpUploadDir['basedir'] . '/fluent-support/helpscout-ticket-'. $this->originId . '/';
244
245 foreach ($attachments as $attachment){
246 $remoteUrl = $attachment['_links']['web']['href'];
247 $fileName = basename($remoteUrl);
248
249 $filePath = $this->downloadFile($remoteUrl, $baseDir);
250
251 if ($filePath) {
252 $fileInfo = wp_check_filetype($filePath);
253 $fileUrl = $wpUploadDir['baseurl'] . '/fluent-support/helpscout-ticket-'. $this->originId . '/' . $fileName;
254
255 $formattedAttachments[] = [
256 'full_url' => $fileUrl,
257 'title' => $fileName,
258 'file_path' => $filePath,
259 'driver' => 'local',
260 'status' => 'active',
261 'file_type' => (!empty($fileInfo['type'])) ? $fileInfo['type'] : ''
262 ];
263 }
264 }
265 return $formattedAttachments;
266 }
267
268 public function setAccessToken($accessToken)
269 {
270 $this->accessToken = $accessToken;
271 }
272
273 public function setMailboxId($mailbox_id)
274 {
275 $this->mailbox_id = $mailbox_id;
276 }
277
278 public function deleteTickets($page)
279 {
280 return;
281 }
282
283 // Download a file from a remote URL and create a new directory for this if not exists
284 // Then save the file to the new directory and move this directory to a new given directory
285 private function downloadFile($remoteUrl, $baseDir)
286 {
287 $fileName = basename($remoteUrl);
288 $filePath = $baseDir . $fileName;
289
290 if (!file_exists($baseDir)) {
291 mkdir($baseDir, 0777, true);
292 }
293
294 if (!file_exists($filePath)) {
295 $response = wp_remote_get($remoteUrl, [
296 'timeout' => 60,
297 'stream' => false
298 ]);
299
300 if (is_wp_error($response)) {
301 return false;
302 }
303
304 $file_contents = wp_remote_retrieve_body($response);
305 if (empty($file_contents)) {
306 return false;
307 }
308
309 file_put_contents($filePath, $file_contents);
310 }
311
312 return $filePath;
313 }
314 }
315