bullet.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. package bullet
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "io"
  6. "mime"
  7. "net/http"
  8. "path/filepath"
  9. "time"
  10. )
  11. const (
  12. shortTimeFormat = "2006-01-02 15:04"
  13. )
  14. //Bullet client for the Pushbullet API
  15. type Bullet struct {
  16. token string
  17. baseURL string
  18. }
  19. //NewBullet creates new Bullet using provided token
  20. func NewBullet(token string) Bullet {
  21. b := Bullet{token: token, baseURL: "https://api.pushbullet.com/v2"}
  22. return b
  23. }
  24. func (b Bullet) newRequest(body io.Reader, URL string) (*http.Request, error) {
  25. request, err := http.NewRequest(http.MethodPost, URL, body)
  26. if err != nil {
  27. return nil, err
  28. }
  29. request.Header.Add("Access-Token", b.token)
  30. request.Header.Add("Content-Type", "application/json")
  31. return request, nil
  32. }
  33. func (b Bullet) newRequestPush(body io.Reader) (*http.Request, error) {
  34. return b.newRequest(body, b.baseURL+"/pushes")
  35. }
  36. func (b Bullet) newRequestUpload(body io.Reader) (*http.Request, error) {
  37. return b.newRequest(body, b.baseURL+"/upload-request")
  38. }
  39. func doRequest(request *http.Request) (*http.Response, error) {
  40. client := http.Client{}
  41. response, errResponse := client.Do(request)
  42. if errResponse != nil {
  43. return nil, errResponse
  44. }
  45. if response.StatusCode != http.StatusOK {
  46. defer response.Body.Close()
  47. var errBullet bulletError
  48. decoder := json.NewDecoder(response.Body)
  49. errJSON := decoder.Decode(&errBullet)
  50. if errJSON != nil {
  51. return nil, errJSON
  52. }
  53. return nil, errBullet.getError()
  54. }
  55. return response, nil
  56. }
  57. func (b Bullet) send(push pushInterface) error {
  58. reader, errReader := push.getReader()
  59. if errReader != nil {
  60. return errReader
  61. }
  62. request, errRequest := b.newRequestPush(reader)
  63. if errRequest != nil {
  64. return errRequest
  65. }
  66. response, errResponse := doRequest(request)
  67. if errResponse != nil {
  68. return errResponse
  69. }
  70. response.Body.Close()
  71. return nil
  72. }
  73. //SendNote sends push note with given title and text, use empty string as deviceID to send to all
  74. func (b Bullet) SendNote(title, text, deviceID string) error {
  75. note := newNotePush(title, text, deviceID)
  76. err := b.send(note)
  77. return err
  78. }
  79. //SendLink sends push link with given title, text and link, use empty string as deviceID to send to all
  80. func (b Bullet) SendLink(title, text, link, deviceID string) error {
  81. linkPush := newLinkPush(title, text, link, deviceID)
  82. err := b.send(linkPush)
  83. return err
  84. }
  85. func (b Bullet) requestUpload(file fileUpload) (*fileUpload, error) {
  86. reader, errReader := file.getReader()
  87. if errReader != nil {
  88. return nil, errReader
  89. }
  90. request, errRequest := b.newRequestUpload(reader)
  91. if errRequest != nil {
  92. return nil, errRequest
  93. }
  94. response, errResponse := doRequest(request)
  95. if errResponse != nil {
  96. return nil, errResponse
  97. }
  98. defer response.Body.Close()
  99. var result fileUpload
  100. decoder := json.NewDecoder(response.Body)
  101. errJSON := decoder.Decode(&result)
  102. if errJSON != nil {
  103. return nil, errJSON
  104. }
  105. return &result, nil
  106. }
  107. func (b Bullet) uploadFile(path string) (*fileUpload, error) {
  108. extension := filepath.Ext(path)
  109. mimeType := mime.TypeByExtension(extension)
  110. if mimeType == "" {
  111. mimeType = "application/octet-stream"
  112. }
  113. filename := filepath.Base(path)
  114. fileUpload := newFileUpload(filename, mimeType)
  115. uploadResponse, errUpload := b.requestUpload(fileUpload)
  116. if errUpload != nil {
  117. return nil, errUpload
  118. }
  119. request, errRequest := getFileRequest(path, *uploadResponse)
  120. if errRequest != nil {
  121. return nil, errRequest
  122. }
  123. client := http.Client{}
  124. response, errResponse := client.Do(request)
  125. if errResponse != nil {
  126. return nil, errResponse
  127. }
  128. defer response.Body.Close()
  129. if response.StatusCode < http.StatusOK || response.StatusCode >= http.StatusMultipleChoices {
  130. return nil, fmt.Errorf("Error uploading file: %s", response.Status)
  131. }
  132. return uploadResponse, nil
  133. }
  134. //SendFile sends file push with given title, text and file, use empty string as deviceID to send to all
  135. func (b Bullet) SendFile(title, text, file, deviceID string) error {
  136. uploadResult, errUpload := b.uploadFile(file)
  137. if errUpload != nil {
  138. return errUpload
  139. }
  140. if title != "" {
  141. uploadResult.Title = title
  142. } else {
  143. uploadResult.Title = time.Now().Format(shortTimeFormat)
  144. }
  145. uploadResult.Body = text
  146. uploadResult.Type = "file"
  147. uploadResult.DeviceID = deviceID
  148. err := b.send(uploadResult)
  149. return err
  150. }
  151. // ListDevices returns Devices structure which contains slice of devices
  152. func (b Bullet) ListDevices() (*Devices, error) {
  153. request, err := http.NewRequest(http.MethodGet, b.baseURL+"/devices", nil)
  154. if err != nil {
  155. return nil, err
  156. }
  157. request.Header.Add("Access-Token", b.token)
  158. response, errResponse := doRequest(request)
  159. if errResponse != nil {
  160. return nil, errResponse
  161. }
  162. defer response.Body.Close()
  163. var result Devices
  164. decoder := json.NewDecoder(response.Body)
  165. errJSON := decoder.Decode(&result)
  166. if errJSON != nil {
  167. return nil, errJSON
  168. }
  169. return &result, nil
  170. }