Completed
Push — master ( 6051f2...6377da )
by Dispositif
02:48
created

Bot::checkStopOnTalkpage()   B

Complexity

Conditions 9
Paths 11

Size

Total Lines 40
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 26
c 0
b 0
f 0
nc 11
nop 1
dl 0
loc 40
rs 8.0555
1
<?php
2
/**
3
 * This file is part of dispositif/wikibot application
4
 * 2019 : 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\ServiceFactory;
14
use App\Infrastructure\SMS;
15
use Bluora\LaravelGitInfo\GitInfo;
16
use Mediawiki\Api\UsageException;
17
18
/**
19
 * Define wiki configuration of the bot.
20
 * See also .env file for parameters.
21
 * Class Bot.
22
 */
23
class Bot
24
{
25
    const WIKI_STATE_FILENAME = __DIR__.'/resources/wiki_state.json';
26
    const WATCHPAGE_FILENAME  = __DIR__.'/resources/watch_pages.json';
27
28
    const EXIT_ON_CHECK_WATCHPAGE = true;
29
30
    const BOT_FLAG = false;
31
32
    const MODE_AUTO = false;
33
34
    const EXIT_ON_WIKIMESSAGE = true;
35
36
    const EDIT_LAPS = 20;
37
38
    const EDIT_LAPS_MANUAL = 20;
39
40
    const EDIT_LAPS_AUTOBOT = 60;
41
42
    const EDIT_LAPS_FLAGBOT = 8;
43
44
    public $taskName = 'Améliorations bibliographiques';
45
46
    public static $gitVersion;
47
    /**
48
     * @var \DateTimeImmutable
49
     */
50
    private $lastCheckStopDate;
51
52
    public function __construct()
53
    {
54
        ini_set('user_agent', getenv('USER_AGENT'));
55
    }
56
57
    /**
58
     * Return start of wiki edit commentary.
59
     *
60
     * @return string
61
     */
62
    public function getCommentary(): string
63
    {
64
        return sprintf(
65
            '[%s] %s',
66
            str_replace('v', '', self::getGitVersion()),
67
            $this->taskName
68
        );
69
    }
70
71
    /**
72
     * Return last version (tag) from Git.
73
     *
74
     * @return string|null
75
     */
76
    public static function getGitVersion(): ?string
77
    {
78
        if (self::$gitVersion) {
79
            return self::$gitVersion;
80
        }
81
        $git = new GitInfo();
82
        $raw = $git->version();
83
        if (preg_match('#^(v[0-9.a-e]+)#', $raw, $matches) > 0) {
84
            self::$gitVersion = $matches[1];
85
86
            return self::$gitVersion;
87
        }
88
89
        return null;
90
    }
91
92
    /**
93
     * Return start of last commit id.
94
     *
95
     * @return string|null
96
     */
97
    public static function getCommitId(): ?string
98
    {
99
        $git = new GitInfo();
100
        $raw = $git->version();
101
        if (preg_match('#g([0-9a-f]+)#', $raw, $matches) > 0) {
102
            return $matches[1];
103
        }
104
105
        return null;
106
    }
107
108
    public function checkStopOnTalkpage(?bool $botTalk = false): void
109
    {
110
        $title = 'Discussion_utilisateur:'.getenv('BOT_NAME');
111
112
        if ($this->lastCheckStopDate
113
            && new \DateTimeImmutable() < $this->lastCheckStopDate->add(
114
                new \DateInterval('PT2M')
115
            )
116
        ) {
117
            return;
118
        }
119
        $this->lastCheckStopDate = new \DateTimeImmutable();
120
121
122
        $wiki = ServiceFactory::wikiApi();
123
        $pageAction = new WikiPageAction($wiki, $title);
124
        $text = $pageAction->getText();
125
        $lastEditor = $pageAction->getLastEditor() ?? 'unknown';
126
127
        if (preg_match('#({{stop}}|{{Stop}}|STOP)#', $text) > 0) {
128
            echo date('Y-m-d H:i');
129
            echo sprintf(
130
                "\n*** STOP ON TALK PAGE BY %s ***\n\n",
131
                $lastEditor
132
            );
133
            if (class_exists(SMS::class)) {
134
                try {
135
                    new SMS('Bot stop by '.$lastEditor);
136
                } catch (\Exception $e) {
137
                    unset($e);
138
                }
139
            }
140
            if ($botTalk && class_exists(ZiziBot::class)) {
141
                try {
142
                    (new ZiziBot())->botTalk();
143
                } catch (UsageException $e) {
144
                    unset($e);
145
                }
146
            }
147
            exit();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
148
        }
149
    }
150
151
    /**
152
     * Is there a new message on the discussion page of the bot (or owner) ?
153
     * Stop on new message ?
154
     *
155
     * @param bool $botTalk
156
     *
157
     * @throws ConfigException
158
     * @throws \Mediawiki\Api\UsageException
159
     */
160
    public function checkWatchPages()
161
    {
162
        foreach ($this->getWatchPages() as $title => $lastTime) {
163
            $pageTime = $this->getTimestamp($title);
164
165
            // the page has been edited since last check ?
166
            if (!$pageTime || $pageTime !== $lastTime) {
167
                echo sprintf(
168
                    "WATCHPAGE '%s' has been edited since %s.\n",
169
                    $title,
170
                    $lastTime
171
                );
172
173
                // Ask? Mettre à jour $watchPages ?
174
                echo "Replace with $title => '$pageTime'";
175
176
                if (self::EXIT_ON_CHECK_WATCHPAGE) {
177
                    echo "EXIT_ON_CHECK_WATCHPAGE\n";
178
                    exit();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
179
                }
180
            }
181
        }
182
    }
183
184
    /**
185
     * @return array
186
     * @throws ConfigException
187
     */
188
    protected function getWatchPages(): array
189
    {
190
        if (!file_exists(static::WATCHPAGE_FILENAME)) {
191
            throw new ConfigException('No watchpage file found.');
192
        }
193
194
        try {
195
            $json = file_get_contents(static::WATCHPAGE_FILENAME);
196
            $array = json_decode($json, true);
197
        } catch (\Throwable $e) {
198
            throw new ConfigException('Watchpage file malformed.');
199
        }
200
201
        return $array;
202
    }
203
204
    private function getTimestamp(string $title): ?string
205
    {
206
        $wiki = ServiceFactory::wikiApi();
207
        $page = new WikiPageAction($wiki, $title);
208
209
        return $page->page->getRevisions()->getLatest()->getTimestamp();
210
    }
211
212
    /**
213
     * How many minutes since last edit ? Do not to disturb human editors !
214
     *
215
     * @param string $title
216
     *
217
     * @return int minutes
218
     */
219
    public function minutesSinceLastEdit(string $title): int
220
    {
221
        $time = $this->getTimestamp($title);  // 2011-09-02T16:31:13Z
222
223
        return (int)round((time() - strtotime($time)) / 60);
224
    }
225
226
    /**
227
     * Detect {{nobots}}, {{bots|deny=all}}, {{bots|deny=MyBot,BobBot}}.
228
     * Relevant out of the "main" wiki-namespace (talk pages, etc).
229
     *
230
     * @param string      $text
231
     * @param string|null $botName
232
     *
233
     * @return bool
234
     */
235
    public static function isNoBotTag(string $text, ?string $botName = null): bool
236
    {
237
        $botName = ($botName) ? $botName : getenv('BOT_NAME');
238
        $denyReg = (!empty($botName)) ? '|\{\{bots ?\| ?deny\=[^\}]*'.preg_quote($botName, '#').'[^\}]*\}\}' : '';
239
240
        if (preg_match('#({{nobots}}|{{bots ?\| ?(optout|deny) ?= ?all ?}}'.$denyReg.')#i', $text) > 0) {
241
            return true;
242
        }
243
244
        return false;
245
    }
246
247
    /**
248
     * Detect wiki-templates restricting the edition on a frwiki page.
249
     *
250
     * @param string $text
251
     *
252
     * @return bool
253
     */
254
    public static function isEditionRestricted(string $text): bool
255
    {
256
        if (preg_match('#{{Protection#i', $text) > 0) {
257
            return true;
258
        }
259
260
        return false;
261
    }
262
}
263