PluginProbe
Fluent Support – Helpdesk & Customer Support Ticket System / 2.4.5
Fluent Support – Helpdesk & Customer Support Ticket System v2.4.5
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.4.5, at app/Services/Tickets/Importer/HelpScoutTickets.php

288 lines 10.9 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_safe_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 'redirection' => 0
96 ]
97 );
98
99 $response = wp_remote_retrieve_body($request);
100 $response = json_decode($response, true);
101 $tickets = $response['_embedded']['conversations'];
102
103 $this->totalPage = (int) $response['page']['totalPages'];
104 $this->currentPage = (int) $response['page']['number'];
105 $this->totalTickets = (int) $response['page']['totalElements'];
106
107 $formattedTickets = [];
108 foreach ($tickets as $ticket) {
109 $formattedTickets[] = $this->bindOrginalTicketAndReplies($ticket['id']);
110 }
111
112 return $formattedTickets;
113 }
114
115 // This method will make ticket data with associated replies and attachments
116 private function bindOrginalTicketAndReplies($ticketId)
117 {
118 try{
119 $request = wp_safe_remote_get(
120 $this->apiUrl. 'conversations/' . $ticketId . '?embed=threads',
121 [
122 'headers' => [
123 'Authorization' => 'Bearer ' . $this->accessToken
124 ],
125 'timeout' => 60,
126 'redirection' => 0
127 ]
128 );
129
130 $response = wp_remote_retrieve_body($request);
131 $response = json_decode($response, true);
132
133 $formattedReplies = [];
134 $customerData = $response['primaryCustomer'] ?: [];
135
136 if ($replies = $response['_embedded']['threads']) {
137 $formattedCustomerData = $this->formatPersonData($customerData);
138 $customer = $this->fetchPerson($formattedCustomerData);
139 $this->originId = intval($response['id']);
140
141 if (isset(end($replies)['body'])) {
142 $content = wp_kses_post(end($replies)['body']);
143 } else {
144 $content = wp_kses_post($response['preview']);
145 }
146
147
148 $waitingTime = $response['customerWaitingSince']['time'] ?? $response['createdAt'] ?? null;
149 $waitingSince = $waitingTime ? gmdate('Y-m-d h:i:s', strtotime($waitingTime)) : null;
150 $createdAt = $response['createdAt'] ?? null;
151
152 $ticketData = [
153 'origin_id' => $this->originId,
154 'source' => 'helpscout',
155 'title' => isset($response['subject']) ? $response['subject'] : $response['preview'],
156 'content' => $content,
157 'customer' => $customer,
158 'response_count' => intval($response['threads']),
159 'created_at' => $createdAt ? gmdate('Y-m-d h:i:s', strtotime($createdAt)) : null,
160 'updated_at' => $createdAt ? gmdate('Y-m-d h:i:s', strtotime($createdAt)) : null,
161 'waiting_since' => $waitingSince,
162 'last_customer_response' => NULL,
163 'last_agent_response' => NULL,
164 ];
165
166 if($response['threads'] == 0 && $response['status'] == 'active'){
167 $ticketData['status'] = 'closed';
168 } elseif ($response['status'] == 'closed') {
169 $ticketData['status'] = 'closed';
170 } else {
171 $ticketData['status'] = 'active';
172 }
173
174 if ($attachments = end($replies)['_embedded']['attachments']){
175 $ticketData['attachments'] = $this->download($attachments);
176 }
177
178 array_pop($replies);
179 $replies = array_reverse($replies);
180
181 foreach ($replies as $reply) {
182 $repliedBy = $reply['createdBy'] ?? [];
183 $isLineItem = $reply['type'] == 'lineitem';
184 $user = ($customer->email === ($repliedBy['email'] ?? null)) ? $customer : $this->fetchPerson($this->formatPersonData($repliedBy), 'agent');
185 $replyCreatedAt = $reply['createdAt'] ?? null;
186
187 $formattedReplies[] = [
188 'content' => $isLineItem ? wp_kses_post($reply['action']['text']) : wp_kses_post($reply['body']),
189 'conversation_type' => $isLineItem ? 'internal_info' : 'response',
190 'created_at' => $replyCreatedAt ? gmdate('Y-m-d H:i:s', strtotime($replyCreatedAt)) : null,
191 'updated_at' => $replyCreatedAt ? gmdate('Y-m-d H:i:s', strtotime($replyCreatedAt)) : null,
192 'is_customer_reply' => $isLineItem ? false : ($customer->email === $repliedBy['email']),
193 'user' => $user,
194 'attachments' => $this->download($reply['_embedded']['attachments']),
195 ];
196 }
197 $ticketData['replies'] = $formattedReplies;
198 }
199
200 return $ticketData;
201 } catch (\Exception $e) {
202 throw new \Exception(esc_html($e->getMessage()));
203 }
204
205 }
206
207
208 private function fetchPerson($personData, $type='customer')
209 {
210 $person = Person::updateOrCreate(
211 [
212 'email' => $personData['email']
213 ],
214 [
215 'first_name' => $personData['first_name'],
216 'last_name' => $personData['last_name'],
217 'email' => $personData['email'],
218 'type' => $type
219 ]
220 );
221
222 return $person;
223 }
224
225 private function formatPersonData($personData)
226 {
227 return[
228 'first_name' => $personData['first'],
229 'last_name' => $personData['last'],
230 'email' => isset($personData['email']) ? $personData['email'] : null,
231 ];
232 }
233
234 // This method will download attachments from Help Scout
235 // It will create a new folder for each ticket and store attachments in it with their original name
236 // Example folder name: helpscout-ticket-{helpscout_ticket_id}
237 private function download($attachments)
238 {
239 $formattedAttachments = [];
240
241 if (empty($attachments) || !is_array($attachments) || count($attachments) < 1) {
242 return $formattedAttachments;
243 }
244
245 $wpUploadDir = wp_upload_dir();
246 $baseDir = $wpUploadDir['basedir'] . '/fluent-support/helpscout-ticket-'. $this->originId . '/';
247
248 foreach ($attachments as $attachment){
249 $remoteUrl = $attachment['_links']['web']['href'];
250 $fileName = basename($remoteUrl);
251
252 $filePath = Common::downloadFile($remoteUrl, $baseDir, $fileName);
253
254 if (is_wp_error($filePath) || !$filePath) {
255 continue;
256 }
257
258 $fileInfo = wp_check_filetype($filePath);
259 $fileUrl = $wpUploadDir['baseurl'] . '/fluent-support/helpscout-ticket-'. $this->originId . '/' . basename($filePath);
260
261 $formattedAttachments[] = [
262 'full_url' => $fileUrl,
263 'title' => $fileName,
264 'file_path' => $filePath,
265 'driver' => 'local',
266 'status' => 'active',
267 'file_type' => (!empty($fileInfo['type'])) ? $fileInfo['type'] : ''
268 ];
269 }
270 return $formattedAttachments;
271 }
272
273 public function setAccessToken($accessToken)
274 {
275 $this->accessToken = $accessToken;
276 }
277
278 public function setMailboxId($mailbox_id)
279 {
280 $this->mailbox_id = $mailbox_id;
281 }
282
283 public function deleteTickets($page)
284 {
285 return;
286 }
287 }
288