DingTalkChannel.php 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. <?php
  2. namespace App\Channels;
  3. use Cache;
  4. use Helpers;
  5. use Http;
  6. use Illuminate\Notifications\Notification;
  7. use Log;
  8. use Str;
  9. class DingTalkChannel
  10. {
  11. // https://open.dingtalk.com/document/robots/custom-robot-access
  12. public function send($notifiable, Notification $notification)
  13. {
  14. $cacheKey = 'dingTalkCount'.date('d');
  15. if (Cache::has($cacheKey)) {
  16. Cache::increment($cacheKey);
  17. } else {
  18. Cache::put($cacheKey, 1, Minute); // 1分钟
  19. }
  20. if (Cache::get($cacheKey) > 20) { // 每个机器人每分钟最多发送20条消息到群里,如果超过20条,会限流10分钟。
  21. Log::critical('[钉钉] 消息推送异常:每个机器人每分钟最多发送20条消息到群里,如果超过20条,会限流10分钟。');
  22. return false;
  23. }
  24. $message = $notification->toCustom($notifiable);
  25. $url = 'https://oapi.dingtalk.com/robot/send?';
  26. $query['access_token'] = sysConfig('dingTalk_access_token');
  27. if (sysConfig('dingTalk_secret') !== null) {
  28. $timestamp = time() * 1000;
  29. $query['timestamp'] = $timestamp;
  30. $query['sign'] = $this->sign($timestamp);
  31. }
  32. $url .= http_build_query($query);
  33. if (isset($message['button'])) { // 独立跳转ActionCard类型
  34. $body = [
  35. 'msgtype' => 'actionCard',
  36. 'actionCard' => [
  37. 'title' => $message['title'],
  38. 'text' => $message['markdown'],
  39. 'btnOrientation' => 1,
  40. 'btns' => [
  41. [
  42. 'title' => trans('common.status.reject'),
  43. 'actionURL' => $message['button'][0],
  44. ],
  45. [
  46. 'title' => trans('common.confirm'),
  47. 'actionURL' => $message['button'][1],
  48. ],
  49. ],
  50. ],
  51. ];
  52. } elseif (isset($message['url_type'])) { // 文本卡片
  53. $msgId = Str::uuid(); // 生成对公消息查询URL
  54. $body = [
  55. 'msgtype' => 'link',
  56. 'link' => [
  57. 'title' => $message['title'],
  58. 'text' => '请点击下方按钮【查看详情】',
  59. 'messageUrl' => route('message.show', ['type' => $message['url_type'], $msgId]),
  60. ],
  61. ];
  62. } else { // 文本消息
  63. $body = [
  64. 'msgtype' => 'text',
  65. 'text' => [
  66. 'content' => $message['content'],
  67. ],
  68. ];
  69. }
  70. $response = Http::timeout(15)->withBody(json_encode($body, JSON_UNESCAPED_UNICODE), 'application/json;charset=utf-8')->post($url);
  71. // 发送成功
  72. if ($response->ok()) {
  73. $ret = $response->json();
  74. if (! $ret['errcode'] && $ret['errmsg'] === 'ok') {
  75. Helpers::addNotificationLog($message['title'], $message['content'] ?? var_export($message['body'], true), 10, 1, null, $msgId ?? null);
  76. return $ret;
  77. }
  78. // 发送失败
  79. Helpers::addNotificationLog($message['title'], $message['content'] ?? var_export($message['body'], true), 10, -1, $ret ? $ret['errmsg'] : '未知');
  80. return false;
  81. }
  82. // 发送错误
  83. Log::critical('[钉钉] 消息推送异常:'.var_export($response, true));
  84. return false;
  85. }
  86. private function sign(int $timestamp): string // 加签
  87. { // https://open.dingtalk.com/document/robots/customize-robot-security-settings
  88. return base64_encode(hash_hmac('sha256', $timestamp."\n".sysConfig('dingTalk_secret'), sysConfig('dingTalk_secret'), true));
  89. }
  90. }