integration.go 20 KB

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