├── .codeclimate.yml
├── .gitignore
├── .scrutinizer.yml
├── .travis.yml
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── composer.json
├── composer.lock
├── phpmd.xml
├── phpunit.xml
├── src
└── Botonomous
│ ├── AbstractAccessList.php
│ ├── AbstractBaseSlack.php
│ ├── AbstractBot.php
│ ├── AbstractCommandContainer.php
│ ├── AbstractConfig.php
│ ├── AbstractSender.php
│ ├── AbstractSlackEntity.php
│ ├── Action.php
│ ├── BlackList.php
│ ├── BotonomousException.php
│ ├── Channel.php
│ ├── Command.php
│ ├── CommandContainer.php
│ ├── CommandExtractor.php
│ ├── Config.php
│ ├── Dictionary.php
│ ├── Event.php
│ ├── ImChannel.php
│ ├── MessageAction.php
│ ├── OAuth.php
│ ├── Sender.php
│ ├── Slackbot.php
│ ├── Team.php
│ ├── User.php
│ ├── WhiteList.php
│ ├── client
│ ├── AbstractClient.php
│ └── ApiClient.php
│ ├── dictionary
│ ├── access-control.json
│ ├── generic-messages.json
│ ├── punctuations.json
│ ├── question-answer.json
│ ├── stopwords-en.json
│ ├── test-key-value.json
│ └── test.json
│ ├── listener
│ ├── AbstractBaseListener.php
│ ├── EventListener.php
│ └── SlashCommandListener.php
│ ├── plugin
│ ├── AbstractPlugin.php
│ ├── PluginInterface.php
│ ├── help
│ │ ├── Help.php
│ │ └── HelpConfig.php
│ ├── ping
│ │ └── Ping.php
│ └── qa
│ │ └── QA.php
│ ├── public
│ ├── .htaccess
│ ├── favicon.png
│ └── index.php
│ └── utility
│ ├── AbstractUtility.php
│ ├── ArrayUtility.php
│ ├── ClassUtility.php
│ ├── FileUtility.php
│ ├── FormattingUtility.php
│ ├── LanguageProcessingUtility.php
│ ├── LoggerUtility.php
│ ├── MessageUtility.php
│ ├── RequestUtility.php
│ ├── SecurityUtility.php
│ ├── SessionUtility.php
│ └── StringUtility.php
└── tests
├── Botonomous
├── ActionTest.php
├── BlackListTest.php
├── CommandContainerTest.php
├── CommandExtractorTest.php
├── CommandTest.php
├── ConfigTest.php
├── DictionaryTest.php
├── EventTest.php
├── ImChannelTest.php
├── MessageActionTest.php
├── OAuthTest.php
├── PhpunitHelper.php
├── SenderTest.php
├── SlackbotTest.php
├── TeamTest.php
├── UserTest.php
├── WhiteListTest.php
├── client
│ └── ApiClientTest.php
├── listener
│ ├── EventListenerTest.php
│ └── SlashCommandListenerTest.php
├── plugin
│ ├── help
│ │ ├── HelpConfigTest.php
│ │ └── HelpTest.php
│ ├── ping
│ │ └── PingTest.php
│ └── qa
│ │ └── QATest.php
└── utility
│ ├── ArrayUtilityTest.php
│ ├── ClassUtilityTest.php
│ ├── FileUtilityTest.php
│ ├── LanguageProcessingUtilityTest.php
│ ├── LoggerUtilityTest.php
│ ├── MessageUtilityTest.php
│ ├── RequestUtilityTest.php
│ ├── SecurityUtilityTest.php
│ ├── SessionUtilityTest.php
│ └── StringUtilityTest.php
└── bootstrap.php
/.codeclimate.yml:
--------------------------------------------------------------------------------
1 | engines:
2 | phpcodesniffer:
3 | enabled: true
4 | config:
5 | file_extensions: "php"
6 | standard: "PSR2"
7 | fixme:
8 | enabled: true
9 | phpmd:
10 | enabled: true
11 | config:
12 | rulesets: "phpmd.xml"
13 | duplication:
14 | enabled: true
15 | config:
16 | languages:
17 | php:
18 | mass_threshold: 53
19 | sonar-php:
20 | enabled: true
21 | checks:
22 | php:S138:
23 | enabled: false
24 | php:S1117:
25 | enabled: false
26 | php:S1448:
27 | enabled: false
28 | php:S1142:
29 | enabled: false
30 | ratings:
31 | paths:
32 | - "src/**/*"
33 | checks:
34 | argument-count:
35 | enabled: false
36 | complex-logic:
37 | enabled: false
38 | file-lines:
39 | enabled: false
40 | method-complexity:
41 | enabled: false
42 | method-lines:
43 | enabled: false
44 | method-count:
45 | enabled: false
46 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | vendor/*
2 | composer.phar
3 | src/Botonomous/tmp/*
4 | .idea/*
--------------------------------------------------------------------------------
/.scrutinizer.yml:
--------------------------------------------------------------------------------
1 | filter:
2 | excluded_paths:
3 | - 'tests/*'
4 | checks:
5 | php:
6 | use_self_instead_of_fqcn: true
7 | uppercase_constants: true
8 | simplify_boolean_return: true
9 | return_doc_comments: true
10 | remove_extra_empty_lines: true
11 | prefer_while_loop_over_for_loop: true
12 | phpunit_assertions: true
13 | no_short_variable_names:
14 | minimum: '3'
15 | no_short_method_names:
16 | minimum: '3'
17 | no_long_variable_names:
18 | maximum: '20'
19 | no_goto: true
20 | newline_at_end_of_file: true
21 | avoid_fixme_comments: true
22 | avoid_multiple_statements_on_same_line: true
23 | avoid_perl_style_comments: true
24 | avoid_todo_comments: true
25 | check_method_contracts:
26 | verify_interface_like_constraints: true
27 | verify_documented_constraints: true
28 | verify_parent_constraints: true
29 | classes_in_camel_caps: true
30 | encourage_postdec_operator: true
31 | fix_line_ending: true
32 | fix_use_statements:
33 | remove_unused: true
34 | preserve_multiple: false
35 | preserve_blanklines: false
36 | order_alphabetically: false
37 | function_in_camel_caps: true
38 | line_length:
39 | max_length: '120'
40 | instanceof_class_exists: true
41 | function_in_camel_caps: true
42 | foreach_usable_as_reference: true
43 | foreach_traversable: true
44 | fix_line_ending: true
45 | fix_doc_comments: true
46 | encourage_shallow_comparison: true
47 | encourage_postdec_operator: true
48 | duplication: true
49 | deprecated_code_usage: true
50 | deadlock_detection_in_loops: true
51 | comparison_always_same_result: true
52 | code_rating: true
53 | closure_use_not_conflicting: true
54 | closure_use_modifiable: true
55 | classes_in_camel_caps: true
56 | catch_class_exists: true
57 | call_to_parent_method: true
58 | blank_line_after_namespace_declaration: true
59 | avoid_useless_overridden_methods: true
60 | avoid_usage_of_logical_operators: true
61 | avoid_todo_comments: true
62 | avoid_superglobals: true
63 | avoid_perl_style_comments: true
64 | avoid_multiple_statements_on_same_line: true
65 | avoid_length_functions_in_loops: true
66 | avoid_fixme_comments: true
67 | avoid_entity_manager_injection: true
68 | avoid_duplicate_types: true
69 | avoid_corrupting_byteorder_marks: true
70 | avoid_conflicting_incrementers: true
71 | avoid_closing_tag: true
72 | avoid_aliased_php_functions: true
73 | assignment_of_null_return: true
74 | argument_type_checks: true
75 |
76 | coding_style:
77 | php:
78 | indentation:
79 | general:
80 | use_tabs: false
81 | size: 4
82 | switch:
83 | indent_case: true
84 | spaces:
85 | general:
86 | linefeed_character: newline
87 | before_parentheses:
88 | function_declaration: false
89 | closure_definition: false
90 | function_call: false
91 | if: true
92 | for: true
93 | while: true
94 | switch: true
95 | catch: true
96 | array_initializer: false
97 | around_operators:
98 | assignment: true
99 | logical: true
100 | equality: true
101 | relational: true
102 | bitwise: true
103 | additive: true
104 | multiplicative: true
105 | shift: true
106 | unary_additive: false
107 | negation: false
108 | before_left_brace:
109 | class: true
110 | function: true
111 | if: true
112 | else: true
113 | for: true
114 | while: true
115 | do: true
116 | switch: true
117 | try: true
118 | catch: true
119 | finally: true
120 | before_keywords:
121 | else: true
122 | while: true
123 | catch: true
124 | finally: true
125 | within:
126 | brackets: false
127 | array_initializer: false
128 | grouping: false
129 | function_call: false
130 | function_declaration: false
131 | if: false
132 | for: false
133 | while: false
134 | switch: false
135 | catch: false
136 | type_cast: false
137 | ternary_operator:
138 | before_condition: true
139 | after_condition: true
140 | before_alternative: true
141 | after_alternative: true
142 | in_short_version: false
143 | other:
144 | before_comma: false
145 | after_comma: true
146 | before_semicolon: false
147 | after_semicolon: true
148 | after_type_cast: true
149 | braces:
150 | classes_functions:
151 | class: undefined
152 | function: undefined
153 | closure: undefined
154 | if:
155 | opening: undefined
156 | always: true
157 | else_on_new_line: false
158 | for:
159 | opening: undefined
160 | always: true
161 | while:
162 | opening: undefined
163 | always: true
164 | do_while:
165 | opening: undefined
166 | always: true
167 | while_on_new_line: false
168 | switch:
169 | opening: undefined
170 | try:
171 | opening: undefined
172 | catch_on_new_line: false
173 | finally_on_new_line: false
174 | upper_lower_casing:
175 | keywords:
176 | general: undefined
177 | constants:
178 | true_false_null: undefined
179 |
180 | build:
181 | tests:
182 | override:
183 | -
184 | command: 'vendor/bin/phpunit --coverage-clover=coverage-file'
185 | coverage:
186 | file: 'coverage-file'
187 | format: 'clover'
188 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | env:
2 | global:
3 | - coverage=false
4 | - CC_TEST_REPORTER_ID=e3f592753a2a1c938f2fc8597a5344c91f3e1baa6e44ac5fdb88970c3a94265d
5 |
6 | language: php
7 |
8 | matrix:
9 | include:
10 | - php: 7.1
11 | env: coverage=true
12 | - php: 7.2
13 |
14 | sudo: false
15 |
16 | install:
17 | - composer install
18 |
19 | before_script:
20 | - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
21 | - chmod +x ./cc-test-reporter
22 | - if [[ $coverage = 'true' ]]; then ./cc-test-reporter before-build; fi
23 |
24 | script:
25 | - vendor/bin/phpunit --coverage-clover build/logs/clover.xml --configuration phpunit.xml
26 |
27 | after_success:
28 | - if [[ $coverage = 'true' ]]; then ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT; fi
29 |
30 | notifications:
31 | slack:
32 | secure: j0e4qbj2/fHlyyAg/CuTKYMaTtpmhiSzgFQLdPLP7/WPX6tfHYm732K58CMEbHc4MlDmiUelBWhWR39EgBBoX4QBLc+l8EdgT3gYOfwJ7LQWtdMbP2tdXyAPoTe2WOnUr5hG2LMIsM4P6JjGKKAJrlLoRRpkfIz1h1Z70VKR9+4qMjOP2s2NCd82E6g/wDHJbMEXbE/tvh1/r1w7eb23Emkv59or3hD4LA969YqMN+ycPjwGP4vLsiZ8kRdcSS/B9AZLmVvbc8SU4onGDQboiDVJvM8t0XxUGM8yceh2kWSVLkQv+I/BrtNDCiAa3WNVjWEVpkJ6Rn4k/FDNN3AWnFhpy38S+EwDgGS3CioYQmMAa1OgekciMIxsxdwRsCeQhgbnlYDvTNtcAoNHwC3wBBOX2qGjMLoupkk+akATmAAHlBs/n9ap2IUT1+SPOtJQ4COb36rK3Lq+jCYdlRR5O+dbbyFhMt+RijGwER3no0D8QW1Q23c9yj5VQ/NA2VAMCcD04w+AlnT3vni/Gs5UIjNus63YQbfY1GS7sRC/LSuSOslBXTWCr7/w/dNrVLUy3ouBNuep0OAJejAi7o7/JF9gEY9j88TkU6BNb+6D0qgZbHF1N1NuPiR4STh3LpKxXPY+FMNaT8yjy5xvdpANbSe8fJEwcjUUA1Iupp2W/cw=
33 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
6 |
7 | ## Our Standards
8 |
9 | Examples of behavior that contributes to creating a positive environment include:
10 |
11 | * Using welcoming and inclusive language
12 | * Being respectful of differing viewpoints and experiences
13 | * Gracefully accepting constructive criticism
14 | * Focusing on what is best for the community
15 | * Showing empathy towards other community members
16 |
17 | Examples of unacceptable behavior by participants include:
18 |
19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances
20 | * Trolling, insulting/derogatory comments, and personal or political attacks
21 | * Public or private harassment
22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission
23 | * Other conduct which could reasonably be considered inappropriate in a professional setting
24 |
25 | ## Our Responsibilities
26 |
27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
28 |
29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
30 |
31 | ## Scope
32 |
33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
34 |
35 | ## Enforcement
36 |
37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at ehsan.ann@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
38 |
39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
40 |
41 | ## Attribution
42 |
43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
44 |
45 | [homepage]: http://contributor-covenant.org
46 | [version]: http://contributor-covenant.org/version/1/4/
47 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to Botonomous
2 | You are more than welcome for any contributions to this project. The rules are:
3 | * The framework follows `PSR-2` coding standard and the `PSR-4` autoloading standard.
4 | * Make sure all the existing `PHPUnit` tests are passed.
5 | * For any new function write a test and keep the [code coverage](https://codeclimate.com/github/iranianpep/botonomous/coverage) >= `95%`.
6 | * Keep [Code Climate GPA](https://codeclimate.com/github/iranianpep/botonomous) at `4` and make sure there is no issue.
7 | * Keep [Codacy](https://www.codacy.com/app/iranianpep/botonomous/dashboard) rate at `A`.
8 | * Keep [Scrutinizer](https://scrutinizer-ci.com/g/iranianpep/botonomous) rate >= `8.5`.
9 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Ehsan Abbasi
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Botonomous
2 |
3 |
4 |
5 |
6 |
7 |
8 | ## Code Status
9 |
10 | [](https://travis-ci.org/iranianpep/botonomous)
11 | [](https://scrutinizer-ci.com/g/iranianpep/botonomous/build-status/master)
12 | [](https://codeclimate.com/github/iranianpep/botonomous)
13 | [](https://codeclimate.com/github/iranianpep/botonomous/coverage)
14 | [](https://scrutinizer-ci.com/g/iranianpep/botonomous/?branch=master)
15 | [](https://scrutinizer-ci.com/g/iranianpep/botonomous/?branch=master)
16 | [](https://styleci.io/repos/73189365)
17 | [](https://insight.sensiolabs.com/projects/d9b77f1a-3d4a-423f-b473-30a25496f9a0)
18 | [](https://www.codacy.com/app/iranianpep/botonomous)
19 | [](https://squizlabs.github.io/PHP_CodeSniffer/analysis/iranianpep/botonomous)
20 | [](https://bettercodehub.com/)
21 | [](https://codeclimate.com/github/iranianpep/botonomous)
22 |
23 | Botonomous is a PHP framework for creating autonomous [Slack bots](https://api.slack.com/bot-users). It is specifically designed for [Slack](https://slack.com) and supports [Events API](https://api.slack.com/events-api) and [Slash commands](https://api.slack.com/slash-commands). Botonomous is unique because of:
24 | * Quality Code: Modern, high quality and fully unit tested code base
25 | * Modular System: Pluggable architecture for enhanced management of commands
26 | * OAuth 2.0 Support: Built-in [Add to Slack](https://api.slack.com/docs/slack-button) button using OAuth 2.0
27 | * Utility Classes: A handful of standalone utility classes to make everyone's life (even your partner's!) easier
28 |
29 | ## Get Started
30 |
31 | Please check the [wiki](https://github.com/iranianpep/botonomous/wiki) for the documentation and installation guide or you can go straight to [Getting Started](https://github.com/iranianpep/botonomous/wiki/Getting-Started) section.
32 |
33 | ## Get help
34 |
35 | * [](http://botonomous.herokuapp.com/)
36 | * [Issues](https://github.com/iranianpep/botonomous/issues) - Got issues? We are happy to help! However to report any security vulnerabilities, please drop us an email at ehsan.abb@gmail.com and please do not report it on GitHub.
37 | * [](http://isitmaintained.com/project/iranianpep/botonomous "Average time to resolve an issue")
38 | [](http://isitmaintained.com/project/iranianpep/botonomous "Percentage of issues still open")
39 |
40 | ## Get involved
41 |
42 | Botonomous is free forever but we need your help to make it even better. Let's make it happen by contributing to it. You can check out the [contributing guide](https://github.com/iranianpep/botonomous/blob/master/CONTRIBUTING.md) for guidelines about how to proceed.
43 |
44 | [](https://www.codetriage.com/iranianpep/botonomous)
45 |
46 | ## Built with Botonomous
47 |
48 | If you have built any awesome Slack bot with Botonomous let us know to list it here. You can also add the following badge:
49 |
50 | [](https://github.com/iranianpep/botonomous)
51 |
52 | ```
53 | [](https://github.com/iranianpep/botonomous)
54 | ```
55 |
56 | ## Community
57 |
58 | * [Twitter](https://twitter.com/botonomous)
59 | * [Facebook](https://www.facebook.com/botonomous)
60 |
61 | ## Support
62 |
63 | * [Jet Brains](https://www.jetbrains.com) - Without [PhpStorm](https://www.jetbrains.com/phpstorm) this project was in the middle of nowhere!
64 | * [Sara Salim Pour](http://sarasalimpour.com) - The outstanding artwork is because of her talent.
65 | * [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=BXMKEZ23PX8K2)
66 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "botonomous/botonomous",
3 | "description": "Simple Slackbot that can listen to Slack messages and send back appropriate responses to a channel(s).",
4 | "homepage": "https://github.com/iranianpep/botonomous",
5 | "license": "MIT",
6 | "authors": [
7 | { "name": "Ehsan Abbasi", "email": "ehsan.abb@gmail.com" }
8 | ],
9 | "require": {
10 | "php": ">=7.1",
11 | "nlp-tools/nlp-tools": "^0.1.3",
12 | "guzzlehttp/guzzle": "6.3.*",
13 | "monolog/monolog": "^1.22"
14 | },
15 | "require-dev": {
16 | "phpunit/phpunit": "^6.2 || ^7 || ^8 || ^9"
17 | },
18 | "autoload": {
19 | "psr-4": {
20 | "Botonomous\\": "src/Botonomous"
21 | }
22 | },
23 | "autoload-dev": {
24 | "psr-4": {
25 | "Botonomous\\": "tests/Botonomous"
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/phpmd.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 | Custom rules for checking my project
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | tests/Botonomous/
6 |
7 |
8 |
9 |
10 |
11 | src/Botonomous/
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/src/Botonomous/AbstractAccessList.php:
--------------------------------------------------------------------------------
1 | getDictionary()->get('access-control');
26 | }
27 |
28 | /**
29 | * @param $sublistKey
30 | *
31 | * @throws \Exception
32 | *
33 | * @return mixed
34 | */
35 | protected function getSubAccessControlList($sublistKey)
36 | {
37 | $list = $this->getAccessControlList();
38 |
39 | if (!isset($list[$sublistKey])) {
40 | /* @noinspection PhpInconsistentReturnPointsInspection */
41 | return;
42 | }
43 |
44 | return $list[$sublistKey];
45 | }
46 |
47 | /**
48 | * @param array $list
49 | *
50 | * @throws \Exception
51 | *
52 | * @return bool
53 | */
54 | protected function isEmailInList(array $list): bool
55 | {
56 | // get user info
57 | $userInfo = $this->getSlackUserInfo();
58 |
59 | return !empty($userInfo) && in_array($userInfo['profile']['email'], $list['userEmail']);
60 | }
61 |
62 | /**
63 | * Check if email is white listed or black listed
64 | * If userEmail list is not set, return true for whitelist and false for blacklist.
65 | *
66 | * @throws \Exception
67 | *
68 | * @return bool
69 | */
70 | protected function checkEmail(): bool
71 | {
72 | // load the relevant list based on the class name e.g. BlackList or WhiteList
73 | $list = $this->getSubAccessControlList($this->getShortClassName());
74 |
75 | if (!isset($list['userEmail'])) {
76 | // if list is not set do not check it
77 | return $this->getShortClassName() === 'whitelist' ? true : false;
78 | }
79 |
80 | return $this->isEmailInList($list);
81 | }
82 |
83 | /**
84 | * @param string $requestKey
85 | * @param string $listKey
86 | * @param string $subListKey
87 | *
88 | * @throws \Exception
89 | *
90 | * @return bool|null
91 | */
92 | protected function findInListByRequestKey(string $requestKey, string $listKey, string $subListKey)
93 | {
94 | /**
95 | * load the relevant list to start checking
96 | * The list name is the called class name e.g. WhiteList in lowercase.
97 | */
98 | $list = $this->getSubAccessControlList($listKey);
99 |
100 | // currently if list key is not set we do not check it
101 | if ($list === null || !isset($list[$subListKey])) {
102 | /* @noinspection PhpInconsistentReturnPointsInspection */
103 | return;
104 | }
105 |
106 | return in_array($this->getRequest()[$requestKey], $list[$subListKey]);
107 | }
108 |
109 | /**
110 | * @return mixed
111 | */
112 | protected function getShortClassName()
113 | {
114 | return $this->getClassUtility()->extractClassNameFromFullName(strtolower(get_called_class()));
115 | }
116 |
117 | /**
118 | * @return mixed
119 | */
120 | public function getRequest()
121 | {
122 | return $this->request;
123 | }
124 |
125 | /**
126 | * @param mixed $request
127 | */
128 | public function setRequest($request)
129 | {
130 | $this->request = $request;
131 | }
132 |
133 | /**
134 | * @return Dictionary
135 | */
136 | public function getDictionary(): Dictionary
137 | {
138 | if (!isset($this->dictionary)) {
139 | $this->setDictionary(new Dictionary());
140 | }
141 |
142 | return $this->dictionary;
143 | }
144 |
145 | /**
146 | * @param Dictionary $dictionary
147 | */
148 | public function setDictionary(Dictionary $dictionary)
149 | {
150 | $this->dictionary = $dictionary;
151 | }
152 |
153 | /**
154 | * @return ApiClient
155 | */
156 | public function getApiClient(): ApiClient
157 | {
158 | if (!isset($this->apiClient)) {
159 | $this->setApiClient(new ApiClient());
160 | }
161 |
162 | return $this->apiClient;
163 | }
164 |
165 | /**
166 | * @param ApiClient $apiClient
167 | */
168 | public function setApiClient(ApiClient $apiClient)
169 | {
170 | $this->apiClient = $apiClient;
171 | }
172 |
173 | /**
174 | * @throws \Exception
175 | *
176 | * @return array|bool
177 | */
178 | public function getSlackUserInfo()
179 | {
180 | // get user id in the request
181 | $request = $this->getRequest();
182 |
183 | // currently if user_id is not set we do not check it
184 | if (!isset($request['user_id'])) {
185 | return false;
186 | }
187 |
188 | /**
189 | * email normally does not exist in the request.
190 | * Get it by user_id. For this users:read and users:read.email are needed.
191 | */
192 | $userInfo = $this->getApiClient()->userInfo(['user' => $request['user_id']]);
193 | if (empty($userInfo)) {
194 | /*
195 | * Could not find the user in the team
196 | * Probably there might be some issue with Access token and reading user info but block the access
197 | */
198 | return false;
199 | }
200 |
201 | return $userInfo;
202 | }
203 |
204 | /**
205 | * @return ClassUtility
206 | */
207 | public function getClassUtility(): ClassUtility
208 | {
209 | if (!isset($this->classUtility)) {
210 | $this->setClassUtility(new ClassUtility());
211 | }
212 |
213 | return $this->classUtility;
214 | }
215 |
216 | /**
217 | * @param ClassUtility $classUtility
218 | */
219 | public function setClassUtility(ClassUtility $classUtility)
220 | {
221 | $this->classUtility = $classUtility;
222 | }
223 | }
224 |
--------------------------------------------------------------------------------
/src/Botonomous/AbstractBaseSlack.php:
--------------------------------------------------------------------------------
1 | loadAttributes($this, $info);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/Botonomous/AbstractBot.php:
--------------------------------------------------------------------------------
1 | config === null) {
45 | $this->config = (new Config());
46 | }
47 |
48 | return $this->config;
49 | }
50 |
51 | /**
52 | * @param Config $config
53 | */
54 | public function setConfig(Config $config)
55 | {
56 | $this->config = $config;
57 | }
58 |
59 | /**
60 | * @throws \Exception
61 | *
62 | * @return AbstractBaseListener
63 | */
64 | public function getListener(): AbstractBaseListener
65 | {
66 | if (!isset($this->listener)) {
67 | $listenerClass = __NAMESPACE__.'\\listener\\'.ucwords($this->getConfig()->get('listener')).'Listener';
68 | $this->setListener(new $listenerClass());
69 | }
70 |
71 | return $this->listener;
72 | }
73 |
74 | public function setListener(AbstractBaseListener $listener)
75 | {
76 | $this->listener = $listener;
77 | }
78 |
79 | /**
80 | * @return MessageUtility
81 | */
82 | public function getMessageUtility(): MessageUtility
83 | {
84 | if (!isset($this->messageUtility)) {
85 | $this->setMessageUtility(new MessageUtility());
86 | }
87 |
88 | return $this->messageUtility;
89 | }
90 |
91 | /**
92 | * @param MessageUtility $messageUtility
93 | */
94 | public function setMessageUtility(MessageUtility $messageUtility)
95 | {
96 | $this->messageUtility = $messageUtility;
97 | }
98 |
99 | /**
100 | * @return CommandContainer
101 | */
102 | public function getCommandContainer(): CommandContainer
103 | {
104 | if (!isset($this->commandContainer)) {
105 | $this->setCommandContainer(new CommandContainer());
106 | }
107 |
108 | return $this->commandContainer;
109 | }
110 |
111 | /**
112 | * @param CommandContainer $commandContainer
113 | */
114 | public function setCommandContainer(CommandContainer $commandContainer)
115 | {
116 | $this->commandContainer = $commandContainer;
117 | }
118 |
119 | /**
120 | * @return FormattingUtility
121 | */
122 | public function getFormattingUtility(): FormattingUtility
123 | {
124 | if (!isset($this->formattingUtility)) {
125 | $this->setFormattingUtility(new FormattingUtility());
126 | }
127 |
128 | return $this->formattingUtility;
129 | }
130 |
131 | /**
132 | * @param FormattingUtility $formattingUtility
133 | */
134 | public function setFormattingUtility(FormattingUtility $formattingUtility)
135 | {
136 | $this->formattingUtility = $formattingUtility;
137 | }
138 |
139 | /**
140 | * @return LoggerUtility
141 | */
142 | public function getLoggerUtility(): LoggerUtility
143 | {
144 | if (!isset($this->loggerUtility)) {
145 | $this->setLoggerUtility(new LoggerUtility());
146 | }
147 |
148 | return $this->loggerUtility;
149 | }
150 |
151 | /**
152 | * @param LoggerUtility $loggerUtility
153 | */
154 | public function setLoggerUtility(LoggerUtility $loggerUtility)
155 | {
156 | $this->loggerUtility = $loggerUtility;
157 | }
158 |
159 | /**
160 | * @return OAuth
161 | */
162 | public function getOauth(): OAuth
163 | {
164 | if (!isset($this->oauth)) {
165 | $this->setOauth(new OAuth());
166 | }
167 |
168 | return $this->oauth;
169 | }
170 |
171 | /**
172 | * @param OAuth $oauth
173 | */
174 | public function setOauth(OAuth $oauth)
175 | {
176 | $this->oauth = $oauth;
177 | }
178 |
179 | /**
180 | * @throws \Exception
181 | *
182 | * @return RequestUtility
183 | */
184 | public function getRequestUtility(): RequestUtility
185 | {
186 | return $this->getListener()->getRequestUtility();
187 | }
188 |
189 | /**
190 | * @param RequestUtility $requestUtility
191 | *
192 | * @throws \Exception
193 | */
194 | public function setRequestUtility(RequestUtility $requestUtility)
195 | {
196 | $this->getListener()->setRequestUtility($requestUtility);
197 | }
198 |
199 | /**
200 | * @throws \Exception
201 | *
202 | * @return BlackList
203 | */
204 | public function getBlackList(): BlackList
205 | {
206 | if (!isset($this->blackList)) {
207 | $this->setBlackList(new BlackList($this->getListener()->getRequest()));
208 | }
209 |
210 | return $this->blackList;
211 | }
212 |
213 | /**
214 | * @param BlackList $blackList
215 | */
216 | public function setBlackList(BlackList $blackList)
217 | {
218 | $this->blackList = $blackList;
219 | }
220 |
221 | /**
222 | * @throws \Exception
223 | *
224 | * @return WhiteList
225 | */
226 | public function getWhiteList(): WhiteList
227 | {
228 | if (!isset($this->whiteList)) {
229 | $this->setWhiteList(new WhiteList($this->getListener()->getRequest()));
230 | }
231 |
232 | return $this->whiteList;
233 | }
234 |
235 | /**
236 | * @param WhiteList $whiteList
237 | */
238 | public function setWhiteList(WhiteList $whiteList)
239 | {
240 | $this->whiteList = $whiteList;
241 | }
242 |
243 | /**
244 | * @return Sender
245 | */
246 | public function getSender(): Sender
247 | {
248 | if (!isset($this->sender)) {
249 | $this->setSender(new Sender($this));
250 | }
251 |
252 | return $this->sender;
253 | }
254 |
255 | /**
256 | * @param Sender $sender
257 | */
258 | public function setSender(Sender $sender)
259 | {
260 | $this->sender = $sender;
261 | }
262 |
263 | /**
264 | * @return Dictionary
265 | */
266 | public function getDictionary(): Dictionary
267 | {
268 | if (!isset($this->dictionary)) {
269 | $this->setDictionary(new Dictionary());
270 | }
271 |
272 | return $this->dictionary;
273 | }
274 |
275 | /**
276 | * @param Dictionary $dictionary
277 | */
278 | public function setDictionary(Dictionary $dictionary)
279 | {
280 | $this->dictionary = $dictionary;
281 | }
282 |
283 | /**
284 | * @return CommandExtractor
285 | */
286 | public function getCommandExtractor(): CommandExtractor
287 | {
288 | if (!isset($this->commandExtractor)) {
289 | $this->setCommandExtractor(new CommandExtractor());
290 | }
291 |
292 | return $this->commandExtractor;
293 | }
294 |
295 | /**
296 | * @param CommandExtractor $commandExtractor
297 | */
298 | public function setCommandExtractor(CommandExtractor $commandExtractor)
299 | {
300 | $this->commandExtractor = $commandExtractor;
301 | }
302 | }
303 |
--------------------------------------------------------------------------------
/src/Botonomous/AbstractCommandContainer.php:
--------------------------------------------------------------------------------
1 | getAllAsObject($key);
22 |
23 | if (!array_key_exists($key, $commands)) {
24 | /* @noinspection PhpInconsistentReturnPointsInspection */
25 | return;
26 | }
27 |
28 | return $commands[$key];
29 | }
30 |
31 | /**
32 | * @param $commands
33 | */
34 | public function setAll($commands)
35 | {
36 | static::$commands = $commands;
37 | }
38 |
39 | /**
40 | * @return array
41 | */
42 | public function getAll()
43 | {
44 | return static::$commands;
45 | }
46 |
47 | /**
48 | * @param string $key
49 | *
50 | * @throws \Exception
51 | *
52 | * @return mixed
53 | */
54 | public function getAllAsObject(string $key = null)
55 | {
56 | $commands = $this->getAll();
57 | if (!empty($commands)) {
58 | foreach ($commands as $commandKey => $commandDetails) {
59 | if (!empty($key) && $commandKey !== $key) {
60 | continue;
61 | }
62 |
63 | $commandDetails['key'] = $commandKey;
64 | $commands[$commandKey] = $this->mapToCommandObject($commandDetails);
65 | }
66 | }
67 |
68 | return $commands;
69 | }
70 |
71 | /**
72 | * @param array $row
73 | *
74 | * @throws \Exception
75 | *
76 | * @return Command
77 | */
78 | private function mapToCommandObject(array $row)
79 | {
80 | $mappedObject = new Command($row['key']);
81 |
82 | unset($row['key']);
83 |
84 | if (!empty($row)) {
85 | foreach ($row as $key => $value) {
86 | $methodName = 'set'.ucwords($key);
87 |
88 | // check setter exists
89 | if (!method_exists($mappedObject, $methodName)) {
90 | continue;
91 | }
92 |
93 | $mappedObject->$methodName($value);
94 | }
95 | }
96 |
97 | return $mappedObject;
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/src/Botonomous/AbstractConfig.php:
--------------------------------------------------------------------------------
1 | getPluginConfigs($plugin);
32 | } catch (\Exception $e) {
33 | throw $e;
34 | }
35 | }
36 |
37 | if (!array_key_exists($key, $configs)) {
38 | throw new BotonomousException("Key: '{$key}' does not exist in configs");
39 | }
40 |
41 | $found = $configs[$key];
42 |
43 | return (new StringUtility())->applyReplacements($found, $replacements);
44 | }
45 |
46 | /**
47 | * @param $plugin
48 | *
49 | * @throws \Exception
50 | *
51 | * @return self
52 | */
53 | private function getPluginConfigObject($plugin): self
54 | {
55 | $pluginConfigClass = __NAMESPACE__.'\\plugin\\'.strtolower($plugin)
56 | .'\\'.ucfirst($plugin).'Config';
57 |
58 | if (!class_exists($pluginConfigClass)) {
59 | throw new BotonomousException("Config file: '{$pluginConfigClass}.php' does not exist");
60 | }
61 |
62 | return new $pluginConfigClass();
63 | }
64 |
65 | /**
66 | * @param $plugin
67 | *
68 | * @throws \Exception
69 | *
70 | * @return array
71 | */
72 | public function getPluginConfigs($plugin)
73 | {
74 | try {
75 | return $this->getPluginConfigObject($plugin)->getConfigs();
76 | } catch (\Exception $e) {
77 | throw $e;
78 | }
79 | }
80 |
81 | /**
82 | * @return array
83 | */
84 | public function getConfigs(): array
85 | {
86 | return static::$configs;
87 | }
88 |
89 | /**
90 | * @param $key
91 | * @param $value
92 | * @param null $plugin
93 | *
94 | * @throws \Exception
95 | */
96 | public function set($key, $value, $plugin = null)
97 | {
98 | if ($plugin !== null) {
99 | try {
100 | $this->getPluginConfigObject($plugin)->set($key, $value);
101 | } catch (\Exception $e) {
102 | throw $e;
103 | }
104 | }
105 |
106 | is_array($key) ? (new ArrayUtility())->setNestedArrayValue(static::$configs, $key, $value)
107 | : static::$configs[$key] = $value;
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/src/Botonomous/AbstractSender.php:
--------------------------------------------------------------------------------
1 | loggerUtility)) {
25 | $this->setLoggerUtility(new LoggerUtility());
26 | }
27 |
28 | return $this->loggerUtility;
29 | }
30 |
31 | /**
32 | * @param LoggerUtility $loggerUtility
33 | */
34 | public function setLoggerUtility(LoggerUtility $loggerUtility)
35 | {
36 | $this->loggerUtility = $loggerUtility;
37 | }
38 |
39 | /**
40 | * @return Config
41 | */
42 | public function getConfig(): Config
43 | {
44 | if (!isset($this->config)) {
45 | $this->config = (new Config());
46 | }
47 |
48 | return $this->config;
49 | }
50 |
51 | /**
52 | * @param Config $config
53 | */
54 | public function setConfig(Config $config)
55 | {
56 | $this->config = $config;
57 | }
58 |
59 | /**
60 | * @return ApiClient
61 | */
62 | public function getApiClient(): ApiClient
63 | {
64 | if (!isset($this->apiClient)) {
65 | $this->setApiClient(new ApiClient());
66 | }
67 |
68 | return $this->apiClient;
69 | }
70 |
71 | /**
72 | * @param ApiClient $apiClient
73 | */
74 | public function setApiClient(ApiClient $apiClient)
75 | {
76 | $this->apiClient = $apiClient;
77 | }
78 |
79 | /**
80 | * @return Client
81 | */
82 | public function getClient(): Client
83 | {
84 | if (!isset($this->client)) {
85 | $this->setClient(new Client());
86 | }
87 |
88 | return $this->client;
89 | }
90 |
91 | /**
92 | * @param Client $client
93 | */
94 | public function setClient(Client $client)
95 | {
96 | $this->client = $client;
97 | }
98 |
99 | /**
100 | * @return MessageUtility
101 | */
102 | public function getMessageUtility(): MessageUtility
103 | {
104 | if (!isset($this->messageUtility)) {
105 | $this->setMessageUtility(new MessageUtility());
106 | }
107 |
108 | return $this->messageUtility;
109 | }
110 |
111 | /**
112 | * @param MessageUtility $messageUtility
113 | */
114 | public function setMessageUtility(MessageUtility $messageUtility)
115 | {
116 | $this->messageUtility = $messageUtility;
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/src/Botonomous/AbstractSlackEntity.php:
--------------------------------------------------------------------------------
1 | slackId;
18 | }
19 |
20 | /**
21 | * @param string $slackId
22 | */
23 | public function setSlackId(string $slackId)
24 | {
25 | $this->slackId = $slackId;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Botonomous/Action.php:
--------------------------------------------------------------------------------
1 | name;
18 | }
19 |
20 | /**
21 | * @param string $name
22 | */
23 | public function setName(string $name)
24 | {
25 | $this->name = $name;
26 | }
27 |
28 | /**
29 | * @return string
30 | */
31 | public function getText()
32 | {
33 | return $this->text;
34 | }
35 |
36 | /**
37 | * @param string $text
38 | */
39 | public function setText(string $text)
40 | {
41 | $this->text = $text;
42 | }
43 |
44 | /**
45 | * @return string
46 | */
47 | public function getValue(): string
48 | {
49 | return $this->value;
50 | }
51 |
52 | /**
53 | * @param string $value
54 | */
55 | public function setValue(string $value)
56 | {
57 | $this->value = $value;
58 | }
59 |
60 | /**
61 | * @return string
62 | */
63 | public function getType(): string
64 | {
65 | return $this->type;
66 | }
67 |
68 | /**
69 | * @param string $type
70 | */
71 | public function setType(string $type)
72 | {
73 | $this->type = $type;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/Botonomous/BlackList.php:
--------------------------------------------------------------------------------
1 | setRequest($request);
15 | }
16 |
17 | /**
18 | * @throws \Exception
19 | *
20 | * @return bool
21 | */
22 | public function isBlackListed(): bool
23 | {
24 | return $this->isUsernameBlackListed()
25 | || $this->isUserIdBlackListed()
26 | || $this->isEmailBlackListed();
27 | }
28 |
29 | /**
30 | * @throws \Exception
31 | *
32 | * @return bool
33 | */
34 | public function isUsernameBlackListed(): bool
35 | {
36 | return $this->findInListByRequestKey('user_name', $this->getShortClassName(), 'username') === true
37 | ? true : false;
38 | }
39 |
40 | /**
41 | * @throws \Exception
42 | *
43 | * @return bool
44 | */
45 | public function isUserIdBlackListed(): bool
46 | {
47 | return $this->findInListByRequestKey('user_id', $this->getShortClassName(), 'userId') === true
48 | ? true : false;
49 | }
50 |
51 | /**
52 | * @throws \Exception
53 | *
54 | * @return bool
55 | */
56 | public function isEmailBlackListed()
57 | {
58 | return $this->checkEmail();
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/Botonomous/BotonomousException.php:
--------------------------------------------------------------------------------
1 | name;
18 | }
19 |
20 | /**
21 | * @param string $name
22 | */
23 | public function setName(string $name)
24 | {
25 | $this->name = $name;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Botonomous/Command.php:
--------------------------------------------------------------------------------
1 | setKey($key);
28 | }
29 |
30 | /**
31 | * @return string
32 | */
33 | public function getKey(): string
34 | {
35 | return $this->key;
36 | }
37 |
38 | /**
39 | * @param string $key
40 | */
41 | public function setKey(string $key)
42 | {
43 | $this->key = $key;
44 | }
45 |
46 | /**
47 | * @return string
48 | */
49 | public function getPlugin(): string
50 | {
51 | return $this->plugin;
52 | }
53 |
54 | /**
55 | * @param string $plugin
56 | */
57 | public function setPlugin(string $plugin)
58 | {
59 | $this->plugin = $plugin;
60 | }
61 |
62 | /**
63 | * @return string
64 | */
65 | public function getDescription(): string
66 | {
67 | return $this->description;
68 | }
69 |
70 | /**
71 | * @param string $description
72 | */
73 | public function setDescription(string $description)
74 | {
75 | $this->description = $description;
76 | }
77 |
78 | /**
79 | * @return string
80 | */
81 | public function getAction(): string
82 | {
83 | if (empty($this->action)) {
84 | $this->setAction(self::DEFAULT_ACTION);
85 | }
86 |
87 | return $this->action;
88 | }
89 |
90 | /**
91 | * @param string $action
92 | */
93 | public function setAction(string $action)
94 | {
95 | $this->action = $action;
96 | }
97 |
98 | /**
99 | * @return string
100 | */
101 | public function getClass(): string
102 | {
103 | if (empty($this->class)) {
104 | $class = __NAMESPACE__.'\\'.self::PLUGIN_DIR.'\\'.strtolower($this->getPlugin()).'\\'.$this->getPlugin();
105 | $this->setClass($class);
106 | }
107 |
108 | return $this->class;
109 | }
110 |
111 | /**
112 | * @param string $class
113 | */
114 | public function setClass(string $class)
115 | {
116 | $this->class = $class;
117 | }
118 |
119 | /**
120 | * @return array
121 | */
122 | public function getKeywords()
123 | {
124 | return $this->keywords;
125 | }
126 |
127 | /**
128 | * @param array $keywords
129 | */
130 | public function setKeywords(array $keywords)
131 | {
132 | $this->keywords = $keywords;
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/src/Botonomous/CommandContainer.php:
--------------------------------------------------------------------------------
1 | [
25 | self::PLUGIN_KEY => 'Ping',
26 | self::DESCRIPTION_KEY => self::HEALTH_CHECK_PLUGIN_DESCRIPTION,
27 | ],
28 | 'pong' => [
29 | self::PLUGIN_KEY => 'Ping',
30 | self::ACTION_KEY => 'pong',
31 | self::DESCRIPTION_KEY => self::HEALTH_CHECK_PLUGIN_DESCRIPTION,
32 | ],
33 | 'commandWithoutFunctionForTest' => [
34 | self::PLUGIN_KEY => 'Ping',
35 | self::ACTION_KEY => 'commandWithoutFunctionForTest',
36 | self::DESCRIPTION_KEY => self::HEALTH_CHECK_PLUGIN_DESCRIPTION,
37 | ],
38 | 'help' => [
39 | self::PLUGIN_KEY => 'Help',
40 | self::DESCRIPTION_KEY => 'List all the available commands',
41 | self::KEYWORDS_KEY => [
42 | 'help',
43 | 'ask',
44 | ],
45 | ],
46 | 'qa' => [
47 | self::PLUGIN_KEY => 'QA',
48 | self::DESCRIPTION_KEY => 'Answer questions',
49 | ],
50 | ];
51 | }
52 |
--------------------------------------------------------------------------------
/src/Botonomous/CommandExtractor.php:
--------------------------------------------------------------------------------
1 | setError('Message is empty');
32 |
33 | return;
34 | }
35 |
36 | /*
37 | * Process the message and find explicitly specified command
38 | */
39 | return $this->getCommandObjectByMessage($message);
40 | }
41 |
42 | /**
43 | * @param string $message
44 | *
45 | * @throws \Exception
46 | *
47 | * @return array
48 | */
49 | public function countKeywordOccurrence(string $message): array
50 | {
51 | $stemmer = new PorterStemmer();
52 |
53 | // tokenize $message
54 | $stemmed = implode(' ', $stemmer->stemAll((new WhitespaceAndPunctuationTokenizer())->tokenize($message)));
55 |
56 | $count = [];
57 | foreach ($this->getCommandContainer()->getAllAsObject() as $commandKey => $commandObject) {
58 | $keywordsCount = $this->commandKeywordOccurrence($commandObject, $stemmed);
59 |
60 | $total = 0;
61 | if (empty($keywordsCount)) {
62 | $count[$commandKey] = $total;
63 | continue;
64 | }
65 |
66 | $count[$commandKey] = array_sum($keywordsCount);
67 | }
68 |
69 | return $count;
70 | }
71 |
72 | /**
73 | * @param Command $command
74 | * @param string $message
75 | *
76 | * @return array|void
77 | */
78 | private function commandKeywordOccurrence(Command $command, string $message)
79 | {
80 | $stemmer = new PorterStemmer();
81 | $keywords = $command->getKeywords();
82 | if (empty($keywords)) {
83 | return;
84 | }
85 |
86 | return $this->getMessageUtility()->keywordCount(
87 | $stemmer->stemAll($keywords),
88 | $message
89 | );
90 | }
91 |
92 | /**
93 | * @param string $message
94 | *
95 | * @throws \Exception
96 | *
97 | * @return Command|null
98 | */
99 | private function getCommandObjectByMessage(string $message)
100 | {
101 | $command = $this->getMessageUtility()->extractCommandName($message);
102 | if (empty($command)) {
103 | $command = (new ArrayUtility())->maxPositiveValueKey($this->countKeywordOccurrence($message));
104 | }
105 |
106 | // check command name
107 | if (empty($command)) {
108 | // get the default command if no command is find in the message
109 | $command = $this->checkDefaultCommand();
110 | }
111 |
112 | return $this->getCommandObjectByCommand($command);
113 | }
114 |
115 | /**
116 | * @throws \Exception
117 | *
118 | * @return mixed|void
119 | */
120 | private function checkDefaultCommand()
121 | {
122 | $command = $this->getConfig()->get('defaultCommand');
123 |
124 | if (empty($command)) {
125 | $this->setError($this->getDictionary()->get('generic-messages')['noCommandMessage']);
126 |
127 | return;
128 | }
129 |
130 | return $command;
131 | }
132 |
133 | /**
134 | * @param $command
135 | *
136 | * @throws \Exception
137 | *
138 | * @return Command|void
139 | */
140 | private function getCommandObjectByCommand($command)
141 | {
142 | if (empty($command)) {
143 | return;
144 | }
145 |
146 | $commandObject = $this->getCommandContainer()->getAsObject($command);
147 |
148 | if ($this->validateCommandObject($commandObject) !== true) {
149 | return;
150 | }
151 |
152 | return $commandObject;
153 | }
154 |
155 | /**
156 | * Validate the command object.
157 | *
158 | * @param Command|null $commandObject
159 | *
160 | * @throws \Exception
161 | *
162 | * @return bool
163 | */
164 | private function validateCommandObject($commandObject): bool
165 | {
166 | // check command details
167 | if (empty($commandObject)) {
168 | $this->setError(
169 | $this->getDictionary()->getValueByKey('generic-messages', 'unknownCommandMessage')
170 | );
171 |
172 | return false;
173 | }
174 |
175 | return true;
176 | }
177 |
178 | /**
179 | * @return string
180 | */
181 | public function getError(): string
182 | {
183 | return $this->error;
184 | }
185 |
186 | /**
187 | * @param string $error
188 | */
189 | public function setError(string $error)
190 | {
191 | $this->error = $error;
192 | }
193 |
194 | /**
195 | * @return Config
196 | */
197 | public function getConfig(): Config
198 | {
199 | if ($this->config === null) {
200 | $this->config = (new Config());
201 | }
202 |
203 | return $this->config;
204 | }
205 |
206 | /**
207 | * @param Config $config
208 | */
209 | public function setConfig(Config $config)
210 | {
211 | $this->config = $config;
212 | }
213 |
214 | /**
215 | * @return MessageUtility
216 | */
217 | public function getMessageUtility(): MessageUtility
218 | {
219 | if (!isset($this->messageUtility)) {
220 | $this->setMessageUtility(new MessageUtility());
221 | }
222 |
223 | return $this->messageUtility;
224 | }
225 |
226 | /**
227 | * @param MessageUtility $messageUtility
228 | */
229 | public function setMessageUtility(MessageUtility $messageUtility)
230 | {
231 | $this->messageUtility = $messageUtility;
232 | }
233 |
234 | /**
235 | * @return Dictionary
236 | */
237 | public function getDictionary(): Dictionary
238 | {
239 | if (!isset($this->dictionary)) {
240 | $this->setDictionary(new Dictionary());
241 | }
242 |
243 | return $this->dictionary;
244 | }
245 |
246 | /**
247 | * @param Dictionary $dictionary
248 | */
249 | public function setDictionary(Dictionary $dictionary)
250 | {
251 | $this->dictionary = $dictionary;
252 | }
253 |
254 | /**
255 | * @return CommandContainer
256 | */
257 | public function getCommandContainer(): CommandContainer
258 | {
259 | if (!isset($this->commandContainer)) {
260 | $this->setCommandContainer(new CommandContainer());
261 | }
262 |
263 | return $this->commandContainer;
264 | }
265 |
266 | /**
267 | * @param CommandContainer $commandContainer
268 | */
269 | public function setCommandContainer(CommandContainer $commandContainer)
270 | {
271 | $this->commandContainer = $commandContainer;
272 | }
273 | }
274 |
--------------------------------------------------------------------------------
/src/Botonomous/Config.php:
--------------------------------------------------------------------------------
1 | 'Australia/Melbourne',
14 | 'oAuthToken' => 'OAuth_Access_Token',
15 | 'botUserToken' => 'Bot_User_OAuth_Access_Token',
16 | 'botUserId' => 'YOUR_BOT_USER_ID',
17 | 'botUsername' => 'YOUR_BOT_USERNAME',
18 | 'logger' => [
19 | 'enabled' => true,
20 | 'monolog' => [
21 | 'channel' => 'logger',
22 | 'handlers' => [
23 | 'file' => [
24 | // should be full path
25 | 'fileName' => 'bot.log',
26 | ],
27 | ],
28 | ],
29 | ],
30 | 'iconURL' => 'YOUR_BOT_ICON_URL_48_BY_48',
31 | 'asUser' => true,
32 | // possible values are: SlashCommandListener::KEY, EventListener::KEY
33 | 'listener' => SlashCommandListener::KEY,
34 | // this is used if there is no command has been specified in the message
35 | 'defaultCommand' => 'qa',
36 | 'commandPrefix' => '/',
37 | /*
38 | * App credentials - This is required for Event listener
39 | * Can be found at https://api.slack.com/apps
40 | */
41 | 'clientId' => 'YOUR_APP_CLIENT_ID',
42 | 'clientSecret' => 'YOUR_APP_SECRET',
43 | 'scopes' => ['bot'],
44 | /*
45 | * use this token to verify that requests are actually coming from Slack
46 | */
47 | 'verificationToken' => 'YOUR_APP_VERIFICATION_TOKEN',
48 | 'appId' => 'YOUR_APP_ID',
49 | 'accessControlEnabled' => false,
50 | ];
51 | }
52 |
--------------------------------------------------------------------------------
/src/Botonomous/Dictionary.php:
--------------------------------------------------------------------------------
1 | jsonFileToArray($stopWordsPath);
31 | }
32 |
33 | /**
34 | * @return array
35 | */
36 | public function getData()
37 | {
38 | return $this->data;
39 | }
40 |
41 | /**
42 | * @param array $data
43 | */
44 | public function setData(array $data)
45 | {
46 | $this->data = $data;
47 | }
48 |
49 | /**
50 | * @param string $fileName
51 | *
52 | * @throws \Exception
53 | *
54 | * @return mixed
55 | */
56 | public function get(string $fileName)
57 | {
58 | $data = $this->getData();
59 |
60 | if (!isset($data[$fileName])) {
61 | $data[$fileName] = $this->load($fileName);
62 | $this->setData($data);
63 | }
64 |
65 | return $data[$fileName];
66 | }
67 |
68 | /**
69 | * Return value for the key in the file content.
70 | *
71 | * @param $fileName
72 | * @param $key
73 | * @param array $replacements
74 | *
75 | * @throws \Exception
76 | *
77 | * @return mixed
78 | */
79 | public function getValueByKey(string $fileName, string $key, array $replacements = [])
80 | {
81 | $fileContent = $this->get($fileName);
82 |
83 | if (!array_key_exists($key, $fileContent)) {
84 | throw new BotonomousException("Key: '{$key}' does not exist in file: {$fileName}");
85 | }
86 |
87 | $found = $fileContent[$key];
88 |
89 | return (new StringUtility())->applyReplacements($found, $replacements);
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/Botonomous/Event.php:
--------------------------------------------------------------------------------
1 | setType($type);
30 | }
31 |
32 | /**
33 | * @return string
34 | */
35 | public function getType(): string
36 | {
37 | return $this->type;
38 | }
39 |
40 | /**
41 | * @param string $type
42 | */
43 | public function setType(string $type)
44 | {
45 | $this->type = $type;
46 | }
47 |
48 | /**
49 | * @return string
50 | */
51 | public function getUser(): string
52 | {
53 | return $this->user;
54 | }
55 |
56 | /**
57 | * @param string $user
58 | */
59 | public function setUser(string $user)
60 | {
61 | $this->user = $user;
62 | }
63 |
64 | /**
65 | * @return string
66 | */
67 | public function getText()
68 | {
69 | return $this->text;
70 | }
71 |
72 | /**
73 | * @param string $text
74 | */
75 | public function setText(string $text)
76 | {
77 | $this->text = $text;
78 | }
79 |
80 | /**
81 | * @return string
82 | */
83 | public function getTimestamp(): string
84 | {
85 | return $this->timestamp;
86 | }
87 |
88 | /**
89 | * @param string $timestamp
90 | */
91 | public function setTimestamp(string $timestamp)
92 | {
93 | $this->timestamp = $timestamp;
94 | }
95 |
96 | /**
97 | * @return string
98 | */
99 | public function getEventTimestamp(): string
100 | {
101 | return $this->eventTimestamp;
102 | }
103 |
104 | /**
105 | * @param string $eventTimestamp
106 | */
107 | public function setEventTimestamp(string $eventTimestamp)
108 | {
109 | $this->eventTimestamp = $eventTimestamp;
110 | }
111 |
112 | /**
113 | * @return string
114 | */
115 | public function getChannel()
116 | {
117 | return $this->channel;
118 | }
119 |
120 | /**
121 | * @param string $channel
122 | */
123 | public function setChannel(string $channel)
124 | {
125 | $this->channel = $channel;
126 | }
127 |
128 | /**
129 | * @return string
130 | */
131 | public function getBotId()
132 | {
133 | return $this->botId;
134 | }
135 |
136 | /**
137 | * @param string $botId
138 | */
139 | public function setBotId(string $botId)
140 | {
141 | $this->botId = $botId;
142 | }
143 |
144 | /**
145 | * @return ApiClient
146 | */
147 | public function getApiClient(): ApiClient
148 | {
149 | if (!isset($this->apiClient)) {
150 | $this->setApiClient(new ApiClient());
151 | }
152 |
153 | return $this->apiClient;
154 | }
155 |
156 | /**
157 | * @param ApiClient $apiClient
158 | */
159 | public function setApiClient(ApiClient $apiClient)
160 | {
161 | $this->apiClient = $apiClient;
162 | }
163 |
164 | /**
165 | * Check if the event belongs to a direct message.
166 | *
167 | * @throws \Exception
168 | *
169 | * @return bool|void
170 | */
171 | public function isDirectMessage()
172 | {
173 | $imChannels = $this->getApiClient()->imListAsObject();
174 |
175 | if (empty($imChannels)) {
176 | return;
177 | }
178 |
179 | foreach ($imChannels as $imChannel) {
180 | /** @var ImChannel $imChannel */
181 | if ($imChannel->getUser() === 'USLACKBOT') {
182 | // ignore any direct conversation with the default slack bot
183 | continue;
184 | }
185 |
186 | // if IM Object id equals the event channel id the conversation is with the bot
187 | if ($imChannel->getSlackId() === $this->getChannel()) {
188 | return true;
189 | }
190 | }
191 | }
192 | }
193 |
--------------------------------------------------------------------------------
/src/Botonomous/ImChannel.php:
--------------------------------------------------------------------------------
1 | isIm;
18 | }
19 |
20 | /**
21 | * @param bool $isIm
22 | */
23 | public function setIm(bool $isIm)
24 | {
25 | $this->isIm = $isIm;
26 | }
27 |
28 | /**
29 | * @return string
30 | */
31 | public function getUser(): string
32 | {
33 | return $this->user;
34 | }
35 |
36 | /**
37 | * @param string $user
38 | */
39 | public function setUser(string $user)
40 | {
41 | $this->user = $user;
42 | }
43 |
44 | /**
45 | * @return string
46 | */
47 | public function getCreated(): string
48 | {
49 | return $this->created;
50 | }
51 |
52 | /**
53 | * @param string $created
54 | */
55 | public function setCreated(string $created)
56 | {
57 | $this->created = $created;
58 | }
59 |
60 | /**
61 | * @return bool
62 | */
63 | public function isUserDeleted(): bool
64 | {
65 | return $this->isUserDeleted;
66 | }
67 |
68 | /**
69 | * @param bool $isUserDeleted
70 | */
71 | public function setUserDeleted(bool $isUserDeleted)
72 | {
73 | $this->isUserDeleted = $isUserDeleted;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/Botonomous/MessageAction.php:
--------------------------------------------------------------------------------
1 | actions;
25 | }
26 |
27 | /**
28 | * @param array $actions
29 | */
30 | public function setActions(array $actions)
31 | {
32 | $this->actions = $actions;
33 | }
34 |
35 | /**
36 | * @param Action $action
37 | */
38 | public function addAction(Action $action)
39 | {
40 | $actions = $this->getActions();
41 | $actions[] = $action;
42 | $this->setActions($actions);
43 | }
44 |
45 | /**
46 | * @return string
47 | */
48 | public function getCallbackId(): string
49 | {
50 | return $this->callbackId;
51 | }
52 |
53 | /**
54 | * @param string $callbackId
55 | */
56 | public function setCallbackId(string $callbackId)
57 | {
58 | $this->callbackId = $callbackId;
59 | }
60 |
61 | /**
62 | * @return Team
63 | */
64 | public function getTeam(): Team
65 | {
66 | return $this->team;
67 | }
68 |
69 | /**
70 | * @param mixed $team
71 | */
72 | public function setTeam($team)
73 | {
74 | if ($team instanceof Team) {
75 | $this->team = $team;
76 |
77 | return;
78 | }
79 |
80 | // if array or json is passed create the object
81 | $this->team = (new Team())->load($team);
82 | }
83 |
84 | /**
85 | * @return Channel
86 | */
87 | public function getChannel(): Channel
88 | {
89 | return $this->channel;
90 | }
91 |
92 | /**
93 | * @param mixed $channel
94 | */
95 | public function setChannel($channel)
96 | {
97 | if ($channel instanceof Channel) {
98 | $this->channel = $channel;
99 |
100 | return;
101 | }
102 |
103 | // if array or json is passed create the object
104 | $this->channel = (new Channel())->load($channel);
105 | }
106 |
107 | /**
108 | * @return User
109 | */
110 | public function getUser()
111 | {
112 | return $this->user;
113 | }
114 |
115 | /**
116 | * @param mixed $user
117 | */
118 | public function setUser($user)
119 | {
120 | if ($user instanceof User) {
121 | $this->user = $user;
122 |
123 | return;
124 | }
125 |
126 | // if array or json is passed create the object
127 | $this->user = (new User())->load($user);
128 | }
129 |
130 | /**
131 | * @return string
132 | */
133 | public function getActionTimestamp()
134 | {
135 | return $this->actionTimestamp;
136 | }
137 |
138 | /**
139 | * @param string $actionTimestamp
140 | */
141 | public function setActionTimestamp($actionTimestamp)
142 | {
143 | $this->actionTimestamp = $actionTimestamp;
144 | }
145 |
146 | /**
147 | * @return string
148 | */
149 | public function getMessageTimestamp()
150 | {
151 | return $this->messageTimestamp;
152 | }
153 |
154 | /**
155 | * @param string $messageTimestamp
156 | */
157 | public function setMessageTimestamp($messageTimestamp)
158 | {
159 | $this->messageTimestamp = $messageTimestamp;
160 | }
161 |
162 | /**
163 | * @return string
164 | */
165 | public function getAttachmentId()
166 | {
167 | return $this->attachmentId;
168 | }
169 |
170 | /**
171 | * @param string $attachmentId
172 | */
173 | public function setAttachmentId($attachmentId)
174 | {
175 | $this->attachmentId = $attachmentId;
176 | }
177 |
178 | /**
179 | * @return string
180 | */
181 | public function getToken()
182 | {
183 | return $this->token;
184 | }
185 |
186 | /**
187 | * @param string $token
188 | */
189 | public function setToken($token)
190 | {
191 | $this->token = $token;
192 | }
193 |
194 | /**
195 | * @return string
196 | */
197 | public function getOriginalMessage()
198 | {
199 | return $this->originalMessage;
200 | }
201 |
202 | /**
203 | * @param string $originalMessage
204 | */
205 | public function setOriginalMessage($originalMessage)
206 | {
207 | $this->originalMessage = $originalMessage;
208 | }
209 |
210 | /**
211 | * @return string
212 | */
213 | public function getResponseUrl()
214 | {
215 | return $this->responseUrl;
216 | }
217 |
218 | /**
219 | * @param string $responseUrl
220 | */
221 | public function setResponseUrl($responseUrl)
222 | {
223 | $this->responseUrl = $responseUrl;
224 | }
225 |
226 | /**
227 | * @param $info
228 | *
229 | * @return AbstractBaseSlack
230 | */
231 | public function load($info)
232 | {
233 | $thisObject = parent::load($info);
234 |
235 | $actions = [];
236 | foreach ($info['actions'] as $actionInfo) {
237 | // load action
238 | $actions[] = (new Action())->load($actionInfo);
239 | }
240 | $this->setActions($actions);
241 | /*
242 | * Finish adding actions
243 | */
244 |
245 | return $thisObject;
246 | }
247 | }
248 |
--------------------------------------------------------------------------------
/src/Botonomous/Sender.php:
--------------------------------------------------------------------------------
1 | setSlackbot($slackbot);
26 | }
27 |
28 | /**
29 | * @return AbstractBot
30 | */
31 | public function getSlackbot(): AbstractBot
32 | {
33 | return $this->slackbot;
34 | }
35 |
36 | /**
37 | * @param AbstractBot $slackbot
38 | */
39 | public function setSlackbot(AbstractBot $slackbot)
40 | {
41 | $this->slackbot = $slackbot;
42 | }
43 |
44 | /**
45 | * Final endpoint for the response.
46 | *
47 | * @param $channel
48 | * @param $text
49 | * @param $attachments
50 | *
51 | * @throws \Exception
52 | *
53 | * @return bool
54 | */
55 | public function send($text, $channel = null, $attachments = null)
56 | {
57 | // @codeCoverageIgnoreStart
58 | if ($this->getSlackbot()->getListener()->isThisBot() !== false) {
59 | return false;
60 | }
61 | // @codeCoverageIgnoreEnd
62 |
63 | if (empty($channel)) {
64 | $channel = $this->getSlackbot()->getListener()->getChannelId();
65 | }
66 |
67 | $data = [
68 | 'text' => $text,
69 | 'channel' => $channel,
70 | ];
71 |
72 | if ($attachments !== null) {
73 | $data['attachments'] = json_encode($attachments);
74 | }
75 |
76 | $this->getLoggerUtility()->logChat(__METHOD__, $text, $channel);
77 |
78 | $responseType = $this->getResponseType();
79 | if ($responseType === self::SLACK_RESPONSE_TYPE) {
80 | $this->respondToSlack($data);
81 | } elseif ($responseType === self::SLASH_COMMAND_RESPONSE_TYPE) {
82 | $this->respondToSlashCommand($text);
83 | } elseif ($responseType === self::JSON_RESPONSE_TYPE) {
84 | $this->respondAsJSON($data);
85 | }
86 |
87 | return true;
88 | }
89 |
90 | /**
91 | * @param $response
92 | */
93 | private function respondToSlashCommand($response)
94 | {
95 | /** @noinspection PhpUndefinedClassInspection */
96 | $request = new Request(
97 | 'POST',
98 | $this->getSlackbot()->getRequest('response_url'),
99 | ['Content-Type' => 'application/json'],
100 | json_encode([
101 | 'text' => $response,
102 | 'response_type' => 'in_channel',
103 | ])
104 | );
105 |
106 | /* @noinspection PhpUndefinedClassInspection */
107 | $this->getClient()->send($request);
108 | }
109 |
110 | /**
111 | * @param $data
112 | *
113 | * @throws \Exception
114 | */
115 | private function respondToSlack($data)
116 | {
117 | $this->getApiClient()->chatPostMessage($data);
118 | }
119 |
120 | /**
121 | * @param $data
122 | */
123 | private function respondAsJSON($data)
124 | {
125 | // headers_sent is used to avoid issue in the test
126 | if (!headers_sent()) {
127 | header('Content-type:application/json;charset=utf-8');
128 | }
129 |
130 | echo json_encode($data);
131 | }
132 |
133 | /**
134 | * Specify the response type
135 | * If response in config is set to empty, it will be considered based on listener.
136 | *
137 | * @throws \Exception
138 | *
139 | * @return mixed|string
140 | */
141 | private function getResponseType()
142 | {
143 | if ($this->getSlackbot()->getRequest('debug') === true
144 | || $this->getSlackbot()->getRequest('debug') === 'true') {
145 | return self::JSON_RESPONSE_TYPE;
146 | }
147 |
148 | // response type in the config is empty, so choose it based on listener type
149 | return $this->getResponseByListenerType();
150 | }
151 |
152 | /**
153 | * @throws \Exception
154 | *
155 | * @return string
156 | */
157 | private function getResponseByListenerType(): string
158 | {
159 | $listener = $this->getConfig()->get('listener');
160 | switch ($listener) {
161 | case SlashCommandListener::KEY:
162 | return self::SLASH_COMMAND_RESPONSE_TYPE;
163 | case EventListener::KEY:
164 | return self::SLACK_RESPONSE_TYPE;
165 | default:
166 | return self::SLASH_COMMAND_RESPONSE_TYPE;
167 | }
168 | }
169 |
170 | /**
171 | * Send confirmation.
172 | *
173 | * @throws BotonomousException
174 | */
175 | public function sendConfirmation()
176 | {
177 | try {
178 | $userId = $this->getSlackbot()->getRequest('user_id');
179 |
180 | $user = '';
181 | if (!empty($userId)) {
182 | $user = $this->getMessageUtility()->linkToUser($userId).' ';
183 | }
184 |
185 | $confirmMessage = $this->getSlackbot()->getDictionary()->getValueByKey(
186 | 'generic-messages',
187 | 'confirmReceivedMessage',
188 | ['user' => $user]
189 | );
190 |
191 | if (!empty($confirmMessage)) {
192 | $this->send($confirmMessage);
193 | }
194 | } catch (\Exception $e) {
195 | throw new BotonomousException('Failed to send confirmatyion: '.$e->getMessage());
196 | }
197 | }
198 | }
199 |
--------------------------------------------------------------------------------
/src/Botonomous/Team.php:
--------------------------------------------------------------------------------
1 | name;
21 | }
22 |
23 | /**
24 | * @param string $name
25 | */
26 | public function setName(string $name)
27 | {
28 | $this->name = $name;
29 | }
30 |
31 | /**
32 | * @return string
33 | */
34 | public function getDomain(): string
35 | {
36 | return $this->domain;
37 | }
38 |
39 | /**
40 | * @param string $domain
41 | */
42 | public function setDomain(string $domain)
43 | {
44 | $this->domain = $domain;
45 | }
46 |
47 | /**
48 | * @return string
49 | */
50 | public function getEmailDomain(): string
51 | {
52 | return $this->emailDomain;
53 | }
54 |
55 | /**
56 | * @param string $emailDomain
57 | */
58 | public function setEmailDomain(string $emailDomain)
59 | {
60 | $this->emailDomain = $emailDomain;
61 | }
62 |
63 | /**
64 | * @return array
65 | */
66 | public function getIcon(): array
67 | {
68 | return $this->icon;
69 | }
70 |
71 | /**
72 | * @param array $icon
73 | */
74 | public function setIcon(array $icon)
75 | {
76 | $this->icon = $icon;
77 | }
78 |
79 | /**
80 | * @return bool
81 | */
82 | public function isIconDefault(): bool
83 | {
84 | $icon = $this->getIcon();
85 |
86 | return isset($icon['image_default']) && $icon['image_default'] === true ? true : false;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/Botonomous/User.php:
--------------------------------------------------------------------------------
1 | name;
18 | }
19 |
20 | /**
21 | * @param string $name
22 | */
23 | public function setName(string $name)
24 | {
25 | $this->name = $name;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Botonomous/WhiteList.php:
--------------------------------------------------------------------------------
1 | setRequest($request);
15 | }
16 |
17 | /**
18 | * @throws \Exception
19 | *
20 | * @return bool
21 | */
22 | public function isWhiteListed(): bool
23 | {
24 | $usernameCheck = true;
25 | $userIdCheck = true;
26 | $userEmailCheck = true;
27 |
28 | if ($this->isUsernameWhiteListed() === false) {
29 | $usernameCheck = false;
30 | }
31 |
32 | if ($this->isUserIdWhiteListed() === false) {
33 | $userIdCheck = false;
34 | }
35 |
36 | if ($this->isEmailWhiteListed() === false) {
37 | $userEmailCheck = false;
38 | }
39 |
40 | return $usernameCheck === true && $userIdCheck === true && $userEmailCheck === true ? true : false;
41 | }
42 |
43 | /**
44 | * @throws \Exception
45 | *
46 | * @return bool
47 | */
48 | public function isUsernameWhiteListed(): bool
49 | {
50 | return empty($this->findInListByRequestKey('user_name', $this->getShortClassName(), 'username')) ? false : true;
51 | }
52 |
53 | /**
54 | * @throws \Exception
55 | *
56 | * @return bool
57 | */
58 | public function isUserIdWhiteListed(): bool
59 | {
60 | return empty($this->findInListByRequestKey('user_id', $this->getShortClassName(), 'userId')) ? false : true;
61 | }
62 |
63 | /**
64 | * @throws \Exception
65 | *
66 | * @return bool
67 | */
68 | public function isEmailWhiteListed()
69 | {
70 | return $this->checkEmail();
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/Botonomous/client/AbstractClient.php:
--------------------------------------------------------------------------------
1 | client = $client;
27 | }
28 |
29 | /** @noinspection PhpUndefinedClassInspection
30 | * @return Client|null
31 | */
32 | public function getClient()
33 | {
34 | if (!isset($this->client)) {
35 | /* @noinspection PhpUndefinedClassInspection */
36 | $this->setClient(new Client());
37 | }
38 |
39 | return $this->client;
40 | }
41 |
42 | /**
43 | * @return ArrayUtility
44 | */
45 | public function getArrayUtility()
46 | {
47 | if (!isset($this->arrayUtility)) {
48 | $this->setArrayUtility(new ArrayUtility());
49 | }
50 |
51 | return $this->arrayUtility;
52 | }
53 |
54 | /**
55 | * @param ArrayUtility $arrayUtility
56 | */
57 | public function setArrayUtility(ArrayUtility $arrayUtility)
58 | {
59 | $this->arrayUtility = $arrayUtility;
60 | }
61 |
62 | /**
63 | * @return Config
64 | */
65 | public function getConfig()
66 | {
67 | if (!isset($this->config)) {
68 | $this->setConfig(new Config());
69 | }
70 |
71 | return $this->config;
72 | }
73 |
74 | /**
75 | * @param Config $config
76 | */
77 | public function setConfig(Config $config)
78 | {
79 | $this->config = $config;
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/Botonomous/dictionary/access-control.json:
--------------------------------------------------------------------------------
1 | {
2 | "whitelist": {
3 | "username": [],
4 | "userId": [],
5 | "userEmail": []
6 | },
7 | "blacklist": {
8 | "username": [],
9 | "userId": [],
10 | "userEmail": []
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/Botonomous/dictionary/generic-messages.json:
--------------------------------------------------------------------------------
1 | {
2 | "noCommandMessage": "Sorry. I couldn't find any command in your message. List the available commands using /help",
3 | "blacklistedMessage": "Sorry, we cannot process your message as we detected it in the blacklist",
4 | "whitelistedMessage": "Sorry, we cannot process your message as we could not find it in whitelist",
5 | "unknownCommandMessage": "Sorry. I do not know anything about your command. List the available commands using /help",
6 | "confirmReceivedMessage": ":point_right: {user}I've received your message and am thinking about that ..."
7 | }
8 |
--------------------------------------------------------------------------------
/src/Botonomous/dictionary/punctuations.json:
--------------------------------------------------------------------------------
1 | ["?", "!", ",", ";", "."]
2 |
--------------------------------------------------------------------------------
/src/Botonomous/dictionary/question-answer.json:
--------------------------------------------------------------------------------
1 | {
2 | "Hi": {
3 | "answers": ["Hi there :raised_hand_with_fingers_splayed:", "Hello"]
4 | },
5 | "How are you?": {
6 | "answers": ["I'm fine. Thank you :facepunch:"]
7 | },
8 | "Who are you?": {
9 | "answers": ["My name is :tada: *Botonomous* :tada: a PHP framework to help you create awesome Slack bots :robot_face:"]
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/Botonomous/dictionary/stopwords-en.json:
--------------------------------------------------------------------------------
1 | ["a","a's","able","about","above","according","accordingly","across","actually","after","afterwards","again","against","ain't","all","allow","allows","almost","alone","along","already","also","although","always","am","among","amongst","an","and","another","any","anybody","anyhow","anyone","anything","anyway","anyways","anywhere","apart","appear","appreciate","appropriate","are","aren't","around","as","aside","ask","asking","associated","at","available","away","awfully","b","be","became","because","become","becomes","becoming","been","before","beforehand","behind","being","believe","below","beside","besides","best","better","between","beyond","both","brief","but","by","c","c'mon","c's","came","can","can't","cannot","cant","cause","causes","certain","certainly","changes","clearly","co","com","come","comes","concerning","consequently","consider","considering","contain","containing","contains","corresponding","could","couldn't","course","currently","d","definitely","described","despite","did","didn't","different","do","does","doesn't","doing","don't","done","down","downwards","during","e","each","edu","eg","eight","either","else","elsewhere","enough","entirely","especially","et","etc","even","ever","every","everybody","everyone","everything","everywhere","ex","exactly","example","except","f","far","few","fifth","first","five","followed","following","follows","for","former","formerly","forth","four","from","further","furthermore","g","get","gets","getting","given","gives","go","goes","going","gone","got","gotten","greetings","h","had","hadn't","happens","hardly","has","hasn't","have","haven't","having","he","he's","hello","help","hence","her","here","here's","hereafter","hereby","herein","hereupon","hers","herself","hi","him","himself","his","hither","hopefully","how","howbeit","however","i","i'd","i'll","i'm","i've","ie","if","ignored","immediate","in","inasmuch","inc","indeed","indicate","indicated","indicates","inner","insofar","instead","into","inward","is","isn't","it","it'd","it'll","it's","its","itself","j","just","k","keep","keeps","kept","know","known","knows","l","last","lately","later","latter","latterly","least","less","lest","let","let's","like","liked","likely","little","look","looking","looks","ltd","m","mainly","many","may","maybe","me","mean","meanwhile","merely","might","more","moreover","most","mostly","much","must","my","myself","n","name","namely","nd","near","nearly","necessary","need","needs","neither","never","nevertheless","new","next","nine","no","nobody","non","none","nor","normally","not","nothing","novel","now","nowhere","o","obviously","of","off","often","oh","ok","okay","old","on","once","one","ones","only","onto","or","other","others","otherwise","ought","our","ours","ourselves","out","outside","over","overall","own","p","particular","particularly","per","perhaps","placed","please","plus","possible","presumably","probably","provides","q","que","quite","qv","r","rather","rd","re","really","reasonably","regarding","regardless","regards","relatively","respectively","right","s","said","same","saw","say","saying","says","second","secondly","see","seeing","seem","seemed","seeming","seems","seen","self","selves","sensible","sent","serious","seriously","seven","several","shall","she","should","shouldn't","since","six","so","some","somebody","somehow","someone","something","sometime","sometimes","somewhat","somewhere","soon","sorry","specified","specify","specifying","still","sub","such","sup","sure","t","t's","take","taken","tell","tends","th","than","thank","thanks","that","that's","the","their","theirs","them","themselves","then","thence","there","there's","thereafter","thereby","therefore","therein","thereupon","these","they","they'd","they'll","they're","they've","think","third","this","thorough","thoroughly","those","though","three","through","throughout","thus","to","together","too","took","toward","towards","tried","tries","truly","try","trying","twice","two","u","un","under","unfortunately","unless","unlikely","until","unto","up","upon","us","use","used","useful","uses","using","usually","v","value","various","very","via","viz","vs","w","want","wants","was","wasn't","way","we","we'd","we'll","we're","we've","welcome","well","went","were","weren't","what","what's","whatever","when","whence","whenever","where","where's","whereas","whereby","wherein","whereupon","wherever","whether","which","while","whither","who","who's","whoever","whole","whom","whose","why","will","willing","wish","with","within","without","won't","wonder","would","wouldn't","x","y","yes","yet","you","you'd","you'll","you're","you've","your","yours","yourself","yourselves","z","zero"]
2 |
--------------------------------------------------------------------------------
/src/Botonomous/dictionary/test-key-value.json:
--------------------------------------------------------------------------------
1 | {
2 | "testKey": "testValue",
3 | "testKeyWithReplacements": "testValue {user}"
4 | }
5 |
--------------------------------------------------------------------------------
/src/Botonomous/dictionary/test.json:
--------------------------------------------------------------------------------
1 | ["test1", "test2"]
2 |
--------------------------------------------------------------------------------
/src/Botonomous/listener/AbstractBaseListener.php:
--------------------------------------------------------------------------------
1 | respondOK();
30 |
31 | $request = $this->extractRequest();
32 |
33 | if (empty($request)) {
34 | /* @noinspection PhpInconsistentReturnPointsInspection */
35 | return;
36 | }
37 |
38 | $this->setRequest($request);
39 |
40 | if ($this->isThisBot() !== false) {
41 | /* @noinspection PhpInconsistentReturnPointsInspection */
42 | return;
43 | }
44 |
45 | return $request;
46 | }
47 |
48 | /**
49 | * @return mixed
50 | */
51 | abstract public function extractRequest();
52 |
53 | /**
54 | * @return string
55 | */
56 | abstract public function getChannelId(): string;
57 |
58 | /**
59 | * @param null|string $key
60 | *
61 | * @return mixed
62 | */
63 | public function getRequest(string $key = null)
64 | {
65 | if (!isset($this->request)) {
66 | // each listener has its own way of extracting the request
67 | $this->setRequest($this->extractRequest());
68 | }
69 |
70 | if ($key === null) {
71 | // return the entire request since key is null
72 | return $this->request;
73 | }
74 |
75 | if (is_array($this->request) && array_key_exists($key, $this->request)) {
76 | return $this->request[$key];
77 | }
78 | }
79 |
80 | /**
81 | * @param array $request
82 | */
83 | public function setRequest($request)
84 | {
85 | $this->request = $request;
86 | }
87 |
88 | /**
89 | * @return Config
90 | */
91 | public function getConfig(): Config
92 | {
93 | if (!isset($this->config)) {
94 | $this->setConfig(new Config());
95 | }
96 |
97 | return $this->config;
98 | }
99 |
100 | /**
101 | * @param Config $config
102 | */
103 | public function setConfig(Config $config)
104 | {
105 | $this->config = $config;
106 | }
107 |
108 | /**
109 | * Verify the request comes from Slack
110 | * Each listener must have have this and has got its own way to check the request.
111 | *
112 | * @throws \Exception
113 | *
114 | * @return array
115 | */
116 | abstract public function verifyOrigin();
117 |
118 | /**
119 | * Check if the request belongs to the bot itself.
120 | *
121 | * @throws \Exception
122 | *
123 | * @return bool
124 | */
125 | abstract public function isThisBot(): bool;
126 |
127 | /**
128 | * @return RequestUtility
129 | */
130 | public function getRequestUtility(): RequestUtility
131 | {
132 | if (!isset($this->requestUtility)) {
133 | $this->setRequestUtility((new RequestUtility()));
134 | }
135 |
136 | return $this->requestUtility;
137 | }
138 |
139 | /**
140 | * @param RequestUtility $requestUtility
141 | */
142 | public function setRequestUtility(RequestUtility $requestUtility)
143 | {
144 | $this->requestUtility = $requestUtility;
145 | }
146 |
147 | /**
148 | * respondOK.
149 | */
150 | protected function respondOK()
151 | {
152 | // check if fastcgi_finish_request is callable
153 | if (is_callable('fastcgi_finish_request')) {
154 | /*
155 | * http://stackoverflow.com/a/38918192
156 | * This works in Nginx but the next approach not
157 | */
158 | session_write_close();
159 | fastcgi_finish_request();
160 |
161 | /* @noinspection PhpInconsistentReturnPointsInspection */
162 | return;
163 | }
164 |
165 | ignore_user_abort(true);
166 |
167 | ob_start();
168 | header($this->getRequestUtility()->getServerProtocol().' 200 OK');
169 | // Disable compression (in case content length is compressed).
170 | header('Content-Encoding: none');
171 | header('Content-Length: '.ob_get_length());
172 |
173 | // Close the connection.
174 | header('Connection: close');
175 |
176 | ob_end_flush();
177 | // only if an output buffer is active do ob_flush
178 | if (ob_get_level() > 0) {
179 | ob_flush();
180 | }
181 |
182 | flush();
183 |
184 | /* @noinspection PhpInconsistentReturnPointsInspection */
185 | }
186 |
187 | /**
188 | * @throws \Exception
189 | *
190 | * @return array
191 | */
192 | public function verifyRequest(): array
193 | {
194 | $originCheck = $this->verifyOrigin();
195 | if (!isset($originCheck[self::ORIGIN_VERIFICATION_SUCCESS_KEY])) {
196 | throw new BotonomousException('Success must be provided in verifyOrigin response');
197 | }
198 |
199 | if ($originCheck[self::ORIGIN_VERIFICATION_SUCCESS_KEY] !== true) {
200 | return [
201 | self::ORIGIN_VERIFICATION_SUCCESS_KEY => false,
202 | self::ORIGIN_VERIFICATION_MESSAGE_KEY => $originCheck[self::ORIGIN_VERIFICATION_MESSAGE_KEY],
203 | ];
204 | }
205 |
206 | if ($this->isThisBot() !== false) {
207 | return [
208 | self::ORIGIN_VERIFICATION_SUCCESS_KEY => false,
209 | self::ORIGIN_VERIFICATION_MESSAGE_KEY => 'Request comes from the bot',
210 | ];
211 | }
212 |
213 | return [self::ORIGIN_VERIFICATION_SUCCESS_KEY => true, self::ORIGIN_VERIFICATION_MESSAGE_KEY => 'Yay!'];
214 | }
215 |
216 | /**
217 | * @return string|null
218 | */
219 | public function determineAction()
220 | {
221 | $utility = $this->getRequestUtility();
222 | $getRequest = $utility->getGet();
223 |
224 | if (!empty($getRequest['action'])) {
225 | return strtolower($getRequest['action']);
226 | }
227 |
228 | $request = $utility->getPostedBody();
229 |
230 | if (isset($request['type']) && $request['type'] === 'url_verification') {
231 | return 'url_verification';
232 | }
233 | }
234 |
235 | /**
236 | * Return message based on the listener
237 | * If listener is event and event text is empty, fall back to request text.
238 | *
239 | * @throws \Exception
240 | *
241 | * @return mixed|string
242 | */
243 | public function getMessage()
244 | {
245 | if ($this instanceof EventListener && $this->getEvent() instanceof Event) {
246 | $message = $this->getEvent()->getText();
247 |
248 | if (!empty($message)) {
249 | return $message;
250 | }
251 | }
252 |
253 | return $this->getRequest('text');
254 | }
255 | }
256 |
--------------------------------------------------------------------------------
/src/Botonomous/listener/EventListener.php:
--------------------------------------------------------------------------------
1 | 'timestamp',
26 | 'event_ts' => 'eventTimestamp',
27 | ];
28 |
29 | /**
30 | * @return mixed
31 | */
32 | public function extractRequest()
33 | {
34 | return $this->getRequestUtility()->getPostedBody();
35 | }
36 |
37 | /**
38 | * @return string
39 | */
40 | public function getToken(): string
41 | {
42 | return $this->token;
43 | }
44 |
45 | /**
46 | * @param string $token
47 | */
48 | public function setToken(string $token)
49 | {
50 | $this->token = $token;
51 | }
52 |
53 | /**
54 | * @return string
55 | */
56 | public function getTeamId(): string
57 | {
58 | return $this->teamId;
59 | }
60 |
61 | /**
62 | * @param string $teamId
63 | */
64 | public function setTeamId(string $teamId)
65 | {
66 | $this->teamId = $teamId;
67 | }
68 |
69 | /**
70 | * @return string
71 | */
72 | public function getAppId(): string
73 | {
74 | return $this->appId;
75 | }
76 |
77 | /**
78 | * @param string $appId
79 | */
80 | public function setAppId(string $appId)
81 | {
82 | $this->appId = $appId;
83 | }
84 |
85 | /**
86 | * @throws \Exception
87 | *
88 | * @return Event
89 | */
90 | public function getEvent()
91 | {
92 | if (!isset($this->event)) {
93 | $this->loadEvent();
94 | }
95 |
96 | return $this->event;
97 | }
98 |
99 | /**
100 | * @param Event $event
101 | */
102 | public function setEvent(Event $event)
103 | {
104 | $this->event = $event;
105 | }
106 |
107 | /**
108 | * @throws \Exception
109 | */
110 | private function loadEvent()
111 | {
112 | $request = $this->getRequest();
113 | $eventKey = 'event';
114 |
115 | if (!isset($request[$eventKey])) {
116 | return;
117 | }
118 |
119 | $request = $request[$eventKey];
120 | if (!isset($request['type'])) {
121 | throw new BotonomousException(self::MISSING_EVENT_TYPE_MESSAGE);
122 | }
123 |
124 | // create the event
125 | $eventObject = new Event($request['type']);
126 |
127 | // exclude type from the args since it's already passed
128 | unset($request['type']);
129 |
130 | $stringUtility = new StringUtility();
131 | foreach ($request as $argKey => $argValue) {
132 | if (array_key_exists($argKey, $this->requestEventMaps)) {
133 | $argKey = $this->requestEventMaps[$argKey];
134 | }
135 |
136 | $setterName = 'set'.$stringUtility->snakeCaseToCamelCase($argKey);
137 |
138 | // ignore calling the method if setter does not exist
139 | if (!method_exists($eventObject, $setterName)) {
140 | continue;
141 | }
142 |
143 | $eventObject->$setterName($argValue);
144 | }
145 |
146 | // set it
147 | $this->setEvent($eventObject);
148 | }
149 |
150 | /**
151 | * @throws \Exception
152 | *
153 | * @return array
154 | */
155 | public function verifyOrigin()
156 | {
157 | $request = $this->getRequest();
158 |
159 | if (!isset($request['token']) || !isset($request['api_app_id'])) {
160 | return [
161 | parent::ORIGIN_VERIFICATION_SUCCESS_KEY => false,
162 | parent::ORIGIN_VERIFICATION_MESSAGE_KEY => self::MISSING_TOKEN_OR_APP_ID_MESSAGE,
163 | ];
164 | }
165 |
166 | $verificationToken = $this->getConfig()->get('verificationToken');
167 |
168 | if (empty($verificationToken)) {
169 | throw new BotonomousException('Verification token must be provided');
170 | }
171 |
172 | $expectedAppId = $this->getConfig()->get('appId');
173 |
174 | if (empty($expectedAppId)) {
175 | throw new BotonomousException(self::MISSING_APP_ID_MESSAGE);
176 | }
177 |
178 | if ($verificationToken === $request['token'] &&
179 | $expectedAppId === $request['api_app_id']) {
180 | return [
181 | parent::ORIGIN_VERIFICATION_SUCCESS_KEY => true,
182 | parent::ORIGIN_VERIFICATION_MESSAGE_KEY => 'O La la!',
183 | ];
184 | }
185 |
186 | return [
187 | parent::ORIGIN_VERIFICATION_SUCCESS_KEY => false,
188 | parent::ORIGIN_VERIFICATION_MESSAGE_KEY => 'Token or api_app_id mismatch',
189 | ];
190 | }
191 |
192 | /**
193 | * Check if the request belongs to the bot itself.
194 | *
195 | * @throws \Exception
196 | *
197 | * @return bool
198 | */
199 | public function isThisBot(): bool
200 | {
201 | $subType = $this->getRequest('subtype');
202 |
203 | if ($subType === 'bot_message') {
204 | return true;
205 | }
206 |
207 | $event = $this->getEvent();
208 |
209 | return $event instanceof Event && !empty($event->getBotId());
210 | }
211 |
212 | /**
213 | * @throws \Exception
214 | *
215 | * @return string
216 | */
217 | public function getChannelId(): string
218 | {
219 | return $this->getEvent()->getChannel();
220 | }
221 | }
222 |
--------------------------------------------------------------------------------
/src/Botonomous/listener/SlashCommandListener.php:
--------------------------------------------------------------------------------
1 | getRequestUtility()->getPost();
20 |
21 | if (empty($postRequest)) {
22 | /* @noinspection PhpInconsistentReturnPointsInspection */
23 | return;
24 | }
25 |
26 | return $postRequest;
27 | }
28 |
29 | /**
30 | * @throws \Exception
31 | *
32 | * @return array
33 | */
34 | public function verifyOrigin(): array
35 | {
36 | $token = $this->getRequest('token');
37 |
38 | if (empty($token)) {
39 | return [
40 | parent::ORIGIN_VERIFICATION_SUCCESS_KEY => false,
41 | parent::ORIGIN_VERIFICATION_MESSAGE_KEY => self::MISSING_TOKEN_MESSAGE,
42 | ];
43 | }
44 |
45 | $expectedToken = $this->getConfig()->get(self::VERIFICATION_TOKEN);
46 |
47 | if (empty($expectedToken)) {
48 | throw new BotonomousException(self::MISSING_TOKEN_CONFIG_MESSAGE);
49 | }
50 |
51 | if ($token === $expectedToken) {
52 | return [
53 | parent::ORIGIN_VERIFICATION_SUCCESS_KEY => true,
54 | parent::ORIGIN_VERIFICATION_MESSAGE_KEY => 'Awesome!',
55 | ];
56 | }
57 |
58 | return [
59 | parent::ORIGIN_VERIFICATION_SUCCESS_KEY => false,
60 | parent::ORIGIN_VERIFICATION_MESSAGE_KEY => 'Token is not valid',
61 | ];
62 | }
63 |
64 | /**
65 | * @throws \Exception
66 | *
67 | * @return bool
68 | */
69 | public function isThisBot(): bool
70 | {
71 | $userId = $this->getRequest('user_id');
72 | $username = $this->getRequest('user_name');
73 |
74 | return ($userId == 'USLACKBOT' || $username == 'slackbot') ? true : false;
75 | }
76 |
77 | /**
78 | * @return string
79 | */
80 | public function getChannelId(): string
81 | {
82 | return $this->getRequest('channel_id');
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/Botonomous/plugin/AbstractPlugin.php:
--------------------------------------------------------------------------------
1 | setSlackbot($slackbot);
28 | }
29 |
30 | /**
31 | * Return Botonomous.
32 | *
33 | * @return Slackbot
34 | */
35 | public function getSlackbot(): Slackbot
36 | {
37 | return $this->slackbot;
38 | }
39 |
40 | /**
41 | * Set Botonomous.
42 | *
43 | * @param Slackbot $slackbot
44 | */
45 | public function setSlackbot($slackbot)
46 | {
47 | $this->slackbot = $slackbot;
48 | }
49 |
50 | /**
51 | * @return Dictionary
52 | */
53 | public function getDictionary(): Dictionary
54 | {
55 | if (!isset($this->dictionary)) {
56 | $this->setDictionary((new Dictionary()));
57 | }
58 |
59 | return $this->dictionary;
60 | }
61 |
62 | /**
63 | * @param Dictionary $dictionary
64 | */
65 | public function setDictionary(Dictionary $dictionary)
66 | {
67 | $this->dictionary = $dictionary;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/Botonomous/plugin/PluginInterface.php:
--------------------------------------------------------------------------------
1 | getSlackbot()->getCommands();
22 |
23 | $response = '';
24 | if (!empty($allCommands)) {
25 | $formattingUtility = (new FormattingUtility());
26 |
27 | foreach ($allCommands as $commandName => $commandObject) {
28 | if (!$commandObject instanceof Command) {
29 | continue;
30 | }
31 |
32 | $response .= "/{$commandName} - ".$commandObject->getDescription().$formattingUtility->newLine();
33 | }
34 | }
35 |
36 | return $response;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/Botonomous/plugin/help/HelpConfig.php:
--------------------------------------------------------------------------------
1 | 'testConfigValue',
12 | ];
13 | }
14 |
--------------------------------------------------------------------------------
/src/Botonomous/plugin/ping/Ping.php:
--------------------------------------------------------------------------------
1 | getQuestions();
23 | $stringUtility = new StringUtility();
24 |
25 | if (empty($questions)) {
26 | return '';
27 | }
28 |
29 | foreach ($questions as $question => $questionInfo) {
30 | if ($stringUtility->findInString($question, $this->getSlackbot()->getListener()->getMessage())) {
31 | // found - return random answer
32 | $answers = $questionInfo['answers'];
33 |
34 | return $answers[array_rand($answers)];
35 | }
36 | }
37 |
38 | return '';
39 | }
40 |
41 | /**
42 | * @throws \Exception
43 | *
44 | * @return array
45 | */
46 | public function getQuestions(): array
47 | {
48 | if (!isset($this->questions)) {
49 | $this->setQuestions($this->getDictionary()->get('question-answer'));
50 | }
51 |
52 | return $this->questions;
53 | }
54 |
55 | /**
56 | * @param array $questions
57 | */
58 | public function setQuestions(array $questions)
59 | {
60 | $this->questions = $questions;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/Botonomous/public/.htaccess:
--------------------------------------------------------------------------------
1 |
2 | RewriteEngine On
3 |
4 | # redirect www to non www
5 | RewriteBase /
6 | RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC]
7 | RewriteRule ^(.*)$ http://%1/$1 [R=301,L]
8 |
9 | # redirect all to index.php
10 | RewriteCond %{REQUEST_FILENAME} !-d
11 | RewriteCond %{REQUEST_FILENAME} !-f
12 | RewriteRule ^([^?]*)$ index.php?path=$1 [NC,L,QSA]
13 |
14 |
--------------------------------------------------------------------------------
/src/Botonomous/public/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iranianpep/botonomous/294c1395ec1e86d1784b5c134f195b7b2024e0ee/src/Botonomous/public/favicon.png
--------------------------------------------------------------------------------
/src/Botonomous/public/index.php:
--------------------------------------------------------------------------------
1 | run();
21 | } catch (Exception $e) {
22 | echo $e->getMessage();
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Botonomous/utility/AbstractUtility.php:
--------------------------------------------------------------------------------
1 | setConfig($config);
23 | }
24 | }
25 |
26 | /**
27 | * @return Config
28 | */
29 | public function getConfig(): Config
30 | {
31 | if ($this->config === null) {
32 | $this->config = new Config();
33 | }
34 |
35 | return $this->config;
36 | }
37 |
38 | /**
39 | * @param Config $config
40 | */
41 | public function setConfig(Config $config)
42 | {
43 | $this->config = $config;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/Botonomous/utility/ArrayUtility.php:
--------------------------------------------------------------------------------
1 | 0;
39 | }
40 |
41 | return true;
42 | }
43 |
44 | /**
45 | * Set a value in a nested array based on path.
46 | *
47 | * @param array $array The array to modify
48 | * @param array $path The path in the array
49 | * @param mixed $value The value to set
50 | *
51 | * @return void
52 | */
53 | public function setNestedArrayValue(array &$array, array $path, &$value)
54 | {
55 | $current = &$array;
56 | foreach ($path as $key) {
57 | $current = &$current[$key];
58 | }
59 |
60 | $current = $value;
61 | }
62 |
63 | /**
64 | * Get a value in a nested array based on path
65 | * See https://stackoverflow.com/a/9628276/419887.
66 | *
67 | * @param array $array The array to modify
68 | * @param array $path The path in the array
69 | *
70 | * @return mixed
71 | */
72 | public function getNestedArrayValue(array &$array, array $path)
73 | {
74 | $current = &$array;
75 | foreach ($path as $key) {
76 | $current = &$current[$key];
77 | }
78 |
79 | return $current;
80 | }
81 |
82 | /**
83 | * @param $array
84 | *
85 | * @return mixed
86 | */
87 | public function sortArrayByLength(array $array)
88 | {
89 | usort($array, function ($array1, $array2) {
90 | return strlen($array2) <=> strlen($array1);
91 | });
92 |
93 | return $array;
94 | }
95 |
96 | /**
97 | * Find the key for the max positive value.
98 | *
99 | * @param $array
100 | *
101 | * @return mixed
102 | */
103 | public function maxPositiveValueKey(array $array)
104 | {
105 | $maxValue = max($array);
106 |
107 | if ($maxValue > 0) {
108 | return array_search($maxValue, $array);
109 | }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/Botonomous/utility/ClassUtility.php:
--------------------------------------------------------------------------------
1 | $attributeValue) {
39 | $method = $this->getSetMethodByAttribute($object, $attributeKey);
40 |
41 | // ignore if setter function does not exist
42 | if (empty($method)) {
43 | continue;
44 | }
45 |
46 | $object->$method($attributeValue);
47 | }
48 |
49 | return $object;
50 | }
51 |
52 | /**
53 | * @param AbstractBaseSlack $object
54 | * @param $attributeKey
55 | *
56 | * @return bool|string
57 | */
58 | private function getSetMethodByAttribute(AbstractBaseSlack $object, string $attributeKey)
59 | {
60 | // For id, we cannot use 'set'.$stringUtility->snakeCaseToCamelCase($attributeKey) since it's named slackId
61 | if ($attributeKey === 'id') {
62 | return 'setSlackId';
63 | }
64 |
65 | // handle ts because there is setTimestamp instead of setTs
66 | $camelCase = (new StringUtility())->snakeCaseToCamelCase($this->processTimestamp($attributeKey));
67 |
68 | /**
69 | * If camel case attribute starts with 'is', 'has', ... following by an uppercase letter, remove it
70 | * This is used to handle calling functions such as setIm or setUserDeleted
71 | * instead of setIsIm or setIsUserDeleted.
72 | *
73 | * The style checkers complain about functions such as setIsIm, ...
74 | */
75 | $function = 'set'.$this->removeBooleanPrefix($camelCase);
76 |
77 | return method_exists($object, $function) ? $function : false;
78 | }
79 |
80 | /**
81 | * If text is 'ts' or ends with '_ts' replace it with 'timestamp'.
82 | *
83 | * @param $text
84 | *
85 | * @return mixed
86 | */
87 | private function processTimestamp(string $text): string
88 | {
89 | if ($text === 'ts' || (new StringUtility())->endsWith($text, '_ts')) {
90 | // replace the last ts with timestamp
91 | $text = preg_replace('/ts$/', 'timestamp', $text);
92 | }
93 |
94 | return $text;
95 | }
96 |
97 | /**
98 | * Check if the text starts with boolean prefixes such as 'is', 'has', ...
99 | *
100 | * @param $text
101 | *
102 | * @return mixed
103 | */
104 | private function findBooleanPrefix(string $text)
105 | {
106 | $booleanPrefixes = ['is', 'has'];
107 | sort($booleanPrefixes);
108 |
109 | foreach ($booleanPrefixes as $booleanPrefix) {
110 | if (!preg_match('/^((?i)'.$booleanPrefix.')[A-Z0-9]/', $text)) {
111 | continue;
112 | }
113 |
114 | return $booleanPrefix;
115 | }
116 | }
117 |
118 | /**
119 | * If find the boolean prefix, remove it.
120 | *
121 | * @param $text
122 | *
123 | * @return string
124 | */
125 | private function removeBooleanPrefix(string $text): string
126 | {
127 | $booleanPrefix = $this->findBooleanPrefix($text);
128 | if (!empty($booleanPrefix)) {
129 | // found the boolean prefix - remove it
130 | $text = substr($text, strlen($booleanPrefix));
131 | }
132 |
133 | return $text;
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/src/Botonomous/utility/FileUtility.php:
--------------------------------------------------------------------------------
1 | jsonToArray(file_get_contents($filePath));
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/Botonomous/utility/FormattingUtility.php:
--------------------------------------------------------------------------------
1 | tokenize($text);
22 |
23 | $stemmed = (new PorterStemmer())->stemAll($tokens);
24 |
25 | return implode(' ', $stemmed);
26 | }
27 |
28 | /**
29 | * @param $text
30 | * @param string $language
31 | *
32 | * @throws \Exception
33 | *
34 | * @return string
35 | */
36 | public function removeStopWords($text, $language = 'en')
37 | {
38 | $stopWords = (new Dictionary())->get('stopwords-'.$language);
39 |
40 | $words = explode(' ', $text);
41 | if (!empty($words)) {
42 | foreach ($words as $key => $word) {
43 | if (in_array($word, $stopWords)) {
44 | unset($words[$key]);
45 | }
46 | }
47 | }
48 |
49 | return implode(' ', $words);
50 | }
51 |
52 | /**
53 | * @param $text
54 | *
55 | * @throws \Exception
56 | *
57 | * @return string
58 | */
59 | public function removePunctuations($text)
60 | {
61 | $punctuations = (new Dictionary())->get('punctuations');
62 |
63 | return str_replace($punctuations, '', $text);
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/Botonomous/utility/MessageUtility.php:
--------------------------------------------------------------------------------
1 | getUserLink();
25 |
26 | return preg_replace("/{$userLink}/", '', $message, 1);
27 | }
28 |
29 | /**
30 | * Check if the bot user id is mentioned in the message.
31 | *
32 | * @param $message
33 | *
34 | * @throws \Exception
35 | *
36 | * @return bool
37 | */
38 | public function isBotMentioned(string $message): bool
39 | {
40 | $userLink = $this->getUserLink();
41 |
42 | return (new StringUtility())->findInString($userLink, $message, false);
43 | }
44 |
45 | /**
46 | * Return command name in the message.
47 | *
48 | * @param string $message
49 | *
50 | * @throws \Exception
51 | *
52 | * @return null|string
53 | */
54 | public function extractCommandName(string $message)
55 | {
56 | // remove the bot mention if it exists
57 | $message = $this->removeMentionedBot($message);
58 |
59 | /**
60 | * Command must start with / and at the beginning of the sentence.
61 | */
62 | $commandPrefix = $this->getConfig()->get('commandPrefix');
63 | $commandPrefix = preg_quote($commandPrefix, '/');
64 |
65 | $pattern = '/^('.$commandPrefix.'\w{1,})/';
66 |
67 | preg_match($pattern, ltrim($message), $groups);
68 |
69 | // If command is found, remove command prefix from the beginning of the command
70 | return isset($groups[1]) ? ltrim($groups[1], $commandPrefix) : null;
71 | }
72 |
73 | /**
74 | * Return command details in the message.
75 | *
76 | * @param string $message
77 | *
78 | * @throws \Exception
79 | *
80 | * @return \Botonomous\Command|null
81 | */
82 | public function extractCommandDetails(string $message)
83 | {
84 | // first get the command name
85 | $command = $this->extractCommandName($message);
86 |
87 | // then get the command details
88 | return (new CommandContainer())->getAsObject($command);
89 | }
90 |
91 | /**
92 | * @param $triggerWord
93 | * @param $message
94 | *
95 | * @return string
96 | */
97 | public function removeTriggerWord(string $message, string $triggerWord)
98 | {
99 | $count = 1;
100 |
101 | return ltrim(str_replace($triggerWord, '', $message, $count));
102 | }
103 |
104 | /**
105 | * @param $userId
106 | * @param string $userName
107 | *
108 | * @throws \Exception
109 | *
110 | * @return string
111 | */
112 | public function linkToUser(string $userId, string $userName = '')
113 | {
114 | if (empty($userId)) {
115 | throw new BotonomousException('User id is not provided');
116 | }
117 |
118 | if (!empty($userName)) {
119 | $userName = "|{$userName}";
120 | }
121 |
122 | return "<@{$userId}{$userName}>";
123 | }
124 |
125 | /**
126 | * @throws \Exception
127 | *
128 | * @return string
129 | */
130 | private function getUserLink(): string
131 | {
132 | return $this->linkToUser($this->getConfig()->get('botUserId'));
133 | }
134 |
135 | /**
136 | * @param array $keywords
137 | * @param $message
138 | *
139 | * @return array
140 | */
141 | public function keywordPos(array $keywords, string $message): array
142 | {
143 | $found = [];
144 | if (empty($keywords)) {
145 | return $found;
146 | }
147 |
148 | foreach ((new ArrayUtility())->sortArrayByLength($keywords) as $keyword) {
149 | foreach ((new StringUtility())->findPositionInString($keyword, $message) as $position) {
150 | // check if the keyword does not overlap with one of the already found
151 | if ($this->isPositionTaken($found, $position) === false) {
152 | $found[$keyword][] = $position;
153 | }
154 | }
155 | }
156 |
157 | return $found;
158 | }
159 |
160 | /**
161 | * @param array $keywords
162 | * @param $message
163 | *
164 | * @return array|void
165 | */
166 | public function keywordCount(array $keywords, string $message)
167 | {
168 | $keysPositions = $this->keywordPos($keywords, $message);
169 |
170 | if (empty($keysPositions)) {
171 | return;
172 | }
173 |
174 | foreach ($keysPositions as $key => $positions) {
175 | $keysPositions[$key] = count($positions);
176 | }
177 |
178 | return $keysPositions;
179 | }
180 |
181 | /**
182 | * @param array $tokensPositions
183 | * @param $newPosition
184 | *
185 | * @return bool
186 | */
187 | private function isPositionTaken(array $tokensPositions, int $newPosition)
188 | {
189 | if (empty($tokensPositions)) {
190 | return false;
191 | }
192 |
193 | foreach ($tokensPositions as $token => $positions) {
194 | $tokenLength = strlen($token);
195 | if ($this->isPositionIn($newPosition, $positions, $tokenLength) === true) {
196 | return true;
197 | }
198 | }
199 |
200 | return false;
201 | }
202 |
203 | /**
204 | * @param $newPosition
205 | * @param $positions
206 | * @param $tokenLength
207 | *
208 | * @return bool
209 | */
210 | private function isPositionIn(int $newPosition, array $positions, int $tokenLength)
211 | {
212 | foreach ($positions as $position) {
213 | if ($newPosition >= $position && $newPosition < $position + $tokenLength) {
214 | return true;
215 | }
216 | }
217 |
218 | return false;
219 | }
220 | }
221 |
--------------------------------------------------------------------------------
/src/Botonomous/utility/RequestUtility.php:
--------------------------------------------------------------------------------
1 | content)) {
20 | return $this->content;
21 | }
22 |
23 | return file_get_contents('php://input');
24 | }
25 |
26 | /**
27 | * @param $content
28 | */
29 | public function setContent(string $content)
30 | {
31 | $this->content = $content;
32 | }
33 |
34 | /**
35 | * @return mixed
36 | */
37 | public function getPost()
38 | {
39 | if (isset($this->post)) {
40 | return $this->post;
41 | }
42 |
43 | return filter_input_array(INPUT_POST);
44 | }
45 |
46 | /**
47 | * @return mixed
48 | */
49 | public function getPostedBody()
50 | {
51 | $body = $this->getContent();
52 |
53 | if (empty($body)) {
54 | /* @noinspection PhpInconsistentReturnPointsInspection */
55 | return;
56 | }
57 |
58 | return json_decode($body, true);
59 | }
60 |
61 | /**
62 | * @param array $post
63 | */
64 | public function setPost(array $post)
65 | {
66 | $this->post = $post;
67 | }
68 |
69 | /**
70 | * @return mixed
71 | */
72 | public function getGet()
73 | {
74 | if (isset($this->get)) {
75 | return $this->get;
76 | }
77 |
78 | return filter_input_array(INPUT_GET);
79 | }
80 |
81 | /**
82 | * @param array $get
83 | */
84 | public function setGet(array $get)
85 | {
86 | $this->get = $get;
87 | }
88 |
89 | /**
90 | * @return mixed
91 | */
92 | public function getServerProtocol()
93 | {
94 | return filter_input(INPUT_SERVER, 'SERVER_PROTOCOL', FILTER_SANITIZE_STRING);
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/Botonomous/utility/SecurityUtility.php:
--------------------------------------------------------------------------------
1 | getHashAlgorithm(), uniqid(mt_rand(), true));
26 | }
27 |
28 | /**
29 | * @param string $hashAlgorithm
30 | *
31 | * @throws \Exception
32 | */
33 | public function setHashAlgorithm(string $hashAlgorithm)
34 | {
35 | /*
36 | * check if hashing algorithm is valid
37 | */
38 | if (!in_array($hashAlgorithm, hash_algos(), true)) {
39 | throw new BotonomousException('Hash algorithm is not valid');
40 | }
41 |
42 | $this->hashAlgorithm = $hashAlgorithm;
43 | }
44 |
45 | /**
46 | * @throws \Exception
47 | *
48 | * @return string
49 | */
50 | public function getHashAlgorithm(): string
51 | {
52 | if (empty($this->hashAlgorithm)) {
53 | $this->setHashAlgorithm(self::DEFAULT_HASH_ALGORITHM);
54 | }
55 |
56 | return $this->hashAlgorithm;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/Botonomous/utility/SessionUtility.php:
--------------------------------------------------------------------------------
1 | getSession();
44 | $session[$key] = $value;
45 | $this->setSession($session);
46 | }
47 |
48 | /**
49 | * @param $key
50 | *
51 | * @return mixed
52 | */
53 | public function get($key)
54 | {
55 | $session = $this->getSession();
56 |
57 | if (!isset($session[$key])) {
58 | /* @noinspection PhpInconsistentReturnPointsInspection */
59 | return;
60 | }
61 |
62 | return $session[$key];
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/Botonomous/utility/StringUtility.php:
--------------------------------------------------------------------------------
1 | getFindPattern($toFind, $wordBoundary);
62 |
63 | return preg_match($pattern, $subject) ? true : false;
64 | }
65 |
66 | /**
67 | * @param string $toFind
68 | * @param string $subject
69 | * @param bool $wordBoundary
70 | *
71 | * @return mixed
72 | */
73 | public function findPositionInString(string $toFind, string $subject, bool $wordBoundary = true)
74 | {
75 | $pattern = $this->getFindPattern($toFind, $wordBoundary);
76 | $positions = [];
77 | if (preg_match_all($pattern, $subject, $matches, PREG_OFFSET_CAPTURE) && !empty($matches[0])) {
78 | foreach ($matches[0] as $match) {
79 | $positions[] = $match[1];
80 | }
81 | }
82 |
83 | return $positions;
84 | }
85 |
86 | /**
87 | * Convert snake case to camel case e.g. admin_user becomes AdminUser.
88 | *
89 | * @param string $string
90 | *
91 | * @return string
92 | */
93 | public function snakeCaseToCamelCase(string $string)
94 | {
95 | return str_replace(' ', '', ucwords(str_replace('_', ' ', $string)));
96 | }
97 |
98 | /**
99 | * Check subject to see whether $string1 is followed by $string2.
100 | *
101 | * @param string $subject
102 | * @param string $string1
103 | * @param string $string2
104 | * @param array $exceptions
105 | *
106 | * @return bool
107 | */
108 | public function isString1FollowedByString2(
109 | string $subject,
110 | string $string1,
111 | string $string2,
112 | array $exceptions = []
113 | ): bool {
114 | $exceptionsString = '';
115 | if (!empty($exceptions)) {
116 | $exceptions = implode('|', $exceptions);
117 | $exceptionsString = "(? $value) {
158 | $subject = str_replace('{'.$key.'}', $value, $subject);
159 | }
160 |
161 | return $subject;
162 | }
163 | }
164 |
--------------------------------------------------------------------------------
/tests/Botonomous/ActionTest.php:
--------------------------------------------------------------------------------
1 | load($info);
17 |
18 | $this->assertEquals('Springfield, MA, USA', $action->getName());
19 | $this->assertEquals('button', $action->getType());
20 | $this->assertEquals('Springfield, MA, USA', $action->getValue());
21 | $this->assertEmpty($action->getText());
22 | }
23 |
24 | public function testGetText()
25 | {
26 | $action = new Action();
27 | $action->setText('blah blah');
28 |
29 | $this->assertEquals('blah blah', $action->getText());
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/tests/Botonomous/CommandContainerTest.php:
--------------------------------------------------------------------------------
1 | getAsObject('ping');
21 |
22 | $this->assertTrue($commandObject instanceof Command);
23 |
24 | /* @noinspection PhpUndefinedMethodInspection */
25 | $this->assertEquals($commandObject->getPlugin(), 'Ping');
26 | }
27 |
28 | /**
29 | * Test getAll.
30 | */
31 | public function testGetAllAsObject()
32 | {
33 | $commands = (new CommandContainer())->getAllAsObject();
34 | /* @noinspection PhpUndefinedMethodInspection */
35 | $this->assertEquals($commands['ping']->getPlugin(), 'Ping');
36 | }
37 |
38 | /**
39 | * Test getAll.
40 | */
41 | public function testGetAllAsObjectUnknownSetter()
42 | {
43 | $commands = new CommandContainer();
44 | $allCommands = $commands->getAll();
45 |
46 | // set unknown attribute / property
47 | $allCommands['ping']['testKey'] = 'testValue';
48 |
49 | $commands->setAll($allCommands);
50 |
51 | $commandObjects = $commands->getAllAsObject();
52 |
53 | /* @noinspection PhpUndefinedMethodInspection */
54 | $this->assertEquals($commandObjects['ping']->getPlugin(), 'Ping');
55 | }
56 |
57 | /**
58 | * Test getAll.
59 | */
60 | public function testGetAllAsObjectEmpty()
61 | {
62 | $commands = new CommandContainer();
63 |
64 | $allCommands = $commands->getAll();
65 |
66 | $commands->setAll([]);
67 |
68 | /* @noinspection PhpUndefinedMethodInspection */
69 | $this->assertEquals([], $commands->getAllAsObject());
70 |
71 | // reset the command container
72 | $commands->setAll($allCommands);
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/tests/Botonomous/CommandTest.php:
--------------------------------------------------------------------------------
1 | assertEquals(Command::DEFAULT_ACTION, (new Command(self::PING_KEY))->getAction());
16 | }
17 |
18 | public function testGetKey()
19 | {
20 | $this->assertEquals(self::PING_KEY, (new Command(self::PING_KEY))->getKey());
21 | }
22 |
23 | public function testGetKeywords()
24 | {
25 | $command = new Command(self::PING_KEY);
26 | $keywords = ['help'];
27 | $command->setKeywords($keywords);
28 |
29 | $this->assertEquals($keywords, $command->getKeywords());
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/tests/Botonomous/ConfigTest.php:
--------------------------------------------------------------------------------
1 | set('testKey', 'testValue');
21 |
22 | $this->assertEquals('testValue', $config->get('testKey'));
23 | }
24 |
25 | public function testGetByPlugin()
26 | {
27 | $config = new Config();
28 | $result = $config->get('testConfigKey', [], 'help');
29 |
30 | $this->assertEquals('testConfigValue', $result);
31 | }
32 |
33 | public function testGetByInvalidPlugin()
34 | {
35 | $config = new Config();
36 |
37 | $this->expectException('\Exception');
38 | $this->expectExceptionMessage(
39 | "Config file: 'Botonomous\\plugin\\dummy\\DummyConfig.php' does not exist"
40 | );
41 |
42 | $config->get('testConfigKey', [], 'dummy');
43 | }
44 |
45 | /**
46 | * @throws \Exception
47 | */
48 | public function testGetWithReplace()
49 | {
50 | $config = new Config();
51 | $config->set('testKeyReplace', 'testValue {replaceIt}');
52 |
53 | $this->assertEquals(
54 | 'testValue replaced',
55 | $config->get('testKeyReplace', ['replaceIt' => 'replaced'])
56 | );
57 | }
58 |
59 | /**
60 | * Test getExceptException.
61 | */
62 | public function testGetExceptException()
63 | {
64 | try {
65 | (new Config())->get('dummyKey');
66 | } catch (\Exception $e) {
67 | $this->assertEquals('Key: \'dummyKey\' does not exist in configs', $e->getMessage());
68 | }
69 | }
70 |
71 | /**
72 | * @throws \Exception
73 | */
74 | public function testSet()
75 | {
76 | $config = new Config();
77 |
78 | $config->set('testKey', 'testNewValue');
79 |
80 | $this->assertEquals('testNewValue', $config->get('testKey'));
81 |
82 | $config->set('testConfigKey', 'testConfigValueEdited', 'help');
83 |
84 | $this->assertEquals('testConfigValueEdited', $config->get('testConfigKey', [], 'help'));
85 |
86 | // reset config
87 | $config->set('testConfigKey', 'testConfigValue', 'help');
88 | }
89 |
90 | /**
91 | * @throws \Exception
92 | */
93 | public function testSetExceptException()
94 | {
95 | $this->expectException('\Exception');
96 | $invalidPluginPath = 'Botonomous\plugin\dummyinvalidplugin\DummyInvalidPluginConfig.php';
97 | $this->expectExceptionMessage(
98 | "Config file: '{$invalidPluginPath}' does not exist"
99 | );
100 |
101 | $config = new Config();
102 | $config->set('testConfigKey', 'testConfigValueEdited', 'dummyInvalidPlugin');
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/tests/Botonomous/DictionaryTest.php:
--------------------------------------------------------------------------------
1 | assertEquals($expected, $dictionary->get('test'));
26 |
27 | // get it again to check load only is called once
28 | $this->assertEquals($expected, $dictionary->get('test'));
29 | }
30 |
31 | /**
32 | * Test getValueByKey.
33 | */
34 | public function testGetValueByKey()
35 | {
36 | $dictionary = new Dictionary();
37 |
38 | $this->assertEquals('testValue', $dictionary->getValueByKey('test-key-value', 'testKey'));
39 | }
40 |
41 | /**
42 | * Test getValueByKey.
43 | */
44 | public function testGetValueByInvalidKey()
45 | {
46 | $dictionary = new Dictionary();
47 |
48 | $this->expectException('\Exception');
49 | $this->expectExceptionMessage("Key: 'testInvalidKey' does not exist in file: test-key-value");
50 |
51 | $this->assertEquals('testValue', $dictionary->getValueByKey('test-key-value', 'testInvalidKey'));
52 | }
53 |
54 | /**
55 | * Test getValueByKey.
56 | */
57 | public function testGetValueByKeyWithReplacements()
58 | {
59 | $dictionary = new Dictionary();
60 |
61 | $this->assertEquals(
62 | 'testValue dummy user',
63 | $dictionary->getValueByKey(
64 | 'test-key-value',
65 | 'testKeyWithReplacements',
66 | ['user' => 'dummy user']
67 | )
68 | );
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/tests/Botonomous/EventTest.php:
--------------------------------------------------------------------------------
1 | setBotId('B123');
22 |
23 | $this->assertEquals('B123', $event->getBotId());
24 | }
25 |
26 | /**
27 | * @throws \Exception
28 | */
29 | public function testIsDirectMessageEmpty()
30 | {
31 | $event = new Event('message');
32 | $this->assertEmpty($event->isDirectMessage());
33 | }
34 |
35 | /**
36 | * Test isDirectMessageEmpty.
37 | */
38 | public function testIsDirectMessage()
39 | {
40 | $event = new Event('message');
41 | $event->setChannel('D024BE7RE');
42 |
43 | $apiClientTest = new ApiClientTest();
44 | $apiClient = $apiClientTest->getApiClient($this->getDummyImListResponse());
45 |
46 | $event->setApiClient($apiClient);
47 |
48 | $this->assertEquals(true, $event->isDirectMessage());
49 | }
50 |
51 | /**
52 | * Test isDirectMessageEmpty.
53 | */
54 | public function testIsNotDirectMessage()
55 | {
56 | $event = new Event('message');
57 |
58 | $apiClientTest = new ApiClientTest();
59 | $apiClient = $apiClientTest->getApiClient($this->getDummyImListResponse());
60 |
61 | $event->setApiClient($apiClient);
62 |
63 | $this->assertEmpty($event->isDirectMessage());
64 | }
65 |
66 | /**
67 | * Return im list dummy response content.
68 | */
69 | private function getDummyImListResponse()
70 | {
71 | return '{"ok": true, "ims":[{"id": "D024BFF1M", "is_im": true, "user": "USLACKBOT", "created": 1372105335,
72 | "is_user_deleted": false }, { "id": "D024BE7RE", "is_im": true, "user": "U024BE7LH", "created": 1356250715,
73 | "is_user_deleted": false}]}';
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/tests/Botonomous/ImChannelTest.php:
--------------------------------------------------------------------------------
1 | getImChannel();
15 | $this->assertFalse($imChannel->isIm());
16 | }
17 |
18 | public function testIsUserDeleted()
19 | {
20 | $imChannel = $this->getImChannel();
21 | $this->assertTrue($imChannel->isUserDeleted());
22 | }
23 |
24 | public function testGetCreated()
25 | {
26 | $imChannel = $this->getImChannel();
27 | $this->assertEquals('1372105335', $imChannel->getCreated());
28 | }
29 |
30 | private function getImChannel()
31 | {
32 | $imChannel = new ImChannel();
33 |
34 | $imChannel->setIm(false);
35 | $imChannel->setUserDeleted(true);
36 | $imChannel->setCreated('1372105335');
37 |
38 | return $imChannel;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/tests/Botonomous/MessageActionTest.php:
--------------------------------------------------------------------------------
1 | load($this->getInfo());
13 |
14 | $actions = [];
15 | $action = new Action();
16 | $action->setName('recommend');
17 | $action->setValue('yes');
18 | $action->setType('button');
19 | $actions[] = $action;
20 |
21 | $this->assertEquals($actions, $messageAction->getActions());
22 |
23 | $this->assertEquals('comic_1234_xyz', $messageAction->getCallbackId());
24 |
25 | $team = new Team();
26 | $team->setSlackId('T47563693');
27 | $team->setDomain('watermelonsugar');
28 |
29 | $this->assertEquals($team, $messageAction->getTeam());
30 |
31 | $channel = new Channel();
32 | $channel->setSlackId('C065W1189');
33 | $channel->setName('forgotten-works');
34 |
35 | $this->assertEquals($channel, $messageAction->getChannel());
36 |
37 | $user = new User();
38 | $user->setSlackId('U045VRZFT');
39 | $user->setName('brautigan');
40 |
41 | $this->assertEquals($user, $messageAction->getUser());
42 |
43 | $this->assertEquals('1458170917.164398', $messageAction->getActionTimestamp());
44 | $this->assertEquals('1458170866.000004', $messageAction->getMessageTimestamp());
45 | $this->assertEquals('1', $messageAction->getAttachmentId());
46 | $this->assertEquals('xAB3yVzGS4BQ3O9FACTa8Ho4', $messageAction->getToken());
47 | $this->assertEquals($this->getInfo('original_message'), $messageAction->getOriginalMessage());
48 | $this->assertEquals(
49 | 'https://hooks.slack.com/actions/T47563693/6204672533/x7ZLaiVMoECAW50Gw1ZYAXEM',
50 | $messageAction->getResponseUrl()
51 | );
52 | }
53 |
54 | public function testGetTeam()
55 | {
56 | $messageAction = new MessageAction();
57 |
58 | $team = new Team();
59 | $team->load($this->getInfo('team'));
60 |
61 | $messageAction->setTeam($team);
62 |
63 | $this->assertEquals($team, $messageAction->getTeam());
64 | }
65 |
66 | public function testGeChannel()
67 | {
68 | $messageAction = new MessageAction();
69 |
70 | $channel = new Channel();
71 | $channel->load($this->getInfo('channel'));
72 |
73 | $messageAction->setChannel($channel);
74 |
75 | $this->assertEquals($channel, $messageAction->getChannel());
76 | }
77 |
78 | public function testGeUser()
79 | {
80 | $messageAction = new MessageAction();
81 |
82 | $user = new User();
83 | $user->load($this->getInfo('user'));
84 |
85 | $messageAction->setUser($user);
86 |
87 | $this->assertEquals($user, $messageAction->getUser());
88 | }
89 |
90 | public function testAddAction()
91 | {
92 | $messageAction = new MessageAction();
93 |
94 | $action = new Action();
95 | $action->setType('button');
96 |
97 | $messageAction->addAction($action);
98 | $messageAction->addAction($action);
99 |
100 | $actions = [$action, $action];
101 |
102 | $this->assertEquals($actions, $messageAction->getActions());
103 | }
104 |
105 | private function getInfo($key = null)
106 | {
107 | $originalMessage = [
108 | 'text' => 'New comic book alert!',
109 | 'attachments' => [
110 | 0 => [
111 | 'title' => 'The Further Adventures of Botonomous',
112 | 'fields' => [
113 | 0 => [
114 | 'title' => 'Volume',
115 | 'value' => '1',
116 | 'short' => true,
117 | ],
118 | 1 => [
119 | 'title' => 'Issue',
120 | 'value' => '3',
121 | 'short' => true,
122 | ],
123 | ],
124 | 'author_name' => 'Stanford S. Strickland',
125 | 'author_icon' => 'https://a.slack-edge.com/homepage_custom_integrations-2x.png',
126 | 'image_url' => 'http://i.imgur.com/OJkaVOI.jpg?1',
127 | ],
128 | 1 => [
129 | 'title' => 'Synopsis',
130 | 'text' => 'After @episod pushed exciting changes ...',
131 | ],
132 | 2 => [
133 | 'fallback' => 'Would you recommend it to customers?',
134 | 'title' => 'Would you recommend it to customers?',
135 | 'callback_id' => 'comic_1234_xyz',
136 | 'color' => '#3AA3E3',
137 | 'attachment_type' => 'default',
138 | 'actions' => [
139 | 0 => [
140 | 'name' => 'recommend',
141 | 'text' => 'Recommend',
142 | 'type' => 'button',
143 | 'value' => 'recommend',
144 | ],
145 | 1 => [
146 | 'name' => 'no',
147 | 'text' => 'No',
148 | 'type' => 'button',
149 | 'value' => 'bad',
150 | ],
151 | ],
152 | ],
153 | ],
154 | ];
155 |
156 | $info = [
157 | 'actions' => [
158 | 0 => [
159 | 'name' => 'recommend',
160 | 'value' => 'yes',
161 | 'type' => 'button',
162 | ],
163 | ],
164 | 'callback_id' => 'comic_1234_xyz',
165 | 'team' => [
166 | 'id' => 'T47563693',
167 | 'domain' => 'watermelonsugar',
168 | ],
169 | 'channel' => [
170 | 'id' => 'C065W1189',
171 | 'name' => 'forgotten-works',
172 | ],
173 | 'user' => [
174 | 'id' => 'U045VRZFT',
175 | 'name' => 'brautigan',
176 | ],
177 | 'action_ts' => '1458170917.164398',
178 | 'message_ts' => '1458170866.000004',
179 | 'attachment_id' => '1',
180 | 'token' => 'xAB3yVzGS4BQ3O9FACTa8Ho4',
181 | 'original_message' => $originalMessage,
182 | 'response_url' => 'https://hooks.slack.com/actions/T47563693/6204672533/x7ZLaiVMoECAW50Gw1ZYAXEM',
183 | ];
184 |
185 | if ($key !== null) {
186 | return $info[$key];
187 | }
188 |
189 | return $info;
190 | }
191 | }
192 |
--------------------------------------------------------------------------------
/tests/Botonomous/PhpunitHelper.php:
--------------------------------------------------------------------------------
1 | get('commandPrefix');
15 |
16 | /**
17 | * Form the request.
18 | */
19 | $botUsername = '@'.$config->get('botUsername');
20 | $request = [
21 | 'token' => $config->get(self::VERIFICATION_TOKEN),
22 | 'text' => "{$botUsername} {$commandPrefix}{$command}{$text}",
23 | ];
24 |
25 | $slackbot = new Slackbot();
26 |
27 | // get listener
28 | $listener = $slackbot->getListener();
29 |
30 | // set request
31 | $listener->setRequest($request);
32 |
33 | return $slackbot;
34 | }
35 |
36 | public function getDictionaryData($listKey)
37 | {
38 | return [
39 | 'access-control' => [
40 | $listKey => [
41 | 'username' => [
42 | 'dummyUserName',
43 | ],
44 | 'userId' => [
45 | 'dummyUserId',
46 | ],
47 | ],
48 | ],
49 | ];
50 | }
51 |
52 | public function getRequest()
53 | {
54 | return [
55 | 'user_name' => 'dummyUserName',
56 | 'user_id' => 'dummyUserId',
57 | ];
58 | }
59 |
60 | public function getWhiteList()
61 | {
62 | return new WhiteList($this->getRequest());
63 | }
64 |
65 | public function getBlackList()
66 | {
67 | return new BlackList($this->getRequest());
68 | }
69 |
70 | public function getUserInfoClient()
71 | {
72 | return (new ApiClientTest())->getApiClient('{
73 | "ok": true,
74 | "user": {
75 | "id": "U023BECGF",
76 | "name": "bobby",
77 | "deleted": false,
78 | "color": "9f69e7",
79 | "profile": {
80 | "first_name": "Bobby",
81 | "last_name": "Tables",
82 | "real_name": "Bobby Tables",
83 | "email": "bobby@slack.com",
84 | "skype": "my-skype-name",
85 | "phone": "+1 (123) 456 7890",
86 | "image_24": "https:\/\/...",
87 | "image_32": "https:\/\/...",
88 | "image_48": "https:\/\/...",
89 | "image_72": "https:\/\/...",
90 | "image_192": "https:\/\/..."
91 | },
92 | "is_admin": true,
93 | "is_owner": true,
94 | "has_2fa": true
95 | }
96 | }');
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/tests/Botonomous/SenderTest.php:
--------------------------------------------------------------------------------
1 | setLogFile();
31 |
32 | $slackbot = new Slackbot($config);
33 |
34 | /**
35 | * Overwrite the slackbot.
36 | */
37 | $request = [
38 | 'token' => $config->get(self::VERIFICATION_TOKEN),
39 | 'user_id' => 'dummyId',
40 | 'user_name' => 'dummyUsername',
41 | 'debug' => $debug,
42 | ];
43 |
44 | // get listener
45 | $listener = $slackbot->getListener();
46 |
47 | // set request
48 | $listener->setRequest($request);
49 |
50 | $slackbot->setConfig($config);
51 |
52 | return $slackbot;
53 | }
54 |
55 | /**
56 | * Test send.
57 | */
58 | public function testSendDebug()
59 | {
60 | $sender = new Sender($this->getSlackbot());
61 |
62 | $sender->send('test response 2', '#dummyChannel', []);
63 |
64 | $response = '{"text":"test response 2","channel":"#dummyChannel","attachments":"[]"}';
65 |
66 | $this->expectOutputString($response);
67 | }
68 |
69 | /**
70 | * Test send.
71 | */
72 | public function testSendSlackJson()
73 | {
74 | $sender = new Sender($this->getSlackbot());
75 |
76 | $sender->send('test response 3', '#dummyChannel');
77 |
78 | $response = '{"text":"test response 3","channel":"#dummyChannel"}';
79 |
80 | $this->expectOutputString($response);
81 | }
82 |
83 | /**
84 | * Test send.
85 | */
86 | public function testSendSlackWithDebug()
87 | {
88 | $sender = new Sender($this->getSlackbot());
89 |
90 | $sender->send('test response 4', '#dummyChannel');
91 |
92 | $response = '{"text":"test response 4","channel":"#dummyChannel"}';
93 |
94 | $this->expectOutputString($response);
95 | }
96 |
97 | /**
98 | * Test send.
99 | */
100 | public function testSendSlack()
101 | {
102 | $config = new Config();
103 | $config->set('listener', 'event');
104 |
105 | $sender = new Sender($this->getSlackbot(false));
106 |
107 | $apiClient = (new ApiClientTest())->getApiClient(
108 | '{ "ok": true, "ts": "1405895017.000506", "channel": "C024BE91L", "message": {} }'
109 | );
110 |
111 | $sender->setApiClient($apiClient);
112 |
113 | $result = $sender->send('test response 5', '#dummyChannel');
114 |
115 | $this->assertTrue($result);
116 |
117 | // reset the config
118 | $config->set('listener', 'slashCommand');
119 | }
120 |
121 | /**
122 | * Test send.
123 | */
124 | public function testSendSlashCommand()
125 | {
126 | $sender = new Sender($this->getSlackbot(false));
127 |
128 | $mock = new MockHandler([
129 | new Response(200, [], ''),
130 | ]);
131 |
132 | /** @noinspection PhpUndefinedClassInspection */
133 | $handler = new HandlerStack($mock);
134 | /** @noinspection PhpUndefinedClassInspection */
135 | $client = new Client(['handler' => $handler]);
136 |
137 | // $client
138 | $sender->setClient($client);
139 |
140 | $result = $sender->send('test response 6', '#dummyChannel');
141 |
142 | $this->assertTrue($result);
143 | }
144 |
145 | /**
146 | * Test getConfig.
147 | */
148 | public function testGetConfig()
149 | {
150 | $config = new Config();
151 | $originalTimezone = $config->get('timezone');
152 | $config->set('timezone', 'America/Los_Angeles');
153 |
154 | $sender = new Sender((new Slackbot()));
155 | $sender->setConfig($config);
156 |
157 | $this->assertEquals($config, $sender->getConfig());
158 |
159 | // reset config value
160 | $config->set('timezone', $originalTimezone);
161 | }
162 |
163 | /**
164 | * Test getClient.
165 | */
166 | public function testGetClient()
167 | {
168 | $client = new Client();
169 |
170 | $sender = new Sender(new Slackbot());
171 | $this->assertEquals($client, $sender->getClient());
172 | }
173 |
174 | /**
175 | * Test getApiClient.
176 | */
177 | public function testGetApiClient()
178 | {
179 | $apiClient = new ApiClient();
180 |
181 | $sender = new Sender(new Slackbot());
182 | $sender->setApiClient($apiClient);
183 |
184 | $this->assertEquals($apiClient, $sender->getApiClient());
185 | }
186 |
187 | /**
188 | * Test getApiClient.
189 | */
190 | public function testGetApiClientNotSet()
191 | {
192 | $apiClient = new ApiClient();
193 |
194 | $sender = new Sender(new Slackbot());
195 | $this->assertEquals($apiClient, $sender->getApiClient());
196 | }
197 | }
198 |
--------------------------------------------------------------------------------
/tests/Botonomous/TeamTest.php:
--------------------------------------------------------------------------------
1 | setSlackId('T0LCJF334');
17 |
18 | $this->assertEquals('T0LCJF334', $team->getSlackId());
19 | }
20 |
21 | public function testGetName()
22 | {
23 | $team = new Team();
24 | $team->setName('test');
25 |
26 | $this->assertEquals('test', $team->getName());
27 | }
28 |
29 | public function testGetIcon()
30 | {
31 | $team = new Team();
32 |
33 | $icon = [
34 | 'image_34' => 'http:',
35 | 'image_44' => 'http:',
36 | 'image_default' => true,
37 | ];
38 |
39 | $team->setIcon($icon);
40 |
41 | $this->assertEquals($icon, $team->getIcon());
42 | }
43 |
44 | public function testGetEmailDomain()
45 | {
46 | $team = new Team();
47 | $email = 'test';
48 | $team->setEmailDomain($email);
49 |
50 | $this->assertEquals($email, $team->getEmailDomain());
51 | }
52 |
53 | public function testGetDomain()
54 | {
55 | $team = new Team();
56 | $domain = 'test';
57 | $team->setDomain($domain);
58 |
59 | $this->assertEquals($domain, $team->getDomain());
60 | }
61 |
62 | public function testIsIconDefault()
63 | {
64 | $team = new Team();
65 |
66 | $icon = [
67 | 'image_34' => 'http:',
68 | 'image_44' => 'http:',
69 | 'image_default' => true,
70 | ];
71 |
72 | $team->setIcon($icon);
73 |
74 | $this->assertTrue($team->isIconDefault());
75 |
76 | $icon = [
77 | 'image_34' => 'http:',
78 | 'image_44' => 'http:',
79 | ];
80 |
81 | $team->setIcon($icon);
82 |
83 | $this->assertFalse($team->isIconDefault());
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/tests/Botonomous/UserTest.php:
--------------------------------------------------------------------------------
1 | setSlackId(self::USER_ID);
16 |
17 | $this->assertEquals(self::USER_ID, $channel->getSlackId());
18 | }
19 |
20 | public function testGetName()
21 | {
22 | $channel = new User();
23 | $channel->setName(self::USER_NAME);
24 |
25 | $this->assertEquals(self::USER_NAME, $channel->getName());
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/tests/Botonomous/WhiteListTest.php:
--------------------------------------------------------------------------------
1 | getWhiteList();
14 | }
15 |
16 | public function testGetRequest()
17 | {
18 | $whitelist = $this->getWhiteList();
19 |
20 | $this->assertEquals((new PhpunitHelper())->getRequest(), $whitelist->getRequest());
21 |
22 | // overwrite the request
23 | $whitelist->setRequest([]);
24 |
25 | $this->assertEmpty($whitelist->getRequest());
26 | }
27 |
28 | public function testGetApiClient()
29 | {
30 | $whitelist = $this->getWhiteList();
31 |
32 | $apiClient = new ApiClient();
33 | $this->assertEquals($apiClient, $whitelist->getApiClient());
34 |
35 | // call it again
36 | $this->assertEquals($apiClient, $whitelist->getApiClient());
37 | }
38 |
39 | public function testIsUsernameWhiteListed()
40 | {
41 | $whitelist = $this->getWhiteList();
42 |
43 | $inputsOutputs = [
44 | [
45 | 'input' => [
46 | 'access-control' => [
47 | 'whitelist' => [
48 | 'userId' => [],
49 | ],
50 | ],
51 | ],
52 | 'output' => false,
53 | ],
54 | [
55 | 'input' => (new PhpunitHelper())->getDictionaryData('whitelist'),
56 | 'output' => true,
57 | ],
58 | [
59 | 'input' => [
60 | 'access-control' => [
61 | 'whitelist' => [
62 | 'username' => [
63 | 'blahblah',
64 | ],
65 | 'userId' => [
66 | 'blahblah',
67 | ],
68 | ],
69 | ],
70 | ],
71 | 'output' => false,
72 | ],
73 | ];
74 |
75 | $dictionary = new Dictionary();
76 | foreach ($inputsOutputs as $inputOutput) {
77 | $dictionary->setData($inputOutput['input']);
78 |
79 | // set the dictionary
80 | $whitelist->setDictionary($dictionary);
81 |
82 | $this->assertEquals($inputOutput['output'], $whitelist->isUsernameWhiteListed());
83 | }
84 | }
85 |
86 | public function testIsUserIdWhiteListed()
87 | {
88 | $inputsOutputs = [
89 | [
90 | 'input' => [],
91 | 'output' => false,
92 | ],
93 | [
94 | 'input' => [
95 | 'access-control' => [],
96 | ],
97 | 'output' => false,
98 | ],
99 | [
100 | 'input' => [
101 | 'access-control' => [
102 | 'whitelist' => [
103 | 'username' => [],
104 | ],
105 | ],
106 | ],
107 | 'output' => false,
108 | ],
109 | [
110 | 'input'=> [
111 | 'access-control' => [
112 | 'whitelist' => [
113 | 'username' => [],
114 | 'userId' => [
115 | 'dummyUserId',
116 | ],
117 | ],
118 | ],
119 | ],
120 | 'output' => true,
121 | ],
122 | [
123 | 'input'=> [
124 | 'access-control' => [
125 | 'whitelist' => [
126 | 'username' => [],
127 | 'userId' => [],
128 | ],
129 | ],
130 | ],
131 | 'output' => false,
132 | ],
133 | [
134 | 'input'=> [
135 | 'access-control' => [
136 | 'whitelist' => [
137 | 'username' => [],
138 | 'userId' => [
139 | 'blahblah',
140 | ],
141 | ],
142 | ],
143 | ],
144 | 'output' => false,
145 | ],
146 | ];
147 |
148 | $whitelist = $this->getWhiteList();
149 | $dictionary = new Dictionary();
150 | foreach ($inputsOutputs as $inputOutput) {
151 | $dictionary->setData($inputOutput['input']);
152 |
153 | // set the dictionary
154 | $whitelist->setDictionary($dictionary);
155 |
156 | $this->assertEquals($inputOutput['output'], $whitelist->isUserIdWhiteListed());
157 | }
158 | }
159 |
160 | public function testIsEmailWhiteListedFalse()
161 | {
162 | $client = (new PhpunitHelper())->getUserInfoClient();
163 |
164 | $whitelist = $this->getWhiteList();
165 | $whitelist->setApiClient($client);
166 |
167 | $this->assertEquals(false, $whitelist->isEmailWhiteListed());
168 | }
169 |
170 | public function testIsEmailWhiteListed()
171 | {
172 | $whitelist = $this->getWhiteList();
173 |
174 | $client = (new PhpunitHelper())->getUserInfoClient();
175 | $whitelist->setApiClient($client);
176 |
177 | $dictionary = new Dictionary();
178 | $dictionary->setData([
179 | 'access-control' => [
180 | 'whitelist' => [
181 | 'userEmail' => [
182 | 'bobby@slack.com',
183 | ],
184 | ],
185 | ],
186 | ]);
187 |
188 | $whitelist->setDictionary($dictionary);
189 |
190 | $whitelist->setRequest([
191 | 'user_id' => 'U023BECGF',
192 | ]);
193 |
194 | $this->assertEquals(true, $whitelist->isEmailWhiteListed());
195 | }
196 |
197 | public function testGetSlackUserInfoEmptyRequest()
198 | {
199 | $whitelist = $this->getWhiteList();
200 | $whitelist->setRequest([]);
201 |
202 | $this->assertEmpty($whitelist->getSlackUserInfo());
203 | }
204 |
205 | /**
206 | * @throws \Exception
207 | */
208 | public function testGetSlackUserInfoNotFound()
209 | {
210 | $whitelist = $this->getWhiteList();
211 | $this->assertFalse($whitelist->getSlackUserInfo());
212 | }
213 |
214 | public function testIsWhiteListed()
215 | {
216 | $client = (new PhpunitHelper())->getUserInfoClient();
217 | $whitelist = $this->getWhiteList();
218 | $whitelist->setApiClient($client);
219 |
220 | $whitelist->setRequest([
221 | 'user_id' => 'U023BECGF',
222 | 'user_name' => 'bobby',
223 | ]);
224 |
225 | $dictionary = new Dictionary();
226 | $dictionary->setData([
227 | 'access-control' => [
228 | 'whitelist' => [
229 | 'userId' => ['U023BECGF'],
230 | 'username' => ['bobby'],
231 | 'userEmail' => ['bobby@slack.com'],
232 | ],
233 | ],
234 | ]);
235 |
236 | $whitelist->setDictionary($dictionary);
237 |
238 | $this->assertEquals(true, $whitelist->isWhiteListed());
239 | }
240 |
241 | /**
242 | * @throws \Exception
243 | */
244 | public function testIsWhiteListedFalse()
245 | {
246 | $whitelist = $this->getWhiteList();
247 | $this->assertEquals(false, $whitelist->isWhiteListed());
248 | }
249 |
250 | public function testIsEmailWhiteListedEmptyEmailList()
251 | {
252 | $client = (new PhpunitHelper())->getUserInfoClient();
253 | $whitelist = $this->getWhiteList();
254 | $whitelist->setApiClient($client);
255 |
256 | $dictionary = new Dictionary();
257 | $dictionary->setData([
258 | 'access-control' => [
259 | 'whitelist' => [],
260 | ],
261 | ]);
262 |
263 | $whitelist->setDictionary($dictionary);
264 |
265 | $whitelist->setRequest([
266 | 'user_id' => 'U023BECGF',
267 | ]);
268 |
269 | $this->assertEquals(true, $whitelist->isEmailWhiteListed());
270 | }
271 | }
272 |
--------------------------------------------------------------------------------
/tests/Botonomous/plugin/help/HelpConfigTest.php:
--------------------------------------------------------------------------------
1 | get('testConfigKey');
14 | $this->assertEquals('testConfigValue', $result);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/tests/Botonomous/plugin/help/HelpTest.php:
--------------------------------------------------------------------------------
1 | getSlackbot()))->index();
17 | $this->assertFalse(empty($index));
18 | }
19 |
20 | /**
21 | * test invalid commands in index.
22 | */
23 | public function testIndexInvalidCommands()
24 | {
25 | $slackbot = (new PhpunitHelper())->getSlackbot();
26 | $slackbot->setCommands(['dummy']);
27 |
28 | $index = (new Help($slackbot))->index();
29 |
30 | $this->assertTrue(empty($index));
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/tests/Botonomous/plugin/ping/PingTest.php:
--------------------------------------------------------------------------------
1 | getSlackbot();
21 | $ping = new Ping($slackbot);
22 |
23 | $this->assertEquals('ping', $ping->pong());
24 | }
25 |
26 | /**
27 | * Test getSlackbot.
28 | */
29 | public function testGetSlackbot()
30 | {
31 | $slackbot = (new PhpunitHelper())->getSlackbot();
32 | $ping = new Ping($slackbot);
33 |
34 | $this->assertEquals($slackbot, $ping->getSlackbot());
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/tests/Botonomous/plugin/qa/QATest.php:
--------------------------------------------------------------------------------
1 | getSlackbot('qa', " {$question}");
23 |
24 | $answer = (new QA($slackbot))->index();
25 |
26 | $questionAnswer = (new Dictionary())->get('question-answer');
27 | $this->assertContains($answer, $questionAnswer[$question]['answers']);
28 | }
29 |
30 | /**
31 | * Test index.
32 | */
33 | public function testIndexEmptyQuestions()
34 | {
35 | $question = 'hi';
36 | $slackbot = (new PhpunitHelper())->getSlackbot('qa', " {$question}");
37 |
38 | $qaPlugin = new QA($slackbot);
39 | $qaPlugin->setQuestions([]);
40 |
41 | $this->assertEmpty($qaPlugin->index());
42 | }
43 |
44 | /**
45 | * Test index.
46 | */
47 | public function testIndexNotFoundQuestion()
48 | {
49 | $question = 'dummy';
50 | $slackbot = (new PhpunitHelper())->getSlackbot('qa', " {$question}");
51 |
52 | $answer = (new QA($slackbot))->index();
53 |
54 | $this->assertEmpty($answer);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/tests/Botonomous/utility/ClassUtilityTest.php:
--------------------------------------------------------------------------------
1 | assertEquals('WhiteList', $utility->extractClassNameFromFullName('Botonomous\WhiteList'));
18 |
19 | $this->assertEquals('WhiteList', $utility->extractClassNameFromFullName('WhiteList'));
20 |
21 | $this->assertEquals('test', $utility->extractClassNameFromFullName('Botonomous\WhiteList\test'));
22 |
23 | $this->assertEquals('', $utility->extractClassNameFromFullName(''));
24 | }
25 |
26 | public function testLoadAttributes()
27 | {
28 | $utility = new ClassUtility();
29 |
30 | $channel = new Channel();
31 | $info = [
32 | 'id' => self::DUMMY_ID,
33 | 'name' => self::DUMMY_NAME,
34 | ];
35 |
36 | /** @var Channel $channel */
37 | $channel = $utility->loadAttributes($channel, $info);
38 |
39 | $this->assertEquals(self::DUMMY_ID, $channel->getSlackId());
40 | $this->assertEquals(self::DUMMY_NAME, $channel->getName());
41 |
42 | $event = new Event('message');
43 | $info = [
44 | 'bot_id' => self::DUMMY_ID,
45 | // should be ignore
46 | 'non_existent_attr' => 'dummy_value',
47 | 'ts' => '1355517523.000005',
48 | 'event_ts' => '1355517523.000005',
49 | ];
50 |
51 | /** @var Event $event */
52 | $event = $utility->loadAttributes($event, $info);
53 |
54 | $this->assertEquals(self::DUMMY_ID, $event->getBotId());
55 | $this->assertEquals('1355517523.000005', $event->getTimestamp());
56 | $this->assertEquals('1355517523.000005', $event->getEventTimestamp());
57 | $this->assertEmpty($event->getText());
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/tests/Botonomous/utility/FileUtilityTest.php:
--------------------------------------------------------------------------------
1 | getSlackbotDir().DIRECTORY_SEPARATOR.'dictionary'.DIRECTORY_SEPARATOR.'test.json';
20 |
21 | $array = (new FileUtility())->jsonFileToArray($dir);
22 |
23 | $expected = [
24 | 'test1',
25 | 'test2',
26 | ];
27 |
28 | $this->assertEquals($expected, $array);
29 | }
30 |
31 | /**
32 | * Test jsonFileToArrayEmptyPath.
33 | */
34 | public function testJsonFileToArrayEmptyPath()
35 | {
36 | try {
37 | (new FileUtility())->jsonFileToArray('');
38 | } catch (\Exception $e) {
39 | $this->assertEquals(FileUtility::EMPTY_FILE_PATH_MESSAGE, $e->getMessage());
40 | }
41 | }
42 |
43 | /**
44 | * Test jsonFileToArrayMissingFile.
45 | */
46 | public function testJsonFileToArrayMissingFile()
47 | {
48 | try {
49 | (new FileUtility())->jsonFileToArray('/path/to/dummy.json');
50 | } catch (\Exception $e) {
51 | $this->assertEquals(FileUtility::MISSING_FILE_MESSAGE, $e->getMessage());
52 | }
53 | }
54 |
55 | /**
56 | * Test jsonFileToArrayInvalidFile.
57 | */
58 | public function testJsonFileToArrayInvalidFile()
59 | {
60 | $dir = $this->getSlackbotDir().DIRECTORY_SEPARATOR.'Config.php';
61 |
62 | try {
63 | (new FileUtility())->jsonFileToArray($dir);
64 | } catch (\Exception $e) {
65 | $this->assertEquals(FileUtility::INVALID_JSON_FILE_MESSAGE, $e->getMessage());
66 | }
67 | }
68 |
69 | private function getSlackbotDir()
70 | {
71 | $namespaceParts = explode('\\', __NAMESPACE__);
72 | $rootNamespace = $namespaceParts[0];
73 |
74 | return dirname(dirname(dirname(__DIR__))).DIRECTORY_SEPARATOR.'src'.DIRECTORY_SEPARATOR.$rootNamespace;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/tests/Botonomous/utility/LanguageProcessingUtilityTest.php:
--------------------------------------------------------------------------------
1 | stem('Stemming is funnier than a bummer says the sushi loving computer scientist');
22 |
23 | $this->assertEquals('Stem is funnier than a bummer sai the sushi love comput scientist', $result);
24 | }
25 |
26 | /**
27 | * Test stem.
28 | */
29 | public function testStemEmpty()
30 | {
31 | $utility = new LanguageProcessingUtility();
32 |
33 | $result = $utility->stem('');
34 |
35 | $this->assertEquals('', $result);
36 | }
37 |
38 | /**
39 | * Test removePunctuations.
40 | */
41 | public function testRemovePunctuations()
42 | {
43 | $utility = new LanguageProcessingUtility();
44 |
45 | $result = $utility->removePunctuations('A dummy text?');
46 |
47 | $expected = 'A dummy text';
48 |
49 | $this->assertEquals($expected, $result);
50 |
51 | $result = $utility->removePunctuations('A dummy text.');
52 |
53 | $expected = 'A dummy text';
54 |
55 | $this->assertEquals($expected, $result);
56 | }
57 |
58 | /**
59 | * Test removeStopWords.
60 | */
61 | public function testRemoveStopWords()
62 | {
63 | $utility = new LanguageProcessingUtility();
64 |
65 | $inputsOutputs = [
66 | [
67 | 'i' => 'Stemming is funnier than a bummer says the sushi loving computer scientist',
68 | 'o' => 'Stemming funnier bummer sushi loving computer scientist',
69 | ],
70 | ];
71 |
72 | foreach ($inputsOutputs as $inputOutput) {
73 | $result = $utility->removeStopWords($inputOutput['i']);
74 | $this->assertEquals($inputOutput['o'], $result);
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/tests/Botonomous/utility/LoggerUtilityTest.php:
--------------------------------------------------------------------------------
1 | setTimezone();
25 |
26 | $this->setLogFile();
27 |
28 | $utility = new LoggerUtility($this->getConfig(false));
29 |
30 | $this->assertFalse($utility->logChat(__METHOD__, self::TEST_MESSAGE));
31 | }
32 |
33 | /**
34 | * Test logChatEnabled.
35 | */
36 | public function testLogChatEnabled()
37 | {
38 | $this->setTimezone();
39 |
40 | $this->setLogFile();
41 |
42 | $utility = new LoggerUtility($this->getConfig());
43 |
44 | $this->assertTrue($utility->logChat(__METHOD__, self::TEST_MESSAGE));
45 | }
46 |
47 | /**
48 | * Test logRaw.
49 | */
50 | public function testLogRawLogging()
51 | {
52 | $this->setTimezone();
53 |
54 | $this->setLogFile();
55 |
56 | $utility = new LoggerUtility($this->getConfig());
57 |
58 | $this->assertTrue($utility->logInfo(self::TEST_INFO_LOG));
59 | }
60 |
61 | /**
62 | * Test logRaw.
63 | */
64 | public function testLogRawNotLogging()
65 | {
66 | $this->setTimezone();
67 |
68 | $utility = new LoggerUtility($this->getConfig(false));
69 |
70 | $this->assertFalse($utility->logInfo(self::TEST_INFO_LOG));
71 | }
72 |
73 | public function setLogFile($name = null)
74 | {
75 | if ($name === null) {
76 | $name = self::TEST_LOG_FILE;
77 | }
78 |
79 | $config = new Config();
80 | $config->set(['logger', 'monolog', 'handlers', 'file', 'fileName'], $name);
81 | }
82 |
83 | public function testGetLogContent()
84 | {
85 | $utility = new LoggerUtility($this->getConfig());
86 |
87 | $this->assertEquals(
88 | __METHOD__.'|test message|#dummy',
89 | $utility->getLogContent(__METHOD__, 'test message', '#dummy')
90 | );
91 | }
92 |
93 | public function testLogDebug()
94 | {
95 | $this->setLogFile();
96 | $utility = new LoggerUtility($this->getConfig());
97 |
98 | $this->assertTrue($utility->logDebug('This is a debug log'));
99 | }
100 |
101 | public function testLogNotice()
102 | {
103 | $this->setLogFile();
104 | $utility = new LoggerUtility($this->getConfig());
105 |
106 | $this->assertTrue($utility->logNotice('This is a notice log'));
107 | }
108 |
109 | public function testLogWarning()
110 | {
111 | $this->setLogFile();
112 | $utility = new LoggerUtility($this->getConfig());
113 |
114 | $this->assertTrue($utility->logWarning('This is a warning log'));
115 | }
116 |
117 | public function testLogError()
118 | {
119 | $this->setLogFile();
120 | $utility = new LoggerUtility($this->getConfig());
121 |
122 | $this->assertTrue($utility->logError('This is an error log'));
123 | }
124 |
125 | public function testLogCritical()
126 | {
127 | $this->setLogFile();
128 | $utility = new LoggerUtility($this->getConfig());
129 |
130 | $this->assertTrue($utility->logCritical('This is a critical log'));
131 | }
132 |
133 | public function testLogAlert()
134 | {
135 | $this->setLogFile();
136 | $utility = new LoggerUtility($this->getConfig());
137 |
138 | $this->assertTrue($utility->logAlert('This is an alert log'));
139 | }
140 |
141 | public function testLogEmergency()
142 | {
143 | $this->setLogFile();
144 | $utility = new LoggerUtility($this->getConfig());
145 |
146 | $this->assertTrue($utility->logEmergency('This is an emergency log'));
147 | }
148 |
149 | public function testLogInvalidLevel()
150 | {
151 | $utility = new LoggerUtility($this->getConfig());
152 |
153 | $this->expectException('\Exception');
154 | $this->expectExceptionMessage("'invalidLevel' is an invalid log level");
155 |
156 | $utility->log('invalidLevel', 'dummyMessage');
157 | }
158 |
159 | public function testLogEmptyConfig()
160 | {
161 | $config = new Config();
162 | $config->set('logger', null);
163 |
164 | $this->expectException('\Exception');
165 | $this->expectExceptionMessage('Monolog config is missing');
166 |
167 | new LoggerUtility($config);
168 | }
169 |
170 | private function getConfig($loggerEnabled = true)
171 | {
172 | $config = new Config();
173 | $config->set(['logger', 'enabled'], $loggerEnabled === true ? true : false);
174 |
175 | return $config;
176 | }
177 |
178 | private function setTimezone()
179 | {
180 | $config = new Config();
181 | date_default_timezone_set($config->get('timezone'));
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/tests/Botonomous/utility/MessageUtilityTest.php:
--------------------------------------------------------------------------------
1 | get('botUserId');
25 | $removed = $utility->removeMentionedBot("<@{$botUserId}> /help");
26 |
27 | $this->assertEquals($removed, ' /help');
28 |
29 | $removed = $utility->removeMentionedBot(' /help');
30 |
31 | $this->assertEquals($removed, ' /help');
32 |
33 | $removed = $utility->removeMentionedBot("<@{$botUserId}> /help <@{$botUserId}>");
34 |
35 | $this->assertEquals($removed, " /help <@{$botUserId}>");
36 |
37 | $removed = $utility->removeMentionedBot("<@{$botUserId}> <@{$botUserId}>");
38 |
39 | $this->assertEquals($removed, " <@{$botUserId}>");
40 |
41 | $removed = $utility->removeMentionedBot("Test <@{$botUserId}>");
42 |
43 | $this->assertEquals($removed, 'Test ');
44 | }
45 |
46 | /**
47 | * Test extractCommandName.
48 | */
49 | public function testExtractCommandName()
50 | {
51 | $utility = new MessageUtility();
52 | $config = new Config();
53 | $commandPrefix = $config->get('commandPrefix');
54 |
55 | $command = $utility->extractCommandName("{$commandPrefix}help dummy @test {$commandPrefix}help de");
56 |
57 | $this->assertEquals('help', $command);
58 |
59 | $command = $utility->extractCommandName(" {$commandPrefix}help dummy @test {$commandPrefix}help de");
60 |
61 | $this->assertEquals('help', $command);
62 |
63 | $command = $utility->extractCommandName(" dummy {$commandPrefix}help dummy @test {$commandPrefix}help dummy");
64 |
65 | $this->assertEquals(null, $command);
66 |
67 | $config->set('commandPrefix', '');
68 | $commandPrefix = $config->get('commandPrefix');
69 |
70 | $command = $utility->extractCommandName("{$commandPrefix}help dummy @test {$commandPrefix}help de");
71 |
72 | $this->assertEquals('help', $command);
73 |
74 | $command = $utility->extractCommandName(" dummy {$commandPrefix}help dummy @test {$commandPrefix}help dummy");
75 |
76 | $this->assertEquals('dummy', $command);
77 |
78 | $command = $utility->extractCommandName(" {$commandPrefix}help dummy @test {$commandPrefix}help de");
79 |
80 | $this->assertEquals('help', $command);
81 |
82 | $config->set('commandPrefix', '@');
83 | $commandPrefix = $config->get('commandPrefix');
84 |
85 | $command = $utility->extractCommandName("{$commandPrefix}help dummy @test {$commandPrefix}help de");
86 |
87 | $this->assertEquals('help', $command);
88 |
89 | $command = $utility->extractCommandName(" {$commandPrefix}help dummy @test {$commandPrefix}help de");
90 |
91 | $this->assertEquals('help', $command);
92 |
93 | $command = $utility->extractCommandName(" dummy {$commandPrefix}help dummy @test {$commandPrefix}help dummy");
94 |
95 | $this->assertEquals(null, $command);
96 | }
97 |
98 | /**
99 | * @throws \Exception
100 | */
101 | public function testExtractCommandDetails()
102 | {
103 | $utility = new MessageUtility();
104 | $config = new Config();
105 | $botUserId = $config->get('botUserId');
106 | $commandPrefix = $config->get('commandPrefix');
107 | $commandObject = $utility->extractCommandDetails("<@{$botUserId}> {$commandPrefix}ping");
108 |
109 | $expected = new Command('ping');
110 | $expected->setPlugin('Ping');
111 | $expected->setDescription('Use as a health check');
112 |
113 | $this->assertEquals($expected, $commandObject);
114 | }
115 |
116 | /**
117 | * test removeTriggerWord.
118 | */
119 | public function testRemoveTriggerWord()
120 | {
121 | $utility = new MessageUtility();
122 | $result = $utility->removeTriggerWord('google_bot: do this google_bot', 'google_bot:');
123 |
124 | $this->assertEquals('do this google_bot', $result);
125 | }
126 |
127 | /**
128 | * test linkToUser.
129 | */
130 | public function testLinkToUser()
131 | {
132 | $utility = new MessageUtility();
133 |
134 | $this->assertEquals('<@U024BE7LH>', $utility->linkToUser('U024BE7LH'));
135 |
136 | $this->assertEquals('<@U024BE7LH|bob>', $utility->linkToUser('U024BE7LH', 'bob'));
137 |
138 | $this->assertEquals('<@U024BE7LH>', $utility->linkToUser('U024BE7LH', ''));
139 |
140 | $this->expectException('\Exception');
141 | $this->expectExceptionMessage('User id is not provided');
142 |
143 | $this->assertEquals('<@U024BE7LH>', $utility->linkToUser(''));
144 | }
145 |
146 | public function testIsBotMentioned()
147 | {
148 | $config = new Config();
149 | $utility = new MessageUtility($config);
150 |
151 | $botUserId = $config->get('botUserId');
152 |
153 | $this->assertEquals(true, $utility->isBotMentioned("<@{$botUserId}> /help"));
154 | $this->assertEquals(true, $utility->isBotMentioned("How are you <@{$botUserId}>?"));
155 | $this->assertEquals(false, $utility->isBotMentioned('/help'));
156 | }
157 |
158 | public function testKeywordPos()
159 | {
160 | $utility = new MessageUtility();
161 | $result = $utility->keywordPos([
162 | 'two words',
163 | 'word',
164 | 'two',
165 | 'words',
166 | ' plus',
167 | ], 'This is a two words plus one word and two words');
168 |
169 | $expected = [
170 | 'two words' => [
171 | 10,
172 | 38,
173 | ],
174 | 'word' => [
175 | 29,
176 | ],
177 | ' plus' => [
178 | 19,
179 | ],
180 | ];
181 |
182 | $this->assertEquals($expected, $result);
183 |
184 | $result = $utility->keywordPos([
185 | "What's",
186 | 'the',
187 | 'the weather',
188 | ' like ',
189 | 'tomorrow',
190 | ], "What's the weather like tomorrow?");
191 |
192 | $expected = [
193 | "What's" => [
194 | 0,
195 | ],
196 | 'the weather' => [
197 | 7,
198 | ],
199 | 'tomorrow' => [
200 | 24,
201 | ],
202 | ' like ' => [
203 | 18,
204 | ],
205 | ];
206 |
207 | $this->assertEquals($expected, $result);
208 |
209 | $result = $utility->keywordPos([], "What's the weather like tomorrow?");
210 |
211 | $this->assertEmpty($result);
212 | }
213 |
214 | public function testKeywordCount()
215 | {
216 | $utility = new MessageUtility();
217 |
218 | $result = $utility->keywordCount([
219 | 'two words',
220 | 'word',
221 | 'two',
222 | 'words',
223 | ' plus',
224 | ], 'This is a two words plus one word and two words');
225 |
226 | $expected = [
227 | 'two words' => 2,
228 | 'word' => 1,
229 | ' plus' => 1,
230 | ];
231 |
232 | $this->assertEquals($expected, $result);
233 |
234 | $result = $utility->keywordCount([
235 | "What's",
236 | 'the',
237 | 'the weather',
238 | ' like ',
239 | 'tomorrow',
240 | ], "What's the weather like tomorrow?");
241 |
242 | $expected = [
243 | "What's" => 1,
244 | 'the weather' => 1,
245 | 'tomorrow' => 1,
246 | ' like ' => 1,
247 | ];
248 |
249 | $this->assertEquals($expected, $result);
250 |
251 | $result = $utility->keywordCount([], "What's the weather like tomorrow?");
252 |
253 | $this->assertEmpty($result);
254 | }
255 | }
256 |
--------------------------------------------------------------------------------
/tests/Botonomous/utility/RequestUtilityTest.php:
--------------------------------------------------------------------------------
1 | assertEquals(
14 | filter_input(INPUT_SERVER, 'SERVER_PROTOCOL', FILTER_SANITIZE_STRING),
15 | $requestUtility->getServerProtocol()
16 | );
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/tests/Botonomous/utility/SecurityUtilityTest.php:
--------------------------------------------------------------------------------
1 | generateToken();
17 | $result = hash($securityUtility->getHashAlgorithm(), 'something dummy');
18 |
19 | $this->assertEquals(strlen($result), strlen($token));
20 | }
21 |
22 | /**
23 | * Test getHashAlgorithm.
24 | *
25 | * @throws \Exception
26 | */
27 | public function testGetHashAlgorithm()
28 | {
29 | $securityUtility = new SecurityUtility();
30 |
31 | $this->assertEquals($securityUtility::DEFAULT_HASH_ALGORITHM, $securityUtility->getHashAlgorithm());
32 |
33 | $securityUtility->setHashAlgorithm('sha256');
34 | $this->assertEquals('sha256', $securityUtility->getHashAlgorithm());
35 |
36 | $this->expectException('\Exception');
37 | $this->expectExceptionMessage('Hash algorithm is not valid');
38 |
39 | $securityUtility->setHashAlgorithm('');
40 | $this->assertEquals($securityUtility::DEFAULT_HASH_ALGORITHM, $securityUtility->getHashAlgorithm());
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/tests/Botonomous/utility/SessionUtilityTest.php:
--------------------------------------------------------------------------------
1 | set('testKey', 'testValue');
18 |
19 | $this->assertEquals('testValue', $sessionUtility->get('testKey'));
20 |
21 | $sessionUtility->set('testKey', 'testNewValue');
22 |
23 | $this->assertEquals('testNewValue', $sessionUtility->get('testKey'));
24 | }
25 |
26 | /**
27 | * Test get.
28 | *
29 | * @runInSeparateProcess
30 | */
31 | public function testGet()
32 | {
33 | $sessionUtility = new SessionUtility();
34 |
35 | $this->assertEquals(null, $sessionUtility->get('unknownKey'));
36 | }
37 |
38 | /**
39 | * Test getSession.
40 | *
41 | * @runInSeparateProcess
42 | */
43 | public function testGetSession()
44 | {
45 | $sessionUtility = new SessionUtility();
46 | $session = $sessionUtility->getSession();
47 |
48 | $session['myKey'] = 'meyValue';
49 | $sessionUtility->setSession($session);
50 |
51 | $this->assertEquals($session, $sessionUtility->getSession());
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/tests/bootstrap.php:
--------------------------------------------------------------------------------
1 |