integration.go 20 KB

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