Qiita Advent Calendar Feed Export

export subscription list of Qiita Advent Calendar as OPML

  1. // ==UserScript==
  2. // @name Qiita Advent Calendar Feed Export
  3. // @namespace https://fuwa.dev
  4. // @version 2024-11-30
  5. // @description export subscription list of Qiita Advent Calendar as OPML
  6. // @author fuwa2003
  7. // @match https://qiita.com/advent-calendar/*/feed
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=qiita.com
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14.  
  15. // https://stackoverflow.com/questions/3665115/how-to-create-a-file-in-memory-for-user-to-download-but-not-through-server
  16. function download(filename, text) {
  17. const element = document.createElement('a');
  18. element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
  19. element.setAttribute('download', filename);
  20.  
  21. element.style.display = 'none';
  22. document.body.appendChild(element);
  23.  
  24. element.click();
  25.  
  26. document.body.removeChild(element);
  27. }
  28.  
  29. function run() {
  30.  
  31. const entries = Array.from(document.querySelectorAll('main > div > section ol li > div a'))
  32. .map((a) => ({
  33. text: a.innerText + " Advent Calendar - Qiita",
  34. href: a.href,
  35. }));
  36.  
  37. const entries_text = entries.map((entry) => (
  38. ` <outline title="${entry.text}" text="${entry.text}" type="rss" xmlUrl="${entry.href}/feed" />`
  39. )).join('\n');
  40.  
  41. const opml = `
  42. <?xml version="1.0" encoding="UTF-8"?>
  43. <opml version="1.0">
  44. <head>
  45. <title>Qiita Advent Calendar subscriptions</title>
  46. </head>
  47. <body>
  48. ${entries_text}
  49. </body>
  50. </opml>
  51. `.trim();
  52.  
  53. // download file
  54. download('qiita.opml', opml);
  55. }
  56.  
  57. const btn = document.createElement('button');
  58. btn.innerText = 'Export OPML';
  59. btn.addEventListener('click', run);
  60. document.querySelector('main > div > section').prepend(btn);
  61. console.log(btn);
  62.  
  63. // when btn removed, add again
  64. btn.addEventListener('DOMNodeRemoved', (e) => {
  65. console.log('removed', e);
  66. setTimeout(() => {
  67. document.querySelector('main > div > section').prepend(btn);
  68. }, 100);
  69. });
  70.  
  71. })();