Passed
Push — master ( 853cce...2ef02f )
by Daimona
01:54
created

WikiController::getPageCreationTS()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 14
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 11
nc 1
nop 1
dl 0
loc 14
rs 9.9
c 0
b 0
f 0
1
<?php declare( strict_types=1 );
2
3
namespace BotRiconferme;
4
5
use BotRiconferme\Exception\EditException;
6
use BotRiconferme\Exception\LoginException;
7
use BotRiconferme\Exception\APIRequestException;
8
use BotRiconferme\Exception\MissingPageException;
9
use BotRiconferme\Request\RequestBase;
10
11
/**
12
 * Class for wiki interaction, contains some requests shorthands
13
 */
14
class WikiController {
15
	/** @var bool */
16
	private static $loggedIn = false;
17
	/** @var Logger */
18
	private $logger;
19
	/** @var string */
20
	private $domain;
21
	/** @var string[] */
22
	private $tokens;
23
24
	/**
25
	 * @param string $domain The URL of the wiki, if different from default
26
	 */
27
	public function __construct( string $domain = DEFAULT_URL ) {
28
		$this->logger = new Logger;
29
		$this->domain = $domain;
30
	}
31
32
	/**
33
	 * Gets the content of a wiki page
34
	 *
35
	 * @param string $title
36
	 * @param int|null $section
37
	 * @return string
38
	 * @throws MissingPageException
39
	 */
40
	public function getPageContent( string $title, int $section = null ) : string {
41
		$this->logger->debug( "Retrieving page $title" );
42
		$params = [
43
			'action' => 'query',
44
			'titles' => $title,
45
			'prop' => 'revisions',
46
			'rvslots' => 'main',
47
			'rvprop' => 'content'
48
		];
49
50
		if ( $section !== null ) {
51
			$params['rvsection'] = $section;
52
		}
53
54
		$req = RequestBase::newFromParams( $params )->setUrl( $this->domain );
55
		$data = $req->execute();
56
		$page = reset( $data->query->pages );
57
		if ( isset( $page->missing ) ) {
58
			throw new MissingPageException( $title );
59
		}
60
61
		return $page->revisions[0]->slots->main->{ '*' };
62
	}
63
64
	/**
65
	 * Basically a wrapper for action=edit
66
	 *
67
	 * @param array $params
68
	 * @throws EditException
69
	 */
70
	public function editPage( array $params ) {
71
		$this->login();
72
73
		$params = [
74
			'action' => 'edit',
75
			'token' => $this->getToken( 'csrf' ),
76
			'bot' => Config::getInstance()->get( 'bot-edits' )
77
		] + $params;
78
79
		$res = RequestBase::newFromParams( $params )
80
			->setUrl( $this->domain )
81
			->setPost()
82
			->execute();
83
		if ( $res->edit->result !== 'Success' ) {
84
			throw new EditException( $res->edit->info );
85
		}
86
	}
87
88
	/**
89
	 * Login wrapper. Checks if we're already logged in and clears tokens cache
90
	 * @throws LoginException
91
	 */
92
	public function login() {
93
		if ( self::$loggedIn ) {
94
			$this->logger->debug( 'Already logged in' );
95
			return;
96
		}
97
98
		$this->logger->info( 'Logging in' );
99
100
		$params = [
101
			'action' => 'login',
102
			'lgname' => Config::getInstance()->get( 'username' ),
103
			'lgpassword' => Config::getInstance()->get( 'password' ),
104
			'lgtoken' => $this->getToken( 'login' )
105
		];
106
107
		try {
108
			$res = RequestBase::newFromParams( $params )->setUrl( $this->domain )->setPost()->execute();
109
		} catch ( APIRequestException $e ) {
110
			throw new LoginException( $e->getMessage() );
111
		}
112
113
		if ( !isset( $res->login->result ) || $res->login->result !== 'Success' ) {
114
			throw new LoginException( 'Unknown error' );
115
		}
116
117
		self::$loggedIn = true;
118
		// Clear tokens cache
119
		$this->tokens = [];
120
		$this->logger->info( 'Login succeeded' );
121
	}
122
123
	/**
124
	 * Get a token, cached.
125
	 *
126
	 * @param string $type
127
	 * @return string
128
	 */
129
	public function getToken( string $type ) : string {
130
		if ( !isset( $this->tokens[ $type ] ) ) {
131
			$params = [
132
				'action' => 'query',
133
				'meta'   => 'tokens',
134
				'type'   => $type
135
			];
136
137
			$req = RequestBase::newFromParams( $params )->setUrl( $this->domain );
138
			$res = $req->execute();
139
140
			$this->tokens[ $type ] = $res->query->tokens->{ "{$type}token" };
141
		}
142
143
		return $this->tokens[ $type ];
144
	}
145
146
	/**
147
	 * Get the timestamp of the creation of the given page
148
	 *
149
	 * @param string $title
150
	 * @return int
151
	 */
152
	public function getPageCreationTS( string $title ) : int {
153
		$params = [
154
			'action' => 'query',
155
			'prop' => 'revisions',
156
			'titles' => $title,
157
			'rvprop' => 'timestamp',
158
			'rvslots' => 'main',
159
			'rvlimit' => 1,
160
			'rvdir' => 'newer'
161
		];
162
163
		$res = RequestBase::newFromParams( $params )->setUrl( $this->domain )->execute();
164
		$data = $res->query->pages;
165
		return strtotime( reset( $data )->revisions[0]->timestamp );
166
	}
167
168
	/**
169
	 * Sysop-level inifinite protection for a given page
170
	 *
171
	 * @param string $title
172
	 * @param string $reason
173
	 */
174
	public function protectPage( string $title, string $reason ) {
175
		$this->logger->info( "Protecting page $title" );
176
		$this->login();
177
178
		$params = [
179
			'action' => 'protect',
180
			'title' => $title,
181
			'protections' => 'edit=sysop|move=sysop',
182
			'expiry' => 'infinite',
183
			'reason' => $reason,
184
			'token' => $this->getToken( 'csrf' )
185
		];
186
187
		RequestBase::newFromParams( $params )->setUrl( $this->domain )->setPost()->execute();
188
	}
189
}
190