Test Failed
Push — master ( 319840...0bb54c )
by Dispositif
06:21
created

NotificationWorker::process()   B

Complexity

Conditions 9
Paths 11

Size

Total Lines 66
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 9
eloc 36
nc 11
nop 0
dl 0
loc 66
rs 8.0555
c 2
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * This file is part of dispositif/wikibot application (@github)
4
 * 2019/2020 © Philippe M. <[email protected]>
5
 * For the full copyright and MIT license information, please view the license file.
6
 */
7
8
declare(strict_types=1);
9
10
namespace App\Application;
11
12
use App\Infrastructure\DbAdapter;
13
use App\Infrastructure\Logger;
14
use App\Infrastructure\PageList;
15
use App\Infrastructure\ServiceFactory;
16
use Mediawiki\Api\MediawikiApi;
17
use Mediawiki\Api\SimpleRequest;
18
use Mediawiki\Api\UsageException;
19
use Mediawiki\DataModel\EditInfo;
20
21
/**
22
 * Doc API : https://www.mediawiki.org/wiki/Notifications/API
23
 * TYPES :
24
 * edit-user-talk
25
 * mention-summary (alerte)
26
 * mention (Discussion utilisateur:Codaxbot)
27
 * flowusertalk-post-reply (alerte,   "title": {
28
 * "full": "Discussion utilisateur:Ir\u00f8nie",
29
 * "namespace": "Discussion_utilisateur")
30
 * Parsing last unread notifications to the bot.
31
 * Class NotificationWorker
32
 *
33
 * @package App\Application
34
 */
35
class NotificationWorker
36
{
37
    const DEFAULT_WIKIS             = 'frwiki';
38
    const DIFF_URL                  = 'https://fr.wikipedia.org/w/index.php?diff=';
39
    const ARTICLE_ANALYZED_FILENAME = __DIR__.'/resources/article_externRef_edited.txt';
40
41
    /**
42
     * @var MediawikiApi
43
     */
44
    private $api;
45
    /**
46
     * @var array
47
     */
48
    private $option;
49
50
    /**
51
     * NotificationWorker constructor.
52
     *
53
     * @throws UsageException
54
     */
55
    public function __construct(?array $option = null)
56
    {
57
        $this->api = ServiceFactory::getMediawikiApi();
58
        $this->option = ($option) ?? [];
59
60
        $this->process();
61
    }
62
63
    private function process()
64
    {
65
        $notifications = $this->requestNotifications();
66
        if (empty($notifications)) {
67
            return;
68
        }
69
70
        krsort($notifications);
71
72
        $wikilog = [];
73
        foreach ($notifications as $notif) {
74
            $title = $notif['title']['full'];
75
76
            // Skip bot pages
77
            if (in_array(
78
                $title,
79
                [
80
                    'Utilisateur:CodexBot',
81
                    'Discussion utilisateur:CodexBot',
82
                    'Utilisateur:ZiziBot',
83
                    'Discussion utilisateur:ZiziBot',
84
                ]
85
            )
86
            ) {
87
                continue;
88
            }
89
90
            $date = new \DateTime($notif['timestamp']['utciso8601']);
91
92
            if (isset($notif['title']) && in_array($notif['title']['namespace'], ['', 'Discussion'])) {
93
                $icon = '🌼 '; // Article + Discussion
94
            }
95
96
            $wikilog[] = sprintf(
97
                '* %s %s[[%s]] ([%s%s diff]) par %s',
98
                $date->format('d-m-Y H\hi'),
99
                $icon ?? '',
100
                $title,
101
                self::DIFF_URL,
102
                $notif['revid'] ?? '',
103
                $notif['agent']['name'] ?? '???'
104
            );
105
//            dump($notif);
106
107
            if (!isset($notif['read'])) {
108
                $this->postNotifAsRead($notif['id']);
109
            }
110
111
            if (isset($notif['title']) && in_array($notif['title']['namespace'], ['', 'Discussion'])) {
112
                // PROCESS ARTICLES
113
                $article = $notif['title']['text'];
114
115
                // wikiScan for {ouvrage} completion
116
                $this->processWikiscanForOuvrage($article);
117
118
                // URL => wiki-template completion
119
                $this->deleteEditedArticleFile($article);
120
                $this->processExternLinks($article);
121
            }
122
        }
123
124
        dump($wikilog);
125
126
        echo "sleep 20";
127
        sleep(20);
128
        $this->editWikilog($wikilog);
129
    }
130
131
    private function requestNotifications(): ?array
132
    {
133
        $result = $this->api->getRequest(
134
            new SimpleRequest(
135
                'query', [
136
                           'meta' => 'notifications',
137
                           'notwikis' => self::DEFAULT_WIKIS,
138
                           'notfilter' => '!read', // default: read|!read
139
                           'notlimit' => '30', // max 50
140
                           //                   'notunreadfirst' => '1', // comment for false
141
                           //                   'notgroupbysection' => '1',
142
                           'notsections' => 'alert', // alert|message
143
                           'format' => 'php',
144
                       ]
145
            )
146
        );
147
148
        if (empty($result)) {
149
            return [];
150
        }
151
152
        return $result['query']['notifications']['list'];
153
    }
154
155
    private function postNotifAsRead(int $id): bool
156
    {
157
        sleep(2);
158
        try {
159
            $this->api->postRequest(
160
                new SimpleRequest(
161
                    'echomarkread', [
162
                                      'list' => $id,
163
                                      'token' => $this->api->getToken(),
164
                                  ]
165
                )
166
            );
167
        } catch (\Throwable $e) {
168
            return false;
169
        }
170
171
        return true;
172
    }
173
174
    /**
175
     * Write wikilog of notifications on a dedicated page.
176
     *
177
     * @param array $wikilog
178
     *
179
     * @return bool
180
     * @throws UsageException
181
     */
182
    private function editWikilog(array $wikilog): bool
183
    {
184
        if (empty($wikilog)) {
185
            return false;
186
        }
187
        $text = implode("\n", $wikilog)."\n";
188
189
        $wiki = ServiceFactory::wikiApi();
190
        $pageAction = new WikiPageAction($wiki, 'Utilisateur:CodexBot/Notifications');
191
192
        $success = $pageAction->addToTopOfThePage(
193
            $text,
194
            new EditInfo('⚙ mise à jour notifications', false, false)
195
        );
196
//        dump($success);
197
198
        return $success;
199
    }
200
201
    /**
202
     * Delete article title in a log text file.
203
     *
204
     * @param $title
205
     */
206
    private function deleteEditedArticleFile(string $title): void
207
    {
208
        $text = file_get_contents(self::ARTICLE_ANALYZED_FILENAME);
209
        $newtext = str_replace($title."\n", '', $text);
210
        if (!empty($text) && $text !== $newtext) {
211
            @file_put_contents(self::ARTICLE_ANALYZED_FILENAME, $newtext);
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition for file_put_contents(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

211
            /** @scrutinizer ignore-unhandled */ @file_put_contents(self::ARTICLE_ANALYZED_FILENAME, $newtext);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
212
        }
213
    }
214
215
    /**
216
     * Process external URL completion to wiki-template.
217
     *
218
     * @param string      $article
219
     * @param string|null $username
220
     */
221
    private function processExternLinks(string $article, ?string $username = '')
222
    {
223
        try {
224
            $wiki = ServiceFactory::wikiApi();
225
            $logger = new Logger();
226
            //$logger->debug = true;
227
            $botConfig = new WikiBotConfig($logger);
228
            $botConfig->taskName = sprintf(
229
                "🔔🌐 Complètement de références (@[[User:%s|%s]]) : URL ⇒ ",
230
                $username,
231
                $username
232
            );
233
            new ExternRefWorker($botConfig, $wiki, new PageList([$article]));
234
            sleep(10);
235
        } catch (\Throwable $e) {
236
            unset($e);
237
        }
238
    }
239
240
    /**
241
     * Process wikiSan for future {ouvrage} completion
242
     *
243
     * @param string $article
244
     */
245
    private function processWikiscanForOuvrage(string $article): void
246
    {
247
        try {
248
            $wiki = ServiceFactory::wikiApi();
249
            $list = new PageList([$article]);
250
            new ScanWiki2DB($wiki, new DbAdapter(), new WikiBotConfig(), $list, 15);
251
        } catch (\Throwable $e) {
252
            echo $e->getMessage();
253
        }
254
    }
255
}
256