初始化
This commit is contained in:
commit
4bee43fc17
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
!.gitignore
|
||||||
|
!.gitattributes
|
||||||
|
*.DS_Store
|
||||||
|
*.idea
|
||||||
|
*.svn
|
||||||
|
*.git
|
||||||
|
composer.lock
|
||||||
|
*.cache
|
||||||
|
vendor
|
||||||
|
config
|
85
.php-cs-fixer.php
Normal file
85
.php-cs-fixer.php
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
$header = '';
|
||||||
|
|
||||||
|
return (new \PhpCsFixer\Config())
|
||||||
|
->setRiskyAllowed(true)
|
||||||
|
->setRules([
|
||||||
|
'@PSR2' => true,
|
||||||
|
'@Symfony' => true,
|
||||||
|
'@DoctrineAnnotation' => true,
|
||||||
|
'@PhpCsFixer' => true,
|
||||||
|
'header_comment' => [
|
||||||
|
'comment_type' => '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('public')
|
||||||
|
->exclude('runtime')
|
||||||
|
->exclude('vendor')
|
||||||
|
->in(__DIR__)
|
||||||
|
)
|
||||||
|
->setUsingCache(false)
|
||||||
|
;
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) Vinchan
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
51
README.md
Normal file
51
README.md
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
## 三方平台消息通知组件
|
||||||
|
|
||||||
|
## 功能
|
||||||
|
|
||||||
|
* 支持钉钉群机器人、飞书群机器人、企业微信群机器人
|
||||||
|
* 支持扩展自定义通道&消息模板
|
||||||
|
|
||||||
|
## 环境要求
|
||||||
|
|
||||||
|
* hyperf >= 2.2
|
||||||
|
|
||||||
|
## 安装
|
||||||
|
|
||||||
|
```sh
|
||||||
|
composer require tm-wms/message-notify
|
||||||
|
```
|
||||||
|
|
||||||
|
## 配置文件
|
||||||
|
|
||||||
|
发布配置文件`config/message_notify.php`
|
||||||
|
|
||||||
|
```sh
|
||||||
|
php bin/hyperf.php vendor:publish tm-wms/message-notify
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use TmWms\MessageNotify\Notify;
|
||||||
|
use TmWms\MessageNotify\Channel\DingTalkChannel;
|
||||||
|
use TmWms\MessageNotify\Template\Text\DingTalk;
|
||||||
|
|
||||||
|
// 初始通道
|
||||||
|
$channel = new DingTalkChannel();
|
||||||
|
|
||||||
|
// 初始消息模板
|
||||||
|
$template = (new DingTalk())
|
||||||
|
->setTitle('test')
|
||||||
|
->setText('test')
|
||||||
|
//@所有人
|
||||||
|
->setAt([
|
||||||
|
'all'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 发送
|
||||||
|
Notify::make($channel)->send($template);
|
||||||
|
|
||||||
|
```
|
||||||
|
|
46
composer.json
Normal file
46
composer.json
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
{
|
||||||
|
"name": "tm-wms/message-notify",
|
||||||
|
"description": "MIT",
|
||||||
|
"license": "MIT",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "zyimm",
|
||||||
|
"email": "zyimm.qq.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"TmWms\\MessageNotify\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload-dev": {
|
||||||
|
"psr-4": {
|
||||||
|
"TmWms\\MessageNotifyTest\\": "test/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.4",
|
||||||
|
"ext-json": "*",
|
||||||
|
"hyperf/guzzle": "^1.1|^2.1|^3.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"friendsofphp/php-cs-fixer": "^3.0",
|
||||||
|
"phpunit/phpunit": "^9.4",
|
||||||
|
"hyperf/di": "^2.2|^3.0",
|
||||||
|
"hyperf/utils": "^2.2|^3.0",
|
||||||
|
"hyperf/config": "*",
|
||||||
|
"hyperf/ide-helper": "v2.2.*"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "phpunit -c phpunit.xml --colors=always",
|
||||||
|
"cs-fix": "./vendor/bin/php-cs-fixer fix"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"sort-packages": true
|
||||||
|
},
|
||||||
|
"extra": {
|
||||||
|
"hyperf": {
|
||||||
|
"config": "TmWms\\MessageNotify\\ConfigProvider"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
21
phpunit.xml
Normal file
21
phpunit.xml
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<phpunit backupGlobals="false"
|
||||||
|
backupStaticAttributes="false"
|
||||||
|
bootstrap="./test/bootstrap.php"
|
||||||
|
colors="true"
|
||||||
|
convertErrorsToExceptions="true"
|
||||||
|
convertNoticesToExceptions="true"
|
||||||
|
convertWarningsToExceptions="true"
|
||||||
|
processIsolation="false"
|
||||||
|
stopOnFailure="false">
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="Tests">
|
||||||
|
<directory suffix="Test.php">./test</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
<coverage processUncoveredFiles="true">
|
||||||
|
<include>
|
||||||
|
<directory suffix=".php">./src</directory>
|
||||||
|
</include>
|
||||||
|
</coverage>
|
||||||
|
</phpunit>
|
56
publish/message_notify.php
Normal file
56
publish/message_notify.php
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use TmWms\MessageNotify\Channel\DingTalkChannel;
|
||||||
|
use TmWms\MessageNotify\Channel\FeiShuChannel;
|
||||||
|
use TmWms\MessageNotify\Channel\WechatChannel;
|
||||||
|
use TmWms\MessageNotify\Notify;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'default' => env('NOTIFY_DEFAULT_CHANNEL', 'DingTalk'),
|
||||||
|
'channels' => [
|
||||||
|
// 钉钉群机器人
|
||||||
|
'DingTalk' => [
|
||||||
|
'default' => Notify::INFO,
|
||||||
|
'channel' => DingTalkChannel::class,
|
||||||
|
'pipeline' => [
|
||||||
|
// 业务信息告警群
|
||||||
|
Notify::INFO => [
|
||||||
|
'token' => env('NOTIFY_DINGTALK_TOKEN', ''),
|
||||||
|
'secret' => env('NOTIFY_DINGTALK_SECRET', ''),
|
||||||
|
'keyword' => env('NOTIFY_DINGTALK_KEYWORD', ['test']),
|
||||||
|
],
|
||||||
|
// 错误信息告警群
|
||||||
|
Notify::ERROR => [
|
||||||
|
'token' => env('NOTIFY_DINGTALK_TOKEN', ''),
|
||||||
|
'secret' => env('NOTIFY_DINGTALK_SECRET', ''),
|
||||||
|
'keyword' => env('NOTIFY_DINGTALK_KEYWORD', []),
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
|
||||||
|
// 飞书群机器人
|
||||||
|
'Feishu' => [
|
||||||
|
'default' => Notify::INFO,
|
||||||
|
'channel' => FeiShuChannel::class,
|
||||||
|
'pipeline' => [
|
||||||
|
'info' => [
|
||||||
|
'token' => env('NOTIFY_FEISHU_TOKEN', ''),
|
||||||
|
'secret' => env('NOTIFY_FEISHU_SECRET', ''),
|
||||||
|
'keyword' => env('NOTIFY_FEISHU_KEYWORD'),
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
// 企业微信群机器人
|
||||||
|
'Wechat' => [
|
||||||
|
'default' => Notify::INFO,
|
||||||
|
'channel' => WechatChannel::class,
|
||||||
|
'pipeline' => [
|
||||||
|
'info' => [
|
||||||
|
'token' => env('NOTIFY_WECHAT_TOKEN'),
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
40
src/Channel/AbstractChannel.php
Normal file
40
src/Channel/AbstractChannel.php
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace TmWms\MessageNotify\Channel;
|
||||||
|
|
||||||
|
use Hyperf\Contract\ConfigInterface;
|
||||||
|
use Hyperf\Utils\ApplicationContext;
|
||||||
|
use TmWms\MessageNotify\Contracts\MessageChannelInterface;
|
||||||
|
use TmWms\MessageNotify\Exceptions\MessageNotificationException;
|
||||||
|
use TmWms\MessageNotify\Template\AbstractTemplate;
|
||||||
|
|
||||||
|
abstract class AbstractChannel implements MessageChannelInterface
|
||||||
|
{
|
||||||
|
protected AbstractTemplate $template;
|
||||||
|
|
||||||
|
public string $channelUrl;
|
||||||
|
|
||||||
|
protected string $type;
|
||||||
|
|
||||||
|
public function setTemplate(AbstractTemplate $template): AbstractChannel
|
||||||
|
{
|
||||||
|
$this->template = $template;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getConfig
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getConfig()
|
||||||
|
{
|
||||||
|
if (class_exists(ApplicationContext::class)) {
|
||||||
|
$configContext = make(ConfigInterface::class);
|
||||||
|
return $configContext->get('message_notify.channels.'.$this->type);
|
||||||
|
}
|
||||||
|
throw new MessageNotificationException('ApplicationContext is not exist');
|
||||||
|
}
|
||||||
|
}
|
81
src/Channel/DingTalkChannel.php
Normal file
81
src/Channel/DingTalkChannel.php
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace TmWms\MessageNotify\Channel;
|
||||||
|
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
use GuzzleHttp\Exception\GuzzleException;
|
||||||
|
use GuzzleHttp\RequestOptions;
|
||||||
|
use TmWms\MessageNotify\Exceptions\MessageNotificationException;
|
||||||
|
|
||||||
|
class DingTalkChannel extends AbstractChannel
|
||||||
|
{
|
||||||
|
public string $channelUrl = 'https://oapi.dingtalk.com';
|
||||||
|
|
||||||
|
protected string $type = 'DingTalk';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* send
|
||||||
|
*
|
||||||
|
* @throws GuzzleException
|
||||||
|
*/
|
||||||
|
public function send(): bool
|
||||||
|
{
|
||||||
|
|
||||||
|
$client = $this->getClient();
|
||||||
|
|
||||||
|
$option = [
|
||||||
|
RequestOptions::HEADERS => [],
|
||||||
|
RequestOptions::JSON => $this->template->getBody(),
|
||||||
|
];
|
||||||
|
$request = $client->post('', $option);
|
||||||
|
$result = json_decode($request->getBody()->getContents(), true);
|
||||||
|
|
||||||
|
if ($result['errcode'] !== 0) {
|
||||||
|
throw new MessageNotificationException($result['errmsg']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getClient
|
||||||
|
*
|
||||||
|
* @return Client|mixed
|
||||||
|
*/
|
||||||
|
public function getClient()
|
||||||
|
{
|
||||||
|
$query = $this->buildQuery($this->template->getPipeline());
|
||||||
|
$config['base_uri'] = $this->channelUrl.'/robot/send?'.$query;
|
||||||
|
return make(Client::class, [$config]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* buildQuery
|
||||||
|
*
|
||||||
|
* @param string $pipeline
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function buildQuery(string $pipeline): string
|
||||||
|
{
|
||||||
|
$timestamp = time() * 1000;
|
||||||
|
|
||||||
|
$config = $this->getConfig();
|
||||||
|
var_dump($config);
|
||||||
|
$config = $config['pipeline'][$pipeline] ?? $config['pipeline'][$config['default']];
|
||||||
|
$sign = '';
|
||||||
|
if(isset($config['secret'])){
|
||||||
|
$secret = hash_hmac('sha256', $timestamp."\n".$config['secret'], $config['secret'], true);
|
||||||
|
$sign = urlencode(base64_encode($secret));
|
||||||
|
}
|
||||||
|
|
||||||
|
$query = [
|
||||||
|
'access_token' => $config['token'],
|
||||||
|
'timestamp' => $timestamp,
|
||||||
|
'sign' => $sign
|
||||||
|
];
|
||||||
|
return http_build_query($query);
|
||||||
|
}
|
||||||
|
}
|
86
src/Channel/FeiShuChannel.php
Normal file
86
src/Channel/FeiShuChannel.php
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace TmWms\MessageNotify\Channel;
|
||||||
|
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
use GuzzleHttp\Exception\GuzzleException;
|
||||||
|
use GuzzleHttp\RequestOptions;
|
||||||
|
use TmWms\MessageNotify\Exceptions\MessageNotificationException;
|
||||||
|
|
||||||
|
class FeiShuChannel extends AbstractChannel
|
||||||
|
{
|
||||||
|
public string $channelUrl = 'https://open.feishu.cn';
|
||||||
|
|
||||||
|
protected string $type = 'Feishu';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* send
|
||||||
|
*
|
||||||
|
* @throws GuzzleException
|
||||||
|
*/
|
||||||
|
public function send(): bool
|
||||||
|
{
|
||||||
|
$client = $this->getClient($this->template->getPipeline());
|
||||||
|
|
||||||
|
$timestamp = time();
|
||||||
|
$config = [
|
||||||
|
'timestamp' => $timestamp,
|
||||||
|
'sign' => $this->buildSign($timestamp, $this->template->getPipeline()),
|
||||||
|
];
|
||||||
|
|
||||||
|
$option = [
|
||||||
|
RequestOptions::HEADERS => [],
|
||||||
|
RequestOptions::JSON => array_merge($config, $this->template->getBody()),
|
||||||
|
];
|
||||||
|
|
||||||
|
$request = $client->post('', $option);
|
||||||
|
$result = json_decode($request->getBody()->getContents(), true);
|
||||||
|
|
||||||
|
if (!isset($result['StatusCode']) || $result['StatusCode'] !== 0) {
|
||||||
|
throw new MessageNotificationException($result['msg']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getClient
|
||||||
|
*
|
||||||
|
* @param string $pipeline
|
||||||
|
* @return Client
|
||||||
|
*/
|
||||||
|
public function getClient(string $pipeline): Client
|
||||||
|
{
|
||||||
|
$config = $this->config($pipeline);
|
||||||
|
$config['base_uri'] = $this->channelUrl.'/open-apis/bot/v2/hook/'.$config['token'];
|
||||||
|
return make(Client::class, $config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* buildSign
|
||||||
|
*
|
||||||
|
* @param int $timestamp
|
||||||
|
* @param string $pipeline
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function buildSign(int $timestamp, string $pipeline): string
|
||||||
|
{
|
||||||
|
$config = $this->config($pipeline);
|
||||||
|
$secret = hash_hmac('sha256', '', $timestamp."\n".$config['secret'], true);
|
||||||
|
return base64_encode($secret);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* config
|
||||||
|
*
|
||||||
|
* @param string $pipeline
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
private function config(string $pipeline)
|
||||||
|
{
|
||||||
|
$config = $this->getConfig();
|
||||||
|
return $config['pipeline'][$pipeline] ?? $config['pipeline'][$config['default']];
|
||||||
|
}
|
||||||
|
}
|
72
src/Channel/WechatChannel.php
Normal file
72
src/Channel/WechatChannel.php
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace TmWms\MessageNotify\Channel;
|
||||||
|
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
use GuzzleHttp\Exception\GuzzleException;
|
||||||
|
use GuzzleHttp\RequestOptions;
|
||||||
|
use TmWms\MessageNotify\Exceptions\MessageNotificationException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信通知
|
||||||
|
*/
|
||||||
|
class WechatChannel extends AbstractChannel
|
||||||
|
{
|
||||||
|
public string $channelUrl = 'https://qyapi.weixin.qq.com';
|
||||||
|
|
||||||
|
protected string $type = 'Wechat';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* send
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* @throws GuzzleException
|
||||||
|
*/
|
||||||
|
public function send(): bool
|
||||||
|
{
|
||||||
|
$client = $this->getClient($this->template->getPipeline());
|
||||||
|
$option = [
|
||||||
|
RequestOptions::HEADERS => [],
|
||||||
|
RequestOptions::JSON => $this->template->getBody(),
|
||||||
|
];
|
||||||
|
$request = $client->post('', $option);
|
||||||
|
$result = json_decode($request->getBody()->getContents(), true);
|
||||||
|
|
||||||
|
if ($result['errcode'] !== 0) {
|
||||||
|
throw new MessageNotificationException($result['errmsg']);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getClient
|
||||||
|
*
|
||||||
|
* @param string $pipeline
|
||||||
|
* @return Client|mixed
|
||||||
|
*/
|
||||||
|
private function getClient(string $pipeline)
|
||||||
|
{
|
||||||
|
$config = $this->config($pipeline);
|
||||||
|
|
||||||
|
$config['base_uri'] = $this->channelUrl.'/cgi-bin/webhook/send?key=' . $config['token'];
|
||||||
|
|
||||||
|
return make(Client::class, $config);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* config
|
||||||
|
*
|
||||||
|
* @param string $pipeline
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function config(string $pipeline)
|
||||||
|
{
|
||||||
|
$config = $this->getConfig();
|
||||||
|
return $config['pipeline'][$pipeline] ?? $config['pipeline'][$config['default']];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
31
src/ConfigProvider.php
Normal file
31
src/ConfigProvider.php
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace TmWms\MessageNotify;
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigProvider
|
||||||
|
{
|
||||||
|
public function __invoke(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'dependencies' => [],
|
||||||
|
'annotations' => [
|
||||||
|
'scan' => [
|
||||||
|
'paths' => [
|
||||||
|
__DIR__,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'publish' => [
|
||||||
|
[
|
||||||
|
'id' => 'config',
|
||||||
|
'description' => 'The config of message client.',
|
||||||
|
'source' => __DIR__.'/../publish/message_notify.php',
|
||||||
|
'destination' => BASE_PATH.'/config/autoload/message_notify.php',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
24
src/Connection.php
Normal file
24
src/Connection.php
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace TmWms\MessageNotify;
|
||||||
|
|
||||||
|
use TmWms\MessageNotify\Channel\AbstractChannel;
|
||||||
|
use TmWms\MessageNotify\Template\AbstractTemplate;
|
||||||
|
|
||||||
|
class Connection
|
||||||
|
{
|
||||||
|
protected AbstractChannel $channel;
|
||||||
|
|
||||||
|
public function __construct(AbstractChannel $channel)
|
||||||
|
{
|
||||||
|
$this->channel = $channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function send(AbstractTemplate $template): bool
|
||||||
|
{
|
||||||
|
return $this->channel->setTemplate($template)->send();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
8
src/Contracts/MessageChannelInterface.php
Normal file
8
src/Contracts/MessageChannelInterface.php
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace TmWms\MessageNotify\Contracts;
|
||||||
|
|
||||||
|
interface MessageChannelInterface
|
||||||
|
{
|
||||||
|
public function send();
|
||||||
|
}
|
10
src/Contracts/MessageNotifyInterface.php
Normal file
10
src/Contracts/MessageNotifyInterface.php
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace TmWms\MessageNotify\Contracts;
|
||||||
|
|
||||||
|
interface MessageNotifyInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
9
src/Exceptions/MessageNotificationException.php
Normal file
9
src/Exceptions/MessageNotificationException.php
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace TmWms\MessageNotify\Exceptions;
|
||||||
|
|
||||||
|
class MessageNotificationException extends \RuntimeException
|
||||||
|
{
|
||||||
|
}
|
33
src/Notify.php
Normal file
33
src/Notify.php
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace TmWms\MessageNotify;
|
||||||
|
|
||||||
|
use TmWms\MessageNotify\Channel\AbstractChannel;
|
||||||
|
|
||||||
|
class Notify
|
||||||
|
{
|
||||||
|
public const INFO = 'info';
|
||||||
|
|
||||||
|
public const ERROR = 'error';
|
||||||
|
|
||||||
|
public const EMERGENCY = 'emergency';
|
||||||
|
|
||||||
|
public const ALERT = 'alert';
|
||||||
|
|
||||||
|
public const CRITICAL = 'critical';
|
||||||
|
|
||||||
|
public const WARNING = 'warning';
|
||||||
|
|
||||||
|
public const NOTICE = 'notice';
|
||||||
|
|
||||||
|
public const DEBUG = 'debug';
|
||||||
|
|
||||||
|
public static function make(?AbstractChannel $channel = null): Connection
|
||||||
|
{
|
||||||
|
return make(Connection::class, [
|
||||||
|
$channel
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
69
src/Template/AbstractTemplate.php
Normal file
69
src/Template/AbstractTemplate.php
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace TmWms\MessageNotify\Template;
|
||||||
|
|
||||||
|
use TmWms\MessageNotify\Notify;
|
||||||
|
|
||||||
|
abstract class AbstractTemplate
|
||||||
|
{
|
||||||
|
protected array $at = [];
|
||||||
|
|
||||||
|
protected string $pipeline = Notify::INFO;
|
||||||
|
|
||||||
|
protected string $text = '';
|
||||||
|
|
||||||
|
protected string $title = '';
|
||||||
|
|
||||||
|
abstract public function getBody();
|
||||||
|
|
||||||
|
public function getText(): string
|
||||||
|
{
|
||||||
|
return $this->text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setText(string $text): AbstractTemplate
|
||||||
|
{
|
||||||
|
$this->text = $text;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle(): string
|
||||||
|
{
|
||||||
|
return $this->title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setTitle(string $title): AbstractTemplate
|
||||||
|
{
|
||||||
|
$this->title = $title;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPipeline(): string
|
||||||
|
{
|
||||||
|
return $this->pipeline;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPipeline(string $pipeline): AbstractTemplate
|
||||||
|
{
|
||||||
|
$this->pipeline = $pipeline;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setAt(array $at = []): AbstractTemplate
|
||||||
|
{
|
||||||
|
$this->at = $at;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAt(): array
|
||||||
|
{
|
||||||
|
return $this->at;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAtAll(): bool
|
||||||
|
{
|
||||||
|
return in_array('all', $this->at) || in_array('ALL', $this->at);
|
||||||
|
}
|
||||||
|
}
|
24
src/Template/MarkDown/DingTalk.php
Normal file
24
src/Template/MarkDown/DingTalk.php
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace TmWms\MessageNotify\Template\MarkDown;
|
||||||
|
|
||||||
|
use TmWms\MessageNotify\Template\AbstractTemplate;
|
||||||
|
|
||||||
|
class DingTalk extends AbstractTemplate
|
||||||
|
{
|
||||||
|
|
||||||
|
public function getBody(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'msgtype' => 'markdown',
|
||||||
|
'markdown' => [
|
||||||
|
'title' => $this->getTitle(),
|
||||||
|
'text' => $this->getText(),
|
||||||
|
],
|
||||||
|
'at' => [
|
||||||
|
'isAtAll' => $this->isAtAll(),
|
||||||
|
'atMobiles' => $this->getAt(),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
72
src/Template/MarkDown/FeiShu.php
Normal file
72
src/Template/MarkDown/FeiShu.php
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace TmWms\MessageNotify\Template\MarkDown;
|
||||||
|
|
||||||
|
use TmWms\MessageNotify\Template\AbstractTemplate;
|
||||||
|
|
||||||
|
class FeiShu extends AbstractTemplate
|
||||||
|
{
|
||||||
|
|
||||||
|
public function getBody(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'msg_type' => 'post',
|
||||||
|
'content' => [
|
||||||
|
'post' => [
|
||||||
|
'zh_cn' => [
|
||||||
|
'title' => $this->getTitle(),
|
||||||
|
'content' => [$this->getFeiShuText()],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function getFeiShuText(): array
|
||||||
|
{
|
||||||
|
$text = is_array($this->getText()) ? $this->getText() : json_decode($this->getText(), true) ?? [
|
||||||
|
[
|
||||||
|
'tag' => 'text',
|
||||||
|
'text' => $this->getText(),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$at = $this->getFeiShuAt();
|
||||||
|
|
||||||
|
return array_merge($text, $at);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getFeiShuAt(): array
|
||||||
|
{
|
||||||
|
$result = [];
|
||||||
|
if ($this->isAtAll()) {
|
||||||
|
$result[] = [
|
||||||
|
'tag' => 'at',
|
||||||
|
'user_id' => 'all',
|
||||||
|
];
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
$at = $this->getAt();
|
||||||
|
foreach ($at as $item) {
|
||||||
|
// TODO::需要加入邮箱与收集@人
|
||||||
|
if (strchr($item, '@') === false) {
|
||||||
|
$result[] = [
|
||||||
|
'tag' => 'at',
|
||||||
|
'email' => $item,
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
$result[] = [
|
||||||
|
'tag' => 'at',
|
||||||
|
'user_id' => $item,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
21
src/Template/MarkDown/Wechat.php
Normal file
21
src/Template/MarkDown/Wechat.php
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace TmWms\MessageNotify\Template\MarkDown;
|
||||||
|
|
||||||
|
use TmWms\MessageNotify\Template\AbstractTemplate;
|
||||||
|
|
||||||
|
class Wechat extends AbstractTemplate
|
||||||
|
{
|
||||||
|
|
||||||
|
public function getBody(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'msgtype' => 'markdown',
|
||||||
|
'markdown' => [
|
||||||
|
'content' => $this->getTitle().$this->getText(),
|
||||||
|
'mentioned_list' => in_array('all', $this->getAt()) ? [] : [$this->getAt()],
|
||||||
|
'mentioned_mobile_list' => in_array('all', $this->getAt()) ? ['@all'] : [$this->getAt()],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
22
src/Template/Text/DingTalk.php
Normal file
22
src/Template/Text/DingTalk.php
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace TmWms\MessageNotify\Template\Text;
|
||||||
|
|
||||||
|
use TmWms\MessageNotify\Template\AbstractTemplate;
|
||||||
|
|
||||||
|
class DingTalk extends AbstractTemplate
|
||||||
|
{
|
||||||
|
public function getBody(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'msgtype' => 'text',
|
||||||
|
'text' => [
|
||||||
|
'content' => $this->getText(),
|
||||||
|
],
|
||||||
|
'at' => [
|
||||||
|
'isAtAll' => $this->isAtAll(),
|
||||||
|
'atMobiles' => $this->getAt(),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
42
src/Template/Text/FeiShu.php
Normal file
42
src/Template/Text/FeiShu.php
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace TmWms\MessageNotify\Template\Text;
|
||||||
|
|
||||||
|
use TmWms\MessageNotify\Template\AbstractTemplate;
|
||||||
|
|
||||||
|
class FeiShu extends AbstractTemplate
|
||||||
|
{
|
||||||
|
|
||||||
|
public function getBody(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'msg_type' => 'text',
|
||||||
|
'content' => [
|
||||||
|
'text' => $this->getText() . $this->getFeiShuAt(),
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getFeiShuAt
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function getFeiShuAt(): string
|
||||||
|
{
|
||||||
|
if ($this->isAtAll()) {
|
||||||
|
return '<at user_id="all">所有人</at>';
|
||||||
|
}
|
||||||
|
|
||||||
|
$at = $this->getAt();
|
||||||
|
$result = '';
|
||||||
|
foreach ($at as $item) {
|
||||||
|
if (strchr($item, '@') === false) {
|
||||||
|
$result .= '<at phone="' . $item . '">' . $item . '</at>';
|
||||||
|
} else {
|
||||||
|
$result .= '<at email="' . $item . '">' . $item . '</at>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
21
src/Template/Text/Wechat.php
Normal file
21
src/Template/Text/Wechat.php
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace TmWms\MessageNotify\Template\Text;
|
||||||
|
|
||||||
|
use TmWms\MessageNotify\Template\AbstractTemplate;
|
||||||
|
|
||||||
|
class Wechat extends AbstractTemplate
|
||||||
|
{
|
||||||
|
|
||||||
|
public function getBody(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'msgtype' => 'text',
|
||||||
|
'text' => [
|
||||||
|
'content' => $this->getText(),
|
||||||
|
'mentioned_list' => in_array('all', $this->getAt()) ? [] : [$this->getAt()],
|
||||||
|
'mentioned_mobile_list' => in_array('all', $this->getAt()) ? ['@all'] : [$this->getAt()],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
32
test/NotifyTest.php
Normal file
32
test/NotifyTest.php
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace TmWms\MessageNotifyTest;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use TmWms\MessageNotify\Channel\DingTalkChannel;
|
||||||
|
use TmWms\MessageNotify\Notify;
|
||||||
|
use TmWms\MessageNotify\Template\Text\DingTalk;
|
||||||
|
|
||||||
|
|
||||||
|
class NotifyTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testCase()
|
||||||
|
{
|
||||||
|
// 初始通道
|
||||||
|
$channel = new DingTalkChannel();
|
||||||
|
|
||||||
|
// 初始消息模板
|
||||||
|
$template = (new DingTalk())
|
||||||
|
->setTitle('test')
|
||||||
|
->setText('test')
|
||||||
|
//@所有人
|
||||||
|
->setAt([
|
||||||
|
'all'
|
||||||
|
]);
|
||||||
|
// 发送
|
||||||
|
Notify::make($channel)->send($template);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
12
test/bootstrap.php
Normal file
12
test/bootstrap.php
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
use Hyperf\Di\Container;
|
||||||
|
use Hyperf\Di\Definition\DefinitionSourceFactory;
|
||||||
|
use Hyperf\Utils\ApplicationContext;
|
||||||
|
|
||||||
|
! defined('BASE_PATH') && define('BASE_PATH', dirname(__DIR__, 1));
|
||||||
|
|
||||||
|
$container = new Container((new DefinitionSourceFactory(true))());
|
||||||
|
|
||||||
|
ApplicationContext::setContainer($container);
|
Loading…
Reference in New Issue
Block a user