diff --git a/README.md b/README.md index a49f4b5..0cae472 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ twister-phpbot ============== -Simple Twister bot written in PHP +Simple Twister bot written in PHP. It is used to post messages for twister's @habr_ru account. diff --git a/habr_twister.php b/habr_twister.php new file mode 100644 index 0000000..5a95aa6 --- /dev/null +++ b/habr_twister.php @@ -0,0 +1,51 @@ +twisterPath = '.' . DIRECTORY_SEPARATOR . 'twister-win32-bundle' . DIRECTORY_SEPARATOR; +// Note: we use custom rpc port for twister +$twister->rpcport = 40001; + +// Initialise RSS database +require_once 'habrrssdb.php'; +$db = new HabrRSSDb('habr_db.dat'); + +foreach ($rss->channel->item as $item) { + $link = (string)$item->link; + $title = (string)$item->title; + + // get post id from link + $id = (int)preg_replace('#[^\d]#', '', $link); + + if ($db->isPublished($id)) { + continue; + } + + $msg = $twister->prettyPrint($title, $link, isset($item->category) ? $item->category : null); + +/**/debugLog($msg); + if ($twister->postMessage($msg)) { + $db->setPublished($id); + } +} + +/**/debugLog('=== Done ==='); \ No newline at end of file diff --git a/habrrssdb.php b/habrrssdb.php new file mode 100644 index 0000000..f58e180 --- /dev/null +++ b/habrrssdb.php @@ -0,0 +1,30 @@ +hDatabase = fopen($dbFile, 'r+'); + } + + public function __destruct() + { + fclose($this->hDatabase); + } + + public function isPublished($id) + { + fseek($this->hDatabase, $id); + $status = fgetc($this->hDatabase); + return $status === '1'; + } + + public function setPublished($id) + { + fseek($this->hDatabase, $id); + fwrite($this->hDatabase, '1'); + } +} diff --git a/twisterpost.php b/twisterpost.php new file mode 100644 index 0000000..ab5c5a4 --- /dev/null +++ b/twisterpost.php @@ -0,0 +1,162 @@ +user = $user; + } + + protected function getRpcCommand($method, $params = '') + { + $twisterd_path = $this->twisterPath . "twisterd"; + $twisterd_path .= " -rpcuser={$this->rpcuser}"; + $twisterd_path .= " -rpcpassword={$this->rpcpassword}"; + $twisterd_path .= " -rpcport={$this->rpcport}"; + $twisterd_path .= " $method"; + if (!empty($params)) { + $twisterd_path .= " $params"; + } + + return $twisterd_path; + } + + public function runRpcCommand($method, $params = '') + { + $cmd = $this->getRpcCommand($method, $params); + + $result = null; + if (strncasecmp(PHP_OS, 'WIN', 3) == 0) { + $this->exec_win($cmd, $result); + } else { + exec($cmd, $result); + } + $result = json_decode(implode(' ', $result)); + + return $result; + } + + public function updateMaxId() + { + $this->lastError = null; + $this->maxId = 0; + + $result = $this->runRpcCommand('getposts', "1 '[{\"username\":\"{$this->user}\"}]'"); + if (isset($result->code) && $result->code < 0) { + $this->maxId = -1; + $this->lastError = $result; + return false; + } + if (isset($result[0])) { + if (isset($result[0]->userpost->k)) { + $this->maxId = $result[0]->userpost->k; + } + } + + $result = $this->runRpcCommand('dhtget', "{$this->user} status s"); + if (isset($result->code) && $result->code < 0) { + $this->maxId = -1; + $this->lastError = $result; + return false; + } + if (isset($result[0]) && isset($result[0]->p)) { + if (isset($result[0]->p->seq)) { + $this->maxId = max($this->maxId, $result[0]->p->seq); + } + if (isset($result[0]->p->v) && isset($result[0]->p->v->userpost) && isset($result[0]->p->v->userpost->k)) { + $this->maxId = max($this->maxId, $result[0]->p->v->userpost->k); + } + } + + return true; + } + + public function postMessage($text) + { + $this->lastError = null; + + if ($this->maxId < 0) { + if (!$this->updateMaxId()) { + return false; + } + } + + $k = $this->maxId + 1; + $text = '"' . str_replace('"', '\\"', $text) . '"'; + $result = $this->runRpcCommand('newpostmsg', "{$this->user} $k $text"); + if (isset($result->code) && $result->code < 0) { + $this->maxId = -1; + $this->lastError = $result; + return false; + } + $this->maxId = $k; + + return true; + } + + public function prettyPrint($title, $url = '', $tags = null) + { + $maxLen = 140; + + $title_len = mb_strlen($title); + $url_len = mb_strlen($url); + + if ($url_len === 0) { + + if ($title_len > $maxLen) { + $text = rtrim(mb_substr($title, 0, $maxLen - 1), ' ') . '…'; + } else { + $text = $title; + } + + } else if ($title_len + 1 + $url_len > $maxLen) { + + $text = rtrim(mb_substr($title, 0, $maxLen - 2 - $url_len), ' ') . '… ' . $url; + + } else { + + $text = $title . ' ' . $url; + if (isset($tags)) { + foreach ($tags as $tag) { + $text .= ' #' . str_replace(' ', '_', (string)$tag); + } + } + $text = mb_substr($text, 0, $maxLen + 1); + $pos = $maxLen; + while (mb_substr($text, $pos, 1) !== ' ') { + $pos--; + } + $text = mb_substr($text, 0, $pos); + + } + + return $text; + } + + protected function exec_win($cmd, &$output = null, &$return_var = null) + { + $tempfile = '_php_exec.bat'; + + $bat = "@echo off\r\n" + . "@chcp 65001 >nul\r\n" + . "@cd \"" . getcwd() . "\"\r\n" + . "$cmd\r\n"; + + file_put_contents($tempfile, $bat); + exec("start /b cmd /c $tempfile", $output, $return_var); + unlink($tempfile); + + return count($output) ? $output[count($output) - 1] : ''; + } +}