-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #88 from Nessworthy/master
W3Schools Plugin
- Loading branch information
Showing
2 changed files
with
268 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
<?php declare(strict_types=1); | ||
|
||
namespace Room11\Jeeves\Plugins; | ||
|
||
use Amp\Promise; | ||
use Amp\Success; | ||
use Room11\Jeeves\Chat\Client\ChatClient; | ||
use Room11\Jeeves\Chat\Message\Message; | ||
|
||
class AntiW3Schools extends BasePlugin | ||
{ | ||
|
||
const BAD_HOST_PATTERN = 'w3schools\.com'; | ||
const W3S_CATEGORY_RESPONSES = [ | ||
'html' => '[Check the Mozilla Developer Network HTML documentation](https://developer.mozilla.org/docs/Web/HTML) for help with HTML.', | ||
'css' => '[Check the Mozilla Developer Network CSS documentation](https://developer.mozilla.org/docs/Web/CSS) for help with CSS.', | ||
'js' => '[Check the Mozilla Developer Network JS documentation](https://developer.mozilla.org/docs/Web/JavaScript) for help with JavaScript.', | ||
'sql' => '[Check MySQL\'s official MySQL documentation](https://dev.mysql.com/doc/) for more information.', | ||
'php' => '[Check the official PHP Documentation](https://secure.php.net/docs.php) for help with PHP.', | ||
'bootstrap' => '[Check the official Bootstrap Documentation](https://getbootstrap.com/getting-started/) for help with the Bootstrap framework.', | ||
'jquery' => '[Check the official jQuery API Documentation](https://api.jquery.com/) for help with the jQuery library.', | ||
'angular' => '[Check the official AngularJS API Documentation](https://docs.angularjs.org/api) for help with the AngularJS framework.', | ||
'xml'=> '[Check W3\'s documentation on XML](https://www.w3.org/standards/xml/core) for more information.', | ||
'ajax' => '[Check the Mozilla Developer Network AJAX documentation](https://developer.mozilla.org/docs/AJAX) for more information.', | ||
]; | ||
|
||
private $chatClient; | ||
|
||
public function __construct(ChatClient $chatClient) | ||
{ | ||
$this->chatClient = $chatClient; | ||
} | ||
|
||
/** | ||
* @inheritDoc | ||
*/ | ||
public function getName(): string | ||
{ | ||
return 'AntiW3Schools'; | ||
} | ||
|
||
/** | ||
* @inheritDoc | ||
*/ | ||
public function getDescription(): string | ||
{ | ||
return 'A small monitoring plugin which bothers people when a w3schools link is detected.'; | ||
} | ||
|
||
/** | ||
* @inheritDoc | ||
*/ | ||
public function getMessageHandler(): array | ||
{ | ||
return [$this, 'handleMessage']; | ||
} | ||
|
||
/** | ||
* Entry point for checking a message for w3schools links. | ||
* @param Message $message | ||
* @return Promise | ||
*/ | ||
public function handleMessage(Message $message): Promise | ||
{ | ||
return $this->containsTerribleUri($message->getText()) | ||
? $this->chatClient->postReply($message, $this->createReply($message)) | ||
: new Success(); | ||
} | ||
|
||
/** | ||
* Check if text contains a W3C link. | ||
* @param string $text | ||
* @return bool | ||
*/ | ||
private function containsTerribleUri(string $text) : bool | ||
{ | ||
return preg_match('#' . self::BAD_HOST_PATTERN . '#i', $text) === 1; | ||
} | ||
|
||
/** | ||
* Create a suitable message to bother people about W3C. | ||
* @param Message $message | ||
* @return string | ||
*/ | ||
private function createReply(Message $message) : string | ||
{ | ||
$return = 'W3Schools should not be trusted as a reliable resource. [Click here to read why](http://www.w3fools.com/).'; | ||
|
||
$categories = $this->dissectCategoriesFromString($message->getText()); | ||
|
||
foreach($categories as $category) { | ||
$return .= ' ' . self::W3S_CATEGORY_RESPONSES[$category]; | ||
} | ||
|
||
return $return; | ||
|
||
} | ||
|
||
/** | ||
* Retrieve W3S categories from a given string. | ||
* @param string $text | ||
* @return string[] | ||
*/ | ||
private function dissectCategoriesFromString(string $text) { | ||
|
||
// Extract the category from all W3S URIs found. | ||
$categoryNames = array_keys(self::W3S_CATEGORY_RESPONSES); | ||
|
||
$matchSets = []; | ||
$categoryMatchPattern = '#' . self::BAD_HOST_PATTERN . '/(' . implode('|', $categoryNames) . ')/#i'; | ||
$matchResult = preg_match_all($categoryMatchPattern, $text, $matchSets); | ||
|
||
if($matchResult === 0 || $matchResult === false) { | ||
// If no matches were found (or the regex failed), return no results. | ||
return []; | ||
} | ||
|
||
// Prevent multiple messages for the same category by only returning one of each type found. | ||
return array_unique( | ||
array_map( | ||
'strtolower', | ||
$matchSets[1] | ||
) | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
<?php declare(strict_types = 1); | ||
|
||
namespace Room11\Jeeves\Tests\Chat\Plugin; | ||
|
||
use Amp\Success; | ||
use Room11\Jeeves\Chat\Message\Message; | ||
use Room11\Jeeves\Plugins\AntiW3Schools; | ||
|
||
class AntiW3SchoolsTest extends AbstractPluginTest | ||
{ | ||
public function testCommandName() | ||
{ | ||
$this->assertSame('AntiW3Schools', $this->plugin->getName()); | ||
$this->assertSame( | ||
'A small monitoring plugin which bothers people when a w3schools link is detected.', | ||
$this->plugin->getDescription() | ||
); | ||
$this->assertSame([], $this->plugin->getEventHandlers()); | ||
$this->assertSame([], $this->plugin->getCommandEndpoints()); | ||
} | ||
|
||
public function testValidMessageHandler() | ||
{ | ||
$result = $this->plugin->getMessageHandler(); | ||
|
||
$this->assertTrue(is_callable($result), 'Message handler expected to be callable, but wasn\'t.'); | ||
} | ||
|
||
public function testNoMentionOfW3Schools() | ||
{ | ||
/** @var AntiW3Schools $plugin */ | ||
$plugin = $this->plugin; | ||
|
||
$message = $this->getMockBuilder(Message::class) | ||
->disableOriginalConstructor() | ||
->getMock(); | ||
|
||
$message | ||
->expects($this->once()) | ||
->method('getText') | ||
->will($this->returnValue('Happy Friday!')); | ||
|
||
$result = $plugin->handleMessage($message); | ||
$this->assertInstanceOf(Success::class, $result); | ||
} | ||
|
||
public function testBasicW3SchoolsMention() | ||
{ | ||
/** @var AntiW3Schools $plugin */ | ||
$plugin = $this->plugin; | ||
$client = $this->client; | ||
|
||
$message = $this->getMockBuilder(Message::class) | ||
->disableOriginalConstructor() | ||
->getMock(); | ||
|
||
$message | ||
->expects($this->exactly(2)) | ||
->method('getText') | ||
->will($this->returnValue('What\'s wrong with w3schools.com?')); | ||
|
||
$client | ||
->expects($this->once()) | ||
->method('postReply') | ||
->with( | ||
$this->identicalTo($message), | ||
$this->equalTo('W3Schools should not be trusted as a reliable resource. [Click here to read why](http://www.w3fools.com/).') | ||
); | ||
|
||
$plugin->handleMessage($message); | ||
|
||
} | ||
|
||
public function testSingleCategoryW3SchoolsMention() | ||
{ | ||
/** @var AntiW3Schools $plugin */ | ||
$plugin = $this->plugin; | ||
$client = $this->client; | ||
|
||
$message = $this->getMockBuilder(Message::class) | ||
->disableOriginalConstructor() | ||
->getMock(); | ||
|
||
$message | ||
->expects($this->exactly(2)) | ||
->method('getText') | ||
->will($this->returnValue('How do I nest HTML elements like in http://www.w3schools.com/html/html_elements.asp?')); | ||
|
||
$client | ||
->expects($this->once()) | ||
->method('postReply') | ||
->with( | ||
$this->identicalTo($message), | ||
$this->equalTo( | ||
'W3Schools should not be trusted as a reliable resource. [Click here to read why](http://www.w3fools.com/).' | ||
. ' [Check the Mozilla Developer Network HTML documentation](https://developer.mozilla.org/docs/Web/HTML) for help with HTML.' | ||
) | ||
); | ||
|
||
$plugin->handleMessage($message); | ||
} | ||
|
||
public function testMultipleCategoryW3SchoolsMentions() | ||
{ | ||
/** @var AntiW3Schools $plugin */ | ||
$plugin = $this->plugin; | ||
$client = $this->client; | ||
|
||
$message = $this->getMockBuilder(Message::class) | ||
->disableOriginalConstructor() | ||
->getMock(); | ||
|
||
$message | ||
->expects($this->exactly(2)) | ||
->method('getText') | ||
->will($this->returnValue('How do I nest HTML elements like in w3schools.com/html/html_elements.asp and styled like w3schools.com/css/fake_page.asp?')); | ||
|
||
$client | ||
->expects($this->once()) | ||
->method('postReply') | ||
->with( | ||
$this->identicalTo($message), | ||
$this->equalTo( | ||
'W3Schools should not be trusted as a reliable resource. [Click here to read why](http://www.w3fools.com/).' | ||
. ' [Check the Mozilla Developer Network HTML documentation](https://developer.mozilla.org/docs/Web/HTML) for help with HTML.' | ||
. ' [Check the Mozilla Developer Network CSS documentation](https://developer.mozilla.org/docs/Web/CSS) for help with CSS.' | ||
) | ||
); | ||
|
||
$plugin->handleMessage($message); | ||
} | ||
|
||
/** | ||
* @inheritdoc | ||
*/ | ||
protected function setUp() | ||
{ | ||
parent::setUp(); | ||
|
||
$this->plugin = new AntiW3Schools($this->client); | ||
} | ||
} |