diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..27b765f --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +/tests export-ignore +/.github export-ignore diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..0f7d23f --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,25 @@ +on: + push: + # Sequence of patterns matched against refs/tags + tags: + - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 + +name: Release + +jobs: + release: + name: Release + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v2 + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: Release ${{ github.ref }} + draft: false + prerelease: false diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..bc87c2f --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,64 @@ +name: PHPUnit + +on: [push, pull_request] + +env: + SWOOLE_VERSION: '4.6.5' + SWOW_VERSION: 'develop' + +jobs: + ci: + name: Test PHP ${{ matrix.php-version }} on ${{ matrix.engine }} + runs-on: "${{ matrix.os }}" + strategy: + matrix: + os: [ubuntu-latest] + php-version: ['7.3', '7.4'] + engine: ['none', 'swoole', 'swow'] + max-parallel: 5 + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + tools: phpize + ini-values: opcache.enable_cli=1 + coverage: none + - name: Setup Swoole + if: ${{ matrix.engine == 'swoole' }} + run: | + sudo apt-get install libcurl4-openssl-dev + wget https://github.com/swoole/swoole-src/archive/v${SWOOLE_VERSION}.tar.gz -O swoole.tar.gz + mkdir -p swoole + tar -xf swoole.tar.gz -C swoole --strip-components=1 + rm swoole.tar.gz + cd swoole + phpize + ./configure --enable-openssl --enable-http2 --enable-swoole-curl --enable-swoole-json + make -j$(nproc) + sudo make install + sudo sh -c "echo extension=swoole > /etc/php/${{ matrix.php-version }}/cli/conf.d/swoole.ini" + php --ri swoole + - name: Setup Swow + if: ${{ matrix.engine == 'swow' }} + run: | + wget https://github.com/swow/swow/archive/"${SWOW_VERSION}".tar.gz -O swow.tar.gz + mkdir -p swow + tar -xf swow.tar.gz -C swow --strip-components=1 + rm swow.tar.gz + cd swow/ext || exit + + phpize + ./configure --enable-debug + make -j "$(nproc)" + sudo make install + sudo sh -c "echo extension=swow > /etc/php/${{ matrix.php-version }}/cli/conf.d/swow.ini" + php --ri swow + - name: Setup Packages + run: composer update -o --no-scripts + - name: Run Test Cases + run: | + composer analyse + composer test diff --git a/.gitignore b/.gitignore index 297959a..6f051fa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,23 +1,5 @@ /vendor/ -node_modules/ -npm-debug.log -yarn-error.log - -# Laravel 4 specific -bootstrap/compiled.php -app/storage/ - -# Laravel 5 & Lumen specific -public/storage -public/hot - -# Laravel 5 & Lumen specific with changed public path -public_html/storage -public_html/hot - -storage/*.key -.env -Homestead.yaml -Homestead.json -/.vagrant -.phpunit.result.cache +composer.lock +*.cache +*.log +.idea diff --git a/.php_cs b/.php_cs new file mode 100644 index 0000000..fa694e3 --- /dev/null +++ b/.php_cs @@ -0,0 +1,89 @@ +setRiskyAllowed(true) + ->setRules([ + '@PSR2' => true, + '@Symfony' => true, + '@DoctrineAnnotation' => true, + '@PhpCsFixer' => true, + 'header_comment' => [ + 'commentType' => 'PHPDoc', + 'header' => $header, + 'separate' => 'none', + 'location' => 'after_declare_strict', + ], + 'array_syntax' => [ + 'syntax' => 'short' + ], + 'list_syntax' => [ + 'syntax' => 'short' + ], + 'concat_space' => [ + 'spacing' => 'one' + ], + 'blank_line_before_statement' => [ + 'statements' => [ + 'declare', + ], + ], + 'general_phpdoc_annotation_remove' => [ + 'annotations' => [ + 'author' + ], + ], + 'ordered_imports' => [ + 'imports_order' => [ + 'class', 'function', 'const', + ], + 'sort_algorithm' => 'alpha', + ], + 'single_line_comment_style' => [ + 'comment_types' => [ + ], + ], + 'yoda_style' => [ + 'always_move_variable' => false, + 'equal' => false, + 'identical' => false, + ], + 'phpdoc_align' => [ + 'align' => 'left', + ], + 'multiline_whitespace_before_semicolons' => [ + 'strategy' => 'no_multi_line', + ], + 'constant_case' => [ + 'case' => 'lower', + ], + 'class_attributes_separation' => true, + 'combine_consecutive_unsets' => true, + 'declare_strict_types' => true, + 'linebreak_after_opening_tag' => true, + 'lowercase_static_reference' => true, + 'no_useless_else' => true, + 'no_unused_imports' => true, + 'not_operator_with_successor_space' => true, + 'not_operator_with_space' => false, + 'ordered_class_elements' => true, + 'php_unit_strict' => false, + 'phpdoc_separation' => false, + 'single_quote' => true, + 'standardize_not_equals' => true, + 'multiline_comment_opening_closing' => true, + ]) + ->setFinder( + PhpCsFixer\Finder::create() + ->exclude('bin') + ->exclude('public') + ->exclude('runtime') + ->exclude('vendor') + ->in(__DIR__) + ) + ->setUsingCache(false); diff --git a/.phpstorm.meta.php b/.phpstorm.meta.php new file mode 100644 index 0000000..1014069 --- /dev/null +++ b/.phpstorm.meta.php @@ -0,0 +1,6 @@ +=7.3 +- hyperf版本: hyperf2.1+ + +# 安装 + +```php +composer require hyperf-glory/alert-manager + +``` + +# 在hyperf项目中使用 + +安装成功后执行 + +```php +php bin/hyperf.php vendor:publish hyperf-glory/alert-manager + +``` + +会自动将`alert.php`添加到您项目的配置文件当中 + +# 相关配置 + +### 钉钉启用开关 + +(可选)默认为开启 + +```php +DING_ENABLED=true +``` + +### 钉钉的推送token + +- (必选)发送钉钉机器人的token,即在您创建机器人之后的access_token +- 钉钉推送链接:https://oapi.dingtalk.com/robot/send?access_token=you-push-token + +```php +DING_TOKEN=you-push-token +``` + +### 多机器人配置 + +如果想要添加多个机器人,则在`ding.php`当中添加机器人名字和相关的配置即可 + +```php +return [ + + 'default' => [ + 'enabled' => env('DING_ENABLED',true), + + 'token' => env('DING_TOKEN',''), + + 'ssl_verify' => env('DING_SSL_VERIFY',true), + + 'secret' => env('DING_SECRET',true), + + 'options' => [ + 'timeout' => env('DING_TIME_OUT', 2.0), + ], + ], + + 'other' => [ + 'enabled' => env('OTHER_DING_ENABLED',true), + + 'token' => env('OTHER_DING_TOKEN',''), + + 'ssl_verify' => env('DING_SSL_VERIFY',true), + + 'secret' => env('OTHER_DING_SECRET',true), + ] + +]; +``` + +### 钉钉发送的超时时间 + +- (可选) 默认为2.0秒 + +```php +DING_TIME_OUT= +``` + +### 是否开启SSL验证 + +- (可选)默认为开启,关闭请手动设置 + +```php +DING_SSL_VERIFY=false +``` + +### 开启钉钉安全配置 + +- (可选)默认为无 + +```php +DING_SECRET= +``` + +# 使用 + +## 发送纯文字消息 + +```php +ding('我就是我, xxx 是不一样的烟火') +``` + +or + +```php +ding()->text('我就是我, xxx 是不一样的烟火') +``` + +发送过程@其他人或者所有人 + +```php +make(\HyperfGlory\AlertManager\DingTalk::class)->at(["13888888888"],true) + ->text("我就是我,@13888888888 是不一样的烟火") +``` + +## 发送链接类型的消息 + +```php + +$title = "自定义机器人协议"; +$text = "群机器人是钉钉群的高级扩展功能。群机器人可以将第三方服务的信息聚合到群聊中,实现自动化的信息同步。例如:通过聚合GitHub,GitLab等源码管理服务,实现源码更新同步;通过聚合Trello,JIRA等项目协调服务,实现项目信息同步。不仅如此,群机器人支持Webhook协议的自定义接入,支持更多可能性,例如:你可将运维报警提醒通过自定义机器人聚合到钉钉群。"; +$picUrl = ""; +$messageUrl = "https://open-doc.dingtalk.com/docs/doc.htm?spm=a219a.7629140.0.0.Rqyvqo&treeId=257&articleId=105735&docType=1"; + +make(\HyperfGlory\AlertManager\DingTalk::class)->link($title,$text,$messageUrl,$picUrl) +``` + +## 发送markdown类型的消息 + +```php +$title = '杭州天气'; +$markdown = "#### 杭州天气 \n ". + "> 9度,@1825718XXXX 西北风1级,空气良89,相对温度73%\n\n ". + "> ![screenshot](http://i01.lw.aliimg.com/media/lALPBbCc1ZhJGIvNAkzNBLA_1200_588.png)\n". + "> ###### 10点20分发布 [天气](http://www.thinkpage.cn/) "; + +make(\HyperfGlory\AlertManager\DingTalk::class)->markdown($title,$markdown); +``` + +or + +```php +ding()->at([],true) + ->markdown($title,$markdown) +``` + +## 发送Action类型的消息 + +### 发送single类型的消息 + +```php +$title = "乔布斯 20 年前想打造一间苹果咖啡厅,而它正是 Apple Store 的前身"; +$text = "![screenshot](@lADOpwk3K80C0M0FoA) \n". + " #### 乔布斯 20 年前想打造的苹果咖啡厅 \n\n". + " Apple Store 的设计正从原来满满的科技感走向生活化,而其生活化的走向其实可以追溯到 20 年前苹果一个建立咖啡馆的计划"; + +make(\HyperfGlory\AlertManager\DingTalk::class)->actionCard($title,$text,1) + ->single("阅读全文","https://www.dingtalk.com/") + ->send() +``` + +### 发送btns类型的消息 + +```php +make(\HyperfGlory\AlertManager\DingTalk::class)->actionCard($title,$text,1) + ->addButtons("内容不错","https://www.dingtalk.com/") + ->addButtons("不感兴趣","https://www.dingtalk.com/") + ->send(); +``` + +## 发送Feed类型的消息 + +```php +$messageUrl = "https://mp.weixin.qq.com/s?__biz=MzA4NjMwMTA2Ng==&mid=2650316842&idx=1&sn=60da3ea2b29f1dcc43a7c8e4a7c97a16&scene=2&srcid=09189AnRJEdIiWVaKltFzNTw&from=timeline&isappinstalled=0&key=&ascene=2&uin=&devicetype=android-23&version=26031933&nettype=WIFI"; +$picUrl = "https://www.dingtalk.com"; +make(\HyperfGlory\AlertManager\DingTalk::class)->feed() + ->addLinks('时代的火车向前开',$messageUrl,$picUrl) + ->addLinks('时代的火车向前开2',$messageUrl,$picUrl) + ->send(); +``` + +## 多机器人消息发送 + +### 发送纯文字消息 + +```php +ding('我就是我, xxx 是不一样的烟火','other') +``` + +or + +```php +ding()->with('other')->text('我就是我, xxx 是不一样的烟火'); +``` + +### 通过其他机器人发送其他类型消息 + +```php +ding()->with('other')->markdown($title,$markdown); + +ding()->with('other') + ->feed() + ->addLinks('时代的火车向前开',$messageUrl,$picUrl) + ->addLinks('时代的火车向前开2',$messageUrl,$picUrl) + ->send(); +``` + +enjoy :) + + + diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..ed44931 --- /dev/null +++ b/composer.json @@ -0,0 +1,48 @@ +{ + "name": "hyperf-glory/alert-manager", + "type": "library", + "license": "MIT", + "keywords": [ + "php", + "hyperf" + ], + "description": "", + "autoload": { + "psr-4": { + "HyperfGlory\\AlertManager\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "HyperfTest\\": "tests" + } + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.14", + "mockery/mockery": "^1.0", + "phpstan/phpstan": "^0.12", + "phpunit/phpunit": ">=7.0", + "swoole/ide-helper": "dev-master", + "swow/swow": "dev-develop", + "symfony/var-dumper": "^5.1" + }, + "minimum-stability": "dev", + "prefer-stable": true, + "config": { + "optimize-autoloader": true, + "sort-packages": true + }, + "scripts": { + "test": "phpunit -c phpunit.xml --colors=always", + "analyse": "phpstan analyse --memory-limit 1024M -l 0 ./src", + "cs-fix": "php-cs-fixer fix $1" + }, + "extra": { + "hyperf": { + "config": "HyperfGlory\\AlertManager\\ConfigProvider" + } + } +} diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..d2c615a --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,15 @@ + + + + ./tests/ + + \ No newline at end of file diff --git a/publish/alert.php b/publish/alert.php new file mode 100644 index 0000000..579777e --- /dev/null +++ b/publish/alert.php @@ -0,0 +1,19 @@ + [ + 'enabled' => env('DING_ENABLED', true), + 'token' => env('DING_TOKEN', ''), + 'ssl_verify' => env('DING_SSL_VERIFY', true), + 'secret' => env('DING_SECRET', true), + 'options' => [ + 'timeout' => env('DING_TIME_OUT', 2.0), + ], + ], +]; diff --git a/src/ClientInterface.php b/src/ClientInterface.php new file mode 100644 index 0000000..f2a5af8 --- /dev/null +++ b/src/ClientInterface.php @@ -0,0 +1,26 @@ + [ + ], + 'commands' => [ + ], + 'annotations' => [ + 'scan' => [ + 'paths' => [ + __DIR__, + ], + ], + ], + 'listeners' => [], + 'publish' => [ + [ + 'id' => 'config', + 'description' => 'description of this config file.', + 'source' => __DIR__ . '/../publish/alert.php', + 'destination' => BASE_PATH . '/config/autoload/alert.php', + ], + ], + ]; + } +} diff --git a/src/DingTalk.php b/src/DingTalk.php new file mode 100644 index 0000000..0d42203 --- /dev/null +++ b/src/DingTalk.php @@ -0,0 +1,134 @@ +config = $config ?? config('alert', []); + $this->client = $client; + $this->with(); + } + + /** + * @return $this + */ + public function with(string $robot = 'default'): self + { + $this->robot = $robot; + $this->dingTalkService = new DingTalkService($this->config[$robot], $this->client); + + return $this; + } + + /** + * @return false|\Psr\Http\Message\ResponseInterface + */ + public function text(string $content = '') + { + return $this->dingTalkService + ->setTextMessage($content) + ->send(); + } + + /** + * @param $title + * @param $text + * + * @return \HyperfGlory\AlertManager\Messages\ActionCard|\HyperfGlory\AlertManager\Messages\Message + */ + public function action($title, $text) + { + return $this->dingTalkService + ->setActionCardMessage($title, $text); + } + + /** + * @return $this + */ + public function at(array $mobiles = [], bool $atAll = false): self + { + $this->dingTalkService + ->setAt($mobiles, $atAll); + + return $this; + } + + /** + * @param $title + * @param $text + * @param $url + * + * @return false|\Psr\Http\Message\ResponseInterface + */ + public function link($title, $text, $url, string $picUrl = '') + { + return $this->dingTalkService + ->setLinkMessage($title, $text, $url, $picUrl) + ->send(); + } + + /** + * @param $title + * @param $markdown + * + * @return false|\Psr\Http\Message\ResponseInterface + */ + public function markdown($title, $markdown) + { + return $this->dingTalkService + ->setMarkdownMessage($title, $markdown) + ->send(); + } + + /** + * @param $title + * @param $markdown + * + * @return \HyperfGlory\AlertManager\Messages\ActionCard|\HyperfGlory\AlertManager\Messages\Message + */ + public function actionCard($title, $markdown, int $hideAvatar = 0, int $btnOrientation = 0) + { + return $this->dingTalkService + ->setActionCardMessage($title, $markdown, $hideAvatar, $btnOrientation); + } + + /** + * @return \HyperfGlory\AlertManager\Messages\FeedCard|\HyperfGlory\AlertManager\Messages\Message + */ + public function feed() + { + return $this->dingTalkService + ->setFeedCardMessage(); + } +} diff --git a/src/DingTalkService.php b/src/DingTalkService.php new file mode 100644 index 0000000..586c32f --- /dev/null +++ b/src/DingTalkService.php @@ -0,0 +1,169 @@ +config = $config; + $this->setTextMessage('null'); + + if ($client !== null) { + $this->client = $client; + + return; + } + $this->client = $this->createClient($config); + } + + public function setMessage(Message $message): void + { + $this->message = $message; + } + + public function getMessage(): array + { + return $this->message->getMessage(); + } + + public function setAt(array $mobiles = [], bool $atAll = false): void + { + $this->mobiles = $mobiles; + $this->atAll = $atAll; + if ($this->message) { + $this->message->sendAt($mobiles, $atAll); + } + } + + /** + * @param $content + * + * @return $this + */ + public function setTextMessage($content): self + { + $this->message = new Text($content); + $this->message->sendAt($this->mobiles, $this->atAll); + + return $this; + } + + /** + * @param $title + * @param $text + * @param $messageUrl + * + * @return $this + */ + public function setLinkMessage($title, $text, $messageUrl, string $picUrl = ''): self + { + $this->message = new Link($title, $text, $messageUrl, $picUrl); + $this->message->sendAt($this->mobiles, $this->atAll); + + return $this; + } + + /** + * @param $title + * @param $markdown + * + * @return $this + */ + public function setMarkdownMessage($title, $markdown): self + { + $this->message = new Markdown($title, $markdown); + $this->message->sendAt($this->mobiles, $this->atAll); + + return $this; + } + + /** + * @param $title + * @param $markdown + * + * @return ActionCard|Message + */ + public function setActionCardMessage($title, $markdown, int $hideAvatar = 0, int $btnOrientation = 0) + { + $this->message = new ActionCard($this, $title, $markdown, $hideAvatar, $btnOrientation); + $this->message->sendAt($this->mobiles, $this->atAll); + + return $this->message; + } + + /** + * @return FeedCard|Message + */ + public function setFeedCardMessage() + { + $this->message = new FeedCard($this); + $this->message->sendAt($this->mobiles, $this->atAll); + + return $this->message; + } + + /** + * @return false|\Psr\Http\Message\ResponseInterface + */ + public function send() + { + if (! $this->config['enabled']) { + return false; + } + try { + return $this->client->send($this->message->getBody()); + } catch (Throwable $e) { + ApplicationContext::getContainer()->get(StdoutLoggerInterface::class)->error(format_throwable($e)); + + return false; + } + } + + /** + * @param $config + */ + protected function createClient($config): Client + { + return new Client($config); + } +} diff --git a/src/Http/Client.php b/src/Http/Client.php new file mode 100644 index 0000000..5e5ccb1 --- /dev/null +++ b/src/Http/Client.php @@ -0,0 +1,87 @@ +config = $config; + $options = $config['options'] ?? []; + if (empty($config['token'])) { + throw new InvalidArgumentException('Token cannot be empty'); + } + $this->token = $config['token']; + if (! isset($options['base_uri'])) { + $options['base_uri'] = sprintf('https://%s', 'oapi.dingtalk.com/robot/send'); + } + if (! isset($options['handler']) && class_exists(CoroutineHandler::class)) { + $options['handler'] = new CoroutineHandler(); + } + $this->options = $options; + } + + public function getOptions(): array + { + return $this->options; + } + + /** + * @throws \GuzzleHttp\Exception\GuzzleException + * @throws \JsonException + */ + public function send(array $params = []): ResponseInterface + { + $client = new \GuzzleHttp\Client($this->options); + + return $client->post($this->getRobotUrl(), array_merge($this->options, [ + 'body' => json_encode($params, JSON_THROW_ON_ERROR), + 'headers' => [ + 'Content-Type' => 'application/json', + ], + 'verify' => $this->config['ssl_verify'] ?? true, + ])); + } + + protected function getRobotUrl(): string + { + $query['access_token'] = $this->token; + if (isset($this->config['secret']) && $secret = $this->config['secret']) { + try { + $timestamp = time() . sprintf('%03d', random_int(1, 999)); + $sign = hash_hmac('sha256', $timestamp . "\n" . $secret, $secret, true); + $query['timestamp'] = $timestamp; + $query['sign'] = base64_encode($sign); + } catch (\Exception $e) { + } + } + + return $this->options['base_uri'] . '?' . http_build_query($query); + } +} diff --git a/src/Messages/ActionCard.php b/src/Messages/ActionCard.php new file mode 100644 index 0000000..c4cb79f --- /dev/null +++ b/src/Messages/ActionCard.php @@ -0,0 +1,61 @@ +service = $service; + $this->setMessage($title, $markdown, $hideAvatar, $btnOrientation); + } + + public function setMessage($title, $markdown, $hideAvatar = 0, $btnOrientation = 0): void + { + $this->message = [ + 'msgtype' => 'actionCard', + 'actionCard' => [ + 'title' => $title, + 'text' => $markdown, + 'hideAvatar' => $hideAvatar, + 'btnOrientation' => $btnOrientation, + ], + ]; + } + + public function single($title, $url): ActionCard + { + $this->message['actionCard']['singleTitle'] = $title; + $this->message['actionCard']['singleURL'] = $url; + $this->service->setMessage($this); + + return $this; + } + + public function addButtons($title, $url): ActionCard + { + $this->message['actionCard']['btns'][] = [ + 'title' => $title, + 'actionURL' => $url, + ]; + + return $this; + } + + public function send() + { + $this->service->setMessage($this); + + return $this->service->send(); + } +} diff --git a/src/Messages/FeedCard.php b/src/Messages/FeedCard.php new file mode 100644 index 0000000..173b139 --- /dev/null +++ b/src/Messages/FeedCard.php @@ -0,0 +1,50 @@ +service = $service; + $this->setMessage(); + } + + public function setMessage(): void + { + $this->message = [ + 'feedCard' => [ + 'links' => [], + ], + 'msgtype' => 'feedCard', + ]; + } + + public function addLinks($title, $messageUrl, $picUrl): FeedCard + { + $this->message['feedCard']['links'][] = [ + 'title' => $title, + 'messageURL' => $messageUrl, + 'picURL' => $picUrl, + ]; + + return $this; + } + + public function send() + { + $this->service->setMessage($this); + + return $this->service->send(); + } +} diff --git a/src/Messages/Link.php b/src/Messages/Link.php new file mode 100644 index 0000000..a7c4e05 --- /dev/null +++ b/src/Messages/Link.php @@ -0,0 +1,30 @@ +setMessage($title, $text, $messageUrl, $picUrl); + } + + public function setMessage($title, $text, $messageUrl, $picUrl = ''): void + { + $this->message = [ + 'msgtype' => 'link', + 'link' => [ + 'text' => $text, + 'title' => $title, + 'picUrl' => $picUrl, + 'messageUrl' => $messageUrl, + ], + ]; + } +} diff --git a/src/Messages/Markdown.php b/src/Messages/Markdown.php new file mode 100644 index 0000000..8d29173 --- /dev/null +++ b/src/Messages/Markdown.php @@ -0,0 +1,28 @@ +setMessage($title, $markdown); + } + + public function setMessage($title, $markdown): void + { + $this->message = [ + 'msgtype' => 'markdown', + 'markdown' => [ + 'title' => $title, + 'text' => $markdown, + ], + ]; + } +} diff --git a/src/Messages/Message.php b/src/Messages/Message.php new file mode 100644 index 0000000..93d03c2 --- /dev/null +++ b/src/Messages/Message.php @@ -0,0 +1,47 @@ +message; + } + + public function sendAt($mobiles = [], $atAll = false): Message + { + $this->at = $this->makeAt($mobiles, $atAll); + + return $this; + } + + public function getBody(): array + { + if (empty($this->at)) { + $this->sendAt(); + } + + return $this->message + $this->at; + } + + protected function makeAt($mobiles = [], $atAll = false): array + { + return [ + 'at' => [ + 'atMobiles' => $mobiles, + 'isAtAll' => $atAll, + ], + ]; + } +} diff --git a/src/Messages/Text.php b/src/Messages/Text.php new file mode 100644 index 0000000..aa32fc4 --- /dev/null +++ b/src/Messages/Text.php @@ -0,0 +1,22 @@ +message = [ + 'msgtype' => 'text', + 'text' => [ + 'content' => $content, + ], + ]; + } +} diff --git a/tests/Cases/AbstractTestCase.php b/tests/Cases/AbstractTestCase.php new file mode 100644 index 0000000..dc959ef --- /dev/null +++ b/tests/Cases/AbstractTestCase.php @@ -0,0 +1,18 @@ +assertTrue(true); + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..524b358 --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,9 @@ +