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

TalkBotConfig::botContribs()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 0
dl 0
loc 7
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
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\Domain\Utils\TextUtil;
14
use App\Infrastructure\ServiceFactory;
15
use Exception;
16
use Mediawiki\Api\UsageException;
17
use Mediawiki\DataModel\EditInfo;
18
19
/**
20
 * Freaky customization of WikiBotConfig class
21
 * Class TalkBotConfig.
22
 */
23
class TalkBotConfig extends WikiBotConfig
24
{
25
    const BOT_TALK_SUMMARY = 'Réponse artificielle';
26
27
    const BOT_TALK_FILE       = __DIR__.'/resources/phrases_zizibot.txt';
28
    const TALKCONFIG_FILENAME = __DIR__.'/resources/botTalk_config.json';
29
30
    /**
31
     * Add a freaky response in the bottom of the talk page.
32
     *
33
     * @param string|null $pageTitle
34
     *
35
     * @return bool
36
     * @throws UsageException
37
     */
38
    public function botTalk(?string $pageTitle = null): bool
39
    {
40
        $talkConfig = $this->getTalkConfig();
41
42
        // ugly dependency
43
        $wiki = ServiceFactory::wikiApi();
44
        if (!$pageTitle) {
45
            $pageTitle = 'Discussion utilisateur:'.getenv('BOT_NAME');
46
        }
47
        $page = new WikiPageAction($wiki, $pageTitle);
48
        $last = $page->page->getRevisions()->getLatest();
49
50
        // No response if the last edition from bot or bot owner
51
        if (!$last->getUser() || 'Flow talk page manager' === $last->getUser()
52
            || in_array($last->getUser(), [getenv('BOT_NAME')])
53
        ) {
54
            return false;
55
        }
56
        if (in_array($last->getUser(), [getenv('BOT_OWNER')])) {
57
            $talkConfig['owner_last_time'] = time();
58
            file_put_contents(self::TALKCONFIG_FILENAME, json_encode($talkConfig));
59
60
            return false;
61
        }
62
        // No response if time < 24h after last owner response
63
        if (!isset($talkConfig['owner_last_time']) || $talkConfig['owner_last_time'] > (time() - 60 * 60 * 24)) {
64
            echo "No response if time < 24h after last owner response\n";
65
            return false;
66
        }
67
68
        $identation = $this->predictTalkIndentation($page->getText() ?? '', $last->getUser()); // ':::'
69
        $addText = $this->generateTalkText($last->getUser(), $identation);
70
71
        echo "Prepare to talk on $pageTitle / Sleep 3 min...\n";
72
        echo sprintf("-> %s \n", $addText);
73
        sleep(180);
74
75
        $editInfo = new EditInfo(static::BOT_TALK_SUMMARY);
76
        $success = $page->addToBottomOfThePage($addText, $editInfo);
77
78
        return (bool)$success;
79
    }
80
81
    /**
82
     * @param string|null $toEditor
83
     * @param string|null $identation
84
     *
85
     * @return string
86
     * @throws Exception
87
     */
88
    private function generateTalkText(?string $toEditor = null, ?string $identation = ':')
89
    {
90
        if ($toEditor === 'Flow talk page manager') {
91
            $toEditor = null;
92
        }
93
        $to = ($toEditor) ? sprintf('@[[User:%s|%s]] : ', $toEditor, $toEditor) : ''; // {{notif}}
94
        $sentence = TextUtil::mb_ucfirst($this->getRandomSentence());
95
        if (!$sentence) {
96
            throw new Exception('no sentence');
97
        }
98
99
        return sprintf('%s%s%s --~~~~', $identation, $to, $sentence);
100
    }
101
102
    /**
103
     * Stupid ":::" talk page indentation prediction.
104
     *
105
     * @param string $text
106
     * @param string $author
107
     *
108
     * @return string ":::"
109
     */
110
    private function predictTalkIndentation(string $text, ?string $author = null): string
111
    {
112
        // extract last line
113
        $lines = explode("\n", trim($text));
114
        $lastLine = $lines[count($lines) - 1];
115
        if (preg_match('#^(:*).+#', $lastLine, $matches)) {
116
            if (!empty($matches[1])) {
117
                $nextIdent = $matches[1].':';
118
                if (empty($author)) {
119
                    return $nextIdent;
120
                }
121
                // search author signature link to check that he wrote on the page bottom
122
                if (preg_match(
123
                    '#\[\[(?:User|Utilisateur|Utilisatrice):'.preg_quote($author).'[|\]]#i',
124
                    $matches[0]
125
                )
126
                ) {
127
                    return $nextIdent;
128
                }
129
            }
130
        }
131
132
        return ':';
133
    }
134
135
    /**
136
     * @return string|null
137
     * @throws ConfigException
138
     */
139
    private function getRandomSentence(): string
140
    {
141
        $sentences = file(self::BOT_TALK_FILE, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
142
        if (!$sentences) {
143
            throw new ConfigException('Pas de phrases disponibles pour TalkBot');
144
        }
145
146
        return (string)trim($sentences[array_rand($sentences)]);
147
    }
148
149
    /**
150
     * Todo
151
     * https://www.mediawiki.org/wiki/API:Usercontribs.
152
     */
153
    public function botContribs(): string
154
    {
155
        $url
156
            = 'https://fr.wikipedia.org/w/api.php?action=query&list=usercontribs&ucuser='.getenv('BOT_NAME')
157
            .'&ucnamespace=0&uclimit=40&ucprop=title|timestamp|comment&format=json';
158
159
        return file_get_contents($url);
160
    }
161
162
    private function getTalkConfig(): ?array
163
    {
164
        try {
165
            $text = file_get_contents(self::TALKCONFIG_FILENAME);
166
167
            return json_decode($text, true);
168
        } catch (\Throwable $e) {
169
            return [];
170
        }
171
    }
172
}
173