diff --git a/app/Espinoso/BrainNode.php b/app/Espinoso/BrainNode.php new file mode 100644 index 0000000..8788631 --- /dev/null +++ b/app/Espinoso/BrainNode.php @@ -0,0 +1,66 @@ +regex = $regex; + $this->reply = $data['reply'] ?? ''; + $this->ignored = collect($data['ignored'] ?? []); + } + + public function matchMessage(Message $message) + { + $this->match = preg_match($this->regex, $message->getText(), $this->matches) === 1; + + return !empty($this->reply) + && $this->shouldResponseTo($message->getFrom()) + && $this->match; + } + + public function pickReply(Message $message) + { + return is_array($this->reply) + ? $this->pickFromBag($message) + : $this->reply; + } + + public function addIgnored(Collection $ignored) { + $this->ignored->merge($ignored); + } + + protected function shouldResponseTo(TelegramUser $from) + { + // TODO + return true; + } + + protected function pickFromBag(Message $message) + { + // FIXME: make a better behavior than simple random + $number = rand(0, count($this->reply) - 1); + + $reply = $this->reply[$number]; + + if (str_contains($reply, ':name:')) { + $reply = str_replace(':name:', $message->getFrom()->getFirstName(), $reply); + } + + return $reply; + } + +} diff --git a/app/Espinoso/Espinoso.php b/app/Espinoso/Espinoso.php new file mode 100644 index 0000000..dc64dbd --- /dev/null +++ b/app/Espinoso/Espinoso.php @@ -0,0 +1,68 @@ +handlers = $handlers; + } + + /** + * @param ApiTelegram $telegram + * @param Message $message + */ + public function executeHandlers(ApiTelegram $telegram, Message $message) + { + $this->getHandlers()->map(function ($handler) use ($telegram) { + return new $handler($this, $telegram); + })->filter(function (EspinosoHandler $handler) use ($message) { + return $handler->shouldHandle($message); + })->each(function (EspinosoHandler $handler) use ($message) { + try { + $handler->handle($message); + } catch (Exception $e) { + $handler->handleError($e, $message); + } + }); + } + + /** + * @return Collection + */ + public function getHandlers(): Collection + { + return $this->handlers; + } + +// public function register(stdClass $update) +// { +// $from = $update->message->from; +// +// $user = TelegramUser::whereTelegramId($from->id)->first(); +// if (!$user) { +// $user = new TelegramUser; +// $user->telegram_id = $from->id; +// } +// +// $user->first_name = $from->first_name ?? ''; +// $user->last_name = $from->last_name ?? ''; +// $user->username = $from->username ?? ''; +// $user->save(); +// } + +} diff --git a/app/Espinoso/Handlers/BardoDelEspinosoHandler.php b/app/Espinoso/Handlers/BardoDelEspinosoHandler.php index 839bb4a..8fdefed 100644 --- a/app/Espinoso/Handlers/BardoDelEspinosoHandler.php +++ b/app/Espinoso/Handlers/BardoDelEspinosoHandler.php @@ -1,21 +1,27 @@ isTextMessage($updates) - && preg_match('/^send me nudes$/i', $updates->message->text) ; - } + /** + * @var string + */ + protected $allow_ignore_prefix = true; + /** + * @var string + */ + protected $pattern = "send me nudes$"; + + protected $signature = "[espi] send me nudes"; + protected $description = "no sé, fijate"; - public function handle($updates, $context = null) + public function handle(Message $message) { - return Telegram::sendPhoto([ - 'chat_id' => $updates->message->chat->id, + return $this->telegram->sendPhoto([ + 'chat_id' => $message->getChat()->getId(), 'photo' => 'https://cdn.drawception.com/images/panels/2012/4-4/FErsE1a6t7-8.png', - 'caption' => 'Acá tenés tu nude, puto del orto!' + 'caption' => 'Acá tenés tu nude, hijo de puta!' ]); } -} \ No newline at end of file +} diff --git a/app/Espinoso/Handlers/BrainHandler.php b/app/Espinoso/Handlers/BrainHandler.php new file mode 100644 index 0000000..eb80451 --- /dev/null +++ b/app/Espinoso/Handlers/BrainHandler.php @@ -0,0 +1,95 @@ +matchedNodes = collect([]); + $this->allNodes = collect(config('brain.patterns'))->map(function ($data, $regex) { + return new BrainNode($regex, $data); + }); + } + + public function shouldHandle(Message $message): bool + { + $this->matchedNodes = $this->allNodes->filter(function ($node) use ($message) { + $node->addIgnored($this->globalIgnored()); + return $node->matchMessage($message); + }); + + return $this->matchedNodes->isNotEmpty(); + } + + public function handle(Message $message) + { + $this->matchedNodes->each(function (BrainNode $node) use ($message) { + $this->telegram->sendMessage([ + 'chat_id' => $message->getChat()->getId(), + 'text' => $node->pickReply($message), + 'parse_mode' => 'Markdown' + ]); + }); + } + + /* + * Internals + */ + + protected function globalIgnored() + { + return collect(config('brain.ignore_to')); + } + +// public function handle(Message $message) +// { +// if ($this->ignoringSender($message->getFrom())) { +// $fromName = $message->getFrom()->getFirstName(); +// $msg = Msg::md("Con vos no hablo porque no viniste al asado $fromName")->build($message); +// $this->telegram->sendMessage($msg); +// return; +// } +// +// foreach ($this->mappings() as $pattern => $response) { +// if ( preg_match($pattern, $message->getText()) ) { +// $msg = $this->buildMessage($response, $pattern, $message); +// $this->telegram->sendMessage($msg); +// } +// } +// } + +// private function buildMessage($response, $pattern, Message $message) +// { +// if ($response instanceof Msg) +// return $response->build($message, $pattern); +// else +// return Msg::plain($response)->build($message, $pattern); +// } +// +// private function mappings() +// { +// return config('espinoso_data.ResponseByMatch.mappings'); +// } +// + +// private function ignoringSender($sender) +// { +// foreach ($this->ignoredNames() as $name) +// if ( preg_match("/$name/i", $sender->first_name) ) +// return true ; +// return false ; +// } + +} diff --git a/app/Espinoso/Handlers/CinemaHandler.php b/app/Espinoso/Handlers/CinemaHandler.php index 8a6cb2f..baf200f 100644 --- a/app/Espinoso/Handlers/CinemaHandler.php +++ b/app/Espinoso/Handlers/CinemaHandler.php @@ -2,17 +2,19 @@ use Illuminate\Support\Str; use App\Facades\GoutteClient; -use Telegram\Bot\Laravel\Facades\Telegram; +use Telegram\Bot\Objects\Message; class CinemaHandler extends EspinosoCommandHandler { - public function shouldHandle($updates, $context = null) - { - return parent::shouldHandle($updates, $context) - && $this->matchCommand('.*\bcine\b.*', $updates); - } + /** + * @var string + */ + protected $pattern = ".{0,100}\b(cine)\b.{0,100}$"; + + protected $signature = "espi cine"; + protected $description = "te muestro que hay para ver en el cine y ponerla"; - public function handle($updates, $context = null) + public function handle(Message $message) { $crawler = GoutteClient::request('GET', config('espinoso.url.cinema')); @@ -25,14 +27,14 @@ public function handle($updates, $context = null) return " - {$movie}"; })->implode("\n"); - $message = "¿La pensás poner? + $response = "¿La pensás poner? ¡Mete Netflix pelotud@, es mas barato! Pero igual podes ver todas estas:\n {$movies}"; - Telegram::sendMessage([ - 'chat_id' => $updates->message->chat->id, - 'text' => $message, + $this->telegram->sendMessage([ + 'chat_id' => $message->getChat()->getId(), + 'text' => $response, ]); } } diff --git a/app/Espinoso/Handlers/EspinosoCommandHandler.php b/app/Espinoso/Handlers/EspinosoCommandHandler.php index 821d637..2427064 100644 --- a/app/Espinoso/Handlers/EspinosoCommandHandler.php +++ b/app/Espinoso/Handlers/EspinosoCommandHandler.php @@ -1,9 +1,13 @@ matchCommand($this->pattern, $message, $this->matches); + } + /** * @param $pattern - * @param $updates + * @param Message $message * @param array|null $matches - * @return int + * @return bool */ - protected function matchCommand($pattern, $updates, array &$matches = null) + protected function matchCommand($pattern, Message $message, array &$matches = null): bool { - $quantifier = $this->allow_ignore_prefix ? '?' : '{1,3}'; - $text = $this->isTextMessage($updates) ? $updates->message->text : ''; + $quantifier = $this->allow_ignore_prefix ? '{0,3}' : '{1,3}'; + $text = $message->getText(); + $pattern = "/{$this->prefix_regex}{$quantifier}{$pattern}/{$this->flags}"; - return preg_match( - "/{$this->prefix_regex}{$quantifier}{$pattern}/{$this->flags}", - $text, - $matches - ); + return preg_match($pattern, $text, $matches) === 1; } } diff --git a/app/Espinoso/Handlers/EspinosoHandler.php b/app/Espinoso/Handlers/EspinosoHandler.php index c1d9c41..9219a28 100644 --- a/app/Espinoso/Handlers/EspinosoHandler.php +++ b/app/Espinoso/Handlers/EspinosoHandler.php @@ -1,47 +1,85 @@ isTextMessage($updates); + return empty($this->signature) ? '' : "*{$this->signature}*\n\t\t\t{$this->description}"; } - protected function isTextMessage($updates) + public function __construct(Espinoso $espinoso, ApiTelegram $telegram) { - return isset($updates->message) && isset($updates->message->text); + $this->espinoso = $espinoso; + $this->telegram = $telegram; + } + + abstract public function shouldHandle(Message $message): bool; + + abstract public function handle(Message $message); + + /** + * @param Message $message + */ + protected function replyNotFound(Message $message) + { + $this->telegram->sendMessage([ + 'chat_id' => $message->getChat()->getId(), + 'text' => 'No encontré una mierda, che', + ]); } - public function handleError(Exception $e, $updates) + /** + * @param Message $message + */ + protected function replyError(Message $message) + { + $this->telegram->sendMessage([ + 'chat_id' => $message->getChat()->getId(), + 'text' => 'Ups! Esta cosa anda como el culo...', + ]); + } + + public function handleError(Exception $e, Message $message) { $clazz = get_called_class(); Log::error($clazz); - Log::error(json_encode($updates)); + Log::error($message); Log::error($e); - $chat = $updates->message->chat->type == 'group' - ? "{$updates->message->chat->title}" - : ($updates->message->chat->type == 'private' - ? "{$updates->message->chat->first_name} (@{$updates->message->chat->username})" - : ""); - $username = isset($updates->message->from->username) - ? " (@{$updates->message->from->username})" - : ''; + $chat = $message->getChat(); + $username = $chat->getUsername() ? " (@{$chat->getUsername()})" : ""; + $fromUser = $chat->getFirstName() . $username; + + // chat could be private, group, supergroup or channel + $fromChat = $chat->getType() == 'private' ? $fromUser : $chat->getTitle(); + $error = "Fuck! Something blow up on {$clazz} - `{$e->getMessage()}` - - *From:* {$updates->message->from->first_name}{$username} - - *Chat:* {$chat} - - *Text:* _{$updates->message->text}_ + - *From:* {$fromUser} + - *Chat:* {$fromChat} + - *Text:* _{$message->getText()}_ View Log for details"; - Telegram::sendMessage([ + $this->telegram->sendMessage([ 'chat_id' => config('espinoso.chat.dev'), 'text' => $error, 'parse_mode' => 'Markdown', @@ -50,7 +88,7 @@ public function handleError(Exception $e, $updates) public function __toString() { - return self::class; + return get_called_class(); } diff --git a/app/Espinoso/Handlers/GitHubHandler.php b/app/Espinoso/Handlers/GitHubHandler.php index 6aae7bd..08a0584 100644 --- a/app/Espinoso/Handlers/GitHubHandler.php +++ b/app/Espinoso/Handlers/GitHubHandler.php @@ -1,7 +1,7 @@ matchCommand($this->pattern, $updates, $matches); - $this->title = $matches['title'] ?? ''; - - return parent::shouldHandle($updates) && $match; - } + protected $signature = "espi issue "; + protected $description = "genera un issue en el repo"; - public function handle($updates, $context = null) + public function handle(Message $message) { $response = GuzzleClient::post(config('espinoso.url.issues'), [ - 'headers' => [ - 'Authorization' => "token ".config('espinoso.github.token'), - ], - 'json' => ['title' => $this->title] + 'headers' => ['Authorization' => "token ".config('espinoso.token.github')], + 'json' => ['title' => $this->matches['title']] ]); if ($response->getStatusCode() == 201) { $data = json_decode($response->getBody()); - $message = "[Issue creado!]({$data->html_url})"; + $text = "[Issue creado!]({$data->html_url})"; } else { - $message = "No pude crear el issue, status ".$response->getStatusCode()."\n"; - $message .= $response->getBody(); + $text = "No pude crear el issue, status ".$response->getStatusCode()."\n"; + $text .= $response->getBody(); } - Telegram::sendMessage([ - 'chat_id' => $updates->message->chat->id, - 'text' => $message, + $this->telegram->sendMessage([ + 'chat_id' => $message->getChat()->getId(), + 'text' => $text, 'parse_mode' => 'Markdown', ]); } diff --git a/app/Espinoso/Handlers/GoogleInfoBoxHandler.php b/app/Espinoso/Handlers/GoogleInfoBoxHandler.php index ad45576..762643f 100644 --- a/app/Espinoso/Handlers/GoogleInfoBoxHandler.php +++ b/app/Espinoso/Handlers/GoogleInfoBoxHandler.php @@ -3,9 +3,8 @@ namespace App\Espinoso\Handlers; use App\Facades\GoutteClient; -use App\Espinoso\Helpers\Msg; +use Telegram\Bot\Objects\Message; use Symfony\Component\DomCrawler\Crawler; -use Telegram\Bot\Laravel\Facades\Telegram; class GoogleInfoBoxHandler extends EspinosoCommandHandler { @@ -16,29 +15,21 @@ class GoogleInfoBoxHandler extends EspinosoCommandHandler /** * @var string */ - protected $pattern = "(?'i'\binfo\b)(?'query'.+)$"; - protected $query; + protected $pattern = "(?'i'\b(info)\b)(?'query'.+)$"; - public function shouldHandle($updates, $context = null) - { - $match = $this->matchCommand($this->pattern, $updates, $matches); - $this->query = isset($matches['query']) - ? rawurlencode(trim($matches['query'])) - : ''; - - return parent::shouldHandle($updates) && $match; - } + protected $signature = "[espi] info <cosa a buscar>"; + protected $description = "trato de traer data"; - public function handle($updates, $context = null) + public function handle(Message $message) { - $response = $this->buildResponse(); + $response = $this->buildResponse(rawurlencode(trim($this->matches['query']))); $content = collect(explode("\n", $response['message'])); $images = collect($response['images']); if ($images->isNotEmpty()) { $title = $content->shift(); - Telegram::sendPhoto([ - 'chat_id' => $updates->message->chat->id, + $this->telegram->sendPhoto([ + 'chat_id' => $message->getChat()->getId(), 'photo' => $images->first(), 'caption' => $title ]); @@ -49,8 +40,8 @@ public function handle($updates, $context = null) ? "Uhhh... no hay un carajo!!\nO buscaste como el orto o estoy haciendo cualquiera!" // FIXME lang! : $text; - Telegram::sendMessage([ - 'chat_id' => $updates->message->chat->id, + $this->telegram->sendMessage([ + 'chat_id' => $message->getChat()->getId(), 'text' => $text, 'parse_mode' => 'Markdown', ]); @@ -61,17 +52,18 @@ public function handle($updates, $context = null) * Content extracted should be more rich than plain. * For example, it should keep links as Markdown. * + * @param string $query * @return mixed */ - public function buildResponse() + public function buildResponse(string $query) { - $crawler = GoutteClient::request('GET', config('espinoso.url.info') . $this->query); + $crawler = GoutteClient::request('GET', config('espinoso.url.info') . $query); $block = $crawler->filter('#rhs_block'); $message = $this->getText($block); $message = array_filter($message, function ($text) { return !is_null($text); }); - $result['message'] = implode("\n", $this->tunning($message)); + $result['message'] = implode("\n", $this->tuning($message)); $result['images'] = $this->getImages($block); return $result; } @@ -102,7 +94,7 @@ private function getText(Crawler $node) }); } - protected function tunning(array $lines = []) + protected function tuning(array $lines = []) { // Change "Plataforma: :" to "**Plataforma:**" return collect($lines)->map(function ($line) { @@ -126,5 +118,4 @@ private function keyValueSelectors() '._gS' => '._tA', ]; } - -} \ No newline at end of file +} diff --git a/app/Espinoso/Handlers/GoogleStaticMaps.php b/app/Espinoso/Handlers/GoogleStaticMaps.php deleted file mode 100644 index 6562330..0000000 --- a/app/Espinoso/Handlers/GoogleStaticMaps.php +++ /dev/null @@ -1,84 +0,0 @@ -<?php -namespace App\Espinoso\Handlers ; -use App\Espinoso\Helpers\Msg; -use Telegram\Bot\Laravel\Facades\Telegram; - -class GoogleStaticMaps extends EspinosoHandler -{ - const KEYWORD = 'gsm'; - - public function shouldHandle($updates, $context=null) - { - return $this->isTextMessage($updates) && preg_match($this->regex(), $updates->message->text); - } - - public function handle($updates, $context=null) - { - $location = $this->extractLocation($updates->message->text); - $parameters = $this->extractParameters($updates->message->text); - $image = $this->getMapUrl($location, $parameters); - - if (preg_match('/malvinas/i', $updates->message->text)) - Telegram::sendMessage(Msg::plain("Argentinas!")->build($updates)); - - Telegram::sendPhoto([ - 'chat_id' => $updates->message->chat->id, - 'photo' => $image - ]); - - - } - - private function extractLocation($message) - { - preg_match($this->regex(), $message, $matches); - return $matches[2]; - } - - private function extractParameters($message) - { - $regex = '/[ ]*-([a-z]):([^ ][^ ]*)/i'; - $matches = [] ; - $params = [] ; - preg_match_all($regex, $message, $matches, PREG_SET_ORDER); - foreach($matches as [$disposable, $key, $value]) - { - if ($this->isValidParamKey($key)) - $params[$this->getParamName($key)] = $value; - } - return $params ; - } - - private function getMapUrl($location, $params) - { - extract(array_merge($this->defaults(), $params)); - - $location = urlencode($location); - return "https://maps.googleapis.com/maps/api/staticmap?center=$location&zoom=$zoom&size=$size&maptype=$maptype&markers=color:$color%7Clabel:S%7C$location"; - } - - private function regex() - { - return "/^" . self::KEYWORD . "([ ]*\-[a-z]:[^ ])*[ ]*(.*)$/i"; - } - - private function defaults() - { - return ['maptype' => 'roadmap', 'zoom'=> 13, 'size'=>"600x500", 'color'=>'blue']; - } - - private function paramsMapping() - { - return [ 'z' => 'zoom', 't' => 'maptype', 's' => 'size', 'c' => 'color', ]; - } - - private function isValidParamKey($key) - { - return array_key_exists($key, $this->paramsMapping()); - } - - private function getParamName($key) - { - return $this->paramsMapping()[$key]; - } -} \ No newline at end of file diff --git a/app/Espinoso/Handlers/GoogleStaticMapsHandler.php b/app/Espinoso/Handlers/GoogleStaticMapsHandler.php new file mode 100644 index 0000000..79a3a6b --- /dev/null +++ b/app/Espinoso/Handlers/GoogleStaticMapsHandler.php @@ -0,0 +1,119 @@ +<?php namespace App\Espinoso\Handlers; + +use Telegram\Bot\Objects\Message; + +class GoogleStaticMapsHandler extends EspinosoCommandHandler +{ + protected $allow_ignore_prefix = true; + + /** + * gsm [param] address + * -z : numeric zoom + * example: gsm -z:10 malvinas argentinas + * gsm malvinas argentinas + * @var string + */ + protected $pattern = "(?'gsm'\b(gsm)\b)\s+(?'params'(\S+:\S+\s+)*)(?'address'.+)$"; + + protected $signature = "[espi] gsm <lugar>"; + protected $description = "te tiro un mapa... tiene algunos params pero me da paja decírtelos"; + + /** + * Default options + * + * @var array + */ + protected $options = [ + 'maptype' => 'roadmap', + 'zoom' => 12, + 'size' => '600x500', + 'color' => 'blue' + ]; + + /** + * @var array + */ + protected $shortcuts = [ + 't' => 'maptype', + 'z' => 'zoom', + 's' => 'size', + 'c' => 'color', + ]; + + /** + * @param Message $message + */ + public function handle(Message $message) + { + $address = $this->getAddress(); + $image = $this->getMap($address, $this->getOptions($address)); + $address .= str_contains(strtolower($address), 'malvinas') ? ', Argentinas!' : ''; + + $this->telegram->sendPhoto([ + 'chat_id' => $message->getChat()->getId(), + 'photo' => $image, + 'caption' => $address + ]); + } + + /** + * @return string + */ + protected function getAddress() + { + return $this->matches['address'] ?? ''; + } + + /** + * It's not a really cool method... + * + * @param string $address + * @return string + */ + protected function getOptions(string $address) + { + $defaults = collect($this->options); + $params = isset($this->matches['params']) ? clean_string($this->matches['params']) : ''; + $params = explode(' ', $params); + $params = collect($params)->mapWithKeys(function ($param) { + $param = explode(':', $param); + return [$this->parseParamKey($param[0]) => $param[1]]; + }); + + $options = $defaults->merge($params); + $color = $options->get('color'); + $options->forget('color'); + $options->put('markers', "color:{$color}|label:X|{$address}"); + + return $options->map(function ($value, $key) { + return "{$key}={$value}"; + })->implode('&'); + } + + /** + * If key is shortcut, return return origin param name + * + * @param string $key + * @return mixed|string + */ + protected function parseParamKey(string $key) + { + $shortcuts = collect($this->shortcuts); + + return $shortcuts->has($key) ? $shortcuts->get($key) : $key; + } + + /** + * Just url... + * + * @param $address + * @param $options + * @return string + */ + protected function getMap($address, $options) + { + $address = urlencode($address); + + return config('espinoso.url.map') . "?center={$address}&{$options}"; + } +} \ No newline at end of file diff --git a/app/Espinoso/Handlers/HelpHandler.php b/app/Espinoso/Handlers/HelpHandler.php new file mode 100644 index 0000000..414a4e8 --- /dev/null +++ b/app/Espinoso/Handlers/HelpHandler.php @@ -0,0 +1,31 @@ +<?php namespace App\Espinoso\Handlers; + +use Telegram\Bot\Objects\Message; + +class HelpHandler extends EspinosoCommandHandler +{ + /** + * @var string + */ + protected $pattern = "(ayuda|help|aiiiuuuda)(!)*"; + + protected $signature = "espi help|ayuda|aiiiuuda"; + protected $description = "muestra cosas que entiendo"; + + public function handle(Message $message) + { + $data = $this->espinoso->getHandlers()->map(function ($handler) { + return new $handler($this->espinoso, $this->telegram); + })->map(function (EspinosoHandler $handler) { + return $handler->help(); + })->reject(function (string $help) { + return empty($help); + })->implode("\n"); + + return $this->telegram->sendMessage([ + 'chat_id' => $message->getChat()->getId(), + 'text' => "Entiendo masomenos estas cosas:\n\n{$data}", + 'parse_mode' => 'Markdown' + ]); + } +} diff --git a/app/Espinoso/Handlers/IMDbHandler.php b/app/Espinoso/Handlers/IMDbHandler.php new file mode 100644 index 0000000..ad63939 --- /dev/null +++ b/app/Espinoso/Handlers/IMDbHandler.php @@ -0,0 +1,116 @@ +<?php namespace App\Espinoso\Handlers; + +use Imdb\Title; +use App\Facades\IMDbSearch; +use Telegram\Bot\Objects\Message; + +/** + * Class IMDbHandler + * @package App\Espinoso\Handlers + */ +class IMDbHandler extends EspinosoCommandHandler +{ + /** + * @var string + */ + protected $pattern = "(?'type'\b(imdb|movie|peli|serie|tv)\b)(?'query'.+)"; + + protected $signature = "espi imdb|movie|peli|serie|tv <cosa a buscar>"; + protected $description = "busco pelis y series, vieja!"; + + /** + * @var array + */ + protected $types = [ + 'imdb' => [Title::MOVIE, Title::TV_SERIES], + 'movie' => [Title::MOVIE], + 'peli' => [Title::MOVIE], + 'serie' => [Title::TV_SERIES], + 'tv' => [Title::TV_SERIES], + ]; + + /** + * @param Message $message + */ + public function handle(Message $message) + { + $types = $this->parseTypes($this->matches['type']); + $result = $this->getData($this->matches['query'], $types); + + if (empty($result)) { + $this->replyError($message); + return; + } + + $matching = $result[0]; + + if (!empty($matching->photo())) { + $this->telegram->sendPhoto([ + 'chat_id' => $message->getChat()->getId(), + 'photo' => $matching->photo(), + 'caption' => $matching->title() + ]); + } + + $this->telegram->sendMessage([ + 'chat_id' => $message->getChat()->getId(), + 'text' => $this->parseAsMarkdown($matching), + 'parse_mode' => 'Markdown', + ]); + } + + /* + * Internals + */ + + /** + * @param string $key + * @return array + */ + protected function parseTypes(string $key) + { + $types = collect($this->types); + + return $types->has($key) ? $types->get($key) : []; + } + + /** + * @param string $query + * @param array $types + * @return mixed + */ + protected function getData(string $query, array $types = []) + { + return IMDbSearch::search(trim($query), $types); + } + + /** + * @param Title $result + * @return string + */ + protected function parseAsMarkdown(Title $result) + { + $star = "\u{2B50}"; + $sinopsis = str_limit(trim($result->storyline()), 250); + $cast = collect($result->cast())->take(3)->pluck('name')->implode(', '); + $genres = collect($result->genres())->implode(', '); + $seasons = $result->seasons() > 0 ? "\n*Seasons:* {$result->seasons()}" : ''; + $directors = collect($result->director())->take(3)->pluck('name')->implode(', '); + $creators = empty($result->creator()) + ? '' + : "\n*Creators:* " . collect($result->creator())->take(3)->pluck('name')->implode(', '); + $writers = collect($result->writing())->take(3)->pluck('name')->implode(', '); + + return "*{$result->title()}* ({$result->year()}) +{$star} {$result->rating()}/10 | {$result->runtime()}min +_{$genres}_ + +{$sinopsis} +{$seasons}{$creators} +*Writers:* {$writers} +*Directors:* {$directors} +*Cast:* {$cast} + +[View on IMDb]({$result->main_url()})"; + } +} diff --git a/app/Espinoso/Handlers/ImdbHandler.php b/app/Espinoso/Handlers/ImdbHandler.php deleted file mode 100644 index 1625c11..0000000 --- a/app/Espinoso/Handlers/ImdbHandler.php +++ /dev/null @@ -1,67 +0,0 @@ -<?php namespace App\Espinoso\Handlers; - -use App\Espinoso\Helpers\Msg; -use App\Espinoso\Handlers\ImdbScraper\Imdb; -use Telegram\Bot\Laravel\Facades\Telegram; - -class ImdbHandler extends EspinosoHandler -{ - const KEYWORD = 'imdb'; - - public function shouldHandle($updates, $context=null) - { - return $this->isTextMessage($updates) && preg_match($this->regex(), $updates->message->text); - } - - public function handle($updates, $context=null) - { - $response = $this->buildResponse($updates->message->text); - $response = Msg::md($response); - return Telegram::sendMessage( $response->build($updates) ); - } - - private function extractName($message) - { - preg_match($this->regex(), $message, $matches); - return $matches['name']; - } - - - private function regex() - { - return "/^" . self::KEYWORD . "[ ]*(?'name'.*)$/i"; - } - - /** - * @param $updates - */ - public function buildResponse($text) - { - $name = $this->extractName($text); - $data = $this->movieInfo($name); - - $msg = "``` -{$data['TITLE']} -{$data['IMDB_URL']} -Rating: {$data['RATING']} -Plot: {$data['PLOT']} -Release: {$data['RELEASE_DATES'][0]}```"; - - return $msg; - } - - /** - * @param $imdb - * @param $name - * @return mixed - */ - private function movieInfo($name) - { - $imdb = new Imdb; - $output = $imdb->getMovieInfo($name); - - return array_change_key_case($output, CASE_UPPER); - } - - -} \ No newline at end of file diff --git a/app/Espinoso/Handlers/ImdbScraper/Imdb.php b/app/Espinoso/Handlers/ImdbScraper/Imdb.php deleted file mode 100644 index 5792e43..0000000 --- a/app/Espinoso/Handlers/ImdbScraper/Imdb.php +++ /dev/null @@ -1,248 +0,0 @@ -<?php - -namespace App\Espinoso\Handlers\ImdbScraper; - -///////////////////////////////////////////////////////////////////////////////////////////////////////// -// Free PHP IMDb Scraper API for the new IMDb Template. -// Version: 4.4 -// Author: Abhinay Rathore -// Website: http://www.AbhinayRathore.com -// Blog: http://web3o.blogspot.com -// Demo: http://lab.abhinayrathore.com/imdb/ -// More Info: http://web3o.blogspot.com/2010/10/php-imdb-scraper-for-new-imdb-template.html -// Last Updated: May 6, 2014 -///////////////////////////////////////////////////////////////////////////////////////////////////////// - - -class Imdb -{ - // Get movie information by Movie Title. - // This method searches the given title on Google, Bing or Ask to get the best possible match. - public function getMovieInfo($title, $getExtraInfo = true) - { - $imdbId = $this->getIMDbIdFromSearch(trim($title)); - if($imdbId === NULL){ - $arr = array(); - $arr['error'] = "No Title found in Search Results!"; - return $arr; - } - return $this->getMovieInfoById($imdbId, $getExtraInfo); - } - - // Get movie information by IMDb Id. - public function getMovieInfoById($imdbId, $getExtraInfo = true) - { - $arr = array(); - $imdbUrl = "http://www.imdb.com/title/" . trim($imdbId) . "/"; - return $this->scrapeMovieInfo($imdbUrl, $getExtraInfo); - } - - // Scrape movie information from IMDb page and return results in an array. - private function scrapeMovieInfo($imdbUrl, $getExtraInfo = true) - { - $arr = array(); - $html = $this->geturl("${imdbUrl}combined"); - $title_id = $this->match('/<link rel="canonical" href="http:\/\/www.imdb.com\/title\/(tt\d+)\/combined" \/>/ms', $html, 1); - if(empty($title_id) || !preg_match("/tt\d+/i", $title_id)) { - $arr['error'] = "No Title found on IMDb!"; - return $arr; - } - $arr['title_id'] = $title_id; - $arr['imdb_url'] = $imdbUrl; - $arr['title'] = str_replace('"', '', trim($this->match('/<title>(IMDb \- )*(.*?) \(.*?<\/title>/ms', $html, 2))); - $arr['original_title'] = trim($this->match('/class="title-extra">(.*?)</ms', $html, 1)); - $arr['year'] = trim($this->match('/<title>.*?\(.*?(\d{4}).*?\).*?<\/title>/ms', $html, 1)); - $arr['rating'] = $this->match('/<b>(\d.\d)\/10<\/b>/ms', $html, 1); - $arr['genres'] = $this->match_all('/<a.*?>(.*?)<\/a>/ms', $this->match('/Genre.?:(.*?)(<\/div>|See more)/ms', $html, 1), 1); - $arr['directors'] = $this->match_all_key_value('/<td valign="top"><a.*?href="\/name\/(.*?)\/">(.*?)<\/a>/ms', $this->match('/Directed by<\/a><\/h5>(.*?)<\/table>/ms', $html, 1)); - $arr['writers'] = $this->match_all_key_value('/<td valign="top"><a.*?href="\/name\/(.*?)\/">(.*?)<\/a>/ms', $this->match('/Writing credits<\/a><\/h5>(.*?)<\/table>/ms', $html, 1)); - $arr['cast'] = $this->match_all_key_value('/<td class="nm"><a.*?href="\/name\/(.*?)\/".*?>(.*?)<\/a>/ms', $this->match('/<h3>Cast<\/h3>(.*?)<\/table>/ms', $html, 1)); - $arr['cast'] = array_slice($arr['cast'], 0, 30); - $arr['stars'] = array_slice($arr['cast'], 0, 5); - $arr['producers'] = $this->match_all_key_value('/<td valign="top"><a.*?href="\/name\/(.*?)\/">(.*?)<\/a>/ms', $this->match('/Produced by<\/a><\/h5>(.*?)<\/table>/ms', $html, 1)); - $arr['musicians'] = $this->match_all_key_value('/<td valign="top"><a.*?href="\/name\/(.*?)\/">(.*?)<\/a>/ms', $this->match('/Original Music by<\/a><\/h5>(.*?)<\/table>/ms', $html, 1)); - $arr['cinematographers'] = $this->match_all_key_value('/<td valign="top"><a.*?href="\/name\/(.*?)\/">(.*?)<\/a>/ms', $this->match('/Cinematography by<\/a><\/h5>(.*?)<\/table>/ms', $html, 1)); - $arr['editors'] = $this->match_all_key_value('/<td valign="top"><a.*?href="\/name\/(.*?)\/">(.*?)<\/a>/ms', $this->match('/Film Editing by<\/a><\/h5>(.*?)<\/table>/ms', $html, 1)); - $arr['mpaa_rating'] = $this->match('/MPAA<\/a>:<\/h5><div class="info-content">Rated (G|PG|PG-13|PG-14|R|NC-17|X) /ms', $html, 1); - $arr['release_date'] = $this->match('/Release Date:<\/h5>.*?<div class="info-content">.*?([0-9][0-9]? (January|February|March|April|May|June|July|August|September|October|November|December) (19|20)[0-9][0-9])/ms', $html, 1); - $arr['tagline'] = trim(strip_tags($this->match('/Tagline:<\/h5>.*?<div class="info-content">(.*?)(<a|<\/div)/ms', $html, 1))); - $arr['plot'] = trim(strip_tags($this->match('/Plot:<\/h5>.*?<div class="info-content">(.*?)(<a|<\/div|\|)/ms', $html, 1))); - $arr['plot_keywords'] = $this->match_all('/<a.*?>(.*?)<\/a>/ms', $this->match('/Plot Keywords:<\/h5>.*?<div class="info-content">(.*?)<\/div/ms', $html, 1), 1); - $arr['poster'] = $this->match('/<a name="poster".*?><img.*?id="primary-poster".*?src="(.*?)"/ms', $html, 1); - $arr['poster_large'] = ""; - $arr['poster_full'] = ""; - if ($arr['poster'] != '' && strpos($arr['poster'], "amazon.com") > 0) { //Get large and small posters - $arr['poster'] = preg_replace('/_V1.*?.jpg/ms', "_V1._SY200.jpg", $arr['poster']); - $arr['poster_large'] = preg_replace('/_V1.*?.jpg/ms', "_V1._SY500.jpg", $arr['poster']); - $arr['poster_full'] = preg_replace('/_V1.*?.jpg/ms', "_V1._SY0.jpg", $arr['poster']); - } else { - $arr['poster'] = ""; - } - $arr['runtime'] = trim($this->match('/Runtime:<\/h5><div class="info-content">.*?(\d+) min.*?<\/div>/ms', $html, 1)); - $arr['top_250'] = trim($this->match('/Top 250: #(\d+)</ms', $html, 1)); - $arr['oscars'] = trim($this->match('/Won (\d+) Oscars?\./ms', $html, 1)); - if(empty($arr['oscars']) && preg_match("/Won Oscar\./i", $html)) $arr['oscars'] = "1"; - $arr['awards'] = trim($this->match('/(\d+) wins/ms',$html, 1)); - $arr['nominations'] = trim($this->match('/(\d+) nominations/ms',$html, 1)); - $arr['votes'] = $this->match('/>([0-9,]*) votes</ms', $html, 1); - $arr['language'] = $this->match_all('/<a.*?>(.*?)<\/a>/ms', $this->match('/Language.?:(.*?)(<\/div>|>.?and )/ms', $html, 1), 1); - $arr['country'] = $this->match_all('/<a.*?>(.*?)<\/a>/ms', $this->match('/Country:(.*?)(<\/div>|>.?and )/ms', $html, 1), 1); - - if($getExtraInfo == true) { - $plotPageHtml = $this->geturl("${imdbUrl}plotsummary"); - $arr['storyline'] = trim(strip_tags($this->match('/<li class="odd">.*?<p>(.*?)(<|<\/p>)/ms', $plotPageHtml, 1))); - $releaseinfoHtml = $this->geturl("http://www.imdb.com/title/" . $arr['title_id'] . "/releaseinfo"); - $arr['also_known_as'] = $this->getAkaTitles($releaseinfoHtml); - $arr['release_dates'] = $this->getReleaseDates($releaseinfoHtml); - $arr['recommended_titles'] = $this->getRecommendedTitles($arr['title_id']); - $arr['media_images'] = $this->getMediaImages($arr['title_id']); - $arr['videos'] = $this->getVideos($arr['title_id']); - } - - return $arr; - } - - // Scan all Release Dates. - private function getReleaseDates($html){ - $releaseDates = array(); - foreach($this->match_all('/<tr.*?>(.*?)<\/tr>/ms', $this->match('/<table id="release_dates".*?>(.*?)<\/table>/ms', $html, 1), 1) as $r) { - $country = trim(strip_tags($this->match('/<td>(.*?)<\/td>/ms', $r, 1))); - $date = trim(strip_tags($this->match('/<td class="release_date">(.*?)<\/td>/ms', $r, 1))); - array_push($releaseDates, $country . " = " . $date); - } - return array_filter($releaseDates); - } - - // Scan all AKA Titles. - private function getAkaTitles($html){ - $akaTitles = array(); - foreach($this->match_all('/<tr.*?>(.*?)<\/tr>/msi', $this->match('/<table id="akas".*?>(.*?)<\/table>/ms', $html, 1), 1) as $m) { - $akaTitleMatch = $this->match_all('/<td>(.*?)<\/td>/ms', $m, 1); - $akaCountry = trim($akaTitleMatch[0]); - $akaTitle = trim($akaTitleMatch[1]); - array_push($akaTitles, $akaTitle . " = " . $akaCountry); - } - return array_filter($akaTitles); - } - - // Collect all Media Images. - private function getMediaImages($titleId){ - $url = "http://www.imdb.com/title/" . $titleId . "/mediaindex"; - $html = $this->geturl($url); - $media = array(); - $media = array_merge($media, $this->scanMediaImages($html)); - foreach($this->match_all('/<a.*?>(\d*)<\/a>/ms', $this->match('/<span class="page_list">(.*?)<\/span>/ms', $html, 1), 1) as $p) { - $html = $this->geturl($url . "?page=" . $p); - $media = array_merge($media, $this->scanMediaImages($html)); - } - return $media; - } - - // Scan all media images. - private function scanMediaImages($html){ - $pics = array(); - foreach($this->match_all('/src="(.*?)"/msi', $this->match('/<div class="media_index_thumb_list".*?>(.*?)<\/div>/msi', $html, 1), 1) as $i) { - array_push($pics, preg_replace('/_V1\..*?.jpg/ms', "_V1._SY0.jpg", $i)); - } - return array_filter($pics); - } - - // Get recommended titles by IMDb title id. - public function getRecommendedTitles($titleId){ - $json = $this->geturl("http://www.imdb.com/widget/recommendations/_ajax/get_more_recs?specs=p13nsims%3A${titleId}"); - $resp = json_decode($json, true); - $arr = array(); - if(isset($resp["recommendations"])) { - foreach($resp["recommendations"] as $val) { - $name = $this->match('/title="(.*?)"/msi', $val['content'], 1); - $arr[$val['tconst']] = $name; - } - } - return array_filter($arr); - } - - // Get all Videos and Trailers - public function getVideos($titleId){ - $html = $this->geturl("http://www.imdb.com/title/${titleId}/videogallery"); - $videos = array(); - foreach ($this->match_all('/<a.*?href="(\/video\/imdb\/vi\d+)".*?>.*?<\/a>/ms', $html, 1) as $v) { - $videos[] = "http://www.imdb.com${v}"; - } - return array_filter($videos); - } - - // Get Top 250 Movie List - public function getTop250(){ - $html = $this->geturl("http://www.imdb.com/chart/top"); - $top250 = array(); - $rank = 1; - foreach ($this->match_all('/<tr class="(even|odd)">(.*?)<\/tr>/ms', $html, 2) as $m) { - $id = $this->match('/<td class="titleColumn">.*?<a href="\/title\/(tt\d+)\/.*?"/msi', $m, 1); - $title = $this->match('/<td class="titleColumn">.*?<a.*?>(.*?)<\/a>/msi', $m, 1); - $year = $this->match('/<td class="titleColumn">.*?<span class="secondaryInfo">\((.*?)\)<\/span>/msi', $m, 1); - $rating = $this->match('/<td class="ratingColumn"><strong.*?>(.*?)<\/strong>/msi', $m, 1); - $poster = $this->match('/<td class="posterColumn">.*?<img src="(.*?)"/msi', $m, 1); - $poster = preg_replace('/_V1.*?.jpg/ms', "_V1._SY200.jpg", $poster); - $url = "http://www.imdb.com/title/${id}/"; - $top250[] = array("id"=>$id, "rank"=>$rank, "title"=>$title, "year"=>$year, "rating"=>$rating, "poster"=>$poster, "url"=>$url); - $rank++; - } - return $top250; - } - - //************************[ Extra Functions ]****************************** - - // Movie title search on Google, Bing or Ask. If search fails, return FALSE. - private function getIMDbIdFromSearch($title, $engine = "google"){ - switch ($engine) { - case "google": $nextEngine = "bing"; break; - case "bing": $nextEngine = "ask"; break; - case "ask": $nextEngine = FALSE; break; - case FALSE: return NULL; - default: return NULL; - } - $url = "http://www.${engine}.com/search?q=imdb+" . rawurlencode($title); - $ids = $this->match_all('/<a.*?href="http:\/\/www.imdb.com\/title\/(tt\d+).*?".*?>.*?<\/a>/ms', $this->geturl($url), 1); - if (!isset($ids[0]) || empty($ids[0])) //if search failed - return $this->getIMDbIdFromSearch($title, $nextEngine); //move to next search engine - else - return $ids[0]; //return first IMDb result - } - - private function geturl($url){ - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); - $ip=rand(0,255).'.'.rand(0,255).'.'.rand(0,255).'.'.rand(0,255); - curl_setopt($ch, CURLOPT_HTTPHEADER, array("REMOTE_ADDR: $ip", "HTTP_X_FORWARDED_FOR: $ip")); - curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/".rand(3,5).".".rand(0,3)." (Windows NT ".rand(3,5).".".rand(0,2)."; rv:2.0.1) Gecko/20100101 Firefox/".rand(3,5).".0.1"); - $html = curl_exec($ch); - curl_close($ch); - return $html; - } - - private function match_all_key_value($regex, $str, $keyIndex = 1, $valueIndex = 2){ - $arr = array(); - preg_match_all($regex, $str, $matches, PREG_SET_ORDER); - foreach($matches as $m){ - $arr[$m[$keyIndex]] = $m[$valueIndex]; - } - return $arr; - } - - private function match_all($regex, $str, $i = 0){ - if(preg_match_all($regex, $str, $matches) === false) - return false; - else - return $matches[$i]; - } - - private function match($regex, $str, $i = 0){ - if(preg_match($regex, $str, $match) == 1) - return $match[$i]; - else - return false; - } -} -?> diff --git a/app/Espinoso/Handlers/ImdbScraper/ImdbWebService.php b/app/Espinoso/Handlers/ImdbScraper/ImdbWebService.php deleted file mode 100644 index 6175627..0000000 --- a/app/Espinoso/Handlers/ImdbScraper/ImdbWebService.php +++ /dev/null @@ -1,68 +0,0 @@ -<?php -// -/////////////////////////////////////////////////////////////////////////////////////////////////////////// -//// Free PHP IMDb Scraper Web Service API -//// Author: Abhinay Rathore -//// Website: http://www.AbhinayRathore.com -//// Blog: http://web3o.blogspot.com -//// Demo: http://lab.abhinayrathore.com/imdb/ -//// More Info: http://web3o.blogspot.com/2010/10/php-imdb-scraper-for-new-imdb-template.html -//// Last Updated: July 3, 2011 -/////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -//use App\Espinoso\Handlers\ImdbScraper\Imdb; -// -//include_once("Imdb.php"); -// -//$movieName = $_REQUEST["m"] ?? ''; -//$output = strtolower($_REQUEST["o"] ?? ''); -//if($output != "xml" && $output != "json" && $output != "jsonp"){ -// $output = "xml"; //Set default to XML -//} -// -//$i = new Imdb(); -//$mArr = array_change_key_case($i->getMovieInfo($movieName), CASE_UPPER); -// -/////////////////[ XML Output ]///////////////// -//if($output == "xml") { -// header("Content-Type: text/xml"); -// $doc = new DomDocument('1.0'); -// $doc->formatOutput = true; -// $movie = $doc->createElement('MOVIE'); -// $movie = $doc->appendChild($movie); -// foreach ($mArr as $k=>$v){ -// if(is_array($v)){ -// $node = $doc->createElement($k); -// $node = $movie->appendChild($node); -// $c = 0; -// foreach($v as $a){ -// $c++; -// $child = $doc->createElement($k . "_"); -// $child = $node->appendChild($child); -// $child->setAttribute('n', $c); -// $value = $doc->createTextNode($a); -// $value = $child->appendChild($value); -// } -// } else { -// $node = $doc->createElement($k); -// $node = $movie->appendChild($node); -// $value = $doc->createTextNode($v); -// $value = $node->appendChild($value); -// } -// } -// $xml_string = $doc->saveXML(); -// echo $xml_string; -//} //End XML Outout -// -/////////////////[ JSON Output ]///////////////// -//if($output == "json") { -// header('Content-type: application/json'); -// echo json_encode($mArr); -//} //End JSON Outout -// -/////////////////[ JSONP Output ]///////////////// -//if($output == "jsonp") { -// header('Content-type: application/json'); -// echo isset($_GET['callback']) ? $_GET['callback']."(". json_encode($mArr) .")" : json_encode($mArr); -//} //End JSONP Outout -//?> diff --git a/app/Espinoso/Handlers/ImdbScraper/LICENSE b/app/Espinoso/Handlers/ImdbScraper/LICENSE deleted file mode 100644 index 787ca5b..0000000 --- a/app/Espinoso/Handlers/ImdbScraper/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2013 Abhinay Rathore - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/app/Espinoso/Handlers/ImdbScraper/README.md b/app/Espinoso/Handlers/ImdbScraper/README.md deleted file mode 100644 index 0e47596..0000000 --- a/app/Espinoso/Handlers/ImdbScraper/README.md +++ /dev/null @@ -1,4 +0,0 @@ -PHP-IMDb-Scraper -================ - -Demo: http://lab.abhinayrathore.com/imdb/ diff --git a/app/Espinoso/Handlers/InstagramHandler.php b/app/Espinoso/Handlers/InstagramHandler.php new file mode 100644 index 0000000..3c91832 --- /dev/null +++ b/app/Espinoso/Handlers/InstagramHandler.php @@ -0,0 +1,64 @@ +<?php namespace App\Espinoso\Handlers; + +use App\Facades\InstagramSearch; +use Telegram\Bot\Objects\Message; +use Vinkla\Instagram\InstagramException; + +class InstagramHandler extends EspinosoCommandHandler +{ + protected $allow_ignore_prefix = true; + protected $pattern = "(\b(ig)\b)(\s+)(?'username'\b(\S+)\b)(?'param'\s*(last|pos:\d+))?$"; + + protected $signature = "[espi] ig username [last|pos:n]"; + protected $description = "y... fijate"; + + + public function handle(Message $message) + { + try { + $username = $this->getUsername($message->getText()); + + $this->telegram->sendPhoto([ + 'chat_id' => $message->getChat()->getId(), + 'photo' => $this->getImage($username, $this->getParam()), + 'caption' => "Ver https://www.instagram.com/{$username}" + ]); + } catch (InstagramException $e) { + $this->replyNotFound($message); + } + } + + protected function getUsername() + { + return trim($this->matches['username']); + } + + protected function getParam() + { + if (empty($this->matches['param'])) { + return 'random'; + } + + $param = trim($this->matches['param']); + + if ($param == 'last') { + return 0; + } + + if (starts_with($param, 'pos:')) { + return intval(explode(':', $param)[1]); + } + + return $param; + + } + + protected function getImage($username, $param) + { + $response = InstagramSearch::get($username); + + $i = $param === 'random' ? rand(0, count($response) - 1) : intval($param); + + return $response[$i]['images']['standard_resolution']['url']; + } +} diff --git a/app/Espinoso/Handlers/NextHolidayHandler.php b/app/Espinoso/Handlers/NextHolidayHandler.php deleted file mode 100644 index 2b263a9..0000000 --- a/app/Espinoso/Handlers/NextHolidayHandler.php +++ /dev/null @@ -1,58 +0,0 @@ -<?php -/** - * Created by PhpStorm. - * User: alan - * Date: 5/5/17 - * Time: 6:56 PM - */ - -namespace App\Espinoso\Handlers; - - -use App\Espinoso\Helpers\Msg; -use Goutte\Client; -use Telegram\Bot\Laravel\Facades\Telegram; - -class NextHolidayHandler extends EspinosoHandler -{ - - public function shouldHandle($updates, $context = null) - { - return $this->isTextMessage($updates) && preg_match('/feriado.?$/i', $updates->message->text); - } - - public function handle($updates, $context = null) - { - $holidays = $this->getHolidays(); - - $message = "Manga de vagos, **quedan " . count($holidays) . " feriados** en todo el año.\n"; - - foreach ($holidays as $holiday) { - $message .= ' - **' . $holiday->phrase . '**, ' . $holiday->description . ' (' . $holiday->count . " días)\n"; - } - - Telegram::sendMessage(Msg::md($message)->build($updates)); - } - - /** - * Método dedicado a Dan. Chorea data de elproximoferiado.com y de algún modo saca - * un json que tienen guardado en un <script> y lo transforma en objects. - * - * @return array - */ - private function getHolidays() - { - $client = new Client(); - $crawler = $client->request('GET', 'http://www.elproximoferiado.com/'); - - // here starts crap - $data = str_replace("\n", "", $crawler->filter('script')->first()->text()); - $data = str_replace("\t", "", $data); - $data = str_replace("var json = '", '', $data); - $data = str_replace("';var position = 0;", '', $data); - - // here finishes crap - - return json_decode($data); - } -} \ No newline at end of file diff --git a/app/Espinoso/Handlers/NextHolidaysHandler.php b/app/Espinoso/Handlers/NextHolidaysHandler.php new file mode 100644 index 0000000..a62e1a6 --- /dev/null +++ b/app/Espinoso/Handlers/NextHolidaysHandler.php @@ -0,0 +1,54 @@ +<?php namespace App\Espinoso\Handlers; + +use stdClass; +use App\Facades\GoutteClient; +use Telegram\Bot\Objects\Message; + +class NextHolidaysHandler extends EspinosoCommandHandler +{ + /** + * @var string + */ + protected $pattern = "(\b(pr(o|ó)x(imo(s?))?)\b\s+)?(\b(feriado(s?))\b)$"; + + protected $signature = "espi feriados"; + protected $description = "feriados para rascarse la pelusa"; + + + public function handle(Message $message) + { + $holidays = collect($this->getHolidays()); + $count = $holidays->count(); + $list = $holidays->map(function (stdClass $holiday) { + return " - *{$holiday->phrase}*, {$holiday->description} ({$holiday->count} días)"; + })->implode("\n"); + + $text = "Manga de vagos, *quedan {$count} feriados* en todo el año.\n{$list}"; + + $this->telegram->sendMessage([ + 'chat_id' => $message->getChat()->getId(), + 'text' => $text, + 'parse_mode' => 'Markdown' + ]); + } + + /** + * Método dedicado a Dan. Chorea data de elproximoferiado.com y de algún modo saca + * un json que tienen guardado en un <script> y lo transforma en objects. + * + * @return array + */ + private function getHolidays() + { + $crawler = GoutteClient::request('GET', config('espinoso.url.holidays')); + + // here starts crap + $data = str_replace("\n", "", $crawler->filter('script')->eq(2)->text()); + $data = str_replace("\t", "", $data); + $data = str_replace("var json = '", '', $data); + $data = str_replace("';var position = 0;", '', $data); + // here finishes crap + + return json_decode($data); + } +} diff --git a/app/Espinoso/Handlers/RandomInstagram.php b/app/Espinoso/Handlers/RandomInstagram.php deleted file mode 100644 index ab34e1a..0000000 --- a/app/Espinoso/Handlers/RandomInstagram.php +++ /dev/null @@ -1,52 +0,0 @@ -<?php -namespace App\Espinoso\Handlers ; - -use Telegram\Bot\Laravel\Facades\Telegram; -use Vinkla\Instagram\Instagram; -use Vinkla\Instagram\InstagramException; -use App\Espinoso\Helpers\Msg; - -class RandomInstagram extends EspinosoHandler -{ - const KEYWORD = 'ig'; - - public function shouldHandle($updates, $context=null) - { - return $this->isTextMessage($updates) && preg_match($this->regex(), $updates->message->text); - } - - public function handle($updates, $context=null) - { - try { - $user = $this->extract_user($updates->message->text); - $image = $this->get_random_image($user); - return Telegram::sendPhoto([ - 'chat_id' => $updates->message->chat->id, - 'photo' => $image - ]); - } catch (InstagramException $e) { - return Telegram::sendMessage( Msg::plain("no papu, le erraste de instagram")->build($updates) ); - } - } - - private function extract_user($message) - { - preg_match($this->regex(), $message, $matches); - return $matches[1]; - } - - private function get_random_image($user) - { - $instagram = new Instagram(); - $response = $instagram->get($user); - if (empty($response)) - throw new InstagramException("no media found"); - $i = array_rand($response); - return $response[$i]['images']['standard_resolution']['url']; - } - - private function regex() - { - return "/^" . self::KEYWORD . "[ ]*([^ ]*)$/i"; - } -} diff --git a/app/Espinoso/Handlers/ResponseByMatch.php b/app/Espinoso/Handlers/ResponseByMatch.php deleted file mode 100644 index 89d805f..0000000 --- a/app/Espinoso/Handlers/ResponseByMatch.php +++ /dev/null @@ -1,67 +0,0 @@ -<?php -namespace App\Espinoso\Handlers ; - -use \App\Espinoso\Helpers\Msg; -use Telegram\Bot\Laravel\Facades\Telegram; - -class ResponseByMatch extends EspinosoHandler -{ - public function shouldHandle($updates, $context=null) - { - if ( ! $this->isTextMessage($updates) ) return false ; - - foreach ($this->mappings() as $needle => $response) - if ( preg_match($needle, $updates->message->text) ) - return true ; - return false ; - } - - public function handle($updates, $context=null) - { - if ($this->ignoringSender($updates->message->from)) - { - $fromName = $updates->message->from->first_name; - $msg = Msg::md("Con vos no hablo porque no viniste al asado $fromName")->build($updates); - Telegram::sendMessage($msg); - return ; - } - - foreach ($this->mappings() as $pattern => $response) - { - if ( preg_match($pattern, $updates->message->text) ) - { - $msg = $this->buildMessage($response, $pattern, $updates); - Telegram::sendMessage($msg); - } - } - } - - private function buildMessage($response, $pattern, $updates) - { - if ($response instanceof Msg) - return $response->build($updates, $pattern); - else - return Msg::plain($response)->build($updates, $pattern); - } - - private function mappings() - { - return config('espinoso_data.ResponseByMatch.mappings'); - } - - private function ignoredNames() - { - return config('espinoso_data.ResponseByMatch.ignore_names'); - } - - private function ignoringSender($sender) - { - foreach ($this->ignoredNames() as $name) - if ( preg_match("/$name/i", $sender->first_name) ) - return true ; - return false ; - } - -} - - diff --git a/app/Espinoso/Handlers/StickersHandler.php b/app/Espinoso/Handlers/StickersHandler.php index 7ab4dde..424cd07 100644 --- a/app/Espinoso/Handlers/StickersHandler.php +++ b/app/Espinoso/Handlers/StickersHandler.php @@ -1,9 +1,13 @@ <?php namespace App\Espinoso\Handlers; -use Telegram\Bot\Laravel\Facades\Telegram; +use Telegram\Bot\Objects\Message; class StickersHandler extends EspinosoCommandHandler { + /** + * @var bool + */ + protected $allow_ignore_prefix = true; /** * FIXME load from some config * @var string @@ -12,31 +16,33 @@ class StickersHandler extends EspinosoCommandHandler [ 'user' => 'Facundo', 'pattern' => ".*\bmaybe\b.*", - 'sticker' => 'CAADAgADiwUAAvoLtgh812FBxEdUAgI' // LazyPanda + 'sticker' => 'CAADAgADiwUAAvoLtgh812FBxEdUAgI' // LazyPanda FIXME put on agnostic class ] ]; - protected $match = null; - protected $allow_ignore_prefix = true; + protected $signature = "[espi] maybe"; + protected $description = "solo funciona para facu... los demás a comerla"; + + + /** + * @var null + */ + protected $match = null; - public function shouldHandle($updates, $context = null) + public function shouldHandle(Message $message): bool { - $this->match = collect($this->patterns)->filter(function ($pattern) use ($updates) { - // FIXME that shit - return isset($updates->message) - && isset($updates->message->from) - && isset($updates->message->from->first_name) - && $updates->message->from->first_name === $pattern['user'] - && $this->matchCommand($pattern['pattern'], $updates); + $this->match = collect($this->patterns)->filter(function ($pattern) use ($message) { + return $message->getFrom()->getFirstName() === $pattern['user'] + && $this->matchCommand($pattern['pattern'], $message); }); return $this->match->isNotEmpty(); } - public function handle($updates, $context = null) + public function handle(Message $message) { - Telegram::sendSticker([ - 'chat_id' => $updates->message->chat->id, + $this->telegram->sendSticker([ + 'chat_id' => $message->getChat()->getId(), 'sticker' => $this->match->first()['sticker'], ]); } diff --git a/app/Espinoso/Handlers/Weather.php b/app/Espinoso/Handlers/Weather.php deleted file mode 100644 index b8010a7..0000000 --- a/app/Espinoso/Handlers/Weather.php +++ /dev/null @@ -1,107 +0,0 @@ -<?php -namespace App\Espinoso\Handlers ; - -use App\Espinoso\Helpers\Msg; -use Cmfcmf\OpenWeatherMap\Forecast; -use \DateTime; -use Gmopx\LaravelOWM\LaravelOWM; -use Mockery\CountValidator\Exception; -use Telegram\Bot\Laravel\Facades\Telegram; - -class Weather extends EspinosoHandler -{ - public function shouldHandle($updates, $context=null) - { - if ( ! $this->isTextMessage($updates) ) return false ; - - return preg_match($this->regex(), $updates->message->text); - } - - public function handle($updates, $context=null) - { - $day = $this->extractDay($updates->message->text); - $dayEn = $this->translateDay($day); - $date = $this->getNearestDateFromDay($dayEn); - - $response = $this->buildResponse($date); - - Telegram::sendMessage(Msg::html($response)->build($updates)); - } - - private function regex() - { - return "/clima[^a-z0-9]+(?:este|el)[^a-z0-9].*(?'dia'lunes|martes|miercoles|jueves|viernes|sabado|domingo).*\??/i"; - } - - public function buildResponse(DateTime $date) : string - { - try { - $weather = $this->getWeatherDescriptionForDate($date); - if (empty($weather)) - throw new \Exception() ; - $response = "está pronosticado " . $weather; - } catch (Exception $e) { - $response = "que se yo, forro"; - } - - return $response; - } - - private function extractDay($text) - { - preg_match($this->regex(), $text, $matches); - return $matches['dia']; - } - - private function translateDay($day) - { - $days = [ - 'lunes' => 'Monday', - 'martes' => 'Tuesday', - 'miercoles' => 'Wednesday', - 'jueves' => 'Thursday', - 'viernes' => 'Friday', - 'sabado' => 'Saturday' , - 'domingo' => 'Sunday' - ]; - return $days[$day]; - } - - private function getNearestDateFromDay($day) : DateTime - { - $time = strtotime("next $day"); - return DateTime::createFromFormat('U', $time); - } - - private function getWeatherDescriptionForDate(DateTime $date) : string - { - $owm = new LaravelOWM(); - - $forecasts = $owm->getWeatherForecast('Quilmes, AR', "es", "metric", 10, ''); - - return collect($forecasts) - ->filter( function (Forecast $forecast) use ($date) { return $this->isForecastForDate($date, $forecast); } ) - ->map( function (Forecast $forecast) { return $this->forecastToDescription($forecast); } ) - ->reduce( function ($carry, $str) { return empty($carry) ? $str : $carry . "," . $str; } , "") - ; - } - - private function isForecastForDate(DateTime $date, Forecast $forecast) : bool - { - return $forecast->time->day->format('Y-m-d') == $date->format('Y-m-d'); - } - - private function forecastToDescription(Forecast $forecast) : string - { - $from = $forecast->time->from->format('H:i'); - $to = $forecast->time->to->format('H:i'); - $minTemperature = $forecast->temperature->min->getValue(); - $maxTemperature = $forecast->temperature->max->getValue(); - $description = $forecast->weather->description; - - return "de " . $from . " a " . $to . " " . $description . " con temperaturas entre " . $minTemperature . " y " . $maxTemperature . " grados "; - } - -} - - diff --git a/app/Espinoso/Handlers/WeatherHandler.php b/app/Espinoso/Handlers/WeatherHandler.php new file mode 100644 index 0000000..27efe36 --- /dev/null +++ b/app/Espinoso/Handlers/WeatherHandler.php @@ -0,0 +1,87 @@ +<?php namespace App\Espinoso\Handlers; + +use Carbon\Carbon; +use Illuminate\Support\Str; +use App\Facades\WeatherSearch; +use Telegram\Bot\Objects\Message; +use Cmfcmf\OpenWeatherMap\Forecast; + +class WeatherHandler extends EspinosoCommandHandler +{ + protected $allow_ignore_prefix = true; + protected $pattern = "(\b(clima)\b)\s+(?'p'(este|el)\s+)?(?'day'(lunes|martes|mi(e|é)rcoles|jueves|viernes|s(a|á)bado|domingo))$"; + + protected $signature = "[espi] clima este lunez|martes|..."; + protected $description = "odio esta mierda..."; + + + public function handle(Message $message) + { + $date = $this->getNearestDateFromDay($this->getDay()); + + $weather = $this->getWeatherDescriptionForDate($date); + + if (empty($weather)) { + $this->replyNotFound($message); + } + + $this->telegram->sendMessage([ + 'chat_id' => $message->getChat()->getId(), + 'text' => "está pronosticado " . $weather, + 'parse_mode' => 'HTML' + ]); + } + + protected function getDay() + { + $days = collect([ + 'lunes' => 'Monday', + 'martes' => 'Tuesday', + 'miercoles' => 'Wednesday', + 'jueves' => 'Thursday', + 'viernes' => 'Friday', + 'sabado' => 'Saturday' , + 'domingo' => 'Sunday' + ]); + + $day = strtolower(Str::ascii(trim($this->matches['day']))); + + return $days->has($day) ? $days->get($day) : ''; + } + + protected function getNearestDateFromDay($day): Carbon + { + $time = strtotime("next $day"); + + return Carbon::createFromFormat('U', $time); + } + + protected function getWeatherDescriptionForDate(Carbon $date): string + { + $forecasts = WeatherSearch::getWeatherForecast('Quilmes, AR', "es", "metric", 10, ''); + + return collect($forecasts)->filter(function (Forecast $forecast) use ($date) { + return $this->isForecastForDate($date, $forecast); + })->map(function (Forecast $forecast) { + return $this->forecastToDescription($forecast); + })->reduce(function ($carry, $str) { + return empty($carry) ? $str : $carry . "," . $str; + }, ''); + } + + protected function isForecastForDate(Carbon $date, Forecast $forecast): bool + { + return $forecast->time->day->format('Y-m-d') == $date->format('Y-m-d'); + } + + protected function forecastToDescription(Forecast $forecast) : string + { + $from = $forecast->time->from->format('H:i'); + $to = $forecast->time->to->format('H:i'); + $min = $forecast->temperature->min->getValue(); + $max = $forecast->temperature->max->getValue(); + $descrip = $forecast->weather->description; + + return "de {$from} a {$to} {$descrip} con temperaturas entre {$min} y {$max} grados"; + } +} diff --git a/app/Espinoso/Helpers/Msg.php b/app/Espinoso/Helpers/Msg.php index 949799e..b875b24 100644 --- a/app/Espinoso/Helpers/Msg.php +++ b/app/Espinoso/Helpers/Msg.php @@ -1,6 +1,8 @@ <?php namespace App\Espinoso\Helpers; +use Telegram\Bot\Objects\Message; + class Msg { protected $msg; @@ -21,11 +23,11 @@ public static function plain($msg) return new Msg($msg); } - public function build($updates, $pattern='') + public function build(Message $message, $pattern = '') { - $text = $this->parseMsg($pattern, $updates); + $text = $this->parseMsg($pattern, $message); return [ - 'chat_id' => $updates->message->chat->id, + 'chat_id' => $message->getChat()->getId(), 'text' => $text, 'parse_mode' => $this->parse_mode, ]; @@ -34,23 +36,21 @@ public function build($updates, $pattern='') private function __construct($msg, $parse_mode=null) { $this->parse_mode = $parse_mode; - $this->msg = $msg ; + $this->msg = $msg; } - private function parseMsg($pattern, $updates) + private function parseMsg(string $pattern, Message $message) { $msg = $this->msg; - if (is_array($msg)) - { + if (is_array($msg)) { $text = $this->choose($msg); - } else if ( is_callable($msg) ) - { + } else if ( is_callable($msg) ) { $text = $msg($pattern, $updates); } else { $text = $msg; } - return $text ; + return $text; } private function choose($responses) diff --git a/app/Facades/IMDbSearch.php b/app/Facades/IMDbSearch.php new file mode 100644 index 0000000..6955586 --- /dev/null +++ b/app/Facades/IMDbSearch.php @@ -0,0 +1,21 @@ +<?php namespace App\Facades; + +use Imdb\TitleSearch; +use Illuminate\Support\Facades\Facade; + +/** + * Class IMDbSearch + * @see TitleSearch + */ +class IMDbSearch extends Facade +{ + /** + * Get the registered name of the component. + * + * @return string + */ + protected static function getFacadeAccessor() + { + return 'IMDbSearch'; + } +} diff --git a/app/Facades/InstagramSearch.php b/app/Facades/InstagramSearch.php new file mode 100644 index 0000000..6fdd5e5 --- /dev/null +++ b/app/Facades/InstagramSearch.php @@ -0,0 +1,21 @@ +<?php namespace App\Facades; + +use Vinkla\Instagram\Instagram; +use Illuminate\Support\Facades\Facade; + +/** + * Class InstagramSearch + * @see Instagram + */ +class InstagramSearch extends Facade +{ + /** + * Get the registered name of the component. + * + * @return string + */ + protected static function getFacadeAccessor() + { + return 'InstagramSearch'; + } +} diff --git a/app/Facades/WeatherSearch.php b/app/Facades/WeatherSearch.php new file mode 100644 index 0000000..55cdc06 --- /dev/null +++ b/app/Facades/WeatherSearch.php @@ -0,0 +1,21 @@ +<?php namespace App\Facades; + +use Gmopx\LaravelOWM\LaravelOWM; +use Illuminate\Support\Facades\Facade; + +/** + * Class WeatherSearch + * @see LaravelOWM + */ +class WeatherSearch extends Facade +{ + /** + * Get the registered name of the component. + * + * @return string + */ + protected static function getFacadeAccessor() + { + return 'WeatherSearch'; + } +} diff --git a/app/Http/Controllers/TelegramController.php b/app/Http/Controllers/TelegramController.php index eb80eab..ce275ef 100644 --- a/app/Http/Controllers/TelegramController.php +++ b/app/Http/Controllers/TelegramController.php @@ -1,36 +1,56 @@ <?php namespace App\Http\Controllers; -use Exception; use GuzzleHttp\Client; -use Telegram\Bot\Laravel\Facades\Telegram; -use App\Espinoso\Handlers\EspinosoHandler; +use App\Espinoso\Espinoso; +use Telegram\Bot\Objects\Message; +use Telegram\Bot\Api as ApiTelegram; +use Telegram\Bot\TelegramResponse; class TelegramController extends Controller { - public function handleUpdates() + /** + * Handle Telegram incoming message. + * + * @param ApiTelegram $telegram + * @param Espinoso $espinoso + */ + public function handleUpdates(ApiTelegram $telegram, Espinoso $espinoso) { - $updates = json_decode(Telegram::getWebhookUpdates()); - - collect(config('espinoso.handlers'))->map(function ($handler) { - return resolve($handler); - })->filter(function (EspinosoHandler $handler) use ($updates) { - return $handler->shouldHandle($updates); - })->each(function (EspinosoHandler $handler) use ($updates) { - // FIXME make try-catch an aspect - try { - $handler->handle($updates); - } catch (Exception $e) { - $handler->handleError($e, $updates); - } - }); + $message = $telegram->getWebhookUpdates()->getMessage(); + + if ($this->isNotTextMessage($message)) { + return; + } + + $command = $this->parseCommand($message->getText()); + + if (!empty($command)) { + $message['text'] = $this->parseCommandAsKeyword($command, $message); + } + + $espinoso->executeHandlers($telegram, $message); + + return; } - public function setWebhook() + /** + * Used for associate handleUpdates path as Telegram Webhook. + * By default Telegram use its own hook to catch updates. + * + * @param ApiTelegram $telegram + * @return TelegramResponse + */ + public function setWebhook(ApiTelegram $telegram) { - return Telegram::setWebhook(['url' => secure_url('handle-update')]); + return $telegram->setWebhook(['url' => secure_url('handle-update')]); } - public function githubWebhook() + /** + * This method is a hook for github to catch & handle last commit. + * + * @param ApiTelegram $telegram + */ + public function githubWebhook(ApiTelegram $telegram) { // FIXME get & send branch of commit $client = new Client; @@ -43,10 +63,53 @@ public function githubWebhook() $message = "De nuevo el pelotudo de `$nombre` commiteando giladas, mirá lo que hizo esta vez:_{$commit->message}_ [View Commit]({$link})"; - Telegram::sendMessage([ + $telegram->sendMessage([ 'chat_id' => config('espinoso.chat.dev'), 'text' => $message, 'parse_mode' => 'Markdown', ]); } + + /* + * Internals + */ + + /** + * @param mixed $message + * @return bool + */ + protected function isTextMessage($message): bool + { + return $message !== null && $message->has('text'); + } + + /** + * @param mixed $message + * @return bool + */ + protected function isNotTextMessage($message): bool + { + return !$this->isTextMessage($message); + } + + /** + * @param string $text + * @return string + */ + protected function parseCommand(string $text) + { + preg_match('/^\/([^\s@]+)@?(\S+)?\s?(.*)$/', $text, $matches); + + return isset($matches[1]) ? trim($matches[1]) : ''; + } + + /** + * @param string $command + * @param Message $message + * @return string + */ + protected function parseCommandAsKeyword(string $command, Message $message): string + { + return str_replace("/{$command}", "espi {$command}", $message->getText()); + } } diff --git a/app/Model/TelegramUser.php b/app/Model/TelegramUser.php new file mode 100644 index 0000000..a5b4df2 --- /dev/null +++ b/app/Model/TelegramUser.php @@ -0,0 +1,11 @@ +<?php namespace App\Model; + +use Illuminate\Database\Eloquent\Model; + +/** + * @property int telegram_id + */ +class TelegramUser extends Model +{ + // +} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 79c4396..59a94c6 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -2,6 +2,11 @@ namespace App\Providers; +use Imdb\Config; +use Imdb\TitleSearch; +use App\Espinoso\Espinoso; +use Vinkla\Instagram\Instagram; +use Gmopx\LaravelOWM\LaravelOWM; use Goutte\Client as GoutteClient; use GuzzleHttp\Client as GuzzleClient; use Illuminate\Support\ServiceProvider; @@ -28,13 +33,17 @@ public function register() // Facades $this->app->bind('GoutteClient', function () { return new GoutteClient; }); $this->app->bind('GuzzleClient', function () { return new GuzzleClient; }); + $this->app->bind('InstagramSearch', function () { return new Instagram; }); + $this->app->bind('WeatherSearch', function () { return new LaravelOWM; }); + $this->app->bind('IMDbSearch', function () { + $config = new Config; + $config->language = 'es-AR,es,en'; + return new TitleSearch($config); + }); - // Handlers - $handlers = collect(config('espinoso.handlers')); - $handlers->each(function ($handler) { - $this->app->bind($handler, function () use ($handler) { - return new $handler; - }); + // Espinoso + $this->app->bind(Espinoso::class, function () { + return new Espinoso(collect(config('espinoso.handlers'))); }); } } diff --git a/app/helpers.php b/app/helpers.php index 3683e14..a341c80 100644 --- a/app/helpers.php +++ b/app/helpers.php @@ -1,6 +1,13 @@ <?php -function completeWordRegex($word) +/** + * @param string $str + * @return string + */ +function clean_string(string $str) { - return "^(?:.*[^a-z])?" . $word . "[^a-z]?"; -} \ No newline at end of file + $str = trim($str); + + // replace multiple spaces with a single space + return strval(preg_replace('!\s+!', ' ', $str)); +} diff --git a/composer.json b/composer.json index d8413d9..f59bd1d 100644 --- a/composer.json +++ b/composer.json @@ -10,6 +10,7 @@ "fabpot/goutte": "^3.2", "gmopx/laravel-owm": "0.1.1", "guzzlehttp/guzzle": "^6.3", + "imdbphp/imdbphp": "^5.2", "irazasyed/telegram-bot-sdk": "^2.2", "laravel/framework": "5.4.*", "laravel/tinker": "~1.0", diff --git a/composer.lock b/composer.lock index 1252ed0..1ec039b 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "84eb26feaa8939fb3209248379fd653c", + "content-hash": "1a33aa2f9153c7adc80e7150906bb1ee", "packages": [ { "name": "cmfcmf/openweathermap-php-api", @@ -470,6 +470,42 @@ ], "time": "2017-03-20T17:10:46+00:00" }, + { + "name": "imdbphp/imdbphp", + "version": "v5.2.1", + "source": { + "type": "git", + "url": "https://github.com/tboothman/imdbphp.git", + "reference": "b452d39f293fc14605bc37d8ffe08aa51280e84d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/tboothman/imdbphp/zipball/b452d39f293fc14605bc37d8ffe08aa51280e84d", + "reference": "b452d39f293fc14605bc37d8ffe08aa51280e84d", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "php": ">5.3", + "psr/log": "~1.0" + }, + "require-dev": { + "mockery/mockery": "~0.9", + "phpunit/phpunit": "~3.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "Imdb\\": "src/Imdb" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0" + ], + "description": "Library for retrieving film and tv information from IMDb", + "time": "2017-07-06T23:55:44+00:00" + }, { "name": "irazasyed/telegram-bot-sdk", "version": "v2.2.0", @@ -2542,7 +2578,7 @@ "codeclimate", "coverage" ], - "time": "2017-06-01T13:19:55+00:00" + "time": "2017-06-01 13:19:55" }, { "name": "doctrine/instantiator", diff --git a/config/app.php b/config/app.php index 45981a1..0e7cdf7 100644 --- a/config/app.php +++ b/config/app.php @@ -179,7 +179,7 @@ Telegram\Bot\Laravel\TelegramServiceProvider::class, - Gmopx\LaravelOWM\LaravelOWMServiceProvider::class, + Gmopx\LaravelOWM\LaravelOWMServiceProvider::class, ], /* diff --git a/config/brain.php b/config/brain.php new file mode 100644 index 0000000..796afe9 --- /dev/null +++ b/config/brain.php @@ -0,0 +1,150 @@ +<?php + +return [ + + 'ignore_to' => [], + + /* + |-------------------------------------------------------------------------- + | Brain Matches + |-------------------------------------------------------------------------- + | + | The following are matches used by \App\Espinoso\Handlers\BrainHandler + | + */ + + 'patterns' => [ + + '/^macri\W*$/i' => [ + 'reply' => 'Gato', + ], + + '/^(espi(noso)?\s*){0,3}[i\W]*$/i' => [ + 'reply' => 'Otra vez rompiendo los huevos... Que pija quieren?', + ], + + '/\b(asado)\b$/i' => [ + 'reply' => "Próxima juntada: sábado 15/7 en lo de Maru. Traigan bebidas. Canelones a la Maru. Para Facu un buen canelón de carne.", + ], + + '/^(o\s+)?no(,)?\s+(espi(noso)?)(\?)+$/i' => [ + 'reply' => [ + 'Claro que si :name:!', + 'Exactamente :name:', + 'Mas vale :name:!' + ] + ], + + '/\b(alan)\b$/i' => [ + 'reply' => [ + 'Alan lo hace por dinero', + 'acaso dijiste $$$ oriented programming?', + ], + ], + + '/\b(marcos)\b$/i' => [ + 'reply' => '¿Quisiste decir Markos?', + ], + + '/maximo\s?$/i' => [ + 'reply' => 'Para programarme no usaron ni un solo if ;)', + ], + + '/\b(facu(?:ndo)?)\b$/i' => [ + 'reply' => 'Facu, ese es terrible puto', + ], + + '/\b(ines?)\b$/i' => [ + 'reply' => [ + 'esa Ines esa una babosa, siempre mirando abs', + 'Ine es una niñita sensible e inocente!', 'Ine te deja sin pilas' + ], + ], + + '/(j+a+){5,}/i' => [ + 'reply' => 'ajajajajajaja, que plato!', + ], + + '/fu[u]*ck/i' => [ + 'reply' => 'tranquilo vieja, todo va a salir bien.', + ], + + '/mamu/i' => [ + 'reply' => 'papu', + ], + + '/jarvis/i' => [ + 'reply' => 'Es un re puto! Aguante yo! La concha de tu madre all boys', + ], + + '/^hola\s*(espi(noso)?)/i' => [ + 'reply' => 'Que pija queres?', + ], + + '/^chau\s*(espi(noso)?)/i' => [ + 'reply' => [ + 'Chau :name:!', + 'Nos vemos :name:', + 'Aloha :name:', + 'Nos re vimos!', + 'Saludame a tu jermu, :name:', + 'Chupala puto', + ], + ], + + '/papu/i' => [ + 'reply' => 'mamu', + ], + + '/ponerla/i' => [ + 'reply' => 'bash: ponerla: command not found', + ], + + '/contrato/i' => [ + 'reply' => 'el diccionario lo define como un acuerdo legal que no se puede romper, que no se puede romper...', + ], + + '/maldicio[o]*n/i' => [ + 'reply' => 'tranquilo vieja, todo va a salir bien.', + ], + + '/concha.*lora/i' => [ + 'reply' => 'no eh, cuidame la boquita.', + ], + + '/(dan.*tip)|(tip.*dan)/i' => [ + 'reply' => 'dan, no quiero asustarte pero sin TIP no hay titulo.. hace el TIP MIERDA!', + ], + + '/^(.)*(espi(noso)?)\s+(c(o|ó)mo)\s+(and(a|á)s|est(a|á)s)(\?)*$/i' => [ + 'reply' => [ + 'He tenido dias mejores..', + 'De lujo!!' , + 'Qué carajo te importa?', + 'Qué carajo te importa, pelotudo!', + 'Comela puto', + 'Si te digo te miento...', + 'No me jodas :name:', + ] + ], + + '/^(espi(noso)?)\s+(.)*(smalltalk)(\?)*$/i' => [ + 'reply' => [ + 'Amo su pureza..', + '`MNU`', + ] + ], + + // FIXME: it would be better to create an ArithmeticHandler to handle operations + // '/([0-9][0-9]*)[ ]*\+[ ]*([0-9][0-9]*)/' => Msg::md($suma), + + '/empanada/i' => [ + 'reply' => 'mmmm de carne y bien jugosa', + ], + + // FIXME: this should be in a CommandHandler + '/^ayuda gsm$/i' => [ + 'reply' => "gsm [-z:ZOOM -s:SIZE -c:COLOR -t:MAPTYPE ] dirección.\nZOOM es un entero del 1-20\nSIZE es una resolución (600x300)\nMAPTYPE es un tipo de mapa de google, por defecto roadmap\n", + ], + ], +]; diff --git a/config/database.php b/config/database.php index c8b8efd..8ea77c4 100644 --- a/config/database.php +++ b/config/database.php @@ -1,12 +1,5 @@ <?php -//$url = parse_url(getenv("DATABASE_URL")); -// -//$host = $url["host"]; -//$username = $url["user"]; -//$password = $url["pass"]; -//$database = substr($url["path"], 1); - return [ /* @@ -46,6 +39,12 @@ 'prefix' => '', ], + 'phpunit' => [ + 'driver' => 'sqlite', + 'database' => ':memory:', + 'prefix' => '', + ], + 'mysql' => [ 'driver' => 'mysql', 'host' => env('DB_HOST', '127.0.0.1'), diff --git a/config/espinoso.php b/config/espinoso.php index ad56deb..c0111c5 100644 --- a/config/espinoso.php +++ b/config/espinoso.php @@ -1,21 +1,22 @@ <?php return [ 'handlers' => [ + App\Espinoso\Handlers\HelpHandler::class, App\Espinoso\Handlers\GitHubHandler::class, App\Espinoso\Handlers\CinemaHandler::class, - App\Espinoso\Handlers\ResponseByMatch::class, + App\Espinoso\Handlers\BrainHandler::class, App\Espinoso\Handlers\BardoDelEspinosoHandler::class, - App\Espinoso\Handlers\RandomInstagram::class, - App\Espinoso\Handlers\GoogleStaticMaps::class, - App\Espinoso\Handlers\Weather::class, - //App\Espinoso\Handlers\NextHolidayHandler::class, - App\Espinoso\Handlers\ImdbHandler::class, + App\Espinoso\Handlers\InstagramHandler::class, + App\Espinoso\Handlers\GoogleStaticMapsHandler::class, + App\Espinoso\Handlers\WeatherHandler::class, + App\Espinoso\Handlers\NextHolidaysHandler::class, + App\Espinoso\Handlers\IMDbHandler::class, App\Espinoso\Handlers\GoogleInfoBoxHandler::class, - \App\Espinoso\Handlers\StickersHandler::class + App\Espinoso\Handlers\StickersHandler::class ], - 'github' => [ - 'token' => env('GITHUB_TOKEN', '123') + 'token' => [ + 'github' => env('GITHUB_TOKEN', '123'), ], 'chat' => [ @@ -25,6 +26,8 @@ 'url' => [ 'issues' => 'https://api.github.com/repos/12-cactus/espinoso/issues', 'info' => 'https://www.google.com.ar/search?q=', - 'cinema' => 'http://www.hoyts.com.ar/ajaxCartelera.aspx?filter=Home&cine=&premium=False&_=1493929858090' + 'cinema' => 'http://www.hoyts.com.ar/ajaxCartelera.aspx?filter=Home&cine=&premium=False&_=1493929858090', + 'map' => 'https://maps.googleapis.com/maps/api/staticmap', + 'holidays' => 'http://www.elproximoferiado.com/', ] ]; diff --git a/config/espinoso_data.php b/config/espinoso_data.php deleted file mode 100644 index a716ff5..0000000 --- a/config/espinoso_data.php +++ /dev/null @@ -1,61 +0,0 @@ -<?php -use \App\Espinoso\Helpers\Msg; - -$suma = function ($pattern, $updates) { - preg_match($pattern, $updates->message->text, $matches); - $num1 = $matches[1]; - $num2 = $matches[2]; - return $num1 + $num2 ; -}; - -$funAsentirRand = function ($pattern, $update) { - $respuestas = ['claro que si', 'exactamente', 'mas vale']; - $elegida = $respuestas[ array_rand($respuestas) ]; - return $elegida . ", " . $update->message->from->first_name ; -}; - -$funDespedirseRand = function ($pattern, $update) { - $respuestas = ['Chau!', 'Nos vemos', 'Aloha', 'Nos re vimos!','Saludame a tu jermu', 'Chupala puto']; - $elegida = $respuestas[ array_rand($respuestas) ]; - return $elegida . ", " . $update->message->from->first_name ; -}; - -$rbmMappings = [ - '/macri.?$/i' => 'Gato', - '/^espinoso[^\?]?$/i' => 'Otra vez rompiendo los huevos... Que pija quieren?', - '/' . completeWordRegex('asado') . '$/i' => "Próxima juntada: sábado 15/7 en lo de Maru. Traigan bebidas. Canelones a la Maru. Para Facu un buen canelón de carne.", - '/no[, ]* espinoso?/i' => Msg::plain($funAsentirRand), - '/' . completeWordRegex('alan') . '$/i' => [ 'Alan lo hace por dinero', 'acaso dijiste $$$ oriented programming?', ] , - '/' . completeWordRegex('marcos') . '$/i' => '¿Quisiste decir Markos?', - '/maximo.?$/i' => 'Para programarme no usaron ni un solo if ;)', - '/' . completeWordRegex('facu(?:ndo)?') . '$/i' => 'Facu, ese es terrible puto', - '/' . completeWordRegex('ines?') . '$/i' => [ 'esa Ines esa una babosa, siempre mirando abs' , 'Ine es una niñita sensible e inocente!', 'Ine te deja sin pilas' ], - '/(j+a+){5,}/i' => 'ajajajajajaja, que plato!', - '/fu[u]*ck/i' => 'tranquilo vieja, todo va a salir bien.', - '/mamu/i' => 'papu', - '/jarvis/i' => 'Es un re puto! Aguante yo! La concha de tu madre all boys', - '/hola.*espinoso/i' => 'Que pija queres?', - '/chau.*espinoso/i' => Msg::plain($funDespedirseRand), - '/papu/i' => 'mamu', - '/ponerla/i' => 'bash: ponerla: command not found', - '/contrato/i' => 'el diccionario lo define como un acuerdo legal que no se puede romper, que no se puede romper...', - '/maldicio[o]*n/i' => 'tranquilo vieja, todo va a salir bien.', - '/concha.*lora/i' => 'no eh, cuidame la boquita.', - '/(dan.*tip)|(tip.*dan)/i' => 'dan, no quiero asustarte pero sin TIP no hay titulo.. hace el TIP MIERDA!', - '/(espinoso.*como.*(andas|estas))|(como.*(andas|estas).*espinoso)\??$/i' => Msg::md([ 'He tenido dias mejores..', 'de lujo' , 'que carajo te importa?', 'comela puto' ]) , - '/espinoso.*pensas.*smalltalk\??$/i' => Msg::md([ 'Amo su pureza..', '`MNU`' ]) , - '/([0-9][0-9]*)[ ]*\+[ ]*([0-9][0-9]*)/' => Msg::md($suma), - '/empanada/i' => 'mmmm de carne y bien jugosa', - - '/^ayuda gsm$/i' => "gsm [-z:ZOOM -s:SIZE -c:COLOR -t:MAPTYPE ] dirección.\nZOOM es un entero del 1-20\nSIZE es una resolución (600x300)\nMAPTYPE es un tipo de mapa de google, por defecto roadmap\n", - -]; - -$rbmMappings['/espinoso.*claves.*/i'] = "Reconozco todas estas: \n" . implode("\n", array_keys($rbmMappings)); - - -return [ - 'ResponseByMatch' => [ 'mappings' => $rbmMappings, - 'ignore_names' => [], - ], -]; diff --git a/database/migrations/2017_08_04_223235_create_telegram_users_table.php b/database/migrations/2017_08_04_223235_create_telegram_users_table.php new file mode 100644 index 0000000..a835276 --- /dev/null +++ b/database/migrations/2017_08_04_223235_create_telegram_users_table.php @@ -0,0 +1,35 @@ +<?php + +use Illuminate\Support\Facades\Schema; +use Illuminate\Database\Schema\Blueprint; +use Illuminate\Database\Migrations\Migration; + +class CreateTelegramUsersTable extends Migration +{ + /** + * Run the migrations. + * + * @return void + */ + public function up() + { + Schema::create('telegram_users', function (Blueprint $table) { + $table->increments('id'); + $table->integer('telegram_id')->unique(); + $table->string('username')->unique(); + $table->string('first_name'); + $table->string('last_name'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('telegram_users'); + } +} diff --git a/phpunit.xml b/phpunit.xml index eb8d700..55faa1b 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -9,14 +9,8 @@ processIsolation="false" stopOnFailure="false"> <testsuites> - <testsuite name="Feature Tests"> - <directory suffix="Test.php">./tests/Feature</directory> - </testsuite> <testsuite name="Handlers Tests"> - <directory suffix="Test.php">./tests/Handlers</directory> - </testsuite> - <testsuite name="Unit Tests"> - <directory suffix="Test.php">./tests/Unit</directory> + <directory suffix="Test.php">./tests/Espinoso/Handlers</directory> </testsuite> </testsuites> <filter> @@ -29,6 +23,7 @@ </logging> <php> <env name="APP_ENV" value="testing"/> + <env name="DB_CONNECTION" value="phpunit"/> <env name="CACHE_DRIVER" value="array"/> <env name="SESSION_DRIVER" value="array"/> <env name="QUEUE_DRIVER" value="sync"/> diff --git a/tests/DBTestCase.php b/tests/DBTestCase.php new file mode 100644 index 0000000..1fbcd65 --- /dev/null +++ b/tests/DBTestCase.php @@ -0,0 +1,20 @@ +<?php namespace Tests; + +use Illuminate\Support\Facades\Artisan; + +abstract class DBTestCase extends TestCase +{ + protected function setUp() + { + parent::setUp(); + + Artisan::call("migrate"); + } + + protected function tearDown() + { + Artisan::call("migrate:reset"); + + parent::tearDown(); + } +} diff --git a/tests/Espinoso/EspinosoTest.php b/tests/Espinoso/EspinosoTest.php new file mode 100644 index 0000000..cdaa1ff --- /dev/null +++ b/tests/Espinoso/EspinosoTest.php @@ -0,0 +1,37 @@ +<?php namespace Tests\Espinoso; + +use Tests\DBTestCase; + +/** + * Class EspinosoTest + * @package Tests + */ +class EspinosoTest extends DBTestCase +{ + /** + * @test + * + * @return void + */ + public function it_store_user_data_when_receive_an_update() + { +// // Arrange +// $update = $this->update([ +// 'from' => [ +// 'id' => 12345, +// 'first_name' => 'John', +// 'last_name' => 'Doe', +// 'username' => 'JohnDoe' +// ] +// ]); +// +// // Act +// Espinoso::register($update); +// $user = TelegramUser::whereTelegramId(12345)->first(); +// +// // Assert +// $this->assertEquals('John', $user->first_name); +// $this->assertEquals('Doe', $user->last_name); +// $this->assertEquals('JohnDoe', $user->username); + } +} diff --git a/tests/Espinoso/Handlers/BardoDelEspinosoHandlerTest.php b/tests/Espinoso/Handlers/BardoDelEspinosoHandlerTest.php new file mode 100644 index 0000000..5171a7f --- /dev/null +++ b/tests/Espinoso/Handlers/BardoDelEspinosoHandlerTest.php @@ -0,0 +1,66 @@ +<?php namespace Tests\Espinoso\Handlers; + +use App\Espinoso\Handlers\BardoDelEspinosoHandler; + +class BardoDelEspinosoHandlerTest extends HandlersTestCase +{ + /** + * @test + */ + public function it_should_handle_when_receives_send_me_nudes() + { + // Arrange + $handler = $this->makeHandler(); + $message = $this->makeMessage(['text' => 'send me nudes']); + + // Act && Assert + $this->assertTrue($handler->shouldHandle($message)); + } + + /** + * @test + */ + public function it_should_not_handle_when_receives_another_text() + { + // Arrange + $handler = $this->makeHandler(); + $update1 = $this->makeMessage(['text' => 'saraza send me nudes']); + $update2 = $this->makeMessage(['text' => 'send me nudes saraza']); + + // Act & Assert + $this->assertFalse($handler->shouldHandle($update1)); + $this->assertFalse($handler->shouldHandle($update2)); + } + + /** + * @test + */ + public function it_handle_and_send_photo() + { + // Mocking + $photo = [ + 'chat_id' => 123, + 'photo' => 'https://cdn.drawception.com/images/panels/2012/4-4/FErsE1a6t7-8.png', + 'caption' => 'Acá tenés tu nude, hijo de puta!' + ]; + $this->telegram->shouldReceive('sendPhoto')->once()->with($photo); + + // Arrange + $handler = $this->makeHandler(); + $update = $this->makeMessage([ + 'chat' => ['id' => 123], + 'text' => 'send me nudes' + ]); + + // Act + $handler->handle($update); + } + + /** + * @return BardoDelEspinosoHandler + */ + protected function makeHandler(): BardoDelEspinosoHandler + { + return new BardoDelEspinosoHandler($this->espinoso, $this->telegram); + } +} diff --git a/tests/Espinoso/Handlers/BrainHandlerTest.php b/tests/Espinoso/Handlers/BrainHandlerTest.php new file mode 100644 index 0000000..a565b78 --- /dev/null +++ b/tests/Espinoso/Handlers/BrainHandlerTest.php @@ -0,0 +1,115 @@ +<?php namespace Tests\Espinoso\Handlers; + +use App\Espinoso\Handlers\BrainHandler; +use App\Facades\GoutteClient; +use Mockery; + +class BrainHandlerTest extends HandlersTestCase +{ + /** + * @test + */ + public function it_should_respond_this() + { + $this->handler = $this->makeHandler(); + + + $this->assertShouldHandle('macri'); + $this->assertShouldHandle('espi'); + $this->assertShouldHandle('espi espi espi'); + $this->assertShouldHandle('espinoso'); + $this->assertShouldHandle('espiiiii'); + $this->assertShouldHandle('espi!!!!'); + $this->assertShouldHandle('espiii!!'); + $this->assertShouldHandle('asado'); + $this->assertShouldHandle('marcos'); + $this->assertShouldHandle('maximo'); + $this->assertShouldHandle('facu'); + $this->assertShouldHandle('jajajajaja'); + $this->assertShouldHandle('fuck'); + $this->assertShouldHandle('mamu'); + $this->assertShouldHandle('jarvis'); + $this->assertShouldHandle('hola espinoso'); + $this->assertShouldHandle('papu'); + $this->assertShouldHandle('ponerla'); + $this->assertShouldHandle('contrato'); + $this->assertShouldHandle('maldicion'); + $this->assertShouldHandle('concha de la lora'); + $this->assertShouldHandle('dan el tip'); + $this->assertShouldHandle('empanada'); + $this->assertShouldHandle('ayuda gsm'); + } + + /** + * @test + */ + public function it_should_respond_with_concrete_message() + { + $this->shouldRespondWith('macri', 'Gato'); + $this->shouldRespondWith('espi', 'Otra vez rompiendo los huevos... Que pija quieren?'); + $this->shouldRespondWith('empanada', 'mmmm de carne y bien jugosa'); + } + + /** + * @test + */ + public function it_should_respond_with_any_of_messages() + { + $this->shouldRespondWithAny('alan', [ + 'Alan lo hace por dinero', + 'acaso dijiste $$$ oriented programming?', + ]); + + $this->shouldRespondWithAny('ines', [ + 'esa Ines esa una babosa, siempre mirando abs', + 'Ine es una niñita sensible e inocente!', 'Ine te deja sin pilas' + ]); + + } + + /** + * @return BrainHandler + */ + protected function makeHandler(): BrainHandler + { + return new BrainHandler($this->espinoso, $this->telegram); + } + + protected function shouldRespondWith(string $in, string $out) + { + $message = $this->text($in); + + $response = [ + 'chat_id' => $message->getChat()->getId(), + 'text' => $out, + 'parse_mode' => 'Markdown' + ]; + + $this->telegram->shouldReceive('sendMessage')->with($response); + + $this->handler = $this->makeHandler(); + $this->assertTrue($this->handler->shouldHandle($message)); + $this->handler->handle($message); + } + + protected function shouldRespondWithAny(string $in, array $outs = []) + { + $message = $this->text($in); + + $responses = collect($outs)->map(function ($out) use ($message) { + return [ + 'chat_id' => $message->getChat()->getId(), + 'text' => $out, + 'parse_mode' => 'Markdown' + ]; + })->toArray(); + + $this->telegram + ->shouldReceive('sendMessage') + ->with(Mockery::any($responses)); + + $this->handler = $this->makeHandler(); + $this->assertTrue($this->handler->shouldHandle($message)); + $this->handler->handle($message); + } +} diff --git a/tests/Espinoso/Handlers/CinemaHandlerTest.php b/tests/Espinoso/Handlers/CinemaHandlerTest.php new file mode 100644 index 0000000..e371af2 --- /dev/null +++ b/tests/Espinoso/Handlers/CinemaHandlerTest.php @@ -0,0 +1,89 @@ +<?php namespace Tests\Espinoso\Handlers; + +use Mockery; +use App\Facades\GoutteClient; +use App\Espinoso\Handlers\CinemaHandler; +use Symfony\Component\DomCrawler\Crawler; + +class CinemaHandlerTest extends HandlersTestCase +{ + /** + * @test + */ + public function it_should_handle_when_match_regex() + { + // Arrange + $handler = $this->makeHandler(); + $updates = [ + $this->makeMessage(['text' => 'espi cine']), + $this->makeMessage(['text' => 'espinoso cine?']), + $this->makeMessage(['text' => 'espi cine??']), + $this->makeMessage(['text' => 'espi cine!']), + $this->makeMessage(['text' => 'espi cine!!!']), + $this->makeMessage(['text' => 'espi ¿cine?']), + $this->makeMessage(['text' => 'espi que hay en el cine']), + $this->makeMessage(['text' => 'espi que hay en el cine?']), + ]; + + // Act & Assert + collect($updates)->each(function ($update) use ($handler) { + $this->assertTrue($handler->shouldHandle($update)); + }); + } + + /** + * @test + */ + public function it_should_not_handle_when_receives_another_text() + { + // Arrange + $handler = $this->makeHandler(); + $updates = [ + $this->makeMessage(['text' => 'cinema']), + $this->makeMessage(['text' => 'ig lacosacine']), + $this->makeMessage(['text' => 'vamos al cine?']), + $this->makeMessage(['text' => 'vamos al cine espi']), + $this->makeMessage(['text' => 'vamos al cine, espi']), + $this->makeMessage(['text' => 'vamos al cine, espi?']), + ]; + + // Act & Assert + collect($updates)->each(function ($update) use ($handler) { + $this->assertFalse($handler->shouldHandle($update)); + }); + } + + /** + * @test + */ + public function it_handle_and_return_movies() + { + // Mocking + $text = "¿La pensás poner?\n¡Mete Netflix pelotud@, es mas barato!\nPero igual podes ver todas estas:\n\n"; + $message = ['chat_id' => 123, 'text' => $text]; + $this->telegram->shouldReceive('sendMessage')->once()->with($message); + + $crawler = Mockery::mock(Crawler::class); + $crawler->shouldReceive('filter')->andReturnSelf(); + $crawler->shouldReceive('each')->andReturn([]); + GoutteClient::shouldReceive('request') + ->withArgs(['GET', config('espinoso.url.cinema')]) + ->andReturn($crawler); + + // Act + $handler = $this->makeHandler(); + $update = $this->makeMessage([ + 'chat' => ['id' => 123], + 'text' => 'espi cine' + ]); + $handler->handle($update); + } + + /** + * @return CinemaHandler + */ + protected function makeHandler(): CinemaHandler + { + return new CinemaHandler($this->espinoso, $this->telegram); + } +} diff --git a/tests/Handlers/GitHubHandlerTest.php b/tests/Espinoso/Handlers/GitHubHandlerTest.php similarity index 60% rename from tests/Handlers/GitHubHandlerTest.php rename to tests/Espinoso/Handlers/GitHubHandlerTest.php index 430e10d..7d0b7a9 100644 --- a/tests/Handlers/GitHubHandlerTest.php +++ b/tests/Espinoso/Handlers/GitHubHandlerTest.php @@ -1,31 +1,23 @@ -<?php - -namespace Tests\Feature; +<?php namespace Tests\Espinoso\Handlers; use Mockery; use App\Facades\GuzzleClient; use App\Espinoso\Handlers\GitHubHandler; -use Tests\Handlers\HandlersTestCase; use Psr\Http\Message\ResponseInterface; -use Telegram\Bot\Laravel\Facades\Telegram; class GitHubHandlerTest extends HandlersTestCase { - protected function setUp() - { - parent::setUp(); - - $this->handler = new GitHubHandler; - } - /** * @test */ public function it_should_handle_when_receives_issue_command() { - $update = $this->update(['text' => 'espi issue blablatest']); + // Arrange + $handler = $this->makeHandler(); + $update = $this->makeMessage(['text' => 'espi issue blablatest']); - $this->assertTrue($this->handler->shouldHandle($update)); + // Act & Assert + $this->assertTrue($handler->shouldHandle($update)); } /** @@ -33,9 +25,12 @@ public function it_should_handle_when_receives_issue_command() */ public function it_should_not_handle_when_receives_another_text() { - $update = $this->update(['text' => 'not espi issue blablatest']); + // Arrange + $handler = $this->makeHandler(); + $update = $this->makeMessage(['text' => 'not espi issue blablatest']); - $this->assertFalse($this->handler->shouldHandle($update)); + // Act & Assert + $this->assertFalse($handler->shouldHandle($update)); } /** @@ -43,6 +38,7 @@ public function it_should_not_handle_when_receives_another_text() */ public function it_handle_and_create_issue() { + // Mocking $response = Mockery::mock(ResponseInterface::class); $response->shouldReceive('getStatusCode')->andReturn(201); $response->shouldReceive('getBody')->andReturn('{"html_url": "http://url.facades.org/issues/12"}'); @@ -51,26 +47,36 @@ public function it_handle_and_create_issue() config('espinoso.url.issues'), [ 'headers' => [ - 'Authorization' => "token ".config('espinoso.github.token'), + 'Authorization' => "token ".config('espinoso.token.github'), ], 'json' => ['title' => 'test facade'] ] ]) ->andReturn($response); + + // Arrange $message = [ 'chat_id' => 12345678, 'text' => '[Issue creado!](http://url.facades.org/issues/12)', 'parse_mode' => 'Markdown', ]; - Telegram::shouldReceive('sendMessage')->once()->with($message); - - // Act - $update = $this->update([ + $this->telegram->shouldReceive('sendMessage')->once()->with($message); + $handler = $this->makeHandler(); + $update = $this->makeMessage([ 'chat' => ['id' => 12345678], 'text' => 'espi issue test facade', ]); - $this->handler->shouldHandle($update); - $this->handler->handle($update); + // Act + $handler->shouldHandle($update); + $handler->handle($update); + } + + /** + * @return GitHubHandler + */ + protected function makeHandler(): GitHubHandler + { + return new GitHubHandler($this->espinoso, $this->telegram); } } diff --git a/tests/Espinoso/Handlers/GoogleInfoBoxHandlerTest.php b/tests/Espinoso/Handlers/GoogleInfoBoxHandlerTest.php new file mode 100644 index 0000000..6b6f437 --- /dev/null +++ b/tests/Espinoso/Handlers/GoogleInfoBoxHandlerTest.php @@ -0,0 +1,89 @@ +<?php namespace Tests\Espinoso\Handlers; + +use Mockery; +use App\Facades\GoutteClient; +use Symfony\Component\DomCrawler\Crawler; +use App\Espinoso\Handlers\GoogleInfoBoxHandler; + +class GoogleInfoBoxHandlerTest extends HandlersTestCase +{ + /** + * @test + */ + public function it_should_handle_when_match_regex() + { + // Arrange + $handler = $this->makeHandler(); + $updates = [ + $this->makeMessage(['text' => 'espi info bla']), + $this->makeMessage(['text' => 'espinoso info bla bla']), + $this->makeMessage(['text' => 'info bla bla bla']), + ]; + + // Act && Assert + collect($updates)->each(function ($update) use ($handler) { + $this->assertTrue($handler->shouldHandle($update)); + }); + } + + /** + * @test + */ + public function it_should_not_handle_when_receives_another_text() + { + // Arrange + $handler = $this->makeHandler(); + $updates = [ + $this->makeMessage(['text' => 'espiinfo nup']), + $this->makeMessage(['text' => 'espi infonup']), + $this->makeMessage(['text' => 'espinosoinfo tampoco']), + $this->makeMessage(['text' => 'espinoso infotampoco']), + $this->makeMessage(['text' => 'gib nop']), + ]; + + // Act && Assert + collect($updates)->each(function ($update) use ($handler) { + $this->assertFalse($handler->shouldHandle($update)); + }); + } + + /** + * @test + */ + public function it_handle_and_return_movies() + { + // Mocking + $query = 'got'; + $crawler = Mockery::mock(Crawler::class); + $crawler->shouldReceive('filter')->andReturnSelf(); + $crawler->shouldReceive('each')->andReturn([]); + GoutteClient::shouldReceive('request') + ->withArgs(['GET', config('espinoso.url.info') . rawurlencode($query)]) + ->andReturn($crawler); + + // Arrange + $message = [ + 'chat_id' => 123, + 'text' => "Uhhh... no hay un carajo!!\nO buscaste como el orto o estoy haciendo cualquiera!", + 'parse_mode' => 'Markdown', + ]; + $this->telegram->shouldReceive('sendMessage')->once()->with($message); + $handler = $this->makeHandler(); + $update = $this->makeMessage([ + 'chat' => ['id' => 123], + 'text' => 'espi info got' + ]); + + // Act + $handler->shouldHandle($update); + $handler->handle($update); + } + + /** + * @return GoogleInfoBoxHandler + */ + protected function makeHandler(): GoogleInfoBoxHandler + { + return new GoogleInfoBoxHandler($this->espinoso, $this->telegram); + } +} diff --git a/tests/Espinoso/Handlers/GoogleStaticMapHandlerTest.php b/tests/Espinoso/Handlers/GoogleStaticMapHandlerTest.php new file mode 100644 index 0000000..4ec08b3 --- /dev/null +++ b/tests/Espinoso/Handlers/GoogleStaticMapHandlerTest.php @@ -0,0 +1,87 @@ +<?php namespace Tests\Espinoso\Handlers; + +use App\Espinoso\Handlers\GoogleStaticMapsHandler; + +class GoogleStaticMapHandlerTest extends HandlersTestCase +{ + /** + * @test + */ + public function it_should_handle_when_match_regex() + { + // Arrange + $handler = $this->makeHandler(); + $updates = [ + $this->makeMessage(['text' => 'espi gsm islas malvinas']), + $this->makeMessage(['text' => 'espi gsm z:10 islas malvinas']), + $this->makeMessage(['text' => 'espi gsm zoom:12 islas malvinas']), + $this->makeMessage(['text' => 'espi gsm zoom:12 color:blue islas malvinas']), + $this->makeMessage(['text' => 'espi gsm z:12 c:yellow s:100x100 islas malvinas']), + $this->makeMessage(['text' => 'gsm z:12 c:yellow s:100x100 islas malvinas']), + $this->makeMessage(['text' => 'gsm zoom:12 color:blue islas malvinas']), + $this->makeMessage(['text' => 'gsm zoom:12 islas malvinas']), + $this->makeMessage(['text' => 'gsm z:12 islas malvinas']), + $this->makeMessage(['text' => 'gsm islas malvinas']), + ]; + + // Act && Assert + collect($updates)->each(function ($update) use ($handler) { + $this->assertTrue($handler->shouldHandle($update)); + }); + } + + /** + * @test + */ + public function it_should_not_handle_when_receives_another_text() + { + // Arrange + $handler = $handler = $this->makeHandler(); + $updates = [ + $this->makeMessage(['text' => 'espigsm nup']), + $this->makeMessage(['text' => 'espi gsmnup']), + $this->makeMessage(['text' => 'espinosogsm tampoco']), + $this->makeMessage(['text' => 'espinoso gsmtampoco']), + $this->makeMessage(['text' => 'gsmz:10 nup']), + ]; + + // Act && Assert + collect($updates)->each(function ($update) use ($handler) { + $this->assertFalse($handler->shouldHandle($update)); + }); + } + + /** + * @test + */ + public function it_handle_and_return_movies() + { + // Arrange + $address = 'islas malvinas'; + $options = "maptype=roadmap&zoom=10&size=600x500&markers=color:yellow|label:X|{$address}"; + $photo = config('espinoso.url.map') . "?center=" . urlencode($address) . "&{$options}"; + $message = [ + 'chat_id' => 123, + 'photo' => $photo, + 'caption' => $address . ', Argentinas!' + ]; + $this->telegram->shouldReceive('sendPhoto')->once()->with($message); + $handler = $this->makeHandler(); + $update = $this->makeMessage([ + 'chat' => ['id' => 123], + 'text' => 'espi gsm z:10 color:yellow islas malvinas' + ]); + + // Act + $handler->shouldHandle($update); + $handler->handle($update); + } + + /** + * @return GoogleStaticMapsHandler + */ + protected function makeHandler(): GoogleStaticMapsHandler + { + return new GoogleStaticMapsHandler($this->espinoso, $this->telegram); + } +} diff --git a/tests/Espinoso/Handlers/HandlersTestCase.php b/tests/Espinoso/Handlers/HandlersTestCase.php new file mode 100644 index 0000000..f044933 --- /dev/null +++ b/tests/Espinoso/Handlers/HandlersTestCase.php @@ -0,0 +1,38 @@ +<?php namespace Tests\Espinoso\Handlers; + +use App\Espinoso\Espinoso; +use Mockery; +use Tests\TestCase; +use Telegram\Bot\Api as ApiTelegram; + +abstract class HandlersTestCase extends TestCase +{ + protected $handler; + protected $telegram; + protected $espinoso; + + protected function setUp() + { + parent::setUp(); + + $this->espinoso = Mockery::mock(Espinoso::class); + $this->telegram = Mockery::mock(ApiTelegram::class); + } + + protected function tearDown() + { + Mockery::close(); + + parent::tearDown(); + } + + protected function assertShouldHandle($message) + { + $this->assertTrue($this->handler->shouldHandle($this->makeMessage(['text' => $message]))); + } + + protected function assertShouldNotHandle($handler, $message) + { + $this->assertFalse($handler->shouldHandle($this->makeMessage(['text' => $message]))); + } +} diff --git a/tests/Espinoso/Handlers/HelpHandlerTest.php b/tests/Espinoso/Handlers/HelpHandlerTest.php new file mode 100644 index 0000000..b13e190 --- /dev/null +++ b/tests/Espinoso/Handlers/HelpHandlerTest.php @@ -0,0 +1,26 @@ +<?php namespace Tests\Espinoso\Handlers; + +use App\Espinoso\Handlers\HelpHandler; + +class HelpHandlerTest extends HandlersTestCase +{ + /** + * @test + */ + public function it_should_handle_when_receives_help_message() + { + $this->handler = $this->makeHandler(); + + $this->assertShouldHandle('espi ayuda'); + $this->assertShouldHandle('espi help!'); + $this->assertShouldHandle('espi aiiiuuuda'); + } + + /** + * @return HelpHandler + */ + protected function makeHandler(): HelpHandler + { + return new HelpHandler($this->espinoso, $this->telegram); + } +} diff --git a/tests/Espinoso/Handlers/IMDbHandlerTest.php b/tests/Espinoso/Handlers/IMDbHandlerTest.php new file mode 100644 index 0000000..6bee4a5 --- /dev/null +++ b/tests/Espinoso/Handlers/IMDbHandlerTest.php @@ -0,0 +1,121 @@ +<?php namespace Tests\Espinoso\Handlers; + +use Mockery; +use Imdb\Title; +use App\Facades\IMDbSearch; +use App\Espinoso\Handlers\IMDbHandler; + +class IMDbHandlerTest extends HandlersTestCase +{ + /** + * @test + */ + public function it_should_handle_when_match_regex() + { + // Arrange + $handler = $this->makeHandler(); + $updates = [ + $this->makeMessage(['text' => 'espi imdb game of thrones']), + $this->makeMessage(['text' => 'espi movie game of thrones']), + $this->makeMessage(['text' => 'espi peli game of thrones']), + $this->makeMessage(['text' => 'espi serie game of thrones']), + $this->makeMessage(['text' => 'espi tv game of thrones']), + ]; + + // Act && Assert + collect($updates)->each(function ($update) use ($handler) { + $this->assertTrue($handler->shouldHandle($update)); + }); + } + + /** + * @test + */ + public function it_should_not_handle_when_receives_another_text() + { + // Arrange + $handler = $handler = $this->makeHandler(); + $updates = [ + $this->makeMessage(['text' => 'espiimdb game of thrones']), + $this->makeMessage(['text' => 'espi imdbgame of thrones']), + $this->makeMessage(['text' => 'movie game of thrones']), + $this->makeMessage(['text' => 'peli game of thrones']), + $this->makeMessage(['text' => 'serie game of thrones']), + $this->makeMessage(['text' => 'tv game of thrones']), + ]; + + // Act && Assert + collect($updates)->each(function ($update) use ($handler) { + $this->assertFalse($handler->shouldHandle($update)); + }); + } + + /** + * @test + */ + public function it_handle_and_return_info() + { + // Mocking + $mockedTitle = Mockery::mock(Title::class); + $mockedTitle->shouldReceive('storyline')->andReturn('storyline'); + $mockedTitle->shouldReceive('cast')->andReturn([['name' => 'Jon Snow']]); + $mockedTitle->shouldReceive('genres')->andReturn(['Drama']); + $mockedTitle->shouldReceive('seasons')->andReturn(8); + $mockedTitle->shouldReceive('director')->andReturn([['name' => 'Alan Taylor']]); + $mockedTitle->shouldReceive('creator')->andReturn([['name' => 'David Benioff']]); + $mockedTitle->shouldReceive('writing')->andReturn([['name' => 'D.B. Weiss']]); + $mockedTitle->shouldReceive('title')->andReturn('Game of Thrones'); + $mockedTitle->shouldReceive('year')->andReturn(2011); + $mockedTitle->shouldReceive('rating')->andReturn(9.5); + $mockedTitle->shouldReceive('runtime')->andReturn(57); + $mockedTitle->shouldReceive('main_url')->andReturn('http://www.imdb.com/title/tt0944947/'); + $mockedTitle->shouldReceive('photo')->andReturn('https://images-na.ssl-images-amazon.com/;images/M/MV5BMjE3NTQ1NDg1Ml5BMl5BanBnXkFtZTgwNzY2NDA0MjI@._V1_UX182_CR0,0,182,268_AL_.jpg'); + IMDbSearch::shouldReceive('search')->once() + ->with('game of thrones', [Title::MOVIE, Title::TV_SERIES]) + ->andReturn([$mockedTitle]); + + $photo = [ + 'chat_id' => 123, + 'photo' => 'https://images-na.ssl-images-amazon.com/;images/M/MV5BMjE3NTQ1NDg1Ml5BMl5BanBnXkFtZTgwNzY2NDA0MjI@._V1_UX182_CR0,0,182,268_AL_.jpg', + 'caption' => 'Game of Thrones' + ]; + $text = "*Game of Thrones* (2011) +⭐ 9.5/10 | 57min +_Drama_ + +storyline + +*Seasons:* 8 +*Creators:* David Benioff +*Writers:* D.B. Weiss +*Directors:* Alan Taylor +*Cast:* Jon Snow + +[View on IMDb](http://www.imdb.com/title/tt0944947/)"; + $message = [ + 'chat_id' => 123, + 'text' => $text, + 'parse_mode' => 'Markdown' + ]; + + $this->telegram->shouldReceive('sendPhoto')->once()->with($photo); + $this->telegram->shouldReceive('sendMessage')->once()->with($message); + $handler = $this->makeHandler(); + $update = $this->makeMessage([ + 'chat' => ['id' => 123], + 'text' => 'espi imdb game of thrones' + ]); + + // Act + $handler->shouldHandle($update); + $handler->handle($update); + } + + /** + * @return IMDbHandler + */ + protected function makeHandler(): IMDbHandler + { + return new IMDbHandler($this->espinoso, $this->telegram); + } +} diff --git a/tests/Espinoso/Handlers/InstagramHandlerTest.php b/tests/Espinoso/Handlers/InstagramHandlerTest.php new file mode 100644 index 0000000..9d35410 --- /dev/null +++ b/tests/Espinoso/Handlers/InstagramHandlerTest.php @@ -0,0 +1,94 @@ +<?php namespace Tests\Espinoso\Handlers; + +use App\Facades\InstagramSearch; +use App\Espinoso\Handlers\InstagramHandler; + +class InstagramHandlerTest extends HandlersTestCase +{ + /** + * @test + */ + public function it_should_handle_when_match_regex() + { + // Arrange + $handler = $this->makeHandler(); + $updates = [ + $this->makeMessage(['text' => 'espi ig alanmtk']), + $this->makeMessage(['text' => 'ig alanmtk']), + $this->makeMessage(['text' => 'espi ig alanmtk last']), + $this->makeMessage(['text' => 'ig alanmtk pos:2']), + ]; + + // Act && Assert + collect($updates)->each(function ($update) use ($handler) { + $this->assertTrue($handler->shouldHandle($update)); + }); + } + + /** + * @test + */ + public function it_should_not_handle_when_receives_another_text() + { + // Arrange + $handler = $handler = $this->makeHandler(); + $updates = [ + $this->makeMessage(['text' => 'espiig alanmtk']), + $this->makeMessage(['text' => 'ig-alanmtk']), + ]; + + // Act && Assert + collect($updates)->each(function ($update) use ($handler) { + $this->assertFalse($handler->shouldHandle($update)); + }); + } + + /** + * @test + */ + public function it_handle_and_return_info() + { + // Mocking + $response = [[ + 'id' => '1577240841595284729_12860068', + 'code' => 'BXjfDBYhmz5', + 'user' => ['id' => '12860068'], + 'images' => [ + 'standard_resolution' => [ + 'width' => 640, + 'height' => 640, + 'url' => 'https://instagram.fsst1-1.fna.fbcdn.net/t51.2885-15/s640x640/sh0.08/e35/20687889_2047983445218577_9133972975488335872_n.jpg' + ] + ] + ]]; + + InstagramSearch::shouldReceive('get') + ->with('alanmtk') + ->andReturn($response); + + $message = [ + 'chat_id' => 123, + 'photo' => 'https://instagram.fsst1-1.fna.fbcdn.net/t51.2885-15/s640x640/sh0.08/e35/20687889_2047983445218577_9133972975488335872_n.jpg', + 'caption' => 'Ver https://www.instagram.com/alanmtk' + ]; + $this->telegram->shouldReceive('sendPhoto')->once()->with($message); + + $handler = $this->makeHandler(); + $update = $this->makeMessage([ + 'chat' => ['id' => 123], + 'text' => 'ig alanmtk last' + ]); + + // Act + $handler->shouldHandle($update); + $handler->handle($update); + } + + /** + * @return InstagramHandler + */ + protected function makeHandler(): InstagramHandler + { + return new InstagramHandler($this->espinoso, $this->telegram); + } +} diff --git a/tests/Espinoso/Handlers/NextHolidaysHandlerTest.php b/tests/Espinoso/Handlers/NextHolidaysHandlerTest.php new file mode 100644 index 0000000..6997c42 --- /dev/null +++ b/tests/Espinoso/Handlers/NextHolidaysHandlerTest.php @@ -0,0 +1,103 @@ +<?php namespace Tests\Espinoso\Handlers; + +use Mockery; +use App\Facades\GoutteClient; +use Symfony\Component\DomCrawler\Crawler; +use App\Espinoso\Handlers\NextHolidaysHandler; + +class NextHolidaysHandlerTest extends HandlersTestCase +{ + /** + * @test + */ + public function it_should_handle_when_match_regex() + { + // Arrange + $handler = $this->makeHandler(); + $updates = [ + $this->makeMessage(['text' => 'espi feriado']), + $this->makeMessage(['text' => 'espi prox feriado']), + $this->makeMessage(['text' => 'espi próximo feriado']), + $this->makeMessage(['text' => 'espi proximo feriado']), + $this->makeMessage(['text' => 'espi feriados']), + $this->makeMessage(['text' => 'espi prox feriados']), + $this->makeMessage(['text' => 'espi próximos feriados']), + $this->makeMessage(['text' => 'espi proximos feriados']), + ]; + + // Act && Assert + collect($updates)->each(function ($update) use ($handler) { + $this->assertTrue($handler->shouldHandle($update)); + }); + } + + /** + * @test + */ + public function it_should_not_handle_when_receives_another_text() + { + // Arrange + $handler = $handler = $this->makeHandler(); + $updates = [ + $this->makeMessage(['text' => 'feriado']), + $this->makeMessage(['text' => 'feriados']), + $this->makeMessage(['text' => 'próximo feriado']), + $this->makeMessage(['text' => 'proximo feriado']), + $this->makeMessage(['text' => 'prox feriado']), + ]; + + // Act && Assert + collect($updates)->each(function ($update) use ($handler) { + $this->assertFalse($handler->shouldHandle($update)); + }); + } + + /** + * @test + */ + public function it_handle_and_return_info() + { + // Mocking + $jsonText = "var json = '[{\"description\":\"Paso a la Inmortalidad del General Jos\u00e9 de San Mart\u00edn\",\"phrase\":\"Lunes 21 de Agosto\",\"count\":10},{\"description\":\"D\u00eda del Respeto a la Diversidad Cultural\",\"phrase\":\"Lunes 16 de Octubre\",\"count\":66},{\"description\":\"D\u00eda de la Soberan\u00eda Nacional\",\"phrase\":\"Lunes 20 de Noviembre\",\"count\":101},{\"description\":\"Inmaculada Concepci\u00f3n de Mar\u00eda\",\"phrase\":\"Viernes 08 de Diciembre\",\"count\":119},{\"description\":\"Navidad\",\"phrase\":\"Lunes 25 de Diciembre\",\"count\":136}]'; + var position = 0;"; + + $crawler = Mockery::mock(Crawler::class); + $crawler->shouldReceive('filter')->with('script')->andReturnSelf(); + $crawler->shouldReceive('eq')->with(2)->andReturnSelf(); + $crawler->shouldReceive('text')->andReturn($jsonText); + GoutteClient::shouldReceive('request') + ->withArgs(['GET', config('espinoso.url.holidays')]) + ->andReturn($crawler); + + $text = "Manga de vagos, *quedan 5 feriados* en todo el año. + - *Lunes 21 de Agosto*, Paso a la Inmortalidad del General José de San Martín (10 días) + - *Lunes 16 de Octubre*, Día del Respeto a la Diversidad Cultural (66 días) + - *Lunes 20 de Noviembre*, Día de la Soberanía Nacional (101 días) + - *Viernes 08 de Diciembre*, Inmaculada Concepción de María (119 días) + - *Lunes 25 de Diciembre*, Navidad (136 días)"; + $message = [ + 'chat_id' => 123, + 'text' => $text, + 'parse_mode' => 'Markdown' + ]; + $this->telegram->shouldReceive('sendMessage')->once()->with($message); + + $handler = $this->makeHandler(); + $update = $this->makeMessage([ + 'chat' => ['id' => 123], + 'text' => 'espi feriados' + ]); + + // Act + $handler->shouldHandle($update); + $handler->handle($update); + } + + /** + * @return NextHolidaysHandler + */ + protected function makeHandler(): NextHolidaysHandler + { + return new NextHolidaysHandler($this->espinoso, $this->telegram); + } +} diff --git a/tests/Espinoso/Handlers/StickersHandlerTest.php b/tests/Espinoso/Handlers/StickersHandlerTest.php new file mode 100644 index 0000000..e0506b3 --- /dev/null +++ b/tests/Espinoso/Handlers/StickersHandlerTest.php @@ -0,0 +1,86 @@ +<?php namespace Tests\Espinoso\Handlers; + +use App\Espinoso\Handlers\StickersHandler; + +class StickersHandlerTest extends HandlersTestCase +{ + /** + * @test + */ + public function it_should_handle_when_match_regex() + { + // Arrange + $handler = $this->makeHandler(); + $updates = [ + $this->makeMessage([ + 'from' => ['first_name' => 'Facundo'], + 'text' => 'espi maybe'] + ), + $this->makeMessage([ + 'from' => ['first_name' => 'Facundo'], + 'text' => 'maybe'] + ), + ]; + + // Act && Assert + collect($updates)->each(function ($update) use ($handler) { + $this->assertTrue($handler->shouldHandle($update)); + }); + } + + /** + * @test + */ + public function it_should_not_handle_when_receives_another_text() + { + // Arrange + $handler = $this->makeHandler(); + $updates = [ + $this->makeMessage([ + 'from' => ['first_name' => 'Dan'], + 'text' => 'espi maybe'] + ), + $this->makeMessage([ + 'from' => ['first_name' => 'Facundo'], + 'text' => 'espimaybe'] + ), + ]; + + // Act && Assert + collect($updates)->each(function ($update) use ($handler) { + $this->assertFalse($handler->shouldHandle($update)); + }); + } + + /** + * @test + */ + public function it_handle_and_return_info() + { + // Mocking + $message = [ + 'chat_id' => 123, + 'sticker' => 'CAADAgADiwUAAvoLtgh812FBxEdUAgI' // LazyPanda + ]; + $this->telegram->shouldReceive('sendSticker')->once()->with($message); + + $handler = $this->makeHandler(); + $update = $this->makeMessage([ + 'chat' => ['id' => 123], + 'from' => ['first_name' => 'Facundo'], + 'text' => 'espi maybe' + ]); + + // Act + $handler->shouldHandle($update); + $handler->handle($update); + } + + /** + * @return StickersHandler + */ + protected function makeHandler(): StickersHandler + { + return new StickersHandler($this->espinoso, $this->telegram); + } +} diff --git a/tests/Espinoso/Handlers/WeatherHandlerTest.php b/tests/Espinoso/Handlers/WeatherHandlerTest.php new file mode 100644 index 0000000..54a235e --- /dev/null +++ b/tests/Espinoso/Handlers/WeatherHandlerTest.php @@ -0,0 +1,128 @@ +<?php namespace Tests\Espinoso\Handlers; + +use Mockery; +use DateTime; +use Carbon\Carbon; +use Cmfcmf\OpenWeatherMap\Forecast; +use Cmfcmf\OpenWeatherMap\Util\Time; +use Cmfcmf\OpenWeatherMap\Util\Unit; +use Cmfcmf\OpenWeatherMap\Util\Weather; +use Cmfcmf\OpenWeatherMap\CurrentWeather; +use App\Facades\WeatherSearch; +use App\Espinoso\Handlers\WeatherHandler; + +class WeatherHandlerTest extends HandlersTestCase +{ + /** + * @test + */ + public function it_should_handle_when_match_regex() + { + // Arrange + $handler = $this->makeHandler(); + $updates = [ + $this->makeMessage(['text' => 'clima lunes']), + $this->makeMessage(['text' => 'clima el lunes']), + $this->makeMessage(['text' => 'clima este lunes']), + $this->makeMessage(['text' => 'espi clima lunes']), + $this->makeMessage(['text' => 'espi clima el lunes']), + $this->makeMessage(['text' => 'espi clima este lunes']), + $this->makeMessage(['text' => 'clima martes']), + $this->makeMessage(['text' => 'clima miércoles']), + $this->makeMessage(['text' => 'clima miercoles']), + $this->makeMessage(['text' => 'clima jueves']), + $this->makeMessage(['text' => 'clima viernes']), + $this->makeMessage(['text' => 'clima sábado']), + $this->makeMessage(['text' => 'clima sabado']), + $this->makeMessage(['text' => 'clima domingo']), + $this->makeMessage(['text' => 'clima Domingo']), + $this->makeMessage(['text' => 'clima DOMINGO']), + ]; + + // Act && Assert + collect($updates)->each(function ($update) use ($handler) { + $this->assertTrue($handler->shouldHandle($update)); + }); + } + + /** + * @test + */ + public function it_should_not_handle_when_receives_another_text() + { + // Arrange + $handler = $handler = $this->makeHandler(); + $updates = [ + $this->makeMessage(['text' => 'climalunes']), + $this->makeMessage(['text' => 'clima pŕoximo lunes']), + $this->makeMessage(['text' => 'espiclima lunes']), + ]; + + // Act && Assert + collect($updates)->each(function ($update) use ($handler) { + $this->assertFalse($handler->shouldHandle($update)); + }); + } + + /** + * @test + */ + public function it_handle_and_return_info() + { + // Mocking + $min = Mockery::mock(Unit::class); + $max = Mockery::mock(Unit::class); + $day = Mockery::mock(DateTime::class); + $time = Mockery::mock(Time::class); + $dayTo = Mockery::mock(DateTime::class); + $dayFrom = Mockery::mock(DateTime::class); + $weather = Mockery::mock(Weather::class); + $forecast = Mockery::mock(Forecast::class); + $temperature = Mockery::mock(CurrentWeather::class); + + $min->shouldReceive('getValue')->andReturn('10.76'); + $max->shouldReceive('getValue')->andReturn('16.69'); + $temperature->min = $min; + $temperature->max = $max; + $weather->description = 'cielo claro'; + $nextDay = Carbon::createFromTimestamp(strtotime('next monday')); + $day->shouldReceive('format')->with('Y-m-d')->andReturn($nextDay->format('Y-m-d')); + $dayFrom->shouldReceive('format')->with('H:i')->andReturn('00:00'); + $dayTo->shouldReceive('format')->with('H:i')->andReturn('23:59'); + $time->day = $day; + $time->to = $dayTo; + $time->from = $dayFrom; + $forecast->time = $time; + $forecast->temperature = $temperature; + $forecast->weather = $weather; + + WeatherSearch::shouldReceive('getWeatherForecast') + ->withArgs(['Quilmes, AR', "es", "metric", 10, '']) + ->andReturn([$forecast]); + + $message = [ + 'chat_id' => 123, + 'text' => 'está pronosticado de 00:00 a 23:59 cielo claro con temperaturas entre 10.76 y 16.69 grados', + 'parse_mode' => 'HTML' + ]; + $this->telegram->shouldReceive('sendMessage')->once()->with($message); + + $handler = $this->makeHandler(); + $update = $this->makeMessage([ + 'chat' => ['id' => 123], + 'text' => 'espi clima lunes' + ]); + + // Act + $handler->shouldHandle($update); + $handler->handle($update); + } + + /** + * @return WeatherHandler + */ + protected function makeHandler(): WeatherHandler + { + return new WeatherHandler($this->espinoso, $this->telegram); + } +} diff --git a/tests/Feature/WeatherMapTest.php b/tests/Feature/WeatherMapTest.php deleted file mode 100644 index 5fa72fd..0000000 --- a/tests/Feature/WeatherMapTest.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php - -namespace Tests\Feature; - -use App\Espinoso\Handlers\Weather; -use Tests\TestCase; - -class WeatherMap extends TestCase -{ - public function testExample() - { - $w = new Weather(); - - $days = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday']; - foreach ($days as $day) - { - $date = \DateTime::createFromFormat("U", strtotime("next $day")); - $response = $w->buildResponse($date); - $this->assertStringEndsNotWith("está pronosticado ", $response); - } - } -} diff --git a/tests/Handlers/BardoDelEspinosoHandlerTest.php b/tests/Handlers/BardoDelEspinosoHandlerTest.php deleted file mode 100644 index 967d792..0000000 --- a/tests/Handlers/BardoDelEspinosoHandlerTest.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php namespace Tests\Feature; - -use Tests\Handlers\HandlersTestCase; -use Telegram\Bot\Laravel\Facades\Telegram; -use App\Espinoso\Handlers\BardoDelEspinosoHandler; - -class BardoDelEspinosoHandlerTest extends HandlersTestCase -{ - protected function setUp() - { - parent::setUp(); - $this->handler = new BardoDelEspinosoHandler; - } - - /** - * @test - */ - public function it_should_handle_when_receives_send_me_nudes() - { - $update = $this->update(['text' => 'send me nudes']); - - $this->assertTrue($this->handler->shouldHandle($update)); - } - - /** - * @test - */ - public function it_should_not_handle_when_receives_another_text() - { - $update1 = $this->update(['text' => 'saraza send me nudes']); - $update2 = $this->update(['text' => 'send me nudes saraza']); - - $this->assertFalse($this->handler->shouldHandle($update1)); - $this->assertFalse($this->handler->shouldHandle($update2)); - } - - /** - * @test - */ - public function it_handle_and_send_photo() - { - $photo = [ - 'chat_id' => 123, - 'photo' => 'https://cdn.drawception.com/images/panels/2012/4-4/FErsE1a6t7-8.png', - 'caption' => 'Acá tenés tu nude, puto del orto!' - ]; - Telegram::shouldReceive('sendPhoto')->once()->with($photo); - - $update = $this->update([ - 'chat' => ['id' => 123], - 'text' => 'send me nudes' - ]); - - $this->handler->handle($update); - } -} diff --git a/tests/Handlers/CinemaHandlerTest.php b/tests/Handlers/CinemaHandlerTest.php deleted file mode 100644 index 8004aaf..0000000 --- a/tests/Handlers/CinemaHandlerTest.php +++ /dev/null @@ -1,89 +0,0 @@ -<?php namespace Tests\Feature; - - -use Mockery; -use App\Facades\GoutteClient; -use App\Espinoso\Handlers\CinemaHandler; -use Tests\Handlers\HandlersTestCase; -use Symfony\Component\DomCrawler\Crawler; -use Telegram\Bot\Laravel\Facades\Telegram; - -class CinemaHandlerTest extends HandlersTestCase -{ - protected function setUp() - { - parent::setUp(); - - $this->handler = new CinemaHandler; - } - - /** - * @test - */ - public function it_should_handle_when_match_regex() - { - $updates = [ - $this->update(['text' => 'espi cine']), - $this->update(['text' => 'espinoso cine?']), - $this->update(['text' => 'espi cine??']), - $this->update(['text' => 'espi cine!']), - $this->update(['text' => 'espi cine!!!']), - $this->update(['text' => 'espi ¿cine?']), - $this->update(['text' => 'espi que hay en el cine']), - $this->update(['text' => 'espi que hay en el cine?']), - ]; - - collect($updates)->each(function ($update) { - $this->assertTrue($this->handler->shouldHandle($update)); - }); - } - - /** - * @test - */ - public function it_should_not_handle_when_receives_another_text() - { - $updates = [ - $this->update(['text' => 'cinema']), - $this->update(['text' => 'ig lacosacine']), - $this->update(['text' => 'vamos al cine?']), - $this->update(['text' => 'vamos al cine espi']), - $this->update(['text' => 'vamos al cine, espi']), - $this->update(['text' => 'vamos al cine, espi?']), - ]; - - collect($updates)->each(function ($update) { - $this->assertFalse($this->handler->shouldHandle($update)); - }); - } - - /** - * @test - */ - public function it_handle_and_return_movies() - { - $crawler = Mockery::mock(Crawler::class); - $crawler->shouldReceive('filter')->andReturnSelf(); - $crawler->shouldReceive('each')->andReturn([]); - GoutteClient::shouldReceive('request') - ->withArgs(['GET', config('espinoso.url.cinema')]) - ->andReturn($crawler); - - $text = "¿La pensás poner? -¡Mete Netflix pelotud@, es mas barato! -Pero igual podes ver todas estas:\n -"; - $message = [ - 'chat_id' => 123, - 'text' => $text, - ]; - Telegram::shouldReceive('sendMessage')->once()->with($message); - - $update = $this->update([ - 'chat' => ['id' => 123], - 'text' => 'espi cine' - ]); - - $this->handler->handle($update); - } -} diff --git a/tests/Handlers/GoogleInfoBoxHandlerTest.php b/tests/Handlers/GoogleInfoBoxHandlerTest.php deleted file mode 100644 index 1aac1ae..0000000 --- a/tests/Handlers/GoogleInfoBoxHandlerTest.php +++ /dev/null @@ -1,83 +0,0 @@ -<?php namespace Tests\Feature; - -use Mockery; -use App\Facades\GoutteClient; -use Tests\Handlers\HandlersTestCase; -use Symfony\Component\DomCrawler\Crawler; -use Telegram\Bot\Laravel\Facades\Telegram; -use App\Espinoso\Handlers\GoogleInfoBoxHandler; - -class GoogleInfoBoxHandlerTest extends HandlersTestCase -{ - protected function setUp() - { - parent::setUp(); - - $this->handler = new GoogleInfoBoxHandler; - } - - /** - * @test - */ - public function it_should_handle_when_match_regex() - { - $updates = [ - $this->update(['text' => 'espi info bla']), - $this->update(['text' => 'espinoso info bla bla']), - $this->update(['text' => 'info bla bla bla']), - ]; - - collect($updates)->each(function ($update) { - $this->assertTrue($this->handler->shouldHandle($update)); - }); - } - - /** - * @test - */ - public function it_should_not_handle_when_receives_another_text() - { - $updates = [ - $this->update(['text' => 'espiinfo nup']), - $this->update(['text' => 'espi infonup']), - $this->update(['text' => 'espinosoinfo tampoco']), - $this->update(['text' => 'espinoso infotampoco']), - $this->update(['text' => 'gib nop']), - ]; - - collect($updates)->each(function ($update) { - $this->assertFalse($this->handler->shouldHandle($update)); - }); - } - - /** - * @test - */ - public function it_handle_and_return_movies() - { - // Mocking Action - $query = 'got'; - $crawler = Mockery::mock(Crawler::class); - $crawler->shouldReceive('filter')->andReturnSelf(); - $crawler->shouldReceive('each')->andReturn([]); - GoutteClient::shouldReceive('request') - ->withArgs(['GET', config('espinoso.url.info') . rawurlencode($query)]) - ->andReturn($crawler); - - $message = [ - 'chat_id' => 123, - 'text' => "Uhhh... no hay un carajo!!\nO buscaste como el orto o estoy haciendo cualquiera!", - 'parse_mode' => 'Markdown', - ]; - Telegram::shouldReceive('sendMessage')->once()->with($message); - - // Real Action - $update = $this->update([ - 'chat' => ['id' => 123], - 'text' => 'espi info got' - ]); - - $this->handler->shouldHandle($update); - $this->handler->handle($update); - } -} diff --git a/tests/Handlers/HandlersTestCase.php b/tests/Handlers/HandlersTestCase.php deleted file mode 100644 index 197679e..0000000 --- a/tests/Handlers/HandlersTestCase.php +++ /dev/null @@ -1,53 +0,0 @@ -<?php namespace Tests\Handlers; - -use App\Espinoso\Handlers\EspinosoHandler; -use Faker\Factory; -use Tests\TestCase; - -abstract class HandlersTestCase extends TestCase -{ - /** - * @var EspinosoHandler - */ - protected $handler; - - protected function update($params) - { - // FIXME as Builder - $faker = Factory::create(); - - $updates = [ - 'update_id' => $params['update_id'] ?? $faker->randomNumber(), - 'message' => [ - 'message_id' => $params['message_id'] ?? $faker->randomNumber(), - 'from' => [ - 'id' => $params['from']['id'] ?? $faker->randomNumber(), - 'first_name' => $params['from']['first_name'] ?? 'John', - 'last_name' => $params['from']['last_name'] ?? 'Doe', - 'username' => $params['from']['username'] ?? 'JohnDoe' - ], - 'chat' => [ - 'id' => $params['chat']['id'] ?? $faker->randomNumber(), - 'first_name' => $params['chat']['first_name'] ?? 'John', - 'last_name' => $params['chat']['last_name'] ?? 'Doe', - 'username' => $params['chat']['username'] ?? 'JohnDoe', - 'type' => $params['chat']['type'] ?? 'private' - ], - 'date' => $params['date'] ?? 1459957719, - 'text' => $params['text'] ?? $faker->word - ] - ]; - - return json_decode(json_encode($updates)); - } - - protected function assertShouldHandle($handler, $message) - { - $this->assertTrue($handler->shouldHandle($this->update(['text' => $message]))); - } - - protected function assertShouldNotHandle($handler, $message) - { - $this->assertFalse($handler->shouldHandle($this->update(['text' => $message]))); - } -} diff --git a/tests/TestCase.php b/tests/TestCase.php index 2932d4a..200811b 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -1,10 +1,42 @@ -<?php - -namespace Tests; +<?php namespace Tests; +use Faker\Factory; +use Telegram\Bot\Objects\Message; use Illuminate\Foundation\Testing\TestCase as BaseTestCase; abstract class TestCase extends BaseTestCase { use CreatesApplication; + + protected function text(string $text) + { + return $this->makeMessage(['text' => $text]); + } + + // FIXME as Builder + protected function makeMessage(array $params = []) + { + $faker = Factory::create(); + + $data = [ + 'message_id' => $params['message_id'] ?? $faker->randomNumber(), + 'from' => [ + 'id' => $params['from']['id'] ?? $faker->randomNumber(), + 'first_name' => $params['from']['first_name'] ?? 'John', + 'last_name' => $params['from']['last_name'] ?? 'Doe', + 'username' => $params['from']['username'] ?? 'JohnDoe' + ], + 'chat' => [ + 'id' => $params['chat']['id'] ?? $faker->randomNumber(), + 'first_name' => $params['chat']['first_name'] ?? 'John', + 'last_name' => $params['chat']['last_name'] ?? 'Doe', + 'username' => $params['chat']['username'] ?? 'JohnDoe', + 'type' => $params['chat']['type'] ?? 'private' + ], + 'date' => $params['date'] ?? 1459957719, + 'text' => $params['text'] ?? $faker->word + ]; + + return new Message($data); + } }