integration.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642
  1. // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
  2. // SPDX-License-Identifier: Apache-2.0
  3. package integration // import "miniflux.app/v2/internal/integration"
  4. import (
  5. "log/slog"
  6. "miniflux.app/v2/internal/integration/apprise"
  7. "miniflux.app/v2/internal/integration/betula"
  8. "miniflux.app/v2/internal/integration/cubox"
  9. "miniflux.app/v2/internal/integration/discord"
  10. "miniflux.app/v2/internal/integration/espial"
  11. "miniflux.app/v2/internal/integration/instapaper"
  12. "miniflux.app/v2/internal/integration/karakeep"
  13. "miniflux.app/v2/internal/integration/linkace"
  14. "miniflux.app/v2/internal/integration/linkding"
  15. "miniflux.app/v2/internal/integration/linkwarden"
  16. "miniflux.app/v2/internal/integration/matrixbot"
  17. "miniflux.app/v2/internal/integration/notion"
  18. "miniflux.app/v2/internal/integration/ntfy"
  19. "miniflux.app/v2/internal/integration/nunuxkeeper"
  20. "miniflux.app/v2/internal/integration/omnivore"
  21. "miniflux.app/v2/internal/integration/pinboard"
  22. "miniflux.app/v2/internal/integration/pushover"
  23. "miniflux.app/v2/internal/integration/raindrop"
  24. "miniflux.app/v2/internal/integration/readeck"
  25. "miniflux.app/v2/internal/integration/readwise"
  26. "miniflux.app/v2/internal/integration/shaarli"
  27. "miniflux.app/v2/internal/integration/shiori"
  28. "miniflux.app/v2/internal/integration/slack"
  29. "miniflux.app/v2/internal/integration/telegrambot"
  30. "miniflux.app/v2/internal/integration/wallabag"
  31. "miniflux.app/v2/internal/integration/webhook"
  32. "miniflux.app/v2/internal/model"
  33. )
  34. // SendEntry sends the entry to third-party providers when the user click on "Save".
  35. func SendEntry(entry *model.Entry, userIntegrations *model.Integration) {
  36. if userIntegrations.BetulaEnabled {
  37. slog.Debug("Sending entry to Betula",
  38. slog.Int64("user_id", userIntegrations.UserID),
  39. slog.Int64("entry_id", entry.ID),
  40. slog.String("entry_url", entry.URL),
  41. )
  42. client := betula.NewClient(userIntegrations.BetulaURL, userIntegrations.BetulaToken)
  43. err := client.CreateBookmark(
  44. entry.URL,
  45. entry.Title,
  46. entry.Tags,
  47. )
  48. if err != nil {
  49. slog.Error("Unable to send entry to Betula",
  50. slog.Int64("user_id", userIntegrations.UserID),
  51. slog.Int64("entry_id", entry.ID),
  52. slog.String("entry_url", entry.URL),
  53. slog.Any("error", err),
  54. )
  55. }
  56. }
  57. if userIntegrations.PinboardEnabled {
  58. slog.Debug("Sending entry to Pinboard",
  59. slog.Int64("user_id", userIntegrations.UserID),
  60. slog.Int64("entry_id", entry.ID),
  61. slog.String("entry_url", entry.URL),
  62. )
  63. client := pinboard.NewClient(userIntegrations.PinboardToken)
  64. err := client.CreateBookmark(
  65. entry.URL,
  66. entry.Title,
  67. userIntegrations.PinboardTags,
  68. userIntegrations.PinboardMarkAsUnread,
  69. )
  70. if err != nil {
  71. slog.Error("Unable to send entry to Pinboard",
  72. slog.Int64("user_id", userIntegrations.UserID),
  73. slog.Int64("entry_id", entry.ID),
  74. slog.String("entry_url", entry.URL),
  75. slog.Any("error", err),
  76. )
  77. }
  78. }
  79. if userIntegrations.InstapaperEnabled {
  80. slog.Debug("Sending entry to Instapaper",
  81. slog.Int64("user_id", userIntegrations.UserID),
  82. slog.Int64("entry_id", entry.ID),
  83. slog.String("entry_url", entry.URL),
  84. )
  85. client := instapaper.NewClient(userIntegrations.InstapaperUsername, userIntegrations.InstapaperPassword)
  86. if err := client.AddURL(entry.URL, entry.Title); err != nil {
  87. slog.Error("Unable to send entry to Instapaper",
  88. slog.Int64("user_id", userIntegrations.UserID),
  89. slog.Int64("entry_id", entry.ID),
  90. slog.String("entry_url", entry.URL),
  91. slog.Any("error", err),
  92. )
  93. }
  94. }
  95. if userIntegrations.WallabagEnabled {
  96. slog.Debug("Sending entry to Wallabag",
  97. slog.Int64("user_id", userIntegrations.UserID),
  98. slog.Int64("entry_id", entry.ID),
  99. slog.String("entry_url", entry.URL),
  100. )
  101. client := wallabag.NewClient(
  102. userIntegrations.WallabagURL,
  103. userIntegrations.WallabagClientID,
  104. userIntegrations.WallabagClientSecret,
  105. userIntegrations.WallabagUsername,
  106. userIntegrations.WallabagPassword,
  107. userIntegrations.WallabagOnlyURL,
  108. )
  109. if err := client.CreateEntry(entry.URL, entry.Title, entry.Content); err != nil {
  110. slog.Error("Unable to send entry to Wallabag",
  111. slog.Int64("user_id", userIntegrations.UserID),
  112. slog.Int64("entry_id", entry.ID),
  113. slog.String("entry_url", entry.URL),
  114. slog.Any("error", err),
  115. )
  116. }
  117. }
  118. if userIntegrations.NotionEnabled {
  119. slog.Debug("Sending entry to Notion",
  120. slog.Int64("user_id", userIntegrations.UserID),
  121. slog.Int64("entry_id", entry.ID),
  122. slog.String("entry_url", entry.URL),
  123. )
  124. client := notion.NewClient(
  125. userIntegrations.NotionToken,
  126. userIntegrations.NotionPageID,
  127. )
  128. if err := client.UpdateDocument(entry.URL, entry.Title); err != nil {
  129. slog.Error("Unable to send entry to Notion",
  130. slog.Int64("user_id", userIntegrations.UserID),
  131. slog.Int64("entry_id", entry.ID),
  132. slog.String("entry_url", entry.URL),
  133. slog.Any("error", err),
  134. )
  135. }
  136. }
  137. if userIntegrations.NunuxKeeperEnabled {
  138. slog.Debug("Sending entry to NunuxKeeper",
  139. slog.Int64("user_id", userIntegrations.UserID),
  140. slog.Int64("entry_id", entry.ID),
  141. slog.String("entry_url", entry.URL),
  142. )
  143. client := nunuxkeeper.NewClient(
  144. userIntegrations.NunuxKeeperURL,
  145. userIntegrations.NunuxKeeperAPIKey,
  146. )
  147. if err := client.AddEntry(entry.URL, entry.Title, entry.Content); err != nil {
  148. slog.Error("Unable to send entry to NunuxKeeper",
  149. slog.Int64("user_id", userIntegrations.UserID),
  150. slog.Int64("entry_id", entry.ID),
  151. slog.String("entry_url", entry.URL),
  152. slog.Any("error", err),
  153. )
  154. }
  155. }
  156. if userIntegrations.EspialEnabled {
  157. slog.Debug("Sending entry to Espial",
  158. slog.Int64("user_id", userIntegrations.UserID),
  159. slog.Int64("entry_id", entry.ID),
  160. slog.String("entry_url", entry.URL),
  161. )
  162. client := espial.NewClient(
  163. userIntegrations.EspialURL,
  164. userIntegrations.EspialAPIKey,
  165. )
  166. if err := client.CreateLink(entry.URL, entry.Title, userIntegrations.EspialTags); err != nil {
  167. slog.Error("Unable to send entry to Espial",
  168. slog.Int64("user_id", userIntegrations.UserID),
  169. slog.Int64("entry_id", entry.ID),
  170. slog.String("entry_url", entry.URL),
  171. slog.Any("error", err),
  172. )
  173. }
  174. }
  175. if userIntegrations.LinkAceEnabled {
  176. slog.Debug("Sending entry to LinkAce",
  177. slog.Int64("user_id", userIntegrations.UserID),
  178. slog.Int64("entry_id", entry.ID),
  179. slog.String("entry_url", entry.URL),
  180. )
  181. client := linkace.NewClient(
  182. userIntegrations.LinkAceURL,
  183. userIntegrations.LinkAceAPIKey,
  184. userIntegrations.LinkAceTags,
  185. userIntegrations.LinkAcePrivate,
  186. userIntegrations.LinkAceCheckDisabled,
  187. )
  188. if err := client.AddURL(entry.URL, entry.Title); err != nil {
  189. slog.Error("Unable to send entry to LinkAce",
  190. slog.Int64("user_id", userIntegrations.UserID),
  191. slog.Int64("entry_id", entry.ID),
  192. slog.String("entry_url", entry.URL),
  193. slog.Any("error", err),
  194. )
  195. }
  196. }
  197. if userIntegrations.LinkdingEnabled {
  198. slog.Debug("Sending entry to Linkding",
  199. slog.Int64("user_id", userIntegrations.UserID),
  200. slog.Int64("entry_id", entry.ID),
  201. slog.String("entry_url", entry.URL),
  202. )
  203. client := linkding.NewClient(
  204. userIntegrations.LinkdingURL,
  205. userIntegrations.LinkdingAPIKey,
  206. userIntegrations.LinkdingTags,
  207. userIntegrations.LinkdingMarkAsUnread,
  208. )
  209. if err := client.CreateBookmark(entry.URL, entry.Title); err != nil {
  210. slog.Error("Unable to send entry to Linkding",
  211. slog.Int64("user_id", userIntegrations.UserID),
  212. slog.Int64("entry_id", entry.ID),
  213. slog.String("entry_url", entry.URL),
  214. slog.Any("error", err),
  215. )
  216. }
  217. }
  218. if userIntegrations.LinkwardenEnabled {
  219. slog.Debug("Sending entry to linkwarden",
  220. slog.Int64("user_id", userIntegrations.UserID),
  221. slog.Int64("entry_id", entry.ID),
  222. slog.String("entry_url", entry.URL),
  223. )
  224. client := linkwarden.NewClient(
  225. userIntegrations.LinkwardenURL,
  226. userIntegrations.LinkwardenAPIKey,
  227. )
  228. if err := client.CreateBookmark(entry.URL, entry.Title); err != nil {
  229. slog.Error("Unable to send entry to Linkwarden",
  230. slog.Int64("user_id", userIntegrations.UserID),
  231. slog.Int64("entry_id", entry.ID),
  232. slog.String("entry_url", entry.URL),
  233. slog.Any("error", err),
  234. )
  235. }
  236. }
  237. if userIntegrations.ReadeckEnabled {
  238. slog.Debug("Sending entry to Readeck",
  239. slog.Int64("user_id", userIntegrations.UserID),
  240. slog.Int64("entry_id", entry.ID),
  241. slog.String("entry_url", entry.URL),
  242. )
  243. client := readeck.NewClient(
  244. userIntegrations.ReadeckURL,
  245. userIntegrations.ReadeckAPIKey,
  246. userIntegrations.ReadeckLabels,
  247. userIntegrations.ReadeckOnlyURL,
  248. )
  249. if err := client.CreateBookmark(entry.URL, entry.Title, entry.Content); err != nil {
  250. slog.Error("Unable to send entry to Readeck",
  251. slog.Int64("user_id", userIntegrations.UserID),
  252. slog.Int64("entry_id", entry.ID),
  253. slog.String("entry_url", entry.URL),
  254. slog.Any("error", err),
  255. )
  256. }
  257. }
  258. if userIntegrations.ReadwiseEnabled {
  259. slog.Debug("Sending entry to Readwise",
  260. slog.Int64("user_id", userIntegrations.UserID),
  261. slog.Int64("entry_id", entry.ID),
  262. slog.String("entry_url", entry.URL),
  263. )
  264. client := readwise.NewClient(
  265. userIntegrations.ReadwiseAPIKey,
  266. )
  267. if err := client.CreateDocument(entry.URL); err != nil {
  268. slog.Error("Unable to send entry to Readwise",
  269. slog.Int64("user_id", userIntegrations.UserID),
  270. slog.Int64("entry_id", entry.ID),
  271. slog.String("entry_url", entry.URL),
  272. slog.Any("error", err),
  273. )
  274. }
  275. }
  276. if userIntegrations.CuboxEnabled {
  277. slog.Debug("Sending entry to Cubox",
  278. slog.Int64("user_id", userIntegrations.UserID),
  279. slog.Int64("entry_id", entry.ID),
  280. slog.String("entry_url", entry.URL),
  281. )
  282. client := cubox.NewClient(userIntegrations.CuboxAPILink)
  283. if err := client.SaveLink(entry.URL); err != nil {
  284. slog.Error("Unable to send entry to Cubox",
  285. slog.Int64("user_id", userIntegrations.UserID),
  286. slog.Int64("entry_id", entry.ID),
  287. slog.String("entry_url", entry.URL),
  288. slog.Any("error", err),
  289. )
  290. }
  291. }
  292. if userIntegrations.ShioriEnabled {
  293. slog.Debug("Sending entry to Shiori",
  294. slog.Int64("user_id", userIntegrations.UserID),
  295. slog.Int64("entry_id", entry.ID),
  296. slog.String("entry_url", entry.URL),
  297. )
  298. client := shiori.NewClient(
  299. userIntegrations.ShioriURL,
  300. userIntegrations.ShioriUsername,
  301. userIntegrations.ShioriPassword,
  302. )
  303. if err := client.CreateBookmark(entry.URL, entry.Title); err != nil {
  304. slog.Error("Unable to send entry to Shiori",
  305. slog.Int64("user_id", userIntegrations.UserID),
  306. slog.Int64("entry_id", entry.ID),
  307. slog.String("entry_url", entry.URL),
  308. slog.Any("error", err),
  309. )
  310. }
  311. }
  312. if userIntegrations.ShaarliEnabled {
  313. slog.Debug("Sending entry to Shaarli",
  314. slog.Int64("user_id", userIntegrations.UserID),
  315. slog.Int64("entry_id", entry.ID),
  316. slog.String("entry_url", entry.URL),
  317. )
  318. client := shaarli.NewClient(
  319. userIntegrations.ShaarliURL,
  320. userIntegrations.ShaarliAPISecret,
  321. )
  322. if err := client.CreateLink(entry.URL, entry.Title); err != nil {
  323. slog.Error("Unable to send entry to Shaarli",
  324. slog.Int64("user_id", userIntegrations.UserID),
  325. slog.Int64("entry_id", entry.ID),
  326. slog.String("entry_url", entry.URL),
  327. slog.Any("error", err),
  328. )
  329. }
  330. }
  331. if userIntegrations.WebhookEnabled {
  332. var webhookURL string
  333. if entry.Feed != nil && entry.Feed.WebhookURL != "" {
  334. webhookURL = entry.Feed.WebhookURL
  335. } else {
  336. webhookURL = userIntegrations.WebhookURL
  337. }
  338. slog.Debug("Sending entry to Webhook",
  339. slog.Int64("user_id", userIntegrations.UserID),
  340. slog.Int64("entry_id", entry.ID),
  341. slog.String("entry_url", entry.URL),
  342. slog.String("webhook_url", webhookURL),
  343. )
  344. webhookClient := webhook.NewClient(webhookURL, userIntegrations.WebhookSecret)
  345. if err := webhookClient.SendSaveEntryWebhookEvent(entry); err != nil {
  346. slog.Error("Unable to send entry to Webhook",
  347. slog.Int64("user_id", userIntegrations.UserID),
  348. slog.Int64("entry_id", entry.ID),
  349. slog.String("entry_url", entry.URL),
  350. slog.String("webhook_url", webhookURL),
  351. slog.Any("error", err),
  352. )
  353. }
  354. }
  355. if userIntegrations.OmnivoreEnabled {
  356. slog.Debug("Sending entry to Omnivore",
  357. slog.Int64("user_id", userIntegrations.UserID),
  358. slog.Int64("entry_id", entry.ID),
  359. slog.String("entry_url", entry.URL),
  360. )
  361. client := omnivore.NewClient(userIntegrations.OmnivoreAPIKey, userIntegrations.OmnivoreURL)
  362. if err := client.SaveUrl(entry.URL); err != nil {
  363. slog.Error("Unable to send entry to Omnivore",
  364. slog.Int64("user_id", userIntegrations.UserID),
  365. slog.Int64("entry_id", entry.ID),
  366. slog.String("entry_url", entry.URL),
  367. slog.Any("error", err),
  368. )
  369. }
  370. }
  371. if userIntegrations.KarakeepEnabled {
  372. slog.Debug("Sending entry to Karakeep",
  373. slog.Int64("user_id", userIntegrations.UserID),
  374. slog.Int64("entry_id", entry.ID),
  375. slog.String("entry_url", entry.URL),
  376. )
  377. client := karakeep.NewClient(userIntegrations.KarakeepAPIKey, userIntegrations.KarakeepURL)
  378. if err := client.SaveURL(entry.URL); err != nil {
  379. slog.Error("Unable to send entry to Karakeep",
  380. slog.Int64("user_id", userIntegrations.UserID),
  381. slog.Int64("entry_id", entry.ID),
  382. slog.String("entry_url", entry.URL),
  383. slog.Any("error", err),
  384. )
  385. }
  386. }
  387. if userIntegrations.RaindropEnabled {
  388. slog.Debug("Sending entry to Raindrop",
  389. slog.Int64("user_id", userIntegrations.UserID),
  390. slog.Int64("entry_id", entry.ID),
  391. slog.String("entry_url", entry.URL),
  392. )
  393. client := raindrop.NewClient(userIntegrations.RaindropToken, userIntegrations.RaindropCollectionID, userIntegrations.RaindropTags)
  394. if err := client.CreateRaindrop(entry.URL, entry.Title); err != nil {
  395. slog.Error("Unable to send entry to Raindrop",
  396. slog.Int64("user_id", userIntegrations.UserID),
  397. slog.Int64("entry_id", entry.ID),
  398. slog.String("entry_url", entry.URL),
  399. slog.Any("error", err),
  400. )
  401. }
  402. }
  403. }
  404. // PushEntries pushes a list of entries to activated third-party providers during feed refreshes.
  405. func PushEntries(feed *model.Feed, entries model.Entries, userIntegrations *model.Integration) {
  406. if userIntegrations.MatrixBotEnabled {
  407. slog.Debug("Sending new entries to Matrix",
  408. slog.Int64("user_id", userIntegrations.UserID),
  409. slog.Int("nb_entries", len(entries)),
  410. slog.Int64("feed_id", feed.ID),
  411. )
  412. err := matrixbot.PushEntries(
  413. feed,
  414. entries,
  415. userIntegrations.MatrixBotURL,
  416. userIntegrations.MatrixBotUser,
  417. userIntegrations.MatrixBotPassword,
  418. userIntegrations.MatrixBotChatID,
  419. )
  420. if err != nil {
  421. slog.Error("Unable to send new entries to Matrix",
  422. slog.Int64("user_id", userIntegrations.UserID),
  423. slog.Int("nb_entries", len(entries)),
  424. slog.Int64("feed_id", feed.ID),
  425. slog.Any("error", err),
  426. )
  427. }
  428. }
  429. if userIntegrations.WebhookEnabled {
  430. var webhookURL string
  431. if feed.WebhookURL != "" {
  432. webhookURL = feed.WebhookURL
  433. } else {
  434. webhookURL = userIntegrations.WebhookURL
  435. }
  436. slog.Debug("Sending new entries to Webhook",
  437. slog.Int64("user_id", userIntegrations.UserID),
  438. slog.Int("nb_entries", len(entries)),
  439. slog.Int64("feed_id", feed.ID),
  440. slog.String("webhook_url", webhookURL),
  441. )
  442. webhookClient := webhook.NewClient(webhookURL, userIntegrations.WebhookSecret)
  443. if err := webhookClient.SendNewEntriesWebhookEvent(feed, entries); err != nil {
  444. slog.Debug("Unable to send new entries to Webhook",
  445. slog.Int64("user_id", userIntegrations.UserID),
  446. slog.Int("nb_entries", len(entries)),
  447. slog.Int64("feed_id", feed.ID),
  448. slog.String("webhook_url", webhookURL),
  449. slog.Any("error", err),
  450. )
  451. }
  452. }
  453. if userIntegrations.NtfyEnabled && feed.NtfyEnabled {
  454. ntfyTopic := feed.NtfyTopic
  455. if ntfyTopic == "" {
  456. ntfyTopic = userIntegrations.NtfyTopic
  457. }
  458. slog.Debug("Sending new entries to Ntfy",
  459. slog.Int64("user_id", userIntegrations.UserID),
  460. slog.Int("nb_entries", len(entries)),
  461. slog.Int64("feed_id", feed.ID),
  462. slog.String("topic", ntfyTopic),
  463. )
  464. client := ntfy.NewClient(
  465. userIntegrations.NtfyURL,
  466. ntfyTopic,
  467. userIntegrations.NtfyAPIToken,
  468. userIntegrations.NtfyUsername,
  469. userIntegrations.NtfyPassword,
  470. userIntegrations.NtfyIconURL,
  471. userIntegrations.NtfyInternalLinks,
  472. feed.NtfyPriority,
  473. )
  474. if err := client.SendMessages(feed, entries); err != nil {
  475. slog.Warn("Unable to send new entries to Ntfy", slog.Any("error", err))
  476. }
  477. }
  478. if userIntegrations.AppriseEnabled {
  479. slog.Debug("Sending new entries to Apprise",
  480. slog.Int64("user_id", userIntegrations.UserID),
  481. slog.Int("nb_entries", len(entries)),
  482. slog.Int64("feed_id", feed.ID),
  483. )
  484. appriseServiceURLs := userIntegrations.AppriseServicesURL
  485. if feed.AppriseServiceURLs != "" {
  486. appriseServiceURLs = feed.AppriseServiceURLs
  487. }
  488. client := apprise.NewClient(
  489. appriseServiceURLs,
  490. userIntegrations.AppriseURL,
  491. )
  492. if err := client.SendNotification(feed, entries); err != nil {
  493. slog.Warn("Unable to send new entries to Apprise", slog.Any("error", err))
  494. }
  495. }
  496. if userIntegrations.DiscordEnabled {
  497. slog.Debug("Sending new entries to Discord",
  498. slog.Int64("user_id", userIntegrations.UserID),
  499. slog.Int("nb_entries", len(entries)),
  500. slog.Int64("feed_id", feed.ID),
  501. )
  502. client := discord.NewClient(
  503. userIntegrations.DiscordWebhookLink,
  504. )
  505. if err := client.SendDiscordMsg(feed, entries); err != nil {
  506. slog.Warn("Unable to send new entries to Discord", slog.Any("error", err))
  507. }
  508. }
  509. if userIntegrations.SlackEnabled {
  510. slog.Debug("Sending new entries to Slack",
  511. slog.Int64("user_id", userIntegrations.UserID),
  512. slog.Int("nb_entries", len(entries)),
  513. slog.Int64("feed_id", feed.ID),
  514. )
  515. client := slack.NewClient(
  516. userIntegrations.SlackWebhookLink,
  517. )
  518. if err := client.SendSlackMsg(feed, entries); err != nil {
  519. slog.Warn("Unable to send new entries to Slack", slog.Any("error", err))
  520. }
  521. }
  522. if userIntegrations.PushoverEnabled && feed.PushoverEnabled {
  523. slog.Debug("Sending new entries to Pushover",
  524. slog.Int64("user_id", userIntegrations.UserID),
  525. slog.Int("nb_entries", len(entries)),
  526. slog.Int64("feed_id", feed.ID),
  527. )
  528. client := pushover.New(
  529. userIntegrations.PushoverUser,
  530. userIntegrations.PushoverToken,
  531. feed.PushoverPriority,
  532. userIntegrations.PushoverDevice,
  533. userIntegrations.PushoverPrefix,
  534. )
  535. if err := client.SendMessages(feed, entries); err != nil {
  536. slog.Warn("Unable to send new entries to Pushover", slog.Any("error", err))
  537. }
  538. }
  539. // Integrations that only support sending individual entries
  540. if userIntegrations.TelegramBotEnabled {
  541. for _, entry := range entries {
  542. if userIntegrations.TelegramBotEnabled {
  543. slog.Debug("Sending a new entry to Telegram",
  544. slog.Int64("user_id", userIntegrations.UserID),
  545. slog.Int64("entry_id", entry.ID),
  546. slog.String("entry_url", entry.URL),
  547. )
  548. if err := telegrambot.PushEntry(
  549. feed,
  550. entry,
  551. userIntegrations.TelegramBotToken,
  552. userIntegrations.TelegramBotChatID,
  553. userIntegrations.TelegramBotTopicID,
  554. userIntegrations.TelegramBotDisableWebPagePreview,
  555. userIntegrations.TelegramBotDisableNotification,
  556. userIntegrations.TelegramBotDisableButtons,
  557. ); err != nil {
  558. slog.Error("Unable to send entry to Telegram",
  559. slog.Int64("user_id", userIntegrations.UserID),
  560. slog.Int64("entry_id", entry.ID),
  561. slog.String("entry_url", entry.URL),
  562. slog.Any("error", err),
  563. )
  564. }
  565. }
  566. }
  567. }
  568. }