Passed
Push — master ( dafac1...9ad278 )
by Dispositif
08:36
created

memorizeAndSaveAnalyzedPage()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 4
ccs 0
cts 0
cp 0
crap 2
rs 10
c 0
b 0
f 0
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\Domain\Exceptions\ConfigException;
13
use App\Infrastructure\Logger;
14
use App\Infrastructure\PageListInterface;
15
use App\Infrastructure\ServiceFactory;
16
use Codedungeon\PHPCliColors\Color;
17
use Exception;
18
use Mediawiki\Api\MediawikiFactory;
19
use Mediawiki\Api\UsageException;
20
use Mediawiki\DataModel\EditInfo;
21
use Psr\Log\LoggerInterface;
22
use Throwable;
23
24
abstract class AbstractBotTaskWorker
25
{
26
    const TASK_BOT_FLAG               = false;
27
    const SLEEP_AFTER_EDITION         = 60;
28
    const DELAY_AFTER_LAST_HUMAN_EDIT = 15;
29
    const CHECK_EDIT_CONFLICT         = true;
30
    const ARTICLE_ANALYZED_FILENAME   = __DIR__.'/resources/article_edited.txt';
31
    const SKIP_LASTEDIT_BY_BOT       = true;
32
    const SKIP_NOT_IN_MAIN_WIKISPACE = true;
33
34
    /**
35
     * @var PageListInterface
36
     */
37
    protected $pageListGenerator;
38
    /**
39
     * @var WikiBotConfig
40
     */
41
    protected $bot;
42
    /**
43
     * @var MediawikiFactory
44
     */
45
    protected $wiki;
46
    /**
47
     * @var WikiPageAction
48
     */
49
    protected $pageAction;
50
    protected $defaultTaskname;
51
    protected $titleTaskname;
52
53
    // TODO : move botFlag... to to WikiBotConfig
54
55
    protected $minorFlag = false;
56
    protected $titleBotFlag = false;
57
    protected $modeAuto = false;
58
    protected $maxLag = 5;
59
    /**
60
     * @var Logger|LoggerInterface
61
     */
62
    protected $log;
63
    /**
64
     * array des articles déjà anal
65
     */
66
    private $pastAnalyzed;
67
68
    /**
69
     * Goo2ouvrageWorker constructor.
70
     *
71
     * @param WikiBotConfig     $bot
72
     * @param MediawikiFactory  $wiki
73
     * @param PageListInterface $pagesGen
74
     */
75
    public function __construct(WikiBotConfig $bot, MediawikiFactory $wiki, ?PageListInterface $pagesGen = null)
76
    {
77
        $this->log = $bot->log;
78
        $this->wiki = $wiki;
79
        $this->bot = $bot;
80
        if ($pagesGen) {
81
            $this->pageListGenerator = $pagesGen;
82
        }
83
        $this->setUpInConstructor();
84
85
        $this->defaultTaskname = $bot->taskName;
86
87
        $analyzed = @file(static::ARTICLE_ANALYZED_FILENAME, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
88
        $this->pastAnalyzed = ($analyzed !== false ) ? $analyzed : [];
89
90
        // @throw exception on "Invalid CSRF token"
91
        $this->run();//todo delete that and use (Worker)->run($duration) or process management
92
    }
93
94
    protected function setUpInConstructor(): void
95
    {
96
    }
97
98
    /**
99
     * @throws ConfigException
100
     * @throws Throwable
101
     * @throws UsageException
102
     */
103
    public function run()
104
    {
105
        $titles = $this->getTitles();
106
        echo date('d-m-Y H:i')." *** NEW WORKER ***\n";
107
        foreach ($titles as $title) {
108
            $this->titleProcess($title);
109
            sleep(3);
110
        }
111
    }
112
113
    /**
114
     * @return array
115
     * @throws ConfigException
116
     */
117
    protected function getTitles(): array
118
    {
119
        if ($this->pageListGenerator === null) {
120
            throw new ConfigException('Empty PageListGenerator');
121
        }
122
123
        return $this->pageListGenerator->getPageTitles();
124
    }
125
126
    /**
127
     * @param string $title
128
     *
129
     * @throws UsageException
130
     * @throws Throwable
131
     */
132
    protected function titleProcess(string $title): void
133
    {
134
        echo "---------------------\n";
135
        echo date('d-m-Y H:i'). ' '. Color::BG_CYAN."  $title ".Color::NORMAL."\n";
136
        sleep(1);
137
138
        if(in_array($title, $this->pastAnalyzed)) {
139
            echo "Skip : déjà analysé\n";
140
            return;
141
        }
142
        
143
        $this->titleTaskname = $this->defaultTaskname;
144
        $this->titleBotFlag = static::TASK_BOT_FLAG;
145
146
        $text = $this->getText($title);
147
        if( static::SKIP_LASTEDIT_BY_BOT && $this->pageAction->getLastEditor() === getenv('BOT_NAME') ) {
148
            echo "Skip : déjà édité par le bot\n";
149
            return;
150
        }
151
        if (empty($text) || !$this->checkAllowedEdition($title, $text)) {
152
            return;
153
        }
154
155
        $newText = $this->processDomain($title, $text);
156
157
        $this->memorizeAndSaveAnalyzedPage($title);
158
        
159
        if (empty($newText) || $newText === $text) {
160
            echo "Skip identique ou vide\n";
161
162
            return;
163
        }
164
165
        if (!$this->modeAuto) {
166
            $ask = readline(Color::LIGHT_MAGENTA."*** ÉDITION ? [y/n/auto]".Color::NORMAL);
167
            if ('auto' === $ask) {
168
                $this->modeAuto = true;
169
            } elseif ('y' !== $ask) {
170
                return;
171
            }
172
        }
173
174
        $this->doEdition($newText);
175
    }
176
177
    /**
178
     * todo DI
179
     *
180
     * @param string $title
181
     *
182
     * @return string|null
183
     * @throws Exception
184
     * @throws Exception
185
     */
186
    protected function getText(string $title): ?string
187
    {
188
        $this->pageAction = ServiceFactory::wikiPageAction($title);
189
        if (static::SKIP_NOT_IN_MAIN_WIKISPACE && $this->pageAction->getNs() !== 0) {
190
            throw new Exception("La page n'est pas dans Main (ns!==0)");
191
        }
192
193
        return $this->pageAction->getText();
194
    }
195
196
    /**
197
     * todo distinguer 2 methodes : ban temporaire et permanent (=> logAnalyzed)
198
     * Controle droit d'edition.
199
     *
200
     * @param string $title
201
     * @param string $text
202
     *
203
     * @return bool
204
     * @throws UsageException
205
     */
206
    protected function checkAllowedEdition(string $title, string $text): bool
207
    {
208
        $this->bot->checkStopOnTalkpage(true);
209
210
        if (WikiBotConfig::isEditionRestricted($text)) {
211
            echo "SKIP : protection/3R/travaux.\n";
212
213
            return false;
214
        }
215
        if ($this->bot->minutesSinceLastEdit($title) < static::DELAY_AFTER_LAST_HUMAN_EDIT) {
216
            echo "SKIP : édition humaine dans les dernières ".static::DELAY_AFTER_LAST_HUMAN_EDIT." minutes.\n";
217
218
            return false;
219
        }
220
        if (preg_match('#{{ ?En-tête label ?\| ?AdQ#i', $text)) {
221
            echo "SKIP : AdQ.\n"; // BA ??
222
223
            return false;
224
        }
225
226
        return true;
227
    }
228
229
    /**
230
     * return $newText for editing
231
     *
232
     * @param string $title
233
     * @param string $text
234
     *
235
     * @return string|null
236
     */
237
    abstract protected function processDomain(string $title, string $text): ?string;
238
239
    protected function doEdition(string $newText): void
240
    {
241
        $prefixSummary = ($this->titleBotFlag) ? 'bot: ' : '';
242
243
        try {
244
            $result = $this->pageAction->editPage(
245
                $newText,
246
                new EditInfo($prefixSummary.$this->titleTaskname, $this->minorFlag, $this->titleBotFlag, $this->maxLag),
247
                static::CHECK_EDIT_CONFLICT
248
            );
249
        } catch (Throwable $e) {
250
            if (preg_match('#Invalid CSRF token#', $e->getMessage())) {
251
                throw new \Exception('Invalid CSRF token');
252
            }
253
254
            // If not a critical edition error
255
            // example : Wiki Conflict : Page has been edited after getText()
256
            $this->log->warning($e->getMessage());
257
258
            return;
259
        }
260
261
        dump($result);
262
        echo "Sleep ".(string)static::SLEEP_AFTER_EDITION."\n";
263
        sleep(static::SLEEP_AFTER_EDITION);
264
    }
265
266
    /**
267
     * @param string $title
268
     */
269
    private function memorizeAndSaveAnalyzedPage(string $title):void
270
    {
271
        $this->pastAnalyzed[] = $title;
272
        @file_put_contents(static::ARTICLE_ANALYZED_FILENAME, $title.PHP_EOL, FILE_APPEND);
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

272
        /** @scrutinizer ignore-unhandled */ @file_put_contents(static::ARTICLE_ANALYZED_FILENAME, $title.PHP_EOL, FILE_APPEND);

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...
273
    }
274
}
275