expandaction.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. // 浏览器扩展能力
  2. package main
  3. import (
  4. "context"
  5. "crypto/md5"
  6. "encoding/base64"
  7. "fmt"
  8. "io/ioutil"
  9. "net/http"
  10. "os"
  11. "strings"
  12. "time"
  13. "github.com/chromedp/cdproto/page"
  14. "github.com/chromedp/cdproto/fetch"
  15. "github.com/chromedp/chromedp"
  16. "github.com/chromedp/cdproto/browser"
  17. )
  18. // Screenshot
  19. func (b *Browser) Screenshot(tabTitle, tabUrl string, timeout int64,
  20. selectorType int, selector, save2file string) (err error) {
  21. ctx, err := b.findTabContext(tabTitle, tabUrl, timeout)
  22. if err != nil {
  23. return err
  24. }
  25. var res []byte
  26. var act chromedp.QueryAction
  27. switch selectorType {
  28. case selector_type_id:
  29. act = chromedp.Screenshot(selector, &res, chromedp.ByID)
  30. case selector_type_query:
  31. act = chromedp.Screenshot(selector, &res, chromedp.ByQuery)
  32. case selector_type_search:
  33. act = chromedp.Screenshot(selector, &res, chromedp.BySearch)
  34. case selector_type_jspath:
  35. act = chromedp.Screenshot(selector, &res, chromedp.ByJSPath)
  36. default:
  37. act = chromedp.Screenshot(selector, &res, chromedp.ByQueryAll)
  38. }
  39. err = chromedp.Run(ctx, act)
  40. if err != nil {
  41. return err
  42. }
  43. return os.WriteFile(save2file, res, 0777)
  44. }
  45. // PrintToPDF
  46. func (b *Browser) PrintToPDF(tabTitle, tabUrl string, timeout int64, save2file string) (err error) {
  47. ctx, err := b.findTabContext(tabTitle, tabUrl, timeout)
  48. if err != nil {
  49. return err
  50. }
  51. var res []byte
  52. var act chromedp.QueryAction = chromedp.ActionFunc(func(ctx context.Context) error {
  53. buf, _, err := page.PrintToPDF().
  54. WithLandscape(false).
  55. WithPaperWidth(16.3).
  56. WithPaperHeight(11.69).
  57. WithMarginTop(0.1).
  58. WithMarginRight(0).
  59. WithMarginBottom(0.1).
  60. WithMarginLeft(0).
  61. WithPrintBackground(true).
  62. Do(ctx) // 通过cdp执行PrintToPDF
  63. if err != nil {
  64. return err
  65. }
  66. res = buf
  67. return nil
  68. })
  69. err = chromedp.Run(ctx, act)
  70. if err != nil {
  71. return err
  72. }
  73. return os.WriteFile(save2file, res, 0777)
  74. }
  75. // GetBrowserTabs
  76. func (b *Browser) GetBrowserTabs(tabTitle, tabUrl string, timeout int64) ([]map[string]interface{}, error) {
  77. ctx, err := b.findTabContext(tabTitle, tabUrl, timeout)
  78. if err != nil {
  79. return nil, err
  80. }
  81. ts, err := chromedp.Targets(ctx)
  82. if err != nil {
  83. return nil, err
  84. }
  85. ret := make([]map[string]interface{}, 0, 0)
  86. for _, t := range ts {
  87. ret = append(ret, map[string]interface{}{
  88. "title": t.Title,
  89. "url": t.URL,
  90. })
  91. }
  92. return ret, nil
  93. }
  94. // DownloadFile 只有在非headless模式下有效,与click方法其实是一致的
  95. func (b *Browser) DownloadFile(tabTitle, tabUrl string, timeout int64, selector string,
  96. selectorType int, filename string, save2dir string) error {
  97. defer Catch()
  98. ctx, err := b.findTabContext(tabTitle, tabUrl, timeout)
  99. if err != nil {
  100. return err
  101. }
  102. var act chromedp.QueryAction
  103. switch selectorType {
  104. case selector_type_id:
  105. act = chromedp.Click(selector, chromedp.ByID)
  106. case selector_type_query:
  107. act = chromedp.Click(selector, chromedp.ByQuery)
  108. case selector_type_search:
  109. act = chromedp.Click(selector, chromedp.BySearch)
  110. case selector_type_jspath:
  111. act = chromedp.Click(selector, chromedp.ByJSPath)
  112. default:
  113. act = chromedp.Click(selector, chromedp.ByQueryAll)
  114. }
  115. done := make(chan bool, 1)
  116. chromedp.ListenTarget(ctx, func(v interface{}) {
  117. switch ev := v.(type) {
  118. case *fetch.EventRequestPaused:
  119. if ev.ResponseStatusCode == 0 {
  120. go func() {
  121. if err := chromedp.Run(ctx,
  122. fetch.ContinueRequest(ev.RequestID).WithInterceptResponse(true),
  123. ); err != nil {
  124. fmt.Println(err.Error())
  125. }
  126. }()
  127. } else {
  128. go func() {
  129. fulfill := fetch.FulfillRequest(ev.RequestID, ev.ResponseStatusCode)
  130. if ev.ResponseStatusCode == 200 {
  131. headers := append(ev.ResponseHeaders, &fetch.HeaderEntry{
  132. Name: "Content-Disposition",
  133. Value: fmt.Sprintf("attachment; filename=%s", filename),
  134. })
  135. fmt.Println("headers:")
  136. for k, v := range headers {
  137. fmt.Println(k, v.Name, v.Value)
  138. }
  139. fulfill = fulfill.WithResponseHeaders(headers)
  140. }
  141. if err := chromedp.Run(ctx, fulfill); err != nil {
  142. fmt.Println(err.Error())
  143. }
  144. }()
  145. }
  146. case *browser.EventDownloadWillBegin:
  147. //开始下载文件
  148. fmt.Println("start download file:", ev.SuggestedFilename)
  149. case *browser.EventDownloadProgress:
  150. //下载进度
  151. if ev.State == browser.DownloadProgressStateCompleted {
  152. done <- true
  153. }
  154. }
  155. })
  156. err = chromedp.Run(ctx,
  157. fetch.Enable().WithPatterns([]*fetch.RequestPattern{
  158. {URLPattern: "*/*"},
  159. }),
  160. browser.SetDownloadBehavior(browser.SetDownloadBehaviorBehaviorAllowAndName).WithDownloadPath(save2dir).WithEventsEnabled(true),
  161. act)
  162. select {
  163. case <-done:
  164. return err
  165. case <-time.After(60 * time.Second):
  166. return err
  167. }
  168. return err
  169. }
  170. // GoHistoryBack
  171. func (b *Browser) GoHistoryBack(tabTitle, tabUrl string, timeout int64) error {
  172. ctx, err := b.findTabContext(tabTitle, tabUrl, timeout)
  173. if err != nil {
  174. return err
  175. }
  176. var act chromedp.QueryAction = chromedp.NavigateBack()
  177. return chromedp.Run(ctx,
  178. act)
  179. }
  180. // SendImage2ChatBot
  181. func SendImage2ChatBot(uri, img, mentioned string) error {
  182. rawStr := img[22:]
  183. bs, err := base64.StdEncoding.DecodeString(rawStr)
  184. if err != nil {
  185. return err
  186. }
  187. h := md5.New()
  188. h.Write(bs)
  189. hash := h.Sum(nil)
  190. hashStr := fmt.Sprintf("%x", hash)
  191. postBody := fmt.Sprintf(`{
  192. "msgtype": "image",
  193. "image": {
  194. "base64": "%s",
  195. "md5": "%s",
  196. "mentioned_list":["@%s"]
  197. }
  198. }
  199. `, rawStr, strings.ToLower(hashStr), mentioned)
  200. client := new(http.Client)
  201. req, err := http.NewRequest("POST", uri,
  202. strings.NewReader(postBody))
  203. if err != nil {
  204. return err
  205. }
  206. req.Header.Set("Content-Type", "application/json")
  207. resp, err := client.Do(req)
  208. if err != nil {
  209. return err
  210. }
  211. bs, _ = ioutil.ReadAll(resp.Body)
  212. resp.Body.Close()
  213. return nil
  214. }
  215. // SendText2ChatBot
  216. func SendText2ChatBot(uri, text, mentioned string) error {
  217. postBody := fmt.Sprintf(`{
  218. "msgtype": "text",
  219. "text": {
  220. "content": "%s",
  221. "mentioned_list":["@%s"]
  222. }
  223. }
  224. `, text, mentioned)
  225. client := new(http.Client)
  226. req, err := http.NewRequest("POST", uri,
  227. strings.NewReader(postBody))
  228. if err != nil {
  229. return err
  230. }
  231. req.Header.Set("Content-Type", "application/json")
  232. resp, err := client.Do(req)
  233. if err != nil {
  234. return err
  235. }
  236. _, _ = ioutil.ReadAll(resp.Body)
  237. resp.Body.Close()
  238. return nil
  239. }