PushoverHandler.php 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. <?php
  2. /*
  3. * This file is part of the Monolog package.
  4. *
  5. * (c) Jordi Boggiano <j.boggiano@seld.be>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Monolog\Handler;
  11. use Monolog\Logger;
  12. /**
  13. * Sends notifications through the pushover api to mobile phones
  14. *
  15. * @author Sebastian Göttschkes <sebastian.goettschkes@googlemail.com>
  16. * @see https://www.pushover.net/api
  17. */
  18. class PushoverHandler extends SocketHandler
  19. {
  20. private $token;
  21. private $users;
  22. private $title;
  23. private $user;
  24. private $retry;
  25. private $expire;
  26. private $highPriorityLevel;
  27. private $emergencyLevel;
  28. private $useFormattedMessage = false;
  29. /**
  30. * All parameters that can be sent to Pushover
  31. * @see https://pushover.net/api
  32. * @var array
  33. */
  34. private $parameterNames = array(
  35. 'token' => true,
  36. 'user' => true,
  37. 'message' => true,
  38. 'device' => true,
  39. 'title' => true,
  40. 'url' => true,
  41. 'url_title' => true,
  42. 'priority' => true,
  43. 'timestamp' => true,
  44. 'sound' => true,
  45. 'retry' => true,
  46. 'expire' => true,
  47. 'callback' => true,
  48. );
  49. /**
  50. * Sounds the api supports by default
  51. * @see https://pushover.net/api#sounds
  52. * @var array
  53. */
  54. private $sounds = array(
  55. 'pushover', 'bike', 'bugle', 'cashregister', 'classical', 'cosmic', 'falling', 'gamelan', 'incoming',
  56. 'intermission', 'magic', 'mechanical', 'pianobar', 'siren', 'spacealarm', 'tugboat', 'alien', 'climb',
  57. 'persistent', 'echo', 'updown', 'none',
  58. );
  59. /**
  60. * @param string $token Pushover api token
  61. * @param string|array $users Pushover user id or array of ids the message will be sent to
  62. * @param string $title Title sent to the Pushover API
  63. * @param int $level The minimum logging level at which this handler will be triggered
  64. * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not
  65. * @param Boolean $useSSL Whether to connect via SSL. Required when pushing messages to users that are not
  66. * the pushover.net app owner. OpenSSL is required for this option.
  67. * @param int $highPriorityLevel The minimum logging level at which this handler will start
  68. * sending "high priority" requests to the Pushover API
  69. * @param int $emergencyLevel The minimum logging level at which this handler will start
  70. * sending "emergency" requests to the Pushover API
  71. * @param int $retry The retry parameter specifies how often (in seconds) the Pushover servers will send the same notification to the user.
  72. * @param int $expire The expire parameter specifies how many seconds your notification will continue to be retried for (every retry seconds).
  73. */
  74. public function __construct($token, $users, $title = null, $level = Logger::CRITICAL, $bubble = true, $useSSL = true, $highPriorityLevel = Logger::CRITICAL, $emergencyLevel = Logger::EMERGENCY, $retry = 30, $expire = 25200)
  75. {
  76. $connectionString = $useSSL ? 'ssl://api.pushover.net:443' : 'api.pushover.net:80';
  77. parent::__construct($connectionString, $level, $bubble);
  78. $this->token = $token;
  79. $this->users = (array) $users;
  80. $this->title = $title ?: gethostname();
  81. $this->highPriorityLevel = Logger::toMonologLevel($highPriorityLevel);
  82. $this->emergencyLevel = Logger::toMonologLevel($emergencyLevel);
  83. $this->retry = $retry;
  84. $this->expire = $expire;
  85. }
  86. protected function generateDataStream($record)
  87. {
  88. $content = $this->buildContent($record);
  89. return $this->buildHeader($content) . $content;
  90. }
  91. private function buildContent($record)
  92. {
  93. // Pushover has a limit of 512 characters on title and message combined.
  94. $maxMessageLength = 512 - strlen($this->title);
  95. $message = ($this->useFormattedMessage) ? $record['formatted'] : $record['message'];
  96. $message = substr($message, 0, $maxMessageLength);
  97. $timestamp = $record['datetime']->getTimestamp();
  98. $dataArray = array(
  99. 'token' => $this->token,
  100. 'user' => $this->user,
  101. 'message' => $message,
  102. 'title' => $this->title,
  103. 'timestamp' => $timestamp,
  104. );
  105. if (isset($record['level']) && $record['level'] >= $this->emergencyLevel) {
  106. $dataArray['priority'] = 2;
  107. $dataArray['retry'] = $this->retry;
  108. $dataArray['expire'] = $this->expire;
  109. } elseif (isset($record['level']) && $record['level'] >= $this->highPriorityLevel) {
  110. $dataArray['priority'] = 1;
  111. }
  112. // First determine the available parameters
  113. $context = array_intersect_key($record['context'], $this->parameterNames);
  114. $extra = array_intersect_key($record['extra'], $this->parameterNames);
  115. // Least important info should be merged with subsequent info
  116. $dataArray = array_merge($extra, $context, $dataArray);
  117. // Only pass sounds that are supported by the API
  118. if (isset($dataArray['sound']) && !in_array($dataArray['sound'], $this->sounds)) {
  119. unset($dataArray['sound']);
  120. }
  121. return http_build_query($dataArray);
  122. }
  123. private function buildHeader($content)
  124. {
  125. $header = "POST /1/messages.json HTTP/1.1\r\n";
  126. $header .= "Host: api.pushover.net\r\n";
  127. $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
  128. $header .= "Content-Length: " . strlen($content) . "\r\n";
  129. $header .= "\r\n";
  130. return $header;
  131. }
  132. protected function write(array $record)
  133. {
  134. foreach ($this->users as $user) {
  135. $this->user = $user;
  136. parent::write($record);
  137. $this->closeSocket();
  138. }
  139. $this->user = null;
  140. }
  141. public function setHighPriorityLevel($value)
  142. {
  143. $this->highPriorityLevel = $value;
  144. }
  145. public function setEmergencyLevel($value)
  146. {
  147. $this->emergencyLevel = $value;
  148. }
  149. /**
  150. * Use the formatted message?
  151. * @param bool $value
  152. */
  153. public function useFormattedMessage($value)
  154. {
  155. $this->useFormattedMessage = (boolean) $value;
  156. }
  157. }