1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* This file is part of GitterBot package. |
4
|
|
|
* |
5
|
|
|
* @author Serafim <[email protected]> |
6
|
|
|
* @date 24.09.2015 15:27 |
7
|
|
|
* |
8
|
|
|
* For the full copyright and license information, please view the LICENSE |
9
|
|
|
* file that was distributed with this source code. |
10
|
|
|
*/ |
11
|
|
|
namespace Interfaces\Console\Commands; |
12
|
|
|
|
13
|
|
|
use Carbon\Carbon; |
14
|
|
|
use Core\Mappers\UserMapper; |
15
|
|
|
use Domains\Message; |
16
|
|
|
use Domains\User; |
17
|
|
|
use Gitter\Client; |
18
|
|
|
use Gitter\Support\ApiIterator; |
19
|
|
|
use Illuminate\Console\Command; |
20
|
|
|
use Illuminate\Contracts\Config\Repository; |
21
|
|
|
use Illuminate\Contracts\Container\Container; |
22
|
|
|
use Illuminate\Database\Connection; |
23
|
|
|
use InvalidArgumentException; |
24
|
|
|
use Ramsey\Uuid\Uuid; |
25
|
|
|
use Serafim\Evacuator\Evacuator; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* Class GitterSync |
29
|
|
|
* @package Interfaces\Console\Commands |
30
|
|
|
*/ |
31
|
|
|
class GitterSync extends Command |
32
|
|
|
{ |
33
|
|
|
/** |
34
|
|
|
* The name and signature of the console command. |
35
|
|
|
* @var string |
36
|
|
|
*/ |
37
|
|
|
protected $signature = 'gitter:sync {room}'; |
38
|
|
|
|
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* The console command description. |
42
|
|
|
* @var string |
43
|
|
|
*/ |
44
|
|
|
protected $description = 'Fill users karma from all messages of target room.'; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* Execute the console command. |
48
|
|
|
* |
49
|
|
|
* @param Repository $config |
50
|
|
|
* @param Container $container |
51
|
|
|
* @param Connection $db |
52
|
|
|
* |
53
|
|
|
* @return mixed |
54
|
|
|
* @throws \Throwable |
55
|
|
|
*/ |
56
|
|
|
public function handle(Repository $config, Container $container, Connection $db) |
57
|
|
|
{ |
58
|
|
|
$config->set('gitter.output', false); |
59
|
|
|
|
60
|
|
|
|
61
|
|
|
// Sync users |
62
|
|
View Code Duplication |
$db->transaction(function () use ($container) { |
|
|
|
|
63
|
|
|
$this->comment('Start users synchronize at ' . ($start = Carbon::now())); |
64
|
|
|
$container->call([$this, 'importUsers']); |
65
|
|
|
$this->comment('Ends ' . Carbon::now()->diffForHumans($start)); |
66
|
|
|
}); |
67
|
|
|
|
68
|
|
|
|
69
|
|
|
$this->output->newLine(); |
70
|
|
|
|
71
|
|
|
|
72
|
|
|
// Sync messages |
73
|
|
View Code Duplication |
$db->transaction(function () use ($container) { |
|
|
|
|
74
|
|
|
$this->comment('Start messages synchronize at ' . ($start = Carbon::now())); |
75
|
|
|
$container->call([$this, 'importMessages']); |
76
|
|
|
$this->comment('Ends ' . Carbon::now()->diffForHumans($start)); |
77
|
|
|
}); |
78
|
|
|
|
79
|
|
|
|
80
|
|
|
$this->output->newLine(); |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* @param Client $client |
85
|
|
|
* @param Connection $db |
86
|
|
|
* @throws \Throwable |
87
|
|
|
*/ |
88
|
|
|
public function importMessages(Client $client, Connection $db) |
89
|
|
|
{ |
90
|
|
|
$limit = 100; |
91
|
|
|
$lastMessageId = null; |
92
|
|
|
$room = $this->getRoom($client); |
93
|
|
|
$rootTimeZone = new \DateTimeZone('UTC'); |
94
|
|
|
|
95
|
|
|
|
96
|
|
|
$messages = new ApiIterator(function ($page) use ($client, $room, $limit, &$lastMessageId) { |
|
|
|
|
97
|
|
|
$query = ['limit' => $limit]; |
98
|
|
|
|
99
|
|
|
if ($lastMessageId !== null) { |
100
|
|
|
$query['beforeId'] = $lastMessageId; |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
$result = $this->rescue(function () use ($room, $query, $client) { |
104
|
|
|
return $client->http->getMessages($room->id, $query)->wait(); |
105
|
|
|
}); |
106
|
|
|
|
107
|
|
|
if (count($result) > 0) { |
108
|
|
|
$lastMessageId = $result[0]->id; |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
return $result; |
112
|
|
|
}); |
113
|
|
|
|
114
|
|
|
|
115
|
|
|
foreach ($messages as $i => $message) { |
116
|
|
|
$data = [ |
117
|
|
|
'id' => Uuid::uuid4()->toString(), |
118
|
|
|
'gitter_id' => $message->id, |
119
|
|
|
'room_id' => $room->id, |
120
|
|
|
'text' => $message->text, |
121
|
|
|
'text_rendered' => $message->html, |
122
|
|
|
'user_id' => $message->fromUser->id, |
123
|
|
|
'created_at' => new Carbon($message->sent, $rootTimeZone), |
124
|
|
|
'updated_at' => new Carbon($message->sent, $rootTimeZone), |
125
|
|
|
]; |
126
|
|
|
|
127
|
|
|
if (property_exists($message, 'editedAt') && $message->editedAt) { |
128
|
|
|
$data['updated_at'] = new Carbon($message->editedAt, $rootTimeZone); |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
/* |
132
|
|
|
! Bug: Only 1 item inserting by 1 sql query |
133
|
|
|
! @see: https://github.com/gitterHQ/gitter/issues/1184 |
134
|
|
|
! |
135
|
|
|
! TODO This operations (delete + insert) will be very slow. Optimize later %) |
136
|
|
|
*/ |
137
|
|
|
try { |
138
|
|
|
|
139
|
|
|
$db->transaction(function () use ($message, $data, $db) { |
140
|
|
|
$db->table('messages')->where('gitter_id', $data['gitter_id'])->delete(); |
141
|
|
|
$db->table('messages')->insert($data); |
142
|
|
|
|
143
|
|
|
$db->table('urls')->where('message_id', $data['id'])->delete(); |
144
|
|
|
$db->table('urls')->insert( |
145
|
|
|
$this->getUrlsFromMessage($data, (array)$message->urls) |
146
|
|
|
); |
147
|
|
|
|
148
|
|
|
$db->table('mentions')->where('message_id', $data['id'])->delete(); |
149
|
|
|
$db->table('mentions')->insert( |
150
|
|
|
$this->getMentionsFromMessage($data, (array)$message->mentions) |
151
|
|
|
); |
152
|
|
|
|
153
|
|
|
}); |
154
|
|
|
|
155
|
|
|
$this->info( |
156
|
|
|
'#' . $i . ' ' . |
157
|
|
|
$data['created_at'] . ' ' . |
158
|
|
|
mb_substr(str_replace("\n", '', $data['text']), 0, 32) . '... ' |
159
|
|
|
|
160
|
|
|
); |
161
|
|
|
|
162
|
|
|
} catch (\Throwable $e) { |
163
|
|
|
|
164
|
|
|
$this->error($e->getMessage() . "\n" . $e->getTraceAsString()); |
165
|
|
|
|
166
|
|
|
} |
167
|
|
|
} |
168
|
|
|
} |
169
|
|
|
|
170
|
|
|
/** |
171
|
|
|
* This command will returns an object of the room |
172
|
|
|
* |
173
|
|
|
* @param Client $client |
174
|
|
|
* @return mixed |
175
|
|
|
* @throws \Throwable |
176
|
|
|
*/ |
177
|
|
|
private function getRoom(Client $client) |
178
|
|
|
{ |
179
|
|
|
return $this->rescue(function () use ($client) { |
180
|
|
|
return $client->http->getRoomById($this->argument('room'))->wait(); |
|
|
|
|
181
|
|
|
}); |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
/** |
185
|
|
|
* This method must be wrap ALL request actions |
186
|
|
|
* |
187
|
|
|
* @param \Closure $request |
188
|
|
|
* @return mixed |
189
|
|
|
* @throws \Throwable |
190
|
|
|
*/ |
191
|
|
|
private function rescue(\Closure $request) |
192
|
|
|
{ |
193
|
|
|
return (new Evacuator($request)) |
194
|
|
|
->retry(Evacuator::INFINITY_RETRIES) |
195
|
|
|
->catch(function (\Throwable $e) { |
196
|
|
|
$this->error($e->getMessage() . "\n" . '// retry again'); |
197
|
|
|
sleep(1); |
198
|
|
|
}) |
199
|
|
|
->invoke(); |
200
|
|
|
} |
201
|
|
|
|
202
|
|
|
/** |
203
|
|
|
* This method run an users export |
204
|
|
|
* |
205
|
|
|
* @param Client $client |
206
|
|
|
* @throws \Throwable |
207
|
|
|
*/ |
208
|
|
|
public function importUsers(Client $client) |
209
|
|
|
{ |
210
|
|
|
$room = $this->getRoom($client); |
211
|
|
|
|
212
|
|
|
|
213
|
|
|
$users = new ApiIterator(function ($page) use ($client, $room) { |
214
|
|
|
return $this->rescue(function () use ($room, $page, $client) { |
215
|
|
|
|
216
|
|
|
return $client->http->getRoomUsers($room->id, ['limit' => 30, 'skip' => 30 * $page])->wait(); |
217
|
|
|
|
218
|
|
|
}); |
219
|
|
|
}); |
220
|
|
|
|
221
|
|
|
|
222
|
|
|
foreach ($users as $i => $user) { |
223
|
|
|
$user = UserMapper::fromGitterObject($user); |
224
|
|
|
$this->comment('#' . $i . ' @' . $user->login); |
225
|
|
|
} |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
/** |
229
|
|
|
* @param Message $message |
230
|
|
|
* @throws InvalidArgumentException |
231
|
|
|
*/ |
232
|
|
|
protected function onMessage(Message $message) |
233
|
|
|
{ |
234
|
|
|
$collection = $this->karma->validate($message); |
|
|
|
|
235
|
|
|
|
236
|
|
|
foreach ($collection as $state) { |
237
|
|
|
$user = $state->getUser(); |
238
|
|
|
|
239
|
|
|
if ($state->isIncrement()) { |
240
|
|
|
$message->user->addKarmaTo($user, $message); |
241
|
|
|
} |
242
|
|
|
|
243
|
|
|
if ($state->isIncrement() || $state->isTimeout() || $state->isSelf()) { |
244
|
|
|
echo "\r" . '[' . $message->created_at . '] ' . |
245
|
|
|
$state->getTranslation($user->karma_text) . "\n"; |
246
|
|
|
} |
247
|
|
|
} |
248
|
|
|
} |
249
|
|
|
|
250
|
|
|
/** |
251
|
|
|
* @param array $message |
252
|
|
|
* @param array $mentions |
253
|
|
|
* @return array |
254
|
|
|
*/ |
255
|
|
|
private function getMentionsFromMessage(array $message, array $mentions) |
256
|
|
|
{ |
257
|
|
|
$result = []; |
258
|
|
|
|
259
|
|
|
foreach ($mentions as $mention) { |
260
|
|
|
if (property_exists($mention, 'userId') && $mention->userId) { |
261
|
|
|
$result[] = [ |
262
|
|
|
'id' => Uuid::uuid4()->toString(), |
263
|
|
|
'message_id' => $message['id'], |
264
|
|
|
'user_id' => $message['user_id'], |
265
|
|
|
]; |
266
|
|
|
} |
267
|
|
|
} |
268
|
|
|
|
269
|
|
|
return $result; |
270
|
|
|
} |
271
|
|
|
|
272
|
|
|
/** |
273
|
|
|
* @param array $message |
274
|
|
|
* @param array $urls |
275
|
|
|
* @return array |
276
|
|
|
*/ |
277
|
|
|
private function getUrlsFromMessage(array $message, array $urls) |
278
|
|
|
{ |
279
|
|
|
$result = []; |
280
|
|
|
|
281
|
|
|
foreach ($urls as $url) { |
282
|
|
|
$result[] = [ |
283
|
|
|
'id' => Uuid::uuid4()->toString(), |
284
|
|
|
'message_id' => $message['id'], |
285
|
|
|
'url' => $url->url, |
286
|
|
|
]; |
287
|
|
|
} |
288
|
|
|
|
289
|
|
|
return $result; |
290
|
|
|
} |
291
|
|
|
} |
292
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.