├── composer.json ├── .gitignore ├── config.sample.php ├── app.json ├── .editorconfig ├── api.php ├── dist └── css │ └── styles.css ├── LICENSE ├── src └── Archibald │ ├── Api.php │ ├── Archibald.php │ └── Request.php ├── index.php ├── README.md └── composer.lock /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "autoload": { 3 | "psr-4": {"Archibald\\": "src/Archibald"} 4 | }, 5 | "require": { 6 | "guzzlehttp/guzzle": "~4.0" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | composer.phar 3 | config.php 4 | dploy.yaml 5 | TODO.md 6 | .DS_Store 7 | .DS_Store? 8 | *.sublime-* 9 | .Spotlight-V100 10 | .Trashes 11 | .ehthumbs.db 12 | Thumbs.db 13 | *.tmp 14 | .rev 15 | -------------------------------------------------------------------------------- /config.sample.php: -------------------------------------------------------------------------------- 1 | loadConfig(); 13 | $archie->setupConfigVars(); 14 | 15 | /** 16 | * Return errors when there are 17 | */ 18 | if ($archie->hasConfigErrors()) { 19 | $errors = $archie->getConfigErrors(); 20 | 21 | foreach ($errors as $error) { 22 | echo $error . ' '; 23 | } 24 | } 25 | else { 26 | /** 27 | * Make API request when required POST vars are present 28 | */ 29 | if (isset($_POST['command']) && '/archie' == $_POST['command']) { 30 | $post = $_POST; 31 | $request = new Api($post); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /dist/css/styles.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | body { 6 | font-size: 16px; 7 | font-family: Arial, Helvetica, sans-serif; 8 | line-height: 1.25; 9 | color: #555; 10 | } 11 | 12 | .container { 13 | width: 100%; 14 | max-width: 800px; 15 | margin: 0 auto; 16 | padding: 20px 0; 17 | } 18 | 19 | h1 { 20 | color: #361889; 21 | } 22 | 23 | p, img, input[type="text"] { 24 | margin-bottom: 1em; 25 | } 26 | 27 | .info { 28 | padding: 10px 20px; 29 | border-radius: 2px; 30 | width: 75%; 31 | overflow: hidden; 32 | } 33 | 34 | .info.ok { 35 | font-weight: bold; 36 | background: #47FFB5; 37 | color: #164C37; 38 | } 39 | 40 | .info.bad { 41 | background: #FFE780; 42 | } 43 | 44 | .info.bad p { 45 | font-weight: bold; 46 | } 47 | 48 | img { 49 | max-width: 100%; 50 | } 51 | 52 | input[type="text"] { 53 | width: 75%; 54 | line-height: 2; 55 | padding-left: 10px; 56 | padding-right: 10px; 57 | border: 2px solid #ccc; 58 | border-radius: 2px; 59 | font-size: 24px; 60 | color: #555; 61 | } 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /src/Archibald/Api.php: -------------------------------------------------------------------------------- 1 | slashToken = SLASHCOMMAND_TOKEN; 15 | 16 | if ($request['token'] && $request['command']) { 17 | $token = $request['token']; 18 | $command = $request['command']; 19 | 20 | if ($this->isValidToken($token)) { 21 | if ($this->isValidCommand($command)) { 22 | 23 | $data = array( 24 | 'team_id' => $request['team_id'], 25 | 'channel' => $request['channel_id'], 26 | 'user_id' => $request['user_id'], 27 | 'user' => $request['user_name'], 28 | 'body' => $request['text'] 29 | ); 30 | 31 | $request = new Request($data); 32 | } 33 | else { 34 | echo 'Invalid Command'; 35 | } 36 | } 37 | else { 38 | echo 'Invalid Slash Command Token. Please check your config!'; 39 | } 40 | } 41 | else { 42 | echo 'No valid API call.'; 43 | } 44 | } 45 | 46 | private function isValidToken($token) 47 | { 48 | return $token == $this->slashToken; 49 | } 50 | 51 | private function isValidCommand($command) 52 | { 53 | return $command == $this->commandName; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | loadConfig(); 11 | $archie->setupConfigVars(); 12 | 13 | ?> 14 | 15 | 16 | 17 | Archibald 18 | 19 | 20 | 21 |
22 |

Your Archibald server is up and running

23 | hasConfigErrors()): ?> 24 |
25 |

Uh oooh! Troubles ahead…

26 | 33 |
34 | 35 |

I have everything I need. You’re good to go!

36 | 37 |

This is your Slash Command URL

38 | 39 |

Now head over to your Slack Integration settings and insert it into the URL field of the Slash Command Integration:

40 | 41 |
42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/Archibald/Archibald.php: -------------------------------------------------------------------------------- 1 | hasConfig()) { 34 | $this->configPath = DOCUMENT_ROOT . '/' . $this->configName; 35 | require_once($this->configPath); 36 | return true; 37 | } 38 | 39 | return false; 40 | } 41 | 42 | /** 43 | * Checks if the config file exists in the document root 44 | * @return boolean Returns true if file exists 45 | */ 46 | private function hasConfig() 47 | { 48 | return file_exists(DOCUMENT_ROOT . '/' . $this->configName); 49 | } 50 | 51 | /** 52 | * Loops through the config variables and populates $configErrors if there 53 | * are any 54 | */ 55 | public function setupConfigVars() 56 | { 57 | foreach ($this->configVars as $configVar) { 58 | $result = $this->checkConfigVar($configVar); 59 | 60 | if ($result !== true) { 61 | $this->configErrors[] = $result; 62 | } 63 | } 64 | } 65 | 66 | /** 67 | * Checks if a configuration variable is defined and not empty. 68 | * Environment variables take precedence over defined constants in config.php 69 | */ 70 | private function checkConfigVar($configVar) 71 | { 72 | $check = getenv($configVar); 73 | $const = null; 74 | 75 | if ($check !== false) { 76 | if (empty($check)) { 77 | return $configVar . ' is empty. Please make sure you have properly set your configuration variables.'; 78 | } 79 | 80 | return define($configVar, $check); 81 | } 82 | else if (defined($configVar)) { 83 | $const = constant($configVar); 84 | 85 | if (empty($const)) { 86 | return $configVar . ' is empty. Please make sure you have your config.php set up properly'; 87 | } 88 | 89 | return true; 90 | } 91 | else { 92 | return $configVar . ' was not found. Please make sure you have it defined either in your config.php or as a Config Variable.'; 93 | } 94 | } 95 | 96 | /** 97 | * @return boolean Returns true if there are errors 98 | */ 99 | public function hasConfigErrors() 100 | { 101 | return $this->configErrors !== null; 102 | } 103 | 104 | /** 105 | * @return array|null 106 | */ 107 | public function getConfigErrors() 108 | { 109 | return $this->configErrors; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/Archibald/Request.php: -------------------------------------------------------------------------------- 1 | webhookUrl = WEBHOOK_URL; 24 | 25 | $this->body = $request['body']; 26 | $this->channel = $request['channel']; 27 | $this->user = $request['user']; 28 | 29 | $this->client = new Client(); 30 | 31 | $this->parseRequestType(); 32 | } 33 | 34 | public function parseRequestType() 35 | { 36 | switch ($this->body) { 37 | case 'shaq': 38 | $this->body = 'I love you!'; 39 | $shaq = $this->staticRequest('shaq'); 40 | break; 41 | 42 | case 'kannste': 43 | case 'kannsteschonsomachen': 44 | case 'kannstemachen': 45 | case 'kacke': 46 | $kannste = $this->staticRequest('kannste'); 47 | break; 48 | 49 | case 'tags': 50 | $tags = $this->searchTags($this->body); 51 | break; 52 | 53 | case ''; 54 | echo 'Please provide a tag! e.g. `/archie wow`'; 55 | break; 56 | 57 | default: 58 | $search = $this->searchGif($this->body); 59 | break; 60 | } 61 | } 62 | 63 | private function staticRequest($request) 64 | { 65 | $responseBody = ''; 66 | 67 | switch ($request) { 68 | case 'shaq': 69 | $responseBody = 'http://replygif.net/i/1106.gif'; 70 | break; 71 | case 'kannste': 72 | $responseBody = 'http://i.imgur.com/D6iqV0b.png'; 73 | break; 74 | } 75 | 76 | $this->postResponse($responseBody); 77 | } 78 | 79 | public function searchGif($requestString) 80 | { 81 | try { 82 | $response = $this->client->get( 83 | $this->requestGifs, [ 84 | 'query' => [ 85 | 'api-key' => $this->apiKey, 86 | 'tag' => $requestString 87 | ] 88 | ] 89 | ); 90 | } 91 | catch (RequestException $e) { 92 | echo $e->getRequest(); 93 | if ($e->hasResponse()) { 94 | $this->postResponse($e->getResponse()); 95 | } 96 | } 97 | $responseBody = $response->getBody(); 98 | $message = $this->randomGif($responseBody); 99 | 100 | if (false !== $message) { 101 | $this->postResponse($message); 102 | } 103 | else { 104 | echo 'No GIFs found with tag *' . $this->body . '*'; 105 | } 106 | } 107 | 108 | public function searchTags($requestString) 109 | { 110 | try { 111 | $response = $this->client->get( 112 | $this->requestTags, [ 113 | 'query' => [ 114 | 'api-key' => $this->apiKey, 115 | 'reaction' => 1 116 | ] 117 | ] 118 | ); 119 | } 120 | catch (RequestException $e) { 121 | echo $e->getRequest(); 122 | if ($e->hasResponse()) { 123 | $this->postResponse($e->getResponse()); 124 | } 125 | } 126 | 127 | $responseBody = $response->getBody(); 128 | $message = $this->getTagList($responseBody); 129 | } 130 | 131 | public function randomGif($responseBody) 132 | { 133 | $gifs = json_decode($responseBody); 134 | 135 | $size = count($gifs); 136 | $randomIndex = rand(0, $size-1); 137 | 138 | if ($size < 1) { 139 | return false; 140 | } 141 | return $gifs[$randomIndex]->file; 142 | } 143 | 144 | public function getTagList($responseBody) 145 | { 146 | $tags = json_decode($responseBody); 147 | 148 | $tagList = ''; 149 | 150 | foreach ($tags as $tag) { 151 | $tagList .= $tag->title . " (" . $tag->count . ")\t"; 152 | } 153 | 154 | /** 155 | * The Tag List is echoed by slackbot, 156 | * so other don’t see it 157 | */ 158 | echo $tagList; 159 | } 160 | 161 | public function postResponse($message) 162 | { 163 | $finalMessage = $this->user . ": <" . $message . "|" . $this->body . ">"; 164 | $channel = $this->channel; 165 | 166 | $data = array( 167 | 'payload' => json_encode(array( 168 | 'username' => 'Archibald', 169 | 'icon_emoji' => ':hatched_chick:', 170 | 'channel' => $channel, 171 | 'text' => $finalMessage 172 | )) 173 | ); 174 | 175 | $request = $this->client->post($this->webhookUrl, array( 176 | 'body' => $data 177 | )); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Archibald 2 | 3 | Archibald is a Slack integration written in PHP to post tag-selected GIF replies from [replygif.net](http://replygif.net) into your current Slack channel or Direct Messages. You can either self-host it or easily set it up with the **[Heroku Deploy Button](#deploy-to-heroku)**. 4 | 5 | ## How to use Archibald 6 | 7 | ![](https://cloud.githubusercontent.com/assets/2084481/5192177/922eef9a-74f5-11e4-8a4c-f11da8b9f561.gif) 8 | 9 | With Archibald, you can use the following commands in Slack: 10 | 11 | `/archie tags` 12 | Shows a list of all tags that can be used, together with the amount of gifs available in brackets 13 | 14 | `/archie [tag]` 15 | Use a tag to let Archibald search for a gif with that tag and randomly select one for you.
E.g: `/archie magic` 16 | 17 | `/archie shaq` 18 | You’ll love it, because he (you know who) loves you dearly! 19 | 20 | ## Get Archibald up and running 21 | 22 | You will have to take the following steps to set up and configure Archibald for your Slack team: 23 | 24 | 1. Configure Slash Command and Incoming WebHooks integrations in your Slack settings. 25 | 2. Deploy the integration with a Slash Command Token and the Incoming WebHooks URL you are given in step 1. 26 | 27 | ### Slash Command Integration 28 | 29 | Add a new **Slash Command** integration for your team. 30 | 31 | ![](https://cloud.githubusercontent.com/assets/2084481/5191807/e036b3f2-74f1-11e4-9c5a-385503e0fbfd.png) 32 | 33 | For the Integration Settings, use the following values: 34 | 35 | | Setting | Value | 36 | |--- |--- | 37 | | Command | /archie | 38 | | URL | `http://yourOwnDomain.com/archibald/api.php` if you use your own server **or** `https://your-heroku-app-name.heroku.com/api.php` if you use Heroku Deploy Button (see below). If you don’t know the URL yet, come back later. | 39 | | Method | POST | 40 | | Autocomplete help text | ![](https://cloud.githubusercontent.com/assets/2084481/5191903/bdee426e-74f2-11e4-8bcb-61a547cc8fdd.png) | 41 | | Descriptive Label | Archibald | 42 | 43 | You will get a token that you will have to use later to configure Archibald. 44 | 45 | ![](https://cloud.githubusercontent.com/assets/2084481/5192062/73e9adb4-74f4-11e4-8e9d-e38292b313e2.png) 46 | 47 | ### Incoming WebHooks Integration 48 | 49 | Now add a new **Incoming WebHooks** integration for your team. 50 | 51 | ![](https://cloud.githubusercontent.com/assets/2084481/5192319/cb321104-74f6-11e4-90ac-1e952a176534.png) 52 | 53 | It doesn’t matter which channel you choose for the messages to be posted to. All you need to do in the settings for the **Incoming WebHooks** integration is to copy the **Webhook URL**. You will use it when you configure Archibald. All other values of the Incoming WebHooks integration will be overwritten by Archibald. 54 | 55 | ![](https://cloud.githubusercontent.com/assets/2084481/5192055/5b4c7138-74f4-11e4-9e71-5597f30672fe.png) 56 | 57 | ## Deploy to Heroku 58 | 59 | If you set up Archibald the easy way, you can use the Heroku Deploy Button: 60 | 61 | [![Deploy](https://www.herokucdn.com/deploy/button.png)](https://heroku.com/deploy) 62 | 63 | When you press that button, you will be redirected to Heroku, where you can set up your own app. Insert the *Token* from the **Slash Command** integration and the *Webhook URL* from the **Incoming WebHooks** integration into the respective form fields and hit *Deploy*. 64 | 65 | If you’re finished with the deployment, you can go and view the app. 66 | 67 | ![](https://cloud.githubusercontent.com/assets/2084481/8762562/f5ec5898-2d7c-11e5-945c-bd8b6786a0c2.png) 68 | 69 | Now head over to your Slack Integration settings and insert the URL you’ll be given by the app into the URL field of the **Slash Command** integration. 70 | 71 | ![](https://cloud.githubusercontent.com/assets/2084481/8761294/eb2c06ca-2d49-11e5-9d93-0c345706a658.png) 72 | 73 | That’s it. You can now try out Archibald. 74 | 75 | ## Deploy to your own PHP Server 76 | 77 | ### config.php 78 | 79 | Rename `config.sample.php` to `config.php` and set the *Token* from the **Slash Command** integration as well as the *Webhook URL* from the **Incoming WebHooks** integration. 80 | 81 | ```php 82 | define('SLASHCOMMAND_TOKEN', 'Your copied Token here'); 83 | define('WEBHOOK_URL', 'The copied WebHook URL here'); 84 | ``` 85 | 86 | ### Upload files to Webserver 87 | 88 | You need to upload all files to a webserver running PHP version 5.4.x or higher. Before you upload the files, be sure to use [Composer](https://getcomposer.org/) to also include all vendor files. 89 | 90 | ```sh 91 | composer install 92 | ``` 93 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 5 | "This file is @generated automatically" 6 | ], 7 | "hash": "6e8ba63e698c0c932877006e794f463e", 8 | "packages": [ 9 | { 10 | "name": "guzzlehttp/guzzle", 11 | "version": "4.2.3", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/guzzle/guzzle.git", 15 | "reference": "66fd916e9f9130bc22c51450476823391cb2f67c" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/guzzle/guzzle/zipball/66fd916e9f9130bc22c51450476823391cb2f67c", 20 | "reference": "66fd916e9f9130bc22c51450476823391cb2f67c", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "ext-json": "*", 25 | "guzzlehttp/streams": "~2.1", 26 | "php": ">=5.4.0" 27 | }, 28 | "require-dev": { 29 | "ext-curl": "*", 30 | "phpunit/phpunit": "~4.0", 31 | "psr/log": "~1.0" 32 | }, 33 | "suggest": { 34 | "ext-curl": "Guzzle will use specific adapters if cURL is present" 35 | }, 36 | "type": "library", 37 | "extra": { 38 | "branch-alias": { 39 | "dev-master": "4.2-dev" 40 | } 41 | }, 42 | "autoload": { 43 | "psr-4": { 44 | "GuzzleHttp\\": "src/" 45 | }, 46 | "files": [ 47 | "src/functions.php" 48 | ] 49 | }, 50 | "notification-url": "https://packagist.org/downloads/", 51 | "license": [ 52 | "MIT" 53 | ], 54 | "authors": [ 55 | { 56 | "name": "Michael Dowling", 57 | "email": "mtdowling@gmail.com", 58 | "homepage": "https://github.com/mtdowling" 59 | } 60 | ], 61 | "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients", 62 | "homepage": "http://guzzlephp.org/", 63 | "keywords": [ 64 | "client", 65 | "curl", 66 | "framework", 67 | "http", 68 | "http client", 69 | "rest", 70 | "web service" 71 | ], 72 | "time": "2014-10-05 19:29:14" 73 | }, 74 | { 75 | "name": "guzzlehttp/streams", 76 | "version": "2.1.0", 77 | "source": { 78 | "type": "git", 79 | "url": "https://github.com/guzzle/streams.git", 80 | "reference": "f91b721d73f0e561410903b3b3c90a5d0e40b534" 81 | }, 82 | "dist": { 83 | "type": "zip", 84 | "url": "https://api.github.com/repos/guzzle/streams/zipball/f91b721d73f0e561410903b3b3c90a5d0e40b534", 85 | "reference": "f91b721d73f0e561410903b3b3c90a5d0e40b534", 86 | "shasum": "" 87 | }, 88 | "require": { 89 | "php": ">=5.4.0" 90 | }, 91 | "require-dev": { 92 | "phpunit/phpunit": "~4.0" 93 | }, 94 | "type": "library", 95 | "extra": { 96 | "branch-alias": { 97 | "dev-master": "2.0-dev" 98 | } 99 | }, 100 | "autoload": { 101 | "psr-4": { 102 | "GuzzleHttp\\Stream\\": "src/" 103 | }, 104 | "files": [ 105 | "src/functions.php" 106 | ] 107 | }, 108 | "notification-url": "https://packagist.org/downloads/", 109 | "license": [ 110 | "MIT" 111 | ], 112 | "authors": [ 113 | { 114 | "name": "Michael Dowling", 115 | "email": "mtdowling@gmail.com", 116 | "homepage": "https://github.com/mtdowling" 117 | } 118 | ], 119 | "description": "Provides a simple abstraction over streams of data (Guzzle 4+)", 120 | "homepage": "http://guzzlephp.org/", 121 | "keywords": [ 122 | "Guzzle", 123 | "stream" 124 | ], 125 | "time": "2014-08-17 21:15:53" 126 | } 127 | ], 128 | "packages-dev": [], 129 | "aliases": [], 130 | "minimum-stability": "stable", 131 | "stability-flags": [], 132 | "prefer-stable": false, 133 | "prefer-lowest": false, 134 | "platform": [], 135 | "platform-dev": [] 136 | } 137 | --------------------------------------------------------------------------------