TalkBotConfig   A
last analyzed

Complexity

Total Complexity 24

Size/Duplication

Total Lines 142
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 24
eloc 62
c 2
b 0
f 0
dl 0
loc 142
rs 10
ccs 0
cts 71
cp 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A generateTalkText() 0 12 4
A getRandomSentence() 0 8 2
A predictTalkIndentation() 0 21 5
B botTalk() 0 50 10
A getTalkConfig() 0 8 2
A botContribs() 0 8 1
1
<?php
2
/*
3
 * This file is part of dispositif/wikibot application (@github)
4
 * 2019-2023 © Philippe M./Irønie  <[email protected]>
5
 * For the full copyright and MIT license information, 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
    final public const BOT_TALK_SUMMARY = 'Réponse artificielle[[User:Irønie|.]]';
26
    final public const BOT_TALK_FILE = __DIR__ . '/resources/phrases_zizibot.txt';
27
    final public const TALKCONFIG_FILENAME = __DIR__ . '/resources/botTalk_config.json';
28
29
    /**
30
     * Add a freaky response in the bottom of the talk page.
31
     * @throws UsageException
32
     */
33
    public function botTalk(?string $pageTitle = null): bool
34
    {
35
        $talkConfig = $this->getTalkConfig();
36
37
        // ugly dependency
38
        $wiki = ServiceFactory::getMediawikiFactory();
39
        if (!$pageTitle) {
40
            $pageTitle = 'Discussion utilisateur:' . $this::getBotName();
41
        }
42
        $page = new WikiPageAction($wiki, $pageTitle);
43
        $last = $page->page->getRevisions()->getLatest();
44
45
        // No response if the last edition from bot or bot owner
46
        if (!$last->getUser() || 'Flow talk page manager' === $last->getUser()
47
            || $last->getUser() === $this::getBotName()
48
            || $last->getUser() == $this::getBotOwner()
49
        ) {
50
            return false;
51
        }
52
53
        // blacklist users
54
        if (in_array($last->getUser(), self::BLACKLIST_EDITOR)) {
55
            return false;
56
        }
57
58
        // No response if time < 24h since last bot owner response
59
        if ($last->getUser() == self::getBotOwner()) {
60
            $talkConfig['owner_last_time'] = (int)strtotime($last->getTimestamp());
61
            file_put_contents(self::TALKCONFIG_FILENAME, json_encode($talkConfig, JSON_THROW_ON_ERROR));
62
63
            return false;
64
        }
65
        // No response if time < 24h since last owner response
66
        if (isset($talkConfig['owner_last_time']) && (int)$talkConfig['owner_last_time'] > (time() - 60 * 60 * 48)) {
67
            echo "No response if time < 24h after last owner response\n";
68
69
            return false;
70
        }
71
72
        $indentation = $this->predictTalkIndentation($page->getText() ?? '', $last->getUser()); // ':::'
73
        $addText = $this->generateTalkText($last->getUser(), $indentation);
74
75
        echo "Prepare to talk on $pageTitle / Sleep 2 min...\n";
76
        echo sprintf("-> %s \n", $addText);
77
        sleep(120);
78
79
        $editInfo = new EditInfo(static::BOT_TALK_SUMMARY);
80
        $success = $page->addToBottomOfThePage($addText, $editInfo);
81
82
        return (bool)$success;
83
    }
84
85
    /**
86
     * @throws Exception
87
     */
88
    private function generateTalkText(?string $toEditor = null, ?string $identation = ':'): string
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
     * @return string ":::"
106
     */
107
    private function predictTalkIndentation(string $text, ?string $author = null): string
108
    {
109
        // extract last line
110
        $lines = explode("\n", trim($text));
111
        $lastLine = $lines[count($lines) - 1];
112
        if (preg_match('#^(:*).+#', $lastLine, $matches) && !empty($matches[1])) {
113
            $nextIdent = $matches[1] . ':';
114
            if (empty($author)) {
115
                return $nextIdent;
116
            }
117
            // search author signature link to check that he wrote on the page bottom
118
            if (preg_match(
119
                '#\[\[(?:User|Utilisateur|Utilisatrice):' . preg_quote($author, '#') . '[|\]]#i',
120
                $matches[0]
121
            )
122
            ) {
123
                return $nextIdent;
124
            }
125
        }
126
127
        return ':';
128
    }
129
130
    /**
131
     * @throws ConfigException
132
     */
133
    private function getRandomSentence(): string
134
    {
135
        $sentences = file(self::BOT_TALK_FILE, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
136
        if (empty($sentences)) {
137
            throw new ConfigException('Pas de phrases disponibles pour TalkBot');
138
        }
139
140
        return (string)trim($sentences[array_rand($sentences)]);
141
    }
142
143
    /**
144
     * Todo
145
     * https://www.mediawiki.org/wiki/API:Usercontribs.
146
     */
147
    public function botContribs(): string
148
    {
149
        // TODO client
150
        $url
151
            = 'https://fr.wikipedia.org/w/api.php?action=query&list=usercontribs&ucuser=' . $this::getBotName()
152
            . '&ucnamespace=0&uclimit=40&ucprop=title|timestamp|comment&format=json';
153
154
        return file_get_contents($url);
155
    }
156
157
    private function getTalkConfig(): ?array
158
    {
159
        try {
160
            $text = file_get_contents(self::TALKCONFIG_FILENAME);
161
162
            return json_decode($text, true, 512, JSON_THROW_ON_ERROR);
163
        } catch (\Throwable) {
164
            return [];
165
        }
166
    }
167
}
168