1: <?php
2:
3: namespace Budabot\User\Modules;
4:
5: use Exception;
6: use stdClass;
7: use DOMDocument;
8:
9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32:
33: class ItemsController {
34:
35: public $moduleName;
36:
37:
38: public $db;
39:
40:
41: public $chatBot;
42:
43:
44: public $http;
45:
46:
47: public $settingManager;
48:
49:
50: public $text;
51:
52:
53: public $util;
54:
55:
56: public $logger;
57:
58:
59: public function setup() {
60: $this->db->loadSQLFile($this->moduleName, "aodb");
61:
62: $this->settingManager->add($this->moduleName, 'maxitems', 'Number of items shown on the list', 'edit', 'number', '40', '30;40;50;60');
63: }
64:
65: 66: 67: 68: 69:
70: public function itemsCommand($message, $channel, $sender, $sendto, $args) {
71: $msg = $this->findItems($args);
72: $sendto->reply($msg);
73: }
74:
75: 76: 77: 78:
79: public function itemIdCommand($message, $channel, $sender, $sendto, $args) {
80: $id = $args[1];
81:
82: $row = $this->findById($id);
83: if ($row === null) {
84: $msg = "No item found with id <highlight>$id<end>.";
85: } else {
86: $blob = print_r($row, true);
87: $blob .= "\n\n" . $this->formatSearchResults(array($row), null, true);
88: $msg = $this->text->makeBlob($id, $blob);
89: }
90:
91: $sendto->reply($msg);
92: }
93:
94: public function findById($id) {
95: $sql = "SELECT * FROM aodb WHERE highid = ? UNION SELECT * FROM aodb WHERE lowid = ? LIMIT 1";
96: return $this->db->queryRow($sql, $id, $id);
97: }
98:
99: 100: 101: 102:
103: public function updateitemsCommand($message, $channel, $sender, $sendto) {
104: $msg = $this->downloadNewestItemsdb();
105: $sendto->reply($msg);
106: }
107:
108: 109: 110: 111:
112: public function checkForUpdate() {
113: $msg = $this->downloadNewestItemsdb();
114: if (preg_match("/^The items database has been updated/", $msg)) {
115: $this->chatBot->sendGuild($msg);
116: }
117: }
118:
119: public function downloadNewestItemsdb() {
120: $this->logger->log('DEBUG', "Starting items db update");
121:
122:
123: $response = $this->http
124: ->get("https://api.github.com/repos/Budabot/Budabot/contents/modules/ITEMS_MODULE")
125: ->withHeader("Accept", "application/vnd.github.v3+json")
126: ->withHeader('User-Agent', 'Budabot')
127: ->waitAndReturnResponse();
128:
129: try {
130: $json = json_decode($response->body);
131:
132:
133: $latestVersion = null;
134: forEach ($json as $item) {
135: if (preg_match("/^aodb(.*)\\.sql$/i", $item->name, $arr)) {
136: if ($latestVersion === null) {
137: $latestVersion = $arr[1];
138: } else if ($this->util->compareVersionNumbers($arr[1], $currentVersion)) {
139: $latestVersion = $arr[1];
140: }
141: }
142: }
143: } catch (Exception $e) {
144: $msg = "Error updating items db: " . $e->getMessage();
145: $this->logger->log('ERROR', $msg);
146: return $msg;
147: }
148:
149: if ($latestVersion !== null) {
150: $currentVersion = $this->settingManager->get("aodb_db_version");
151:
152:
153: if ($currentVersion === false || $this->util->compareVersionNumbers($latestVersion, $currentVersion) > 0) {
154:
155: $contents = $this->http
156: ->get("https://raw.githubusercontent.com/Budabot/Budabot/master/modules/ITEMS_MODULE/aodb{$latestVersion}.sql")
157: ->withHeader('User-Agent', 'Budabot')
158: ->waitAndReturnResponse()
159: ->body;
160:
161: $fh = fopen("./modules/ITEMS_MODULE/aodb{$latestVersion}.sql", 'w');
162: fwrite($fh, $contents);
163: fclose($fh);
164:
165: $this->db->beginTransaction();
166:
167:
168: $this->db->loadSQLFile("ITEMS_MODULE", "aodb");
169:
170: $this->db->commit();
171:
172: $this->logger->log('INFO', "Items db updated from '$currentVersion' to '$latestVersion'");
173: $msg = "The items database has been updated to the latest version. Version: $latestVersion";
174: } else {
175: $this->logger->log('DEBUG', "Items db already up to date '$currentVersion'");
176: $msg = "The items database is already up to date. Version: $currentVersion";
177: }
178: } else {
179: $this->logger->log('ERROR', "Could not find latest items db on server");
180: $msg = "There was a problem finding the latest version on the server";
181: }
182:
183: $this->logger->log('DEBUG', "Finished items db update");
184:
185: return $msg;
186: }
187:
188: public function findItems($args) {
189: if (count($args) == 3) {
190: $ql = $args[1];
191: if (!($ql >= 1 && $ql <= 500)) {
192: return "QL must be between 1 and 500.";
193: }
194: $search = $args[2];
195: } else {
196: $search = $args[1];
197: $ql = false;
198: }
199:
200: $search = htmlspecialchars_decode($search);
201:
202:
203: $data = $this->findItemsFromLocal($search, $ql);
204:
205: $budabotItemsExtractorLink = $this->text->makeChatcmd("Budabot Items Extractor", "/start https://github.com/Budabot/ItemsExtractor");
206: $footer = "Item DB rips created using the $budabotItemsExtractorLink tool.";
207:
208: $msg = $this->createItemsBlob($data, $search, $ql, $this->settingManager->get('aodb_db_version'), 'local', $footer);
209:
210: return $msg;
211: }
212:
213: public function findItemsFromLocal($search, $ql) {
214: $tmp = explode(" ", $search);
215: list($query, $params) = $this->util->generateQueryFromParams($tmp, 'name');
216:
217: if ($ql) {
218: $query .= " AND `lowql` <= ? AND `highql` >= ?";
219: $params []= $ql;
220: $params []= $ql;
221: }
222:
223: $sql = "SELECT * FROM aodb WHERE $query ORDER BY `name` ASC, highql DESC LIMIT 1000";
224: $data = $this->db->query($sql, $params);
225: $data = $this->orderSearchResults($data, $search);
226: $data = array_slice($data, 0, $this->settingManager->get("maxitems"));
227:
228: return $data;
229: }
230:
231: public function createItemsBlob($data, $search, $ql, $version, $server, $footer, $elapsed = null) {
232: $num = count($data);
233: if ($num == 0) {
234: if ($ql) {
235: $msg = "No QL <highlight>$ql<end> items found matching <highlight>$search<end>.";
236: } else {
237: $msg = "No items found matching <highlight>$search<end>.";
238: }
239: return $msg;
240: } else if ($num < 4) {
241: return trim($this->formatSearchResults($data, $ql, false));
242: } else {
243: $blob = "Version: <highlight>$version<end>\n";
244: if ($ql) {
245: $blob .= "Search: <highlight>QL $ql $search<end>\n";
246: } else {
247: $blob .= "Search: <highlight>$search<end>\n";
248: }
249: $blob .= "Server: <highlight>" . $server . "<end>\n";
250: if ($elapsed) {
251: $blob .= "Time: <highlight>" . round($elapsed, 2) . "s<end>\n";
252: }
253: $blob .= "\n";
254: $blob .= $this->formatSearchResults($data, $ql, true);
255: if ($num == $this->settingManager->get('maxitems')) {
256: $blob .= "\n\n<highlight>*Results have been limited to the first " . $this->settingManager->get("maxitems") . " results.<end>";
257: }
258: $blob .= "\n\n" . $footer;
259: $link = $this->text->makeBlob("Item Search Results ($num)", $blob);
260:
261: return $link;
262: }
263: }
264:
265:
266: public function orderSearchResults($data, $search) {
267: $searchTerms = explode(" ", $search);
268: forEach ($data as $row) {
269: if (strcasecmp($search, $row->name) == 0) {
270: $numExactMatches = 100;
271: } else {
272: $itemKeywords = preg_split("/\s/", $row->name);
273: $numExactMatches = 0;
274: forEach ($itemKeywords as $keyword) {
275: forEach ($searchTerms as $searchWord) {
276: if (strcasecmp($keyword, $searchWord) == 0) {
277: $numExactMatches++;
278: break;
279: }
280: }
281: }
282: }
283: $row->numExactMatches = $numExactMatches;
284: }
285:
286: $this->util->mergesort($data, function($a, $b) {
287: if ($a->numExactMatches == $b->numExactMatches) {
288: return 0;
289: } else {
290: return ($a->numExactMatches > $b->numExactMatches) ? -1 : 1;
291: }
292: });
293:
294: return $data;
295: }
296:
297: public function formatSearchResults($data, $ql, $showImages) {
298: $list = '';
299: forEach ($data as $row) {
300: if ($showImages) {
301: $list .= $this->text->makeImage($row->icon) . "\n";
302: }
303: if ($ql) {
304: $list .= "QL $ql " . $this->text->makeItem($row->lowid, $row->highid, $ql, $row->name);
305: } else {
306: $list .= $this->text->makeItem($row->lowid, $row->highid, $row->highql, $row->name);
307: }
308: if ($row->lowql != $row->highql) {
309: $list .= " (QL" . $row->lowql . " - " . $row->highql . ")\n";
310: } else {
311: $list .= " (QL" . $row->lowql . ")\n";
312: }
313: if ($showImages) {
314: $list .= "\n<pagebreak>";
315: }
316: }
317: return $list;
318: }
319:
320: private function escapeDescription($arr) {
321: return "<description>" . htmlspecialchars($arr[1]) . "</description>";
322: }
323:
324: public function findByName($name, $ql = null) {
325: if ($ql === null) {
326: return $this->db->queryRow("SELECT * FROM aodb WHERE name = ? ORDER BY highql DESC, highid DESC", $name);
327: } else {
328: return $this->db->queryRow("SELECT * FROM aodb WHERE name = ? AND lowql <= ? AND highql >= ? ORDER BY highid DESC", $name, $ql, $ql);
329: }
330: }
331:
332: public function getItem($name, $ql = null) {
333: $row = $this->findByName($name, $ql);
334: $ql = ($ql === null ? $row->highql : $ql);
335: if ($row === null) {
336: $this->logger->log("WARN", "Could not find item '$name' at QL '$ql'");
337: } else {
338: return $this->text->makeItem($row->lowid, $row->highid, $ql, $row->name);
339: }
340: }
341:
342: public function getItemAndIcon($name, $ql = null) {
343: $row = $this->findByName($name, $ql);
344: $ql = ($ql === null ? $row->highql : $ql);
345: if ($row === null) {
346: $this->logger->log("WARN", "Could not find item '$name' at QL '$ql'");
347: } else {
348: return $this->text->makeImage($row->icon) . "\n" .
349: $this->text->makeItem($row->lowid, $row->highid, $ql, $row->name);
350: }
351: }
352: }
353: