├── .gitignore ├── .nvmrc ├── changelog.md ├── config.example.js ├── demo.sh ├── docs ├── favicon.ico ├── index.html ├── logo.svg ├── readme.md ├── squirminal.js ├── styles.css ├── tinytoast.js └── water.css ├── index.js ├── lib ├── helpers.js ├── mastodonCount │ ├── counter.js │ └── url_regex.js ├── posters │ ├── github.js │ ├── index.js │ ├── linkace.js │ ├── mastodon.js │ ├── microblog.js │ ├── omnivore.js │ ├── webhook.js │ └── webmention.js └── presets.js ├── package-lock.json ├── package.json ├── readme.md └── screenshot.png /.gitignore: -------------------------------------------------------------------------------- 1 | config.js 2 | node_modules 3 | data 4 | scratch.txt 5 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 19 2 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | [2023-01-20] 2 | 3 | - Added support for JSON feeds 4 | - Fixed bug where webhooks on the config editor wouldn't work 5 | -------------------------------------------------------------------------------- /config.example.js: -------------------------------------------------------------------------------- 1 | import helpers from './lib/helpers.js' 2 | import presets from './lib/presets.js' 3 | import { SERVICES } from './lib/posters/index.js' 4 | 5 | export default { 6 | services: { 7 | [SERVICES.MICROBLOG]: { 8 | siteUrl: '', // https://mycoolname.micro.blog 9 | apiKey: '', // get an API from https://micro.blog/account/apps 10 | } 11 | }, 12 | sites: [ 13 | { 14 | name: "example.com", 15 | feed: "http://example.com/feed", 16 | categories: ["my category"], 17 | transform: presets.default, 18 | services: [SERVICES.MICROBLOG] 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /demo.sh: -------------------------------------------------------------------------------- 1 | echo "" 2 | echo "" 3 | echo " ⚙️ Fetching for status.lol" 4 | echo " ⭐ Created post on Micro.blog!" 5 | echo " ⭐ Created post on Mastodon!" 6 | echo " ⚙️ Fetching for Letterboxd" 7 | echo " ⭐ Created post on Micro.blog!" 8 | echo " ⚙️ Fetching for Nexus" 9 | echo " ❎ No new items found for Nexus" 10 | echo "" 11 | echo "" 12 | -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rknightuk/echo/8a86e4b0af8b72fa76077851b5c0ed2e44f94e16/docs/favicon.ico -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Echo - RSS Cross Poster 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 53 | 54 | 56 | 57 | 58 | 59 | 61 | 62 | 63 | 64 | 66 | 67 | 68 | 70 | sound 71 | Created with Sketch. 72 | 73 | 74 | 75 | 76 | 79 | 80 | 81 | 82 | 83 | 84 | 86 | 87 | 88 | 89 | 91 | 92 | 93 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 |
104 |

105 | 106 | 107 | Echo classic 108 |

109 |

RSS Cross Poster

110 |

by Robb Knight

111 |
112 | 113 | 121 | 122 | 123 |
124 |                 
125 |
126 |
127 |
128 |
129 | $ node index.js 130 | ⚙️ Fetching for status.lol 131 | ⭐ Created post on Micro.blog! 132 | ⭐ Created post on Mastodon! 133 | ⚙️ Fetching for Letterboxed 134 | ⭐ Created post on Micro.blog! 135 | ⚙️ Fetching for Nexus 136 | ❎ No new items found for Nexus 137 |
138 |
139 | 140 | 141 |

What is Echo?

142 | 143 |

Echo is a node script to post new items from an RSS feed to various services including Micro.blog and Mastodon. Checkout the readme on GitHub for installation instructions. Use the forms below to generate your config.

144 | 145 |

Config Generator

146 | 147 |
148 |

RSS feeds to cross-post

149 |
150 | 151 | 152 |
153 |
154 |
JSON Feed?
155 |
156 |
157 |
158 |
159 |
160 | 161 | 162 |
163 |
164 | 165 | 166 |
167 |
168 |
Send to:
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 | 177 |
178 |
179 | 180 |
181 |
182 | 183 |
184 |
185 | 186 |

Services to send to

187 |
188 |
189 | 190 | 191 | Micro.blog 192 |
193 |
194 | 195 | 196 | Mastodon 197 |
198 |
199 | 200 | 201 | Webhook 202 |
203 |
204 | 205 | 206 | Omnivore 207 |
208 |
209 | 210 | 211 | GitHub 212 |
213 |
214 | 215 | 216 | LinkAce 217 |
218 |
219 | 220 | 221 | Webmention 222 |
223 |
224 | 225 |
226 | 227 |
228 | 229 | 230 |
231 |
232 | 233 | 234 |
235 | 236 | 237 |
238 | 239 | 240 |
241 |
242 | 243 | 244 |
245 |
246 |
Visibility:
247 |
248 |
249 |
250 |
251 | 252 |
253 |
254 |
255 |
Sensitive:
256 |
257 | 258 |
259 |
260 | 261 | 262 |
263 | 264 | 265 |
266 | 267 | 268 |
269 | 270 | 271 |
272 | 273 | 274 |
275 | 276 | 277 |
278 |
279 | 280 | 281 |
282 |
283 | 284 | 285 |
286 |
287 | 288 | 289 |
290 |
291 | 292 | 293 |
294 | 295 |
296 | 297 | 298 |
299 |
300 | 301 | 302 |
303 | 304 |
305 | 306 |
307 | 308 |
309 |
310 | 311 |
312 |
313 | 314 |

Your Config

315 | 316 | 317 |
318 |

319 |         
320 | 321 | 322 | 323 | 324 | 327 | 328 | 329 | 338 | 507 | 508 | 509 | -------------------------------------------------------------------------------- /docs/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /docs/readme.md: -------------------------------------------------------------------------------- 1 | # Basic Template 2 | 3 | This is my template for quick side projects. It uses a modifed version of [water](https://watercss.kognise.dev) and [Inter font](https://rsms.me/inter/). 4 | 5 | ## Usage 6 | 7 | Clone the repo or click _Use this Template_ in GitHub 8 | 9 | MIT 10 | -------------------------------------------------------------------------------- /docs/squirminal.js: -------------------------------------------------------------------------------- 1 | class Squirminal extends HTMLElement { 2 | static define(tagName) { 3 | if("customElements" in window) { 4 | window.customElements.define(tagName || "squirm-inal", Squirminal); 5 | } 6 | } 7 | 8 | constructor() { 9 | super(); 10 | 11 | this.speed = 0.5 ; // higher is faster, 3 is about the fastest it can go. 12 | this.chunkSize = { 13 | min: 5, 14 | max: 30 15 | }; 16 | this.flatDepth = 1000; 17 | 18 | this.attr = { 19 | cursor: "cursor", 20 | autoplay: "autoplay", 21 | buttons: "buttons", 22 | global: "global", 23 | }; 24 | this.classes = { 25 | showCursor: "squirminal-cursor-show", 26 | content: "squirminal-content", 27 | }; 28 | this.events = { 29 | start: "squirminal.start", 30 | end: "squirminal.end", 31 | frameAdded: "squirminal.frameadded", 32 | }; 33 | } 34 | 35 | _serializeContent(node, selector = []) { 36 | if(node.nodeType === 3) { 37 | let text = node.nodeValue; 38 | node.nodeValue = ""; 39 | 40 | // this represents characters that need to be added to the page. 41 | return { 42 | text: text.split(""), 43 | selector: selector 44 | }; 45 | } else if(node.nodeType === 1) { 46 | if(node.tagName.toLowerCase() !== "squirm-inal" && node.innerText) { 47 | node.classList.add("sq-empty"); 48 | } 49 | } 50 | let content = []; 51 | let j = 0; 52 | for(let child of Array.from(node.childNodes)) { 53 | content.push(this._serializeContent(child, [...selector, j])); 54 | j++; 55 | } 56 | 57 | return content; 58 | } 59 | 60 | getNode(target, selector) { 61 | for(let childIndex of selector) { 62 | target = target.childNodes[childIndex]; 63 | } 64 | return target; 65 | } 66 | 67 | removeEmptyClass(node) { 68 | if(node && node.nodeValue) { 69 | while(node) { 70 | if(node.classList) { 71 | node.classList.remove("sq-empty"); 72 | } 73 | node = node.parentNode; 74 | } 75 | } 76 | } 77 | 78 | addCharacters(target, characterCount = 1) { 79 | for(let entry of this.serialized) { 80 | let str = []; 81 | while(entry.text.length && characterCount-- > 0) { 82 | str.push(entry.text.shift()); 83 | } 84 | 85 | let targetNode = this.getNode(target, entry.selector); 86 | targetNode.nodeValue += str.join(""); 87 | this.removeEmptyClass(targetNode); 88 | 89 | if(characterCount === 0) break; 90 | } 91 | } 92 | 93 | hasQueue() { 94 | for(let entry of this.serialized) { 95 | if(entry.text.length > 0) { 96 | return true; 97 | } 98 | } 99 | return false; 100 | } 101 | 102 | connectedCallback() { 103 | this.init(); 104 | 105 | // TODO this is not ideal because the intersectionRatio is based on the empty terminal, not the 106 | // final animated version. So it’s tiny when empty and when the IntersectionRatio is 1 it may 107 | // animate off the bottom of the viewport. 108 | if(this.hasAttribute(this.attr.autoplay)) { 109 | this._whenVisible(this, (isVisible) => { 110 | if(isVisible) { 111 | this.play(); 112 | } 113 | }); 114 | } 115 | 116 | if(this.hasAttribute(this.attr.cursor)) { 117 | this.addEventListener("squirminal.start", () => { 118 | this.classList.add(this.classes.showCursor); 119 | }); 120 | 121 | this.addEventListener("squirminal.end", () => { 122 | this.classList.remove(this.classes.showCursor); 123 | }); 124 | } 125 | 126 | 127 | let href = this.getAttribute("href"); 128 | if(href) { 129 | this.addEventListener("squirminal.end", () => { 130 | window.location.href = href; 131 | }); 132 | } 133 | } 134 | 135 | init() { 136 | this.paused = true; 137 | this.originalContent = this.cloneNode(true); 138 | this.serialized = this._serializeContent(this).flat(this.flatDepth); 139 | 140 | // Add content div 141 | this.content = this.querySelector(`.${this.classes.content}`); 142 | if(!this.content) { 143 | let content = document.createElement("div"); 144 | content.classList.add(this.classes.content); 145 | 146 | // add non-text that have already been emptied by the serializer 147 | for(let child of Array.from(this.childNodes)) { 148 | content.appendChild(child); 149 | } 150 | this.appendChild(content); 151 | this.content = content; 152 | } 153 | 154 | // Play/pause button 155 | this.toggleButton = this.querySelector(":scope button[data-sq-toggle]"); 156 | if(this.hasAttribute(this.attr.buttons) && !this.toggleButton) { 157 | let toggleBtn = document.createElement("button"); 158 | toggleBtn.innerText = "Play"; 159 | toggleBtn.setAttribute("data-sq-toggle", ""); 160 | toggleBtn.addEventListener("click", e => { 161 | this.toggle(); 162 | }) 163 | this.appendChild(toggleBtn); 164 | this.toggleButton = toggleBtn; 165 | } 166 | 167 | this.skipButton = this.querySelector(":scope button[data-sq-skip]"); 168 | if(this.hasAttribute(this.attr.buttons) && !this.skipButton) { 169 | let skipBtn = document.createElement("button"); 170 | skipBtn.innerText = "Skip"; 171 | skipBtn.setAttribute("data-sq-skip", ""); 172 | skipBtn.addEventListener("click", e => { 173 | this.skip(); 174 | }) 175 | this.appendChild(skipBtn); 176 | this.skipButton = skipBtn; 177 | } 178 | } 179 | 180 | onreveal(callback) { 181 | this.addEventListener(this.events.frameAdded, callback, { 182 | passive: true, 183 | }); 184 | this.addEventListener(this.events.end, () => { 185 | this.removeEventListener(this.events.frameAdded, callback); 186 | }, { 187 | passive: true, 188 | once: true, 189 | }); 190 | } 191 | 192 | onstart(callback) { 193 | this.addEventListener(this.events.start, callback, { 194 | passive: true, 195 | once: true, 196 | }); 197 | } 198 | 199 | onend(callback) { 200 | this.addEventListener(this.events.end, callback, { 201 | passive: true, 202 | once: true, 203 | }); 204 | } 205 | 206 | setButtonText(button, text) { 207 | if(button && text) { 208 | button.innerText = text; 209 | } 210 | } 211 | 212 | _whenVisible(el, callback) { 213 | if(!('IntersectionObserver' in window)) { 214 | // run by default without intersectionobserver 215 | callback(undefined); 216 | return; 217 | } 218 | 219 | return new IntersectionObserver(entries => { 220 | entries.forEach(entry => { 221 | callback(entry.isIntersecting) 222 | }); 223 | }, { 224 | threshold: 1 225 | }).observe(el); 226 | } 227 | 228 | toggle() { 229 | if(this.paused) { 230 | this.play(); 231 | } else { 232 | this.pause(); 233 | } 234 | } 235 | 236 | pause() { 237 | this.paused = true; 238 | this.setButtonText(this.toggleButton, "Play"); 239 | } 240 | 241 | skip() { 242 | this.play({ 243 | chunkSize: this.originalContent.innerHTML.length, 244 | delay: 0 245 | }); 246 | } 247 | 248 | play(overrides = {}) { 249 | if(window.matchMedia("(prefers-reduced-motion: reduce)").matches) { 250 | overrides.chunkSize = this.originalContent.innerHTML.length; 251 | overrides.delay = 0; 252 | } 253 | 254 | this.paused = false; 255 | if(this.hasQueue()) { 256 | this.setButtonText(this.toggleButton, "Pause"); 257 | this.dispatchEvent(new CustomEvent(this.events.start)); 258 | } 259 | 260 | requestAnimationFrame(() => this.showMore(overrides)); 261 | } 262 | 263 | showMore(overrides = {}) { 264 | if(this.paused) { 265 | return; 266 | } 267 | 268 | if(!this.hasQueue()) { 269 | this.pause(); 270 | this.dispatchEvent(new CustomEvent(this.events.frameAdded)); 271 | this.dispatchEvent(new CustomEvent(this.events.end)); 272 | return; 273 | } 274 | 275 | // show a random chunk size between min/max 276 | let chunkSize = overrides.chunkSize || Math.round(Math.max(this.chunkSize.min, Math.random() * this.chunkSize.max + 1)); 277 | this.addCharacters(this.content, chunkSize); 278 | 279 | this.dispatchEvent(new CustomEvent(this.events.frameAdded)); 280 | 281 | // the amount we wait is based on how many non-whitespace characters printed to the screen in this chunk 282 | let delay = overrides.delay > -1 ? overrides.delay : chunkSize * (1/this.speed); 283 | if(delay > 16) { 284 | setTimeout(() => { 285 | requestAnimationFrame(() => this.showMore(overrides)); 286 | }, delay); 287 | } else { 288 | requestAnimationFrame(() => this.showMore(overrides)); 289 | } 290 | } 291 | 292 | isGlobalCommand() { 293 | return this.hasAttribute(this.attr.global); 294 | } 295 | 296 | clone() { 297 | let cloned = this.cloneNode(); 298 | // restart from scratch 299 | cloned.innerHTML = this.originalContent.innerHTML; 300 | return cloned; 301 | } 302 | } 303 | 304 | Squirminal.define(); 305 | -------------------------------------------------------------------------------- /docs/styles.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --links: #F26522; 3 | --terminal-text: #dbdbdb; 4 | } 5 | 6 | @media (prefers-color-scheme: dark) { 7 | :root { 8 | --links: #F26522; 9 | } 10 | } 11 | 12 | h1 { 13 | display: flex;; 14 | align-items: center; 15 | justify-content: center; 16 | } 17 | 18 | h1 .icon { 19 | width: 60px; 20 | height: 60px; 21 | color: var(--links); 22 | margin-right: 10px; 23 | } 24 | 25 | header h2 { 26 | margin-top: 0; 27 | font-weight: normal; 28 | } 29 | 30 | .form-row { 31 | display: flex; 32 | align-items: center; 33 | margin-top: 10px; 34 | } 35 | 36 | .form-label { 37 | margin-right: 10px; 38 | min-width: 125px; 39 | font-weight: bold; 40 | text-align: right; 41 | } 42 | 43 | 44 | input { 45 | width: 100%; 46 | max-width: 400px; 47 | } 48 | 49 | .services { 50 | display: flex; 51 | flex-wrap: wrap; 52 | grid-gap: 5px; 53 | justify-content: space-between; 54 | } 55 | 56 | .service { 57 | padding: 0 10px; 58 | display: flex; 59 | flex-direction: column; 60 | align-items: center; 61 | font-size: 0.9em; 62 | } 63 | 64 | .service .icon { 65 | width: 50px; 66 | height: 50px; 67 | } 68 | 69 | .service.active { 70 | color: var(--links); 71 | } 72 | 73 | .service:hover, 74 | .service:hover svg { 75 | fill: var(--links); 76 | color: var(--links)!important; 77 | cursor: pointer; 78 | } 79 | 80 | .service .icon { 81 | margin-bottom: 3px; 82 | } 83 | 84 | .service-mb { fill: #FFA900; } 85 | .service-mastodon { fill: #5B4DE3; } 86 | .service-webhook { fill: #37b24d } 87 | .service-omnivore { fill: #FFDE8C; } 88 | .service-github { fill: #f5f5f5; } 89 | .service-linkace svg { color: #41649a; } 90 | .service-webmention svg { color: #dddddd; } 91 | 92 | .form-services .form-row { 93 | display: none; 94 | } 95 | 96 | body:has(.service-mb.active) .form-services .form-row-mb { 97 | display: flex; 98 | } 99 | 100 | body:has(.service-mastodon.active) .form-services .form-row-masto { 101 | display: flex; 102 | } 103 | 104 | body:has(.service-webhook.active) .form-services .form-row-hook { 105 | display: flex; 106 | } 107 | 108 | body:has(.service-omnivore.active) .form-services .form-row-omnivore { 109 | display: flex; 110 | } 111 | 112 | body:has(.service-github.active) .form-services .form-row-github { 113 | display: flex; 114 | } 115 | 116 | body:has(.service-linkace.active) .form-services .form-row-linkace { 117 | display: flex; 118 | } 119 | 120 | body:has(.service-webmention.active) .form-services .form-row-webmention { 121 | display: flex; 122 | } 123 | 124 | .form-services .form-row-submit { 125 | display: flex; 126 | } 127 | 128 | .terminal-output { 129 | position: relative; 130 | border-radius: 10px; 131 | white-space: pre-wrap; 132 | background-color: #151818; 133 | padding: 15px 24px 0 24px; 134 | height: 180px; 135 | color: var(--terminal-text); 136 | } 137 | 138 | .buttons { 139 | display: flex; 140 | position: absolute; 141 | top: 10px; 142 | left: 10px; 143 | } 144 | 145 | .buttons .circle { 146 | width: 10px; 147 | height: 10px; 148 | border-radius: 10px; 149 | margin-right: 7px; 150 | } 151 | 152 | .circle.red { background: #FF5F56; } 153 | .circle.yellow { background: #FFBD2E; } 154 | .circle.green { background: #27C93F; } 155 | 156 | .banner { 157 | display: flex; 158 | border: 3px solid var(--links); 159 | padding: 10px; 160 | font-size: 1.2em; 161 | align-items: center; 162 | } 163 | 164 | .banner p { 165 | margin-top: 0; 166 | margin-bottom: 0; 167 | } 168 | 169 | .banner svg { 170 | width: 100px; 171 | height: 100px; 172 | margin-right: 10px; 173 | } -------------------------------------------------------------------------------- /docs/tinytoast.js: -------------------------------------------------------------------------------- 1 | !function t(e,o){"object"==typeof exports&&"object"==typeof module?module.exports=o():"function"==typeof define&&define.amd?define([],o):"object"==typeof exports?exports.tinyToast=o():e.tinyToast=o()}(this,function(){return function(t){var e={};function o(n){if(e[n])return e[n].exports;var r=e[n]={exports:{},id:n,loaded:!1};return t[n].call(r.exports,r,r.exports,o),r.loaded=!0,r.exports}return o.m=t,o.c=e,o.p="",o(0)}([function(t,e){"use strict";function o(){n&&(document.body.removeChild(n),n=null)}var n,r={show:function t(e){var o;return o=e,function t(){if(n)return n;let e=function t(){let e="tinyToast",o=function t(){let e=document.createElement("style");return document.head.appendChild(e),e.sheet}();return function t(e,o,n){Object.keys(n).forEach(t=>{let r=n[t];o.insertRule(`.${e} { ${t}: ${r} }`,0)})}(e,o,{color:"#333",position:"fixed",bottom:"0em",right:"1em","background-color":"#7FFFD4","border-radius":"5px","border-width":"1px","border-color":"#73E1BC","border-style":"solid",padding:"1em 2em","z-index":1e3}),e}();return(n=document.createElement("h3")).classList.add(e),document.body.appendChild(n),n}().textContent=o,r},hide:function t(e){var n,r;n=o,(r=e)?setTimeout(n,r):n()}};t.exports=r}])}); 2 | -------------------------------------------------------------------------------- /docs/water.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Automatic version: 3 | * Uses light theme by default but switches to dark theme 4 | * if a system-wide theme preference is set on the user's device. 5 | */ 6 | 7 | :root { 8 | --background-body: #fff; 9 | --background: #efefef; 10 | --background-alt: #f7f7f7; 11 | --selection: #9e9e9e; 12 | --text-main: #202b38; 13 | --text-bright: #000; 14 | --text-muted: #70777f; 15 | --links: #ff4757; /* primary color */ 16 | --focus: #0096bfab; 17 | --border: #dbdbdb; 18 | --code: #000; 19 | --animation-duration: 0.1s; 20 | --button-base: #d0cfcf; 21 | --button-hover: #9b9b9b; 22 | --scrollbar-thumb: rgb(170, 170, 170); 23 | --scrollbar-thumb-hover: var(--button-hover); 24 | --form-placeholder: #949494; 25 | --form-text: #1d1d1d; 26 | --variable: #39a33c; 27 | --highlight: #ff0; 28 | --select-arrow: url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23161f27'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E"); 29 | } 30 | 31 | @media (prefers-color-scheme: dark) { 32 | :root { 33 | --background-body: #202b38; 34 | --background: #161f27; 35 | --background-alt: #1a242f; 36 | --selection: #1c76c5; 37 | --text-main: #dbdbdb; 38 | --text-bright: #fff; 39 | --text-muted: #a9b1ba; 40 | --links: #ff4757; /* primary color */ 41 | --focus: #0096bfab; 42 | --border: #526980; 43 | --code: #ffbe85; 44 | --animation-duration: 0.1s; 45 | --button-base: #0c151c; 46 | --button-hover: #040a0f; 47 | --scrollbar-thumb: var(--button-hover); 48 | --scrollbar-thumb-hover: rgb(0, 0, 0); 49 | --form-placeholder: #a9a9a9; 50 | --form-text: #fff; 51 | --variable: #d941e2; 52 | --highlight: #efdb43; 53 | --select-arrow: url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E"); 54 | } 55 | } 56 | 57 | html { 58 | scrollbar-color: rgb(170, 170, 170) #fff; 59 | scrollbar-color: var(--scrollbar-thumb) var(--background-body); 60 | scrollbar-width: thin; 61 | } 62 | 63 | @media (prefers-color-scheme: dark) { 64 | 65 | html { 66 | scrollbar-color: #040a0f #202b38; 67 | scrollbar-color: var(--scrollbar-thumb) var(--background-body); 68 | } 69 | } 70 | 71 | @media (prefers-color-scheme: dark) { 72 | 73 | html { 74 | scrollbar-color: #040a0f #202b38; 75 | scrollbar-color: var(--scrollbar-thumb) var(--background-body); 76 | } 77 | } 78 | 79 | @media (prefers-color-scheme: dark) { 80 | 81 | html { 82 | scrollbar-color: #040a0f #202b38; 83 | scrollbar-color: var(--scrollbar-thumb) var(--background-body); 84 | } 85 | } 86 | 87 | @media (prefers-color-scheme: dark) { 88 | 89 | html { 90 | scrollbar-color: #040a0f #202b38; 91 | scrollbar-color: var(--scrollbar-thumb) var(--background-body); 92 | } 93 | } 94 | 95 | @media (prefers-color-scheme: dark) { 96 | 97 | html { 98 | scrollbar-color: #040a0f #202b38; 99 | scrollbar-color: var(--scrollbar-thumb) var(--background-body); 100 | } 101 | } 102 | 103 | @media (prefers-color-scheme: dark) { 104 | 105 | html { 106 | scrollbar-color: #040a0f #202b38; 107 | scrollbar-color: var(--scrollbar-thumb) var(--background-body); 108 | } 109 | } 110 | 111 | body { 112 | font-family: system-ui, sans-serif; 113 | font-weight: normal; 114 | line-height: 1.4; 115 | max-width: 800px; 116 | margin: 20px auto; 117 | padding: 0 10px; 118 | word-wrap: break-word; 119 | color: #363636; 120 | color: var(--text-main); 121 | background: #fff; 122 | background: var(--background-body); 123 | text-rendering: optimizeLegibility; 124 | } 125 | 126 | @media (prefers-color-scheme: dark) { 127 | 128 | body { 129 | background: #202b38; 130 | background: var(--background-body); 131 | } 132 | } 133 | 134 | @media (prefers-color-scheme: dark) { 135 | 136 | body { 137 | color: #dbdbdb; 138 | color: var(--text-main); 139 | } 140 | } 141 | 142 | button { 143 | transition: 144 | background-color 0.1s linear, 145 | border-color 0.1s linear, 146 | color 0.1s linear, 147 | box-shadow 0.1s linear, 148 | transform 0.1s ease; 149 | transition: 150 | background-color var(--animation-duration) linear, 151 | border-color var(--animation-duration) linear, 152 | color var(--animation-duration) linear, 153 | box-shadow var(--animation-duration) linear, 154 | transform var(--animation-duration) ease; 155 | } 156 | 157 | @media (prefers-color-scheme: dark) { 158 | 159 | button { 160 | transition: 161 | background-color 0.1s linear, 162 | border-color 0.1s linear, 163 | color 0.1s linear, 164 | box-shadow 0.1s linear, 165 | transform 0.1s ease; 166 | transition: 167 | background-color var(--animation-duration) linear, 168 | border-color var(--animation-duration) linear, 169 | color var(--animation-duration) linear, 170 | box-shadow var(--animation-duration) linear, 171 | transform var(--animation-duration) ease; 172 | } 173 | } 174 | 175 | input { 176 | transition: 177 | background-color 0.1s linear, 178 | border-color 0.1s linear, 179 | color 0.1s linear, 180 | box-shadow 0.1s linear, 181 | transform 0.1s ease; 182 | transition: 183 | background-color var(--animation-duration) linear, 184 | border-color var(--animation-duration) linear, 185 | color var(--animation-duration) linear, 186 | box-shadow var(--animation-duration) linear, 187 | transform var(--animation-duration) ease; 188 | } 189 | 190 | @media (prefers-color-scheme: dark) { 191 | 192 | input { 193 | transition: 194 | background-color 0.1s linear, 195 | border-color 0.1s linear, 196 | color 0.1s linear, 197 | box-shadow 0.1s linear, 198 | transform 0.1s ease; 199 | transition: 200 | background-color var(--animation-duration) linear, 201 | border-color var(--animation-duration) linear, 202 | color var(--animation-duration) linear, 203 | box-shadow var(--animation-duration) linear, 204 | transform var(--animation-duration) ease; 205 | } 206 | } 207 | 208 | textarea { 209 | transition: 210 | background-color 0.1s linear, 211 | border-color 0.1s linear, 212 | color 0.1s linear, 213 | box-shadow 0.1s linear, 214 | transform 0.1s ease; 215 | transition: 216 | background-color var(--animation-duration) linear, 217 | border-color var(--animation-duration) linear, 218 | color var(--animation-duration) linear, 219 | box-shadow var(--animation-duration) linear, 220 | transform var(--animation-duration) ease; 221 | } 222 | 223 | @media (prefers-color-scheme: dark) { 224 | 225 | textarea { 226 | transition: 227 | background-color 0.1s linear, 228 | border-color 0.1s linear, 229 | color 0.1s linear, 230 | box-shadow 0.1s linear, 231 | transform 0.1s ease; 232 | transition: 233 | background-color var(--animation-duration) linear, 234 | border-color var(--animation-duration) linear, 235 | color var(--animation-duration) linear, 236 | box-shadow var(--animation-duration) linear, 237 | transform var(--animation-duration) ease; 238 | } 239 | } 240 | 241 | h1 { 242 | font-size: 2.2em; 243 | margin-top: 0; 244 | } 245 | 246 | h1, 247 | h2, 248 | h3, 249 | h4, 250 | h5, 251 | h6 { 252 | margin-bottom: 12px; 253 | margin-top: 24px; 254 | } 255 | 256 | h1 { 257 | color: #000; 258 | color: var(--text-bright); 259 | } 260 | 261 | @media (prefers-color-scheme: dark) { 262 | 263 | h1 { 264 | color: #fff; 265 | color: var(--text-bright); 266 | } 267 | } 268 | 269 | h2 { 270 | color: #000; 271 | color: var(--text-bright); 272 | } 273 | 274 | @media (prefers-color-scheme: dark) { 275 | 276 | h2 { 277 | color: #fff; 278 | color: var(--text-bright); 279 | } 280 | } 281 | 282 | h3 { 283 | color: #000; 284 | color: var(--text-bright); 285 | } 286 | 287 | @media (prefers-color-scheme: dark) { 288 | 289 | h3 { 290 | color: #fff; 291 | color: var(--text-bright); 292 | } 293 | } 294 | 295 | h4 { 296 | color: #000; 297 | color: var(--text-bright); 298 | } 299 | 300 | @media (prefers-color-scheme: dark) { 301 | 302 | h4 { 303 | color: #fff; 304 | color: var(--text-bright); 305 | } 306 | } 307 | 308 | h5 { 309 | color: #000; 310 | color: var(--text-bright); 311 | } 312 | 313 | @media (prefers-color-scheme: dark) { 314 | 315 | h5 { 316 | color: #fff; 317 | color: var(--text-bright); 318 | } 319 | } 320 | 321 | h6 { 322 | color: #000; 323 | color: var(--text-bright); 324 | } 325 | 326 | @media (prefers-color-scheme: dark) { 327 | 328 | h6 { 329 | color: #fff; 330 | color: var(--text-bright); 331 | } 332 | } 333 | 334 | strong { 335 | color: #000; 336 | color: var(--text-bright); 337 | } 338 | 339 | @media (prefers-color-scheme: dark) { 340 | 341 | strong { 342 | color: #fff; 343 | color: var(--text-bright); 344 | } 345 | } 346 | 347 | h1, 348 | h2, 349 | h3, 350 | h4, 351 | h5, 352 | h6, 353 | b, 354 | strong, 355 | th { 356 | font-weight: 600; 357 | } 358 | 359 | q::before { 360 | content: none; 361 | } 362 | 363 | q::after { 364 | content: none; 365 | } 366 | 367 | blockquote { 368 | border-left: 4px solid #0096bfab; 369 | border-left: 4px solid var(--focus); 370 | margin: 1.5em 0; 371 | padding: 0.5em 1em; 372 | font-style: italic; 373 | } 374 | 375 | @media (prefers-color-scheme: dark) { 376 | 377 | blockquote { 378 | border-left: 4px solid #0096bfab; 379 | border-left: 4px solid var(--focus); 380 | } 381 | } 382 | 383 | q { 384 | border-left: 4px solid #0096bfab; 385 | border-left: 4px solid var(--focus); 386 | margin: 1.5em 0; 387 | padding: 0.5em 1em; 388 | font-style: italic; 389 | } 390 | 391 | @media (prefers-color-scheme: dark) { 392 | 393 | q { 394 | border-left: 4px solid #0096bfab; 395 | border-left: 4px solid var(--focus); 396 | } 397 | } 398 | 399 | blockquote > footer { 400 | font-style: normal; 401 | border: 0; 402 | } 403 | 404 | blockquote cite { 405 | font-style: normal; 406 | } 407 | 408 | address { 409 | font-style: normal; 410 | } 411 | 412 | a[href^='mailto\:']::before { 413 | content: '📧 '; 414 | } 415 | 416 | a[href^='tel\:']::before { 417 | content: '📞 '; 418 | } 419 | 420 | a[href^='sms\:']::before { 421 | content: '💬 '; 422 | } 423 | 424 | mark { 425 | background-color: #ff0; 426 | background-color: var(--highlight); 427 | border-radius: 2px; 428 | padding: 0 2px 0 2px; 429 | color: #000; 430 | } 431 | 432 | @media (prefers-color-scheme: dark) { 433 | 434 | mark { 435 | background-color: #efdb43; 436 | background-color: var(--highlight); 437 | } 438 | } 439 | 440 | a > code, 441 | a > strong { 442 | color: inherit; 443 | } 444 | 445 | button, 446 | select, 447 | input[type='submit'], 448 | input[type='reset'], 449 | input[type='button'], 450 | input[type='checkbox'], 451 | input[type='range'], 452 | input[type='radio'] { 453 | cursor: pointer; 454 | } 455 | 456 | input, 457 | select { 458 | display: block; 459 | } 460 | 461 | [type='checkbox'], 462 | [type='radio'] { 463 | display: initial; 464 | } 465 | 466 | input { 467 | color: #1d1d1d; 468 | color: var(--form-text); 469 | background-color: #efefef; 470 | background-color: var(--background); 471 | font-family: inherit; 472 | font-size: inherit; 473 | margin-right: 6px; 474 | margin-bottom: 6px; 475 | padding: 10px; 476 | border: none; 477 | border-radius: 6px; 478 | outline: none; 479 | } 480 | 481 | @media (prefers-color-scheme: dark) { 482 | 483 | input { 484 | background-color: #161f27; 485 | background-color: var(--background); 486 | } 487 | } 488 | 489 | @media (prefers-color-scheme: dark) { 490 | 491 | input { 492 | color: #fff; 493 | color: var(--form-text); 494 | } 495 | } 496 | 497 | button { 498 | color: #1d1d1d; 499 | color: var(--form-text); 500 | background-color: #efefef; 501 | background-color: var(--background); 502 | font-family: inherit; 503 | font-size: inherit; 504 | margin-right: 6px; 505 | margin-bottom: 6px; 506 | padding: 10px; 507 | border: none; 508 | border-radius: 6px; 509 | outline: none; 510 | } 511 | 512 | @media (prefers-color-scheme: dark) { 513 | 514 | button { 515 | background-color: #161f27; 516 | background-color: var(--background); 517 | } 518 | } 519 | 520 | @media (prefers-color-scheme: dark) { 521 | 522 | button { 523 | color: #fff; 524 | color: var(--form-text); 525 | } 526 | } 527 | 528 | textarea { 529 | color: #1d1d1d; 530 | color: var(--form-text); 531 | background-color: #efefef; 532 | background-color: var(--background); 533 | font-family: inherit; 534 | font-size: inherit; 535 | margin-right: 6px; 536 | margin-bottom: 6px; 537 | padding: 10px; 538 | border: none; 539 | border-radius: 6px; 540 | outline: none; 541 | } 542 | 543 | @media (prefers-color-scheme: dark) { 544 | 545 | textarea { 546 | background-color: #161f27; 547 | background-color: var(--background); 548 | } 549 | } 550 | 551 | @media (prefers-color-scheme: dark) { 552 | 553 | textarea { 554 | color: #fff; 555 | color: var(--form-text); 556 | } 557 | } 558 | 559 | select { 560 | color: #1d1d1d; 561 | color: var(--form-text); 562 | background-color: #efefef; 563 | background-color: var(--background); 564 | font-family: inherit; 565 | font-size: inherit; 566 | margin-right: 6px; 567 | margin-bottom: 6px; 568 | padding: 10px; 569 | border: none; 570 | border-radius: 6px; 571 | outline: none; 572 | } 573 | 574 | @media (prefers-color-scheme: dark) { 575 | 576 | select { 577 | background-color: #161f27; 578 | background-color: var(--background); 579 | } 580 | } 581 | 582 | @media (prefers-color-scheme: dark) { 583 | 584 | select { 585 | color: #fff; 586 | color: var(--form-text); 587 | } 588 | } 589 | 590 | button { 591 | background-color: #d0cfcf; 592 | background-color: var(--button-base); 593 | padding-right: 30px; 594 | padding-left: 30px; 595 | } 596 | 597 | @media (prefers-color-scheme: dark) { 598 | 599 | button { 600 | background-color: #0c151c; 601 | background-color: var(--button-base); 602 | } 603 | } 604 | 605 | input[type='submit'] { 606 | background-color: #d0cfcf; 607 | background-color: var(--button-base); 608 | padding-right: 30px; 609 | padding-left: 30px; 610 | } 611 | 612 | @media (prefers-color-scheme: dark) { 613 | 614 | input[type='submit'] { 615 | background-color: #0c151c; 616 | background-color: var(--button-base); 617 | } 618 | } 619 | 620 | input[type='reset'] { 621 | background-color: #d0cfcf; 622 | background-color: var(--button-base); 623 | padding-right: 30px; 624 | padding-left: 30px; 625 | } 626 | 627 | @media (prefers-color-scheme: dark) { 628 | 629 | input[type='reset'] { 630 | background-color: #0c151c; 631 | background-color: var(--button-base); 632 | } 633 | } 634 | 635 | input[type='button'] { 636 | background-color: #d0cfcf; 637 | background-color: var(--button-base); 638 | padding-right: 30px; 639 | padding-left: 30px; 640 | } 641 | 642 | @media (prefers-color-scheme: dark) { 643 | 644 | input[type='button'] { 645 | background-color: #0c151c; 646 | background-color: var(--button-base); 647 | } 648 | } 649 | 650 | button:hover { 651 | background: #9b9b9b; 652 | background: var(--button-hover); 653 | } 654 | 655 | @media (prefers-color-scheme: dark) { 656 | 657 | button:hover { 658 | background: #040a0f; 659 | background: var(--button-hover); 660 | } 661 | } 662 | 663 | input[type='submit']:hover { 664 | background: #9b9b9b; 665 | background: var(--button-hover); 666 | } 667 | 668 | @media (prefers-color-scheme: dark) { 669 | 670 | input[type='submit']:hover { 671 | background: #040a0f; 672 | background: var(--button-hover); 673 | } 674 | } 675 | 676 | input[type='reset']:hover { 677 | background: #9b9b9b; 678 | background: var(--button-hover); 679 | } 680 | 681 | @media (prefers-color-scheme: dark) { 682 | 683 | input[type='reset']:hover { 684 | background: #040a0f; 685 | background: var(--button-hover); 686 | } 687 | } 688 | 689 | input[type='button']:hover { 690 | background: #9b9b9b; 691 | background: var(--button-hover); 692 | } 693 | 694 | @media (prefers-color-scheme: dark) { 695 | 696 | input[type='button']:hover { 697 | background: #040a0f; 698 | background: var(--button-hover); 699 | } 700 | } 701 | 702 | input[type='color'] { 703 | min-height: 2rem; 704 | padding: 8px; 705 | cursor: pointer; 706 | } 707 | 708 | input[type='checkbox'], 709 | input[type='radio'] { 710 | height: 1em; 711 | width: 1em; 712 | } 713 | 714 | input[type='radio'] { 715 | border-radius: 100%; 716 | } 717 | 718 | input { 719 | vertical-align: top; 720 | } 721 | 722 | label { 723 | vertical-align: middle; 724 | margin-bottom: 4px; 725 | display: inline-block; 726 | } 727 | 728 | input:not([type='checkbox']):not([type='radio']), 729 | input[type='range'], 730 | select, 731 | button, 732 | textarea { 733 | -webkit-appearance: none; 734 | } 735 | 736 | textarea { 737 | display: block; 738 | margin-right: 0; 739 | box-sizing: border-box; 740 | resize: vertical; 741 | } 742 | 743 | textarea:not([cols]) { 744 | width: 100%; 745 | } 746 | 747 | textarea:not([rows]) { 748 | min-height: 40px; 749 | height: 140px; 750 | } 751 | 752 | select { 753 | background: #efefef url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23161f27'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") calc(100% - 12px) 50% / 12px no-repeat; 754 | background: var(--background) var(--select-arrow) calc(100% - 12px) 50% / 12px no-repeat; 755 | padding-right: 35px; 756 | } 757 | 758 | @media (prefers-color-scheme: dark) { 759 | 760 | select { 761 | background: #161f27 url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") calc(100% - 12px) 50% / 12px no-repeat; 762 | background: var(--background) var(--select-arrow) calc(100% - 12px) 50% / 12px no-repeat; 763 | } 764 | } 765 | 766 | @media (prefers-color-scheme: dark) { 767 | 768 | select { 769 | background: #161f27 url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") calc(100% - 12px) 50% / 12px no-repeat; 770 | background: var(--background) var(--select-arrow) calc(100% - 12px) 50% / 12px no-repeat; 771 | } 772 | } 773 | 774 | @media (prefers-color-scheme: dark) { 775 | 776 | select { 777 | background: #161f27 url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") calc(100% - 12px) 50% / 12px no-repeat; 778 | background: var(--background) var(--select-arrow) calc(100% - 12px) 50% / 12px no-repeat; 779 | } 780 | } 781 | 782 | @media (prefers-color-scheme: dark) { 783 | 784 | select { 785 | background: #161f27 url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") calc(100% - 12px) 50% / 12px no-repeat; 786 | background: var(--background) var(--select-arrow) calc(100% - 12px) 50% / 12px no-repeat; 787 | } 788 | } 789 | 790 | select::-ms-expand { 791 | display: none; 792 | } 793 | 794 | select[multiple] { 795 | padding-right: 10px; 796 | background-image: none; 797 | overflow-y: auto; 798 | } 799 | 800 | input:focus { 801 | box-shadow: 0 0 0 2px #0096bfab; 802 | box-shadow: 0 0 0 2px var(--focus); 803 | } 804 | 805 | @media (prefers-color-scheme: dark) { 806 | 807 | input:focus { 808 | box-shadow: 0 0 0 2px #0096bfab; 809 | box-shadow: 0 0 0 2px var(--focus); 810 | } 811 | } 812 | 813 | select:focus { 814 | box-shadow: 0 0 0 2px #0096bfab; 815 | box-shadow: 0 0 0 2px var(--focus); 816 | } 817 | 818 | @media (prefers-color-scheme: dark) { 819 | 820 | select:focus { 821 | box-shadow: 0 0 0 2px #0096bfab; 822 | box-shadow: 0 0 0 2px var(--focus); 823 | } 824 | } 825 | 826 | button:focus { 827 | box-shadow: 0 0 0 2px #0096bfab; 828 | box-shadow: 0 0 0 2px var(--focus); 829 | } 830 | 831 | @media (prefers-color-scheme: dark) { 832 | 833 | button:focus { 834 | box-shadow: 0 0 0 2px #0096bfab; 835 | box-shadow: 0 0 0 2px var(--focus); 836 | } 837 | } 838 | 839 | textarea:focus { 840 | box-shadow: 0 0 0 2px #0096bfab; 841 | box-shadow: 0 0 0 2px var(--focus); 842 | } 843 | 844 | @media (prefers-color-scheme: dark) { 845 | 846 | textarea:focus { 847 | box-shadow: 0 0 0 2px #0096bfab; 848 | box-shadow: 0 0 0 2px var(--focus); 849 | } 850 | } 851 | 852 | input[type='checkbox']:active, 853 | input[type='radio']:active, 854 | input[type='submit']:active, 855 | input[type='reset']:active, 856 | input[type='button']:active, 857 | input[type='range']:active, 858 | button:active { 859 | transform: translateY(2px); 860 | } 861 | 862 | input:disabled, 863 | select:disabled, 864 | button:disabled, 865 | textarea:disabled { 866 | cursor: not-allowed; 867 | opacity: 0.5; 868 | } 869 | 870 | ::-moz-placeholder { 871 | color: #949494; 872 | color: var(--form-placeholder); 873 | } 874 | 875 | :-ms-input-placeholder { 876 | color: #949494; 877 | color: var(--form-placeholder); 878 | } 879 | 880 | ::-ms-input-placeholder { 881 | color: #949494; 882 | color: var(--form-placeholder); 883 | } 884 | 885 | ::placeholder { 886 | color: #949494; 887 | color: var(--form-placeholder); 888 | } 889 | 890 | @media (prefers-color-scheme: dark) { 891 | 892 | ::-moz-placeholder { 893 | color: #a9a9a9; 894 | color: var(--form-placeholder); 895 | } 896 | 897 | :-ms-input-placeholder { 898 | color: #a9a9a9; 899 | color: var(--form-placeholder); 900 | } 901 | 902 | ::-ms-input-placeholder { 903 | color: #a9a9a9; 904 | color: var(--form-placeholder); 905 | } 906 | 907 | ::placeholder { 908 | color: #a9a9a9; 909 | color: var(--form-placeholder); 910 | } 911 | } 912 | 913 | fieldset { 914 | border: 1px #0096bfab solid; 915 | border: 1px var(--focus) solid; 916 | border-radius: 6px; 917 | margin: 0; 918 | margin-bottom: 12px; 919 | padding: 10px; 920 | } 921 | 922 | @media (prefers-color-scheme: dark) { 923 | 924 | fieldset { 925 | border: 1px #0096bfab solid; 926 | border: 1px var(--focus) solid; 927 | } 928 | } 929 | 930 | legend { 931 | font-size: 0.9em; 932 | font-weight: 600; 933 | } 934 | 935 | input[type='range'] { 936 | margin: 10px 0; 937 | padding: 10px 0; 938 | background: transparent; 939 | } 940 | 941 | input[type='range']:focus { 942 | outline: none; 943 | } 944 | 945 | input[type='range']::-webkit-slider-runnable-track { 946 | width: 100%; 947 | height: 9.5px; 948 | -webkit-transition: 0.2s; 949 | transition: 0.2s; 950 | background: #efefef; 951 | background: var(--background); 952 | border-radius: 3px; 953 | } 954 | 955 | @media (prefers-color-scheme: dark) { 956 | 957 | input[type='range']::-webkit-slider-runnable-track { 958 | background: #161f27; 959 | background: var(--background); 960 | } 961 | } 962 | 963 | input[type='range']::-webkit-slider-thumb { 964 | box-shadow: 0 1px 1px #000, 0 0 1px #0d0d0d; 965 | height: 20px; 966 | width: 20px; 967 | border-radius: 50%; 968 | background: #dbdbdb; 969 | background: var(--border); 970 | -webkit-appearance: none; 971 | margin-top: -7px; 972 | } 973 | 974 | @media (prefers-color-scheme: dark) { 975 | 976 | input[type='range']::-webkit-slider-thumb { 977 | background: #526980; 978 | background: var(--border); 979 | } 980 | } 981 | 982 | input[type='range']:focus::-webkit-slider-runnable-track { 983 | background: #efefef; 984 | background: var(--background); 985 | } 986 | 987 | @media (prefers-color-scheme: dark) { 988 | 989 | input[type='range']:focus::-webkit-slider-runnable-track { 990 | background: #161f27; 991 | background: var(--background); 992 | } 993 | } 994 | 995 | input[type='range']::-moz-range-track { 996 | width: 100%; 997 | height: 9.5px; 998 | -moz-transition: 0.2s; 999 | transition: 0.2s; 1000 | background: #efefef; 1001 | background: var(--background); 1002 | border-radius: 3px; 1003 | } 1004 | 1005 | @media (prefers-color-scheme: dark) { 1006 | 1007 | input[type='range']::-moz-range-track { 1008 | background: #161f27; 1009 | background: var(--background); 1010 | } 1011 | } 1012 | 1013 | input[type='range']::-moz-range-thumb { 1014 | box-shadow: 1px 1px 1px #000, 0 0 1px #0d0d0d; 1015 | height: 20px; 1016 | width: 20px; 1017 | border-radius: 50%; 1018 | background: #dbdbdb; 1019 | background: var(--border); 1020 | } 1021 | 1022 | @media (prefers-color-scheme: dark) { 1023 | 1024 | input[type='range']::-moz-range-thumb { 1025 | background: #526980; 1026 | background: var(--border); 1027 | } 1028 | } 1029 | 1030 | input[type='range']::-ms-track { 1031 | width: 100%; 1032 | height: 9.5px; 1033 | background: transparent; 1034 | border-color: transparent; 1035 | border-width: 16px 0; 1036 | color: transparent; 1037 | } 1038 | 1039 | input[type='range']::-ms-fill-lower { 1040 | background: #efefef; 1041 | background: var(--background); 1042 | border: 0.2px solid #010101; 1043 | border-radius: 3px; 1044 | box-shadow: 1px 1px 1px #000, 0 0 1px #0d0d0d; 1045 | } 1046 | 1047 | @media (prefers-color-scheme: dark) { 1048 | 1049 | input[type='range']::-ms-fill-lower { 1050 | background: #161f27; 1051 | background: var(--background); 1052 | } 1053 | } 1054 | 1055 | input[type='range']::-ms-fill-upper { 1056 | background: #efefef; 1057 | background: var(--background); 1058 | border: 0.2px solid #010101; 1059 | border-radius: 3px; 1060 | box-shadow: 1px 1px 1px #000, 0 0 1px #0d0d0d; 1061 | } 1062 | 1063 | @media (prefers-color-scheme: dark) { 1064 | 1065 | input[type='range']::-ms-fill-upper { 1066 | background: #161f27; 1067 | background: var(--background); 1068 | } 1069 | } 1070 | 1071 | input[type='range']::-ms-thumb { 1072 | box-shadow: 1px 1px 1px #000, 0 0 1px #0d0d0d; 1073 | border: 1px solid #000; 1074 | height: 20px; 1075 | width: 20px; 1076 | border-radius: 50%; 1077 | background: #dbdbdb; 1078 | background: var(--border); 1079 | } 1080 | 1081 | @media (prefers-color-scheme: dark) { 1082 | 1083 | input[type='range']::-ms-thumb { 1084 | background: #526980; 1085 | background: var(--border); 1086 | } 1087 | } 1088 | 1089 | input[type='range']:focus::-ms-fill-lower { 1090 | background: #efefef; 1091 | background: var(--background); 1092 | } 1093 | 1094 | @media (prefers-color-scheme: dark) { 1095 | 1096 | input[type='range']:focus::-ms-fill-lower { 1097 | background: #161f27; 1098 | background: var(--background); 1099 | } 1100 | } 1101 | 1102 | input[type='range']:focus::-ms-fill-upper { 1103 | background: #efefef; 1104 | background: var(--background); 1105 | } 1106 | 1107 | @media (prefers-color-scheme: dark) { 1108 | 1109 | input[type='range']:focus::-ms-fill-upper { 1110 | background: #161f27; 1111 | background: var(--background); 1112 | } 1113 | } 1114 | 1115 | a { 1116 | text-decoration: none; 1117 | color: #0076d1; 1118 | color: var(--links); 1119 | } 1120 | 1121 | @media (prefers-color-scheme: dark) { 1122 | 1123 | a { 1124 | color: #41adff; 1125 | color: var(--links); 1126 | } 1127 | } 1128 | 1129 | a:hover { 1130 | text-decoration: underline; 1131 | } 1132 | 1133 | code { 1134 | background: #efefef; 1135 | background: var(--background); 1136 | color: #000; 1137 | color: var(--code); 1138 | padding: 2.5px 5px; 1139 | border-radius: 6px; 1140 | font-size: 1em; 1141 | } 1142 | 1143 | @media (prefers-color-scheme: dark) { 1144 | 1145 | code { 1146 | color: #ffbe85; 1147 | color: var(--code); 1148 | } 1149 | } 1150 | 1151 | @media (prefers-color-scheme: dark) { 1152 | 1153 | code { 1154 | background: #161f27; 1155 | background: var(--background); 1156 | } 1157 | } 1158 | 1159 | samp { 1160 | background: #efefef; 1161 | background: var(--background); 1162 | color: #000; 1163 | color: var(--code); 1164 | padding: 2.5px 5px; 1165 | border-radius: 6px; 1166 | font-size: 1em; 1167 | } 1168 | 1169 | @media (prefers-color-scheme: dark) { 1170 | 1171 | samp { 1172 | color: #ffbe85; 1173 | color: var(--code); 1174 | } 1175 | } 1176 | 1177 | @media (prefers-color-scheme: dark) { 1178 | 1179 | samp { 1180 | background: #161f27; 1181 | background: var(--background); 1182 | } 1183 | } 1184 | 1185 | time { 1186 | background: #efefef; 1187 | background: var(--background); 1188 | color: #000; 1189 | color: var(--code); 1190 | padding: 2.5px 5px; 1191 | border-radius: 6px; 1192 | font-size: 1em; 1193 | } 1194 | 1195 | @media (prefers-color-scheme: dark) { 1196 | 1197 | time { 1198 | color: #ffbe85; 1199 | color: var(--code); 1200 | } 1201 | } 1202 | 1203 | @media (prefers-color-scheme: dark) { 1204 | 1205 | time { 1206 | background: #161f27; 1207 | background: var(--background); 1208 | } 1209 | } 1210 | 1211 | pre > code { 1212 | padding: 10px; 1213 | display: block; 1214 | overflow-x: auto; 1215 | } 1216 | 1217 | var { 1218 | color: #39a33c; 1219 | color: var(--variable); 1220 | font-style: normal; 1221 | font-family: monospace; 1222 | } 1223 | 1224 | @media (prefers-color-scheme: dark) { 1225 | 1226 | var { 1227 | color: #d941e2; 1228 | color: var(--variable); 1229 | } 1230 | } 1231 | 1232 | kbd { 1233 | background: #efefef; 1234 | background: var(--background); 1235 | border: 1px solid #dbdbdb; 1236 | border: 1px solid var(--border); 1237 | border-radius: 2px; 1238 | color: #363636; 1239 | color: var(--text-main); 1240 | padding: 2px 4px 2px 4px; 1241 | } 1242 | 1243 | @media (prefers-color-scheme: dark) { 1244 | 1245 | kbd { 1246 | color: #dbdbdb; 1247 | color: var(--text-main); 1248 | } 1249 | } 1250 | 1251 | @media (prefers-color-scheme: dark) { 1252 | 1253 | kbd { 1254 | border: 1px solid #526980; 1255 | border: 1px solid var(--border); 1256 | } 1257 | } 1258 | 1259 | @media (prefers-color-scheme: dark) { 1260 | 1261 | kbd { 1262 | background: #161f27; 1263 | background: var(--background); 1264 | } 1265 | } 1266 | 1267 | img, 1268 | video { 1269 | max-width: 100%; 1270 | height: auto; 1271 | } 1272 | 1273 | hr { 1274 | border: none; 1275 | border-top: 1px solid #dbdbdb; 1276 | border-top: 1px solid var(--border); 1277 | } 1278 | 1279 | @media (prefers-color-scheme: dark) { 1280 | 1281 | hr { 1282 | border-top: 1px solid #526980; 1283 | border-top: 1px solid var(--border); 1284 | } 1285 | } 1286 | 1287 | table { 1288 | border-collapse: collapse; 1289 | margin-bottom: 10px; 1290 | width: 100%; 1291 | table-layout: fixed; 1292 | } 1293 | 1294 | table caption { 1295 | text-align: left; 1296 | } 1297 | 1298 | td, 1299 | th { 1300 | padding: 6px; 1301 | text-align: left; 1302 | vertical-align: top; 1303 | word-wrap: break-word; 1304 | } 1305 | 1306 | thead { 1307 | border-bottom: 1px solid #dbdbdb; 1308 | border-bottom: 1px solid var(--border); 1309 | } 1310 | 1311 | @media (prefers-color-scheme: dark) { 1312 | 1313 | thead { 1314 | border-bottom: 1px solid #526980; 1315 | border-bottom: 1px solid var(--border); 1316 | } 1317 | } 1318 | 1319 | tfoot { 1320 | border-top: 1px solid #dbdbdb; 1321 | border-top: 1px solid var(--border); 1322 | } 1323 | 1324 | @media (prefers-color-scheme: dark) { 1325 | 1326 | tfoot { 1327 | border-top: 1px solid #526980; 1328 | border-top: 1px solid var(--border); 1329 | } 1330 | } 1331 | 1332 | tbody tr:nth-child(even) { 1333 | background-color: #efefef; 1334 | background-color: var(--background); 1335 | } 1336 | 1337 | @media (prefers-color-scheme: dark) { 1338 | 1339 | tbody tr:nth-child(even) { 1340 | background-color: #161f27; 1341 | background-color: var(--background); 1342 | } 1343 | } 1344 | 1345 | tbody tr:nth-child(even) button { 1346 | background-color: #f7f7f7; 1347 | background-color: var(--background-alt); 1348 | } 1349 | 1350 | @media (prefers-color-scheme: dark) { 1351 | 1352 | tbody tr:nth-child(even) button { 1353 | background-color: #1a242f; 1354 | background-color: var(--background-alt); 1355 | } 1356 | } 1357 | 1358 | tbody tr:nth-child(even) button:hover { 1359 | background-color: #fff; 1360 | background-color: var(--background-body); 1361 | } 1362 | 1363 | @media (prefers-color-scheme: dark) { 1364 | 1365 | tbody tr:nth-child(even) button:hover { 1366 | background-color: #202b38; 1367 | background-color: var(--background-body); 1368 | } 1369 | } 1370 | 1371 | ::-webkit-scrollbar { 1372 | height: 10px; 1373 | width: 10px; 1374 | } 1375 | 1376 | ::-webkit-scrollbar-track { 1377 | background: #efefef; 1378 | background: var(--background); 1379 | border-radius: 6px; 1380 | } 1381 | 1382 | @media (prefers-color-scheme: dark) { 1383 | 1384 | ::-webkit-scrollbar-track { 1385 | background: #161f27; 1386 | background: var(--background); 1387 | } 1388 | } 1389 | 1390 | ::-webkit-scrollbar-thumb { 1391 | background: rgb(170, 170, 170); 1392 | background: var(--scrollbar-thumb); 1393 | border-radius: 6px; 1394 | } 1395 | 1396 | @media (prefers-color-scheme: dark) { 1397 | 1398 | ::-webkit-scrollbar-thumb { 1399 | background: #040a0f; 1400 | background: var(--scrollbar-thumb); 1401 | } 1402 | } 1403 | 1404 | @media (prefers-color-scheme: dark) { 1405 | 1406 | ::-webkit-scrollbar-thumb { 1407 | background: #040a0f; 1408 | background: var(--scrollbar-thumb); 1409 | } 1410 | } 1411 | 1412 | ::-webkit-scrollbar-thumb:hover { 1413 | background: #9b9b9b; 1414 | background: var(--scrollbar-thumb-hover); 1415 | } 1416 | 1417 | @media (prefers-color-scheme: dark) { 1418 | 1419 | ::-webkit-scrollbar-thumb:hover { 1420 | background: rgb(0, 0, 0); 1421 | background: var(--scrollbar-thumb-hover); 1422 | } 1423 | } 1424 | 1425 | @media (prefers-color-scheme: dark) { 1426 | 1427 | ::-webkit-scrollbar-thumb:hover { 1428 | background: rgb(0, 0, 0); 1429 | background: var(--scrollbar-thumb-hover); 1430 | } 1431 | } 1432 | 1433 | ::-moz-selection { 1434 | background-color: #9e9e9e; 1435 | background-color: var(--selection); 1436 | color: #000; 1437 | color: var(--text-bright); 1438 | } 1439 | 1440 | ::selection { 1441 | background-color: #9e9e9e; 1442 | background-color: var(--selection); 1443 | color: #000; 1444 | color: var(--text-bright); 1445 | } 1446 | 1447 | @media (prefers-color-scheme: dark) { 1448 | 1449 | ::-moz-selection { 1450 | color: #fff; 1451 | color: var(--text-bright); 1452 | } 1453 | 1454 | ::selection { 1455 | color: #fff; 1456 | color: var(--text-bright); 1457 | } 1458 | } 1459 | 1460 | @media (prefers-color-scheme: dark) { 1461 | 1462 | ::-moz-selection { 1463 | background-color: #1c76c5; 1464 | background-color: var(--selection); 1465 | } 1466 | 1467 | ::selection { 1468 | background-color: #1c76c5; 1469 | background-color: var(--selection); 1470 | } 1471 | } 1472 | 1473 | details { 1474 | display: flex; 1475 | flex-direction: column; 1476 | align-items: flex-start; 1477 | background-color: #f7f7f7; 1478 | background-color: var(--background-alt); 1479 | padding: 10px 10px 0; 1480 | margin: 1em 0; 1481 | border-radius: 6px; 1482 | overflow: hidden; 1483 | } 1484 | 1485 | @media (prefers-color-scheme: dark) { 1486 | 1487 | details { 1488 | background-color: #1a242f; 1489 | background-color: var(--background-alt); 1490 | } 1491 | } 1492 | 1493 | details[open] { 1494 | padding: 10px; 1495 | } 1496 | 1497 | details > :last-child { 1498 | margin-bottom: 0; 1499 | } 1500 | 1501 | details[open] summary { 1502 | margin-bottom: 10px; 1503 | } 1504 | 1505 | summary { 1506 | display: list-item; 1507 | background-color: #efefef; 1508 | background-color: var(--background); 1509 | padding: 10px; 1510 | margin: -10px -10px 0; 1511 | cursor: pointer; 1512 | outline: none; 1513 | } 1514 | 1515 | @media (prefers-color-scheme: dark) { 1516 | 1517 | summary { 1518 | background-color: #161f27; 1519 | background-color: var(--background); 1520 | } 1521 | } 1522 | 1523 | summary:hover, 1524 | summary:focus { 1525 | text-decoration: underline; 1526 | } 1527 | 1528 | details > :not(summary) { 1529 | margin-top: 0; 1530 | } 1531 | 1532 | summary::-webkit-details-marker { 1533 | color: #363636; 1534 | color: var(--text-main); 1535 | } 1536 | 1537 | @media (prefers-color-scheme: dark) { 1538 | 1539 | summary::-webkit-details-marker { 1540 | color: #dbdbdb; 1541 | color: var(--text-main); 1542 | } 1543 | } 1544 | 1545 | dialog { 1546 | background-color: #f7f7f7; 1547 | background-color: var(--background-alt); 1548 | color: #363636; 1549 | color: var(--text-main); 1550 | border: none; 1551 | border-radius: 6px; 1552 | border-color: #dbdbdb; 1553 | border-color: var(--border); 1554 | padding: 10px 30px; 1555 | } 1556 | 1557 | @media (prefers-color-scheme: dark) { 1558 | 1559 | dialog { 1560 | border-color: #526980; 1561 | border-color: var(--border); 1562 | } 1563 | } 1564 | 1565 | @media (prefers-color-scheme: dark) { 1566 | 1567 | dialog { 1568 | color: #dbdbdb; 1569 | color: var(--text-main); 1570 | } 1571 | } 1572 | 1573 | @media (prefers-color-scheme: dark) { 1574 | 1575 | dialog { 1576 | background-color: #1a242f; 1577 | background-color: var(--background-alt); 1578 | } 1579 | } 1580 | 1581 | dialog > header:first-child { 1582 | background-color: #efefef; 1583 | background-color: var(--background); 1584 | border-radius: 6px 6px 0 0; 1585 | margin: -10px -30px 10px; 1586 | padding: 10px; 1587 | text-align: center; 1588 | } 1589 | 1590 | @media (prefers-color-scheme: dark) { 1591 | 1592 | dialog > header:first-child { 1593 | background-color: #161f27; 1594 | background-color: var(--background); 1595 | } 1596 | } 1597 | 1598 | dialog::-webkit-backdrop { 1599 | background: #0000009c; 1600 | -webkit-backdrop-filter: blur(4px); 1601 | backdrop-filter: blur(4px); 1602 | } 1603 | 1604 | dialog::backdrop { 1605 | background: #0000009c; 1606 | -webkit-backdrop-filter: blur(4px); 1607 | backdrop-filter: blur(4px); 1608 | } 1609 | 1610 | footer { 1611 | border-top: 1px solid #dbdbdb; 1612 | border-top: 1px solid var(--border); 1613 | padding-top: 10px; 1614 | color: #70777f; 1615 | color: var(--text-muted); 1616 | text-align: center; 1617 | } 1618 | 1619 | @media (prefers-color-scheme: dark) { 1620 | 1621 | footer { 1622 | color: #a9b1ba; 1623 | color: var(--text-muted); 1624 | } 1625 | } 1626 | 1627 | @media (prefers-color-scheme: dark) { 1628 | 1629 | footer { 1630 | border-top: 1px solid #526980; 1631 | border-top: 1px solid var(--border); 1632 | } 1633 | } 1634 | 1635 | body > footer { 1636 | margin-top: 40px; 1637 | } 1638 | 1639 | @media print { 1640 | body, 1641 | pre, 1642 | code, 1643 | summary, 1644 | details, 1645 | button, 1646 | input, 1647 | textarea { 1648 | background-color: #fff; 1649 | } 1650 | 1651 | button, 1652 | input, 1653 | textarea { 1654 | border: 1px solid #000; 1655 | } 1656 | 1657 | body, 1658 | h1, 1659 | h2, 1660 | h3, 1661 | h4, 1662 | h5, 1663 | h6, 1664 | pre, 1665 | code, 1666 | button, 1667 | input, 1668 | textarea, 1669 | footer, 1670 | summary, 1671 | strong { 1672 | color: #000; 1673 | } 1674 | 1675 | summary::marker { 1676 | color: #000; 1677 | } 1678 | 1679 | summary::-webkit-details-marker { 1680 | color: #000; 1681 | } 1682 | 1683 | tbody tr:nth-child(even) { 1684 | background-color: #f2f2f2; 1685 | } 1686 | 1687 | a { 1688 | color: #00f; 1689 | text-decoration: underline; 1690 | } 1691 | } 1692 | 1693 | .github-corner { 1694 | fill: var(--links); 1695 | color: var(--background-body); 1696 | } 1697 | 1698 | header { 1699 | text-align: center; 1700 | } 1701 | 1702 | header h1 { 1703 | margin-bottom: 0; 1704 | font-size: 2.5em; 1705 | } 1706 | 1707 | header p { 1708 | margin-top: 0; 1709 | } 1710 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs' 2 | import RSSParser from 'rss-parser' 3 | import posters from './lib/posters/index.js' 4 | 5 | const echoPath = process.argv[1].replace('index.js', '') 6 | 7 | import config from './config.js' 8 | 9 | const args = process.argv.slice(2) 10 | const INIT_MODE = args.includes('init') 11 | const DRY_MODE = args.includes('dry') 12 | 13 | if (DRY_MODE && INIT_MODE) 14 | { 15 | console.log('🚨 You cannot run Echo with init mode AND dry mode enabled at the same time') 16 | process.exit() 17 | } 18 | 19 | if (DRY_MODE) console.log('🌵 Running in dry mode, no posts will be created') 20 | 21 | async function getFeedItems(feed, isJson, customFields) 22 | { 23 | if (isJson) 24 | { 25 | const res = await fetch(feed) 26 | const feedData = await res.json() 27 | return feedData.items 28 | } 29 | 30 | const data = await (new RSSParser({ 31 | customFields: { 32 | item: customFields || [], 33 | } 34 | })).parseURL(feed) 35 | return data.items || [] 36 | } 37 | 38 | if (!fs.existsSync(`${echoPath}data`)) { 39 | fs.mkdirSync(`${echoPath}data`) 40 | console.log('📁 Data folder created!') 41 | } 42 | 43 | for (const site of config.sites) 44 | { 45 | const siteFile = `${site.name}.txt` 46 | if (!fs.existsSync(`${echoPath}data/${siteFile}`)) { 47 | await fs.writeFile(`${echoPath}data/${siteFile}`, JSON.stringify([], '', 2), { flag: "wx" }, (err) => { 48 | if (err) throw err; 49 | console.log(`✅ ${site.name} data file created!`) 50 | }) 51 | } 52 | 53 | console.log(`⚙️ Fetching for ${site.name}`) 54 | let items = await getFeedItems(site.feed, site.json, site.customFields) 55 | 56 | if (items.length === 0) 57 | { 58 | console.log(`0️⃣ No items found for ${site.name}`) 59 | continue; 60 | } 61 | 62 | if (!site.transform.getId(items[0])) 63 | { 64 | console.log(`❌ No ID found for item in ${site.name}, skipping`) 65 | console.log(`👀 To fix this, check the transform.getId function for this site. It's likely you're expecting id but the feed item uses guid instead`) 66 | break; 67 | } 68 | 69 | if (site.transform.filter) 70 | { 71 | items = site.transform.filter(items) 72 | } 73 | const existingIds = JSON.parse(fs.readFileSync(`${echoPath}data/${siteFile}`, 'utf8')) 74 | 75 | if (existingIds.length > 0) { 76 | items = items.filter(item => { 77 | return !existingIds.includes(site.transform.getId(item)) 78 | }) 79 | } 80 | 81 | if (!items.length && !INIT_MODE) 82 | { 83 | console.log(`❎ No new items found for ${site.name}`) 84 | continue 85 | } 86 | 87 | const newIds = items.map(i => site.transform.getId(i)) 88 | 89 | if (!DRY_MODE) 90 | { 91 | fs.writeFileSync(`${echoPath}data/${siteFile}`, JSON.stringify([...newIds, ...existingIds], '', 2)); 92 | } 93 | 94 | if (INIT_MODE) 95 | { 96 | console.log(`⚙️ Echo initialised for ${site.name}!`) 97 | continue 98 | } 99 | 100 | for (const item of items) 101 | { 102 | const formatted = site.transform.format(item) 103 | 104 | if (DRY_MODE) 105 | { 106 | console.log(`✅ Will create ${site.name} post for ${formatted.date}\n\n${formatted.content}`) 107 | } else { 108 | for (const service of site.services) 109 | { 110 | await posters[service](config.services[service], formatted, site) 111 | } 112 | } 113 | } 114 | } 115 | 116 | process.exit() 117 | -------------------------------------------------------------------------------- /lib/helpers.js: -------------------------------------------------------------------------------- 1 | import TurndownService from 'turndown' 2 | import * as Cheerio from 'cheerio' 3 | import { convert } from 'html-to-text' 4 | import { v4 as uuidv4 } from 'uuid' 5 | import urlRegex from 'url-regex' 6 | import { countableText } from './mastodonCount/counter.js' 7 | import { encode, decode } from 'html-entities' 8 | 9 | const turndownForWhat = new TurndownService() 10 | 11 | export default { 12 | toMarkdown: (html) => { 13 | return turndownForWhat.turndown(html) 14 | }, 15 | cheerioLoad: (html) => { 16 | return Cheerio.load(html) 17 | }, 18 | generateUuid: () => { 19 | return uuidv4() 20 | }, 21 | htmlToText: (html) => { 22 | return convert(html, { 23 | selectors: 24 | [ 25 | { 26 | selector: 'a', 27 | options: { 28 | ignoreHref: true 29 | 30 | } 31 | } 32 | ] 33 | }) 34 | }, 35 | getMastodonLength: (string) => { 36 | return countableText(string).length 37 | }, 38 | getLinks: (string) => { 39 | return string.match(urlRegex()) 40 | }, 41 | decode: (string) => { 42 | return decode(string) 43 | }, 44 | encode: (string) => { 45 | return encode(string) 46 | }, 47 | slugify: (string) => { 48 | return string.toLowerCase().replace(/[^\w\s]/gi, '').split(' ').join('-') 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/mastodonCount/counter.js: -------------------------------------------------------------------------------- 1 | import { urlRegex } from './url_regex.js'; 2 | 3 | const urlPlaceholder = 'xxxxxxxxxxxxxxxxxxxxxxx'; 4 | 5 | export function countableText(inputText) { 6 | return inputText 7 | .replace(urlRegex, urlPlaceholder) 8 | .replace(/(^|[^\/\w])@(([a-z0-9_]+)@[a-z0-9\.\-]+[a-z0-9]+)/ig, '$1@$3'); 9 | }; -------------------------------------------------------------------------------- /lib/mastodonCount/url_regex.js: -------------------------------------------------------------------------------- 1 | // https://github.com/mastodon/mastodon/blob/5d5c0f4f4358f4349d9e2db59cf90b1f5de24e81/app/javascript/mastodon/features/compose/util/url_regex.js 2 | 3 | const regexen = {}; 4 | 5 | const regexSupplant = function(regex, flags) { 6 | flags = flags || ''; 7 | if (typeof regex !== 'string') { 8 | if (regex.global && flags.indexOf('g') < 0) { 9 | flags += 'g'; 10 | } 11 | if (regex.ignoreCase && flags.indexOf('i') < 0) { 12 | flags += 'i'; 13 | } 14 | if (regex.multiline && flags.indexOf('m') < 0) { 15 | flags += 'm'; 16 | } 17 | 18 | regex = regex.source; 19 | } 20 | return new RegExp(regex.replace(/#\{(\w+)\}/g, function(match, name) { 21 | var newRegex = regexen[name] || ''; 22 | if (typeof newRegex !== 'string') { 23 | newRegex = newRegex.source; 24 | } 25 | return newRegex; 26 | }), flags); 27 | }; 28 | 29 | const stringSupplant = function(str, values) { 30 | return str.replace(/#\{(\w+)\}/g, function(match, name) { 31 | return values[name] || ''; 32 | }); 33 | }; 34 | 35 | export const urlRegex = (function() { 36 | regexen.spaces_group = /\x09-\x0D\x20\x85\xA0\u1680\u180E\u2000-\u200A\u2028\u2029\u202F\u205F\u3000/; 37 | regexen.invalid_chars_group = /\uFFFE\uFEFF\uFFFF\u202A-\u202E/; 38 | regexen.punct = /\!'#%&'\(\)*\+,\\\-\.\/:;<=>\?@\[\]\^_{|}~\$/; 39 | regexen.validUrlPrecedingChars = regexSupplant(/(?:[^A-Za-z0-9@@$###{invalid_chars_group}]|^)/); 40 | regexen.invalidDomainChars = stringSupplant('#{punct}#{spaces_group}#{invalid_chars_group}', regexen); 41 | regexen.validDomainChars = regexSupplant(/[^#{invalidDomainChars}]/); 42 | regexen.validSubdomain = regexSupplant(/(?:(?:#{validDomainChars}(?:[_-]|#{validDomainChars})*)?#{validDomainChars}\.)/); 43 | regexen.validDomainName = regexSupplant(/(?:(?:#{validDomainChars}(?:-|#{validDomainChars})*)?#{validDomainChars}\.)/); 44 | regexen.validGTLD = regexSupplant(RegExp( 45 | '(?:(?:' + 46 | '삼성|닷컴|닷넷|香格里拉|餐厅|食品|飞利浦|電訊盈科|集团|通販|购物|谷歌|诺基亚|联通|网络|网站|网店|网址|组织机构|移动|珠宝|点看|游戏|淡马锡|机构|書籍|时尚|新闻|政府|' + 47 | '政务|手表|手机|我爱你|慈善|微博|广东|工行|家電|娱乐|天主教|大拿|大众汽车|在线|嘉里大酒店|嘉里|商标|商店|商城|公益|公司|八卦|健康|信息|佛山|企业|中文网|中信|世界|' + 48 | 'ポイント|ファッション|セール|ストア|コム|グーグル|クラウド|みんな|คอม|संगठन|नेट|कॉम|همراه|موقع|موبايلي|كوم|كاثوليك|عرب|شبكة|' + 49 | 'بيتك|بازار|العليان|ارامكو|اتصالات|ابوظبي|קום|сайт|рус|орг|онлайн|москва|ком|католик|дети|' + 50 | 'zuerich|zone|zippo|zip|zero|zara|zappos|yun|youtube|you|yokohama|yoga|yodobashi|yandex|yamaxun|' + 51 | 'yahoo|yachts|xyz|xxx|xperia|xin|xihuan|xfinity|xerox|xbox|wtf|wtc|wow|world|works|work|woodside|' + 52 | 'wolterskluwer|wme|winners|wine|windows|win|williamhill|wiki|wien|whoswho|weir|weibo|wedding|wed|' + 53 | 'website|weber|webcam|weatherchannel|weather|watches|watch|warman|wanggou|wang|walter|walmart|' + 54 | 'wales|vuelos|voyage|voto|voting|vote|volvo|volkswagen|vodka|vlaanderen|vivo|viva|vistaprint|' + 55 | 'vista|vision|visa|virgin|vip|vin|villas|viking|vig|video|viajes|vet|versicherung|' + 56 | 'vermögensberatung|vermögensberater|verisign|ventures|vegas|vanguard|vana|vacations|ups|uol|uno|' + 57 | 'university|unicom|uconnect|ubs|ubank|tvs|tushu|tunes|tui|tube|trv|trust|travelersinsurance|' + 58 | 'travelers|travelchannel|travel|training|trading|trade|toys|toyota|town|tours|total|toshiba|' + 59 | 'toray|top|tools|tokyo|today|tmall|tkmaxx|tjx|tjmaxx|tirol|tires|tips|tiffany|tienda|tickets|' + 60 | 'tiaa|theatre|theater|thd|teva|tennis|temasek|telefonica|telecity|tel|technology|tech|team|tdk|' + 61 | 'tci|taxi|tax|tattoo|tatar|tatamotors|target|taobao|talk|taipei|tab|systems|symantec|sydney|' + 62 | 'swiss|swiftcover|swatch|suzuki|surgery|surf|support|supply|supplies|sucks|style|study|studio|' + 63 | 'stream|store|storage|stockholm|stcgroup|stc|statoil|statefarm|statebank|starhub|star|staples|' + 64 | 'stada|srt|srl|spreadbetting|spot|spiegel|space|soy|sony|song|solutions|solar|sohu|software|' + 65 | 'softbank|social|soccer|sncf|smile|smart|sling|skype|sky|skin|ski|site|singles|sina|silk|shriram|' + 66 | 'showtime|show|shouji|shopping|shop|shoes|shiksha|shia|shell|shaw|sharp|shangrila|sfr|sexy|sex|' + 67 | 'sew|seven|ses|services|sener|select|seek|security|secure|seat|search|scot|scor|scjohnson|' + 68 | 'science|schwarz|schule|school|scholarships|schmidt|schaeffler|scb|sca|sbs|sbi|saxo|save|sas|' + 69 | 'sarl|sapo|sap|sanofi|sandvikcoromant|sandvik|samsung|samsclub|salon|sale|sakura|safety|safe|' + 70 | 'saarland|ryukyu|rwe|run|ruhr|rugby|rsvp|room|rogers|rodeo|rocks|rocher|rmit|rip|rio|ril|' + 71 | 'rightathome|ricoh|richardli|rich|rexroth|reviews|review|restaurant|rest|republican|report|' + 72 | 'repair|rentals|rent|ren|reliance|reit|reisen|reise|rehab|redumbrella|redstone|red|recipes|' + 73 | 'realty|realtor|realestate|read|raid|radio|racing|qvc|quest|quebec|qpon|pwc|pub|prudential|pru|' + 74 | 'protection|property|properties|promo|progressive|prof|productions|prod|pro|prime|press|praxi|' + 75 | 'pramerica|post|porn|politie|poker|pohl|pnc|plus|plumbing|playstation|play|place|pizza|pioneer|' + 76 | 'pink|ping|pin|pid|pictures|pictet|pics|piaget|physio|photos|photography|photo|phone|philips|phd|' + 77 | 'pharmacy|pfizer|pet|pccw|pay|passagens|party|parts|partners|pars|paris|panerai|panasonic|' + 78 | 'pamperedchef|page|ovh|ott|otsuka|osaka|origins|orientexpress|organic|org|orange|oracle|open|ooo|' + 79 | 'onyourside|online|onl|ong|one|omega|ollo|oldnavy|olayangroup|olayan|okinawa|office|off|observer|' + 80 | 'obi|nyc|ntt|nrw|nra|nowtv|nowruz|now|norton|northwesternmutual|nokia|nissay|nissan|ninja|nikon|' + 81 | 'nike|nico|nhk|ngo|nfl|nexus|nextdirect|next|news|newholland|new|neustar|network|netflix|netbank|' + 82 | 'net|nec|nba|navy|natura|nationwide|name|nagoya|nadex|nab|mutuelle|mutual|museum|mtr|mtpc|mtn|' + 83 | 'msd|movistar|movie|mov|motorcycles|moto|moscow|mortgage|mormon|mopar|montblanc|monster|money|' + 84 | 'monash|mom|moi|moe|moda|mobily|mobile|mobi|mma|mls|mlb|mitsubishi|mit|mint|mini|mil|microsoft|' + 85 | 'miami|metlife|merckmsd|meo|menu|men|memorial|meme|melbourne|meet|media|med|mckinsey|mcdonalds|' + 86 | 'mcd|mba|mattel|maserati|marshalls|marriott|markets|marketing|market|map|mango|management|man|' + 87 | 'makeup|maison|maif|madrid|macys|luxury|luxe|lupin|lundbeck|ltda|ltd|lplfinancial|lpl|love|lotto|' + 88 | 'lotte|london|lol|loft|locus|locker|loans|loan|lixil|living|live|lipsy|link|linde|lincoln|limo|' + 89 | 'limited|lilly|like|lighting|lifestyle|lifeinsurance|life|lidl|liaison|lgbt|lexus|lego|legal|' + 90 | 'lefrak|leclerc|lease|lds|lawyer|law|latrobe|latino|lat|lasalle|lanxess|landrover|land|lancome|' + 91 | 'lancia|lancaster|lamer|lamborghini|ladbrokes|lacaixa|kyoto|kuokgroup|kred|krd|kpn|kpmg|kosher|' + 92 | 'komatsu|koeln|kiwi|kitchen|kindle|kinder|kim|kia|kfh|kerryproperties|kerrylogistics|kerryhotels|' + 93 | 'kddi|kaufen|juniper|juegos|jprs|jpmorgan|joy|jot|joburg|jobs|jnj|jmp|jll|jlc|jio|jewelry|jetzt|' + 94 | 'jeep|jcp|jcb|java|jaguar|iwc|iveco|itv|itau|istanbul|ist|ismaili|iselect|irish|ipiranga|' + 95 | 'investments|intuit|international|intel|int|insure|insurance|institute|ink|ing|info|infiniti|' + 96 | 'industries|immobilien|immo|imdb|imamat|ikano|iinet|ifm|ieee|icu|ice|icbc|ibm|hyundai|hyatt|' + 97 | 'hughes|htc|hsbc|how|house|hotmail|hotels|hoteles|hot|hosting|host|hospital|horse|honeywell|' + 98 | 'honda|homesense|homes|homegoods|homedepot|holiday|holdings|hockey|hkt|hiv|hitachi|hisamitsu|' + 99 | 'hiphop|hgtv|hermes|here|helsinki|help|healthcare|health|hdfcbank|hdfc|hbo|haus|hangout|hamburg|' + 100 | 'hair|guru|guitars|guide|guge|gucci|guardian|group|grocery|gripe|green|gratis|graphics|grainger|' + 101 | 'gov|got|gop|google|goog|goodyear|goodhands|goo|golf|goldpoint|gold|godaddy|gmx|gmo|gmbh|gmail|' + 102 | 'globo|global|gle|glass|glade|giving|gives|gifts|gift|ggee|george|genting|gent|gea|gdn|gbiz|' + 103 | 'garden|gap|games|game|gallup|gallo|gallery|gal|fyi|futbol|furniture|fund|fun|fujixerox|fujitsu|' + 104 | 'ftr|frontier|frontdoor|frogans|frl|fresenius|free|fox|foundation|forum|forsale|forex|ford|' + 105 | 'football|foodnetwork|food|foo|fly|flsmidth|flowers|florist|flir|flights|flickr|fitness|fit|' + 106 | 'fishing|fish|firmdale|firestone|fire|financial|finance|final|film|fido|fidelity|fiat|ferrero|' + 107 | 'ferrari|feedback|fedex|fast|fashion|farmers|farm|fans|fan|family|faith|fairwinds|fail|fage|' + 108 | 'extraspace|express|exposed|expert|exchange|everbank|events|eus|eurovision|etisalat|esurance|' + 109 | 'estate|esq|erni|ericsson|equipment|epson|epost|enterprises|engineering|engineer|energy|emerck|' + 110 | 'email|education|edu|edeka|eco|eat|earth|dvr|dvag|durban|dupont|duns|dunlop|duck|dubai|dtv|drive|' + 111 | 'download|dot|doosan|domains|doha|dog|dodge|doctor|docs|dnp|diy|dish|discover|discount|directory|' + 112 | 'direct|digital|diet|diamonds|dhl|dev|design|desi|dentist|dental|democrat|delta|deloitte|dell|' + 113 | 'delivery|degree|deals|dealer|deal|dds|dclk|day|datsun|dating|date|data|dance|dad|dabur|cyou|' + 114 | 'cymru|cuisinella|csc|cruises|cruise|crs|crown|cricket|creditunion|creditcard|credit|courses|' + 115 | 'coupons|coupon|country|corsica|coop|cool|cookingchannel|cooking|contractors|contact|consulting|' + 116 | 'construction|condos|comsec|computer|compare|company|community|commbank|comcast|com|cologne|' + 117 | 'college|coffee|codes|coach|clubmed|club|cloud|clothing|clinique|clinic|click|cleaning|claims|' + 118 | 'cityeats|city|citic|citi|citadel|cisco|circle|cipriani|church|chrysler|chrome|christmas|chloe|' + 119 | 'chintai|cheap|chat|chase|channel|chanel|cfd|cfa|cern|ceo|center|ceb|cbs|cbre|cbn|cba|catholic|' + 120 | 'catering|cat|casino|cash|caseih|case|casa|cartier|cars|careers|career|care|cards|caravan|car|' + 121 | 'capitalone|capital|capetown|canon|cancerresearch|camp|camera|cam|calvinklein|call|cal|cafe|cab|' + 122 | 'bzh|buzz|buy|business|builders|build|bugatti|budapest|brussels|brother|broker|broadway|' + 123 | 'bridgestone|bradesco|box|boutique|bot|boston|bostik|bosch|boots|booking|book|boo|bond|bom|bofa|' + 124 | 'boehringer|boats|bnpparibas|bnl|bmw|bms|blue|bloomberg|blog|blockbuster|blanco|blackfriday|' + 125 | 'black|biz|bio|bingo|bing|bike|bid|bible|bharti|bet|bestbuy|best|berlin|bentley|beer|beauty|' + 126 | 'beats|bcn|bcg|bbva|bbt|bbc|bayern|bauhaus|basketball|baseball|bargains|barefoot|barclays|' + 127 | 'barclaycard|barcelona|bar|bank|band|bananarepublic|banamex|baidu|baby|azure|axa|aws|avianca|' + 128 | 'autos|auto|author|auspost|audio|audible|audi|auction|attorney|athleta|associates|asia|asda|arte|' + 129 | 'art|arpa|army|archi|aramco|arab|aquarelle|apple|app|apartments|aol|anz|anquan|android|analytics|' + 130 | 'amsterdam|amica|amfam|amex|americanfamily|americanexpress|alstom|alsace|ally|allstate|allfinanz|' + 131 | 'alipay|alibaba|alfaromeo|akdn|airtel|airforce|airbus|aigo|aig|agency|agakhan|africa|afl|' + 132 | 'afamilycompany|aetna|aero|aeg|adult|ads|adac|actor|active|aco|accountants|accountant|accenture|' + 133 | 'academy|abudhabi|abogado|able|abc|abbvie|abbott|abb|abarth|aarp|aaa|onion' + 134 | ')(?=[^0-9a-zA-Z@]|$))')); 135 | regexen.validCCTLD = regexSupplant(RegExp( 136 | '(?:(?:' + 137 | '한국|香港|澳門|新加坡|台灣|台湾|中國|中国|გე|ไทย|ලංකා|ഭാരതം|ಭಾರತ|భారత్|சிங்கப்பூர்|இலங்கை|இந்தியா|ଭାରତ|ભારત|ਭਾਰਤ|' + 138 | 'ভাৰত|ভারত|বাংলা|भारोत|भारतम्|भारत|ڀارت|پاکستان|مليسيا|مصر|قطر|فلسطين|عمان|عراق|سورية|سودان|تونس|' + 139 | 'بھارت|بارت|ایران|امارات|المغرب|السعودية|الجزائر|الاردن|հայ|қаз|укр|срб|рф|мон|мкд|ею|бел|бг|ελ|' + 140 | 'zw|zm|za|yt|ye|ws|wf|vu|vn|vi|vg|ve|vc|va|uz|uy|us|um|uk|ug|ua|tz|tw|tv|tt|tr|tp|to|tn|tm|tl|tk|' + 141 | 'tj|th|tg|tf|td|tc|sz|sy|sx|sv|su|st|ss|sr|so|sn|sm|sl|sk|sj|si|sh|sg|se|sd|sc|sb|sa|rw|ru|rs|ro|' + 142 | 're|qa|py|pw|pt|ps|pr|pn|pm|pl|pk|ph|pg|pf|pe|pa|om|nz|nu|nr|np|no|nl|ni|ng|nf|ne|nc|na|mz|my|mx|' + 143 | 'mw|mv|mu|mt|ms|mr|mq|mp|mo|mn|mm|ml|mk|mh|mg|mf|me|md|mc|ma|ly|lv|lu|lt|ls|lr|lk|li|lc|lb|la|kz|' + 144 | 'ky|kw|kr|kp|kn|km|ki|kh|kg|ke|jp|jo|jm|je|it|is|ir|iq|io|in|im|il|ie|id|hu|ht|hr|hn|hm|hk|gy|gw|' + 145 | 'gu|gt|gs|gr|gq|gp|gn|gm|gl|gi|gh|gg|gf|ge|gd|gb|ga|fr|fo|fm|fk|fj|fi|eu|et|es|er|eh|eg|ee|ec|dz|' + 146 | 'do|dm|dk|dj|de|cz|cy|cx|cw|cv|cu|cr|co|cn|cm|cl|ck|ci|ch|cg|cf|cd|cc|ca|bz|by|bw|bv|bt|bs|br|bq|' + 147 | 'bo|bn|bm|bl|bj|bi|bh|bg|bf|be|bd|bb|ba|az|ax|aw|au|at|as|ar|aq|ao|an|am|al|ai|ag|af|ae|ad|ac' + 148 | ')(?=[^0-9a-zA-Z@]|$))')); 149 | regexen.validPunycode = /(?:xn--[0-9a-z]+)/; 150 | regexen.validSpecialCCTLD = /(?:(?:co|tv)(?=[^0-9a-zA-Z@]|$))/; 151 | regexen.validDomain = regexSupplant(/(?:#{validSubdomain}*#{validDomainName}(?:#{validGTLD}|#{validCCTLD}|#{validPunycode}))/); 152 | regexen.validPortNumber = /[0-9]+/; 153 | regexen.pd = /\u002d\u058a\u05be\u1400\u1806\u2010-\u2015\u2e17\u2e1a\u2e3a\u2e40\u301c\u3030\u30a0\ufe31\ufe58\ufe63\uff0d/; 154 | regexen.validGeneralUrlPathChars = regexSupplant(/[^#{spaces_group}\(\)\?]/i); 155 | // Allow URL paths to contain up to two nested levels of balanced parens 156 | // 1. Used in Wikipedia URLs like /Primer_(film) 157 | // 2. Used in IIS sessions like /S(dfd346)/ 158 | // 3. Used in Rdio URLs like /track/We_Up_(Album_Version_(Edited))/ 159 | regexen.validUrlBalancedParens = regexSupplant( 160 | '\\(' + 161 | '(?:' + 162 | '#{validGeneralUrlPathChars}+' + 163 | '|' + 164 | // allow one nested level of balanced parentheses 165 | '(?:' + 166 | '#{validGeneralUrlPathChars}*' + 167 | '\\(' + 168 | '#{validGeneralUrlPathChars}+' + 169 | '\\)' + 170 | '#{validGeneralUrlPathChars}*' + 171 | ')' + 172 | ')' + 173 | '\\)' 174 | , 'i'); 175 | // Valid end-of-path chracters (so /foo. does not gobble the period). 176 | // 1. Allow =&# for empty URL parameters and other URL-join artifacts 177 | regexen.validUrlPathEndingChars = regexSupplant(/[^#{spaces_group}\(\)\?!\*';:=\,\.\$%\[\]#{pd}~&\|@]|(?:#{validUrlBalancedParens})/i); 178 | // Allow @ in a url, but only in the middle. Catch things like http://example.com/@user/ 179 | regexen.validUrlPath = regexSupplant('(?:' + 180 | '(?:' + 181 | '#{validGeneralUrlPathChars}*' + 182 | '(?:#{validUrlBalancedParens}#{validGeneralUrlPathChars}*)*' + 183 | '#{validUrlPathEndingChars}'+ 184 | ')|(?:@#{validGeneralUrlPathChars}+\/)'+ 185 | ')', 'i'); 186 | regexen.validUrlQueryChars = /[a-z0-9!?\*'@\(\);:&=\+\$\/%#\[\]\-_\.,~|]/i; 187 | regexen.validUrlQueryEndingChars = /[a-z0-9_&=#\/]/i; 188 | regexen.validUrl = regexSupplant( 189 | '(' + // $1 URL 190 | '(https?:\\/\\/)' + // $2 Protocol 191 | '(#{validDomain})' + // $3 Domain(s) 192 | '(?::(#{validPortNumber}))?' + // $4 Port number (optional) 193 | '(\\/#{validUrlPath}*)?' + // $5 URL Path 194 | '(\\?#{validUrlQueryChars}*#{validUrlQueryEndingChars})?' + // $6 Query String 195 | ')' 196 | , 'gi'); 197 | return regexen.validUrl; 198 | }()); -------------------------------------------------------------------------------- /lib/posters/github.js: -------------------------------------------------------------------------------- 1 | const createPost = async(formatted, config) => { 2 | const url = `https://api.github.com/repos/${config.repo}/contents/${formatted.filePath}` 3 | const fileContent = formatted.content 4 | 5 | const payload = { 6 | message: formatted.commit || 'New post', 7 | content: Buffer.from(fileContent).toString('base64'), 8 | committer: config.committer, 9 | } 10 | 11 | const options = { 12 | method: 'PUT', 13 | headers: { 14 | 'Content-Type': 'application/vnd.github.v3+json', 15 | Authorization: `token ${config.token}` 16 | }, 17 | body: JSON.stringify(payload) 18 | } 19 | 20 | const res = await fetch(url, options) 21 | 22 | return res.json() 23 | } 24 | 25 | export default async (config, formatted, site) => { 26 | await createPost(formatted, config) 27 | 28 | console.log(`⭐ Created post!`) 29 | } -------------------------------------------------------------------------------- /lib/posters/index.js: -------------------------------------------------------------------------------- 1 | import microblog from './microblog.js' 2 | import webhook from './webhook.js' 3 | import mastodon from './mastodon.js' 4 | import omnivore from './omnivore.js' 5 | import github from './github.js' 6 | import linkace from './linkace.js' 7 | import webmention from './webmention.js' 8 | 9 | export const SERVICES = { 10 | MICROBLOG: 'mb', 11 | WEBHOOK: 'hook', 12 | MASTODON: 'masto', 13 | OMNIVORE: 'omnivore', 14 | GITHUB: 'github', 15 | LINKACE: 'linkace', 16 | WEBMENTION: 'webmention', 17 | } 18 | 19 | export default { 20 | [SERVICES.MICROBLOG]: microblog, 21 | [SERVICES.WEBHOOK]: webhook, 22 | [SERVICES.MASTODON]: mastodon, 23 | [SERVICES.OMNIVORE]: omnivore, 24 | [SERVICES.GITHUB]: github, 25 | [SERVICES.LINKACE]: linkace, 26 | [SERVICES.WEBMENTION]: webmention, 27 | } 28 | -------------------------------------------------------------------------------- /lib/posters/linkace.js: -------------------------------------------------------------------------------- 1 | const createLink = async(formatted, config, site) => { 2 | let tags = site.categories || [] 3 | if (formatted.tags) { 4 | tags = tags.concat(formatted.tags) 5 | } 6 | 7 | const data = { 8 | method: 'POST', 9 | headers: { 10 | 'Accept': 'application/json', 11 | 'Content-Type': 'application/json', 12 | 'Authorization': `Bearer ${config.apiKey}` 13 | }, 14 | body: JSON.stringify({ 15 | url: formatted.content, 16 | tags: tags.join(','), 17 | }) 18 | } 19 | 20 | const res = await fetch(`${config.domain}/api/v1/links`, data) 21 | 22 | return res.json() 23 | } 24 | 25 | export default async (config, formatted, site) => { 26 | await createLink(formatted, config, site) 27 | 28 | console.log(`⭐ Created link!`) 29 | } -------------------------------------------------------------------------------- /lib/posters/mastodon.js: -------------------------------------------------------------------------------- 1 | import helpers from '../helpers.js' 2 | 3 | const createPost = async(content, config, spoilerText) => { 4 | const formData = new FormData() 5 | formData.append('status', content) 6 | formData.append('visibility', config.visibility || 'public') 7 | formData.append('sensitive', config.sensitive || false) 8 | if (spoilerText) 9 | { 10 | formData.append('spoiler_text', spoilerText) 11 | } 12 | 13 | const res = await fetch(`${config.instance}/api/v1/statuses`, { 14 | method: 'POST', 15 | headers: { 16 | 'Accept': 'application/json', 17 | 'Authorization': `Bearer ${config.accessToken}` 18 | }, 19 | body: formData 20 | }) 21 | 22 | return res.json() 23 | } 24 | 25 | export default async (config, formatted, site) => { 26 | if (formatted.spoilers) 27 | { 28 | config.sensitive = true 29 | } 30 | const categories = (site.categories || []).map(c => `#${c}`).join(' ') 31 | let formattedContent = site.skipConversion ? formatted.content : helpers.htmlToText(formatted.content) 32 | formattedContent = `${formattedContent} ${categories}` 33 | 34 | const res = await createPost(formattedContent, config, formatted.spoilers) 35 | 36 | console.log(`⭐ Created post at ${res.url}!`) 37 | } 38 | -------------------------------------------------------------------------------- /lib/posters/microblog.js: -------------------------------------------------------------------------------- 1 | const createPost = async(path, config) => { 2 | const res = await fetch(path, { 3 | method: 'POST', 4 | headers: { 5 | 'Accept': 'application/json', 6 | 'Authorization': `Bearer ${config.apiKey}` 7 | }, 8 | }) 9 | 10 | return res.json() 11 | } 12 | 13 | export default async (config, formatted, site) => { 14 | const categories = (site.categories || []).map(c => `&category[]=${c}`).join('') 15 | 16 | let path = 'https://micro.blog/micropub' + 17 | '?h=entry' + 18 | `&mp-destination=${config.siteUrl}` + 19 | `&content=${encodeURIComponent(formatted.content)}` + 20 | `&published=${formatted.date}` + 21 | categories 22 | 23 | if (formatted.title) 24 | { 25 | path += `&name=${formatted.title}` 26 | } 27 | 28 | const res = await createPost(path, config) 29 | 30 | console.log(`⭐ Created post at ${res.url}!`) 31 | } 32 | -------------------------------------------------------------------------------- /lib/posters/omnivore.js: -------------------------------------------------------------------------------- 1 | import helpers from '../helpers.js' 2 | 3 | export default async (config, formatted, site) => { 4 | const res = await fetch('https://api-prod.omnivore.app/api/graphql', { 5 | method: 'POST', 6 | headers: { 7 | 'content-type': 'application/json', 8 | 'authorization': config.apiKey, 9 | }, 10 | body: JSON.stringify({ 11 | 'query': 'mutation SaveUrl($input: SaveUrlInput!) { saveUrl(input: $input) { ... on SaveSuccess { url clientRequestId } ... on SaveError { errorCodes message } } }', 12 | 'variables': { 13 | 'input': { 14 | 'clientRequestId': helpers.generateUuid(), 15 | 'source': 'api', 16 | 'url': formatted.url, 17 | } 18 | } 19 | }) 20 | }) 21 | 22 | console.log('🔖 Saved to Omnivore!') 23 | } 24 | -------------------------------------------------------------------------------- /lib/posters/webhook.js: -------------------------------------------------------------------------------- 1 | export default async (config, formatted, site) => { 2 | const res = await fetch(config.url, { 3 | method: 'POST', 4 | headers: { 5 | 'Accept': 'application/json', 6 | 'Content-Type': 'application/json' 7 | }, 8 | body: JSON.stringify(formatted) 9 | }) 10 | 11 | console.log('🗣️ Webhook posted!') 12 | } 13 | -------------------------------------------------------------------------------- /lib/posters/webmention.js: -------------------------------------------------------------------------------- 1 | import { exec } from 'child_process' 2 | 3 | export default async (config, formatted, site) => { 4 | exec(`npx webmention ${formatted.content} --send`) 5 | 6 | console.log('🗣️ Webmentions sent!') 7 | } 8 | -------------------------------------------------------------------------------- /lib/presets.js: -------------------------------------------------------------------------------- 1 | export default { 2 | default: { 3 | getId: (data) => { 4 | return data.id 5 | }, 6 | format: (data) => { 7 | return { 8 | content: data.content, 9 | date: data.isoDate, 10 | } 11 | } 12 | }, 13 | statuslol: { 14 | getId: (data) => { 15 | return data.id 16 | }, 17 | format: (data) => { 18 | return { 19 | content: data.summary, 20 | date: data.isoDate, 21 | } 22 | } 23 | }, 24 | letterboxd: { 25 | getId: (data) => { 26 | return data.guid 27 | }, 28 | format: (data) => { 29 | let content = data.content.trim() 30 | content = `${data.title} 31 | 32 | ${content}` 33 | return { 34 | content: content.trim(), 35 | date: new Date(data.isoDate).toISOString(), 36 | } 37 | } 38 | }, 39 | letterboxdNoPoster: { 40 | getId: (data) => { 41 | return data.guid 42 | }, 43 | format: (data) => { 44 | const SPOILER_TEXT = '

This review may contain spoilers.

' 45 | const SPOILER_TEXT_REVIEW = ' (contains spoilers)' 46 | let content = data.content.trim() 47 | data.title = data.title.replace(SPOILER_TEXT_REVIEW, '') 48 | const spoilers = content.includes(SPOILER_TEXT) ? data['letterboxd:filmTitle'] : false 49 | content = content.replace(SPOILER_TEXT, '') 50 | // remove movie poster from content 51 | if (data.content.includes('img src=')) 52 | { 53 | content = content.replace(content.match(/(

.*?<\/p>)/g)[0], '') 54 | } 55 | content = `${data.title} 56 | 57 | ${content}` 58 | return { 59 | content: content, 60 | date: new Date(data.isoDate).toISOString(), 61 | spoilers: spoilers, 62 | } 63 | } 64 | }, 65 | } 66 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "echo", 3 | "lockfileVersion": 2, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "dependencies": { 8 | "@remy/webmention": "^1.5.0", 9 | "cheerio": "^1.0.0-rc.12", 10 | "html-entities": "^2.4.0", 11 | "html-to-text": "^9.0.3", 12 | "rss-parser": "^3.12.0", 13 | "turndown": "^7.1.1", 14 | "url-regex": "^5.0.0", 15 | "uuid": "^9.0.0" 16 | } 17 | }, 18 | "node_modules/@remy/webmention": { 19 | "version": "1.5.0", 20 | "resolved": "https://registry.npmjs.org/@remy/webmention/-/webmention-1.5.0.tgz", 21 | "integrity": "sha512-50HD/WfXlykMgQxX5Oz9Kzl8WqBbkdXP58mbd7VkBh4ZcKBixNoObyeCeomtTPW36kUMBe9icF4MofF4FC42mw==", 22 | "dependencies": { 23 | "cheerio": "^0.22.0", 24 | "clui": "^0.3.6", 25 | "decodeuricomponent": "^0.3.1", 26 | "follow-redirects": "^1.7.0", 27 | "li": "^1.3.0", 28 | "microformat-node": "^2.0.1", 29 | "ms": "^2.1.2", 30 | "node-fetch": "^2.6.1", 31 | "optimist": "^0.6.1", 32 | "rss-parser": "^3.7.0", 33 | "uuid": "^3.3.2" 34 | }, 35 | "bin": { 36 | "webmention": "bin/wm.js", 37 | "wm": "bin/wm.js" 38 | } 39 | }, 40 | "node_modules/@remy/webmention/node_modules/cheerio": { 41 | "version": "0.22.0", 42 | "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz", 43 | "integrity": "sha512-8/MzidM6G/TgRelkzDG13y3Y9LxBjCb+8yOEZ9+wwq5gVF2w2pV0wmHvjfT0RvuxGyR7UEuK36r+yYMbT4uKgA==", 44 | "dependencies": { 45 | "css-select": "~1.2.0", 46 | "dom-serializer": "~0.1.0", 47 | "entities": "~1.1.1", 48 | "htmlparser2": "^3.9.1", 49 | "lodash.assignin": "^4.0.9", 50 | "lodash.bind": "^4.1.4", 51 | "lodash.defaults": "^4.0.1", 52 | "lodash.filter": "^4.4.0", 53 | "lodash.flatten": "^4.2.0", 54 | "lodash.foreach": "^4.3.0", 55 | "lodash.map": "^4.4.0", 56 | "lodash.merge": "^4.4.0", 57 | "lodash.pick": "^4.2.1", 58 | "lodash.reduce": "^4.4.0", 59 | "lodash.reject": "^4.4.0", 60 | "lodash.some": "^4.4.0" 61 | }, 62 | "engines": { 63 | "node": ">= 0.6" 64 | } 65 | }, 66 | "node_modules/@remy/webmention/node_modules/css-select": { 67 | "version": "1.2.0", 68 | "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", 69 | "integrity": "sha512-dUQOBoqdR7QwV90WysXPLXG5LO7nhYBgiWVfxF80DKPF8zx1t/pUd2FYy73emg3zrjtM6dzmYgbHKfV2rxiHQA==", 70 | "dependencies": { 71 | "boolbase": "~1.0.0", 72 | "css-what": "2.1", 73 | "domutils": "1.5.1", 74 | "nth-check": "~1.0.1" 75 | } 76 | }, 77 | "node_modules/@remy/webmention/node_modules/css-what": { 78 | "version": "2.1.3", 79 | "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", 80 | "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", 81 | "engines": { 82 | "node": "*" 83 | } 84 | }, 85 | "node_modules/@remy/webmention/node_modules/dom-serializer": { 86 | "version": "0.1.1", 87 | "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", 88 | "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", 89 | "dependencies": { 90 | "domelementtype": "^1.3.0", 91 | "entities": "^1.1.1" 92 | } 93 | }, 94 | "node_modules/@remy/webmention/node_modules/domelementtype": { 95 | "version": "1.3.1", 96 | "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", 97 | "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" 98 | }, 99 | "node_modules/@remy/webmention/node_modules/domhandler": { 100 | "version": "2.4.2", 101 | "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", 102 | "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", 103 | "dependencies": { 104 | "domelementtype": "1" 105 | } 106 | }, 107 | "node_modules/@remy/webmention/node_modules/domutils": { 108 | "version": "1.5.1", 109 | "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", 110 | "integrity": "sha512-gSu5Oi/I+3wDENBsOWBiRK1eoGxcywYSqg3rR960/+EfY0CF4EX1VPkgHOZ3WiS/Jg2DtliF6BhWcHlfpYUcGw==", 111 | "dependencies": { 112 | "dom-serializer": "0", 113 | "domelementtype": "1" 114 | } 115 | }, 116 | "node_modules/@remy/webmention/node_modules/entities": { 117 | "version": "1.1.2", 118 | "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", 119 | "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" 120 | }, 121 | "node_modules/@remy/webmention/node_modules/htmlparser2": { 122 | "version": "3.10.1", 123 | "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", 124 | "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", 125 | "dependencies": { 126 | "domelementtype": "^1.3.1", 127 | "domhandler": "^2.3.0", 128 | "domutils": "^1.5.1", 129 | "entities": "^1.1.1", 130 | "inherits": "^2.0.1", 131 | "readable-stream": "^3.1.1" 132 | } 133 | }, 134 | "node_modules/@remy/webmention/node_modules/nth-check": { 135 | "version": "1.0.2", 136 | "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", 137 | "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", 138 | "dependencies": { 139 | "boolbase": "~1.0.0" 140 | } 141 | }, 142 | "node_modules/@remy/webmention/node_modules/uuid": { 143 | "version": "3.4.0", 144 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", 145 | "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", 146 | "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", 147 | "bin": { 148 | "uuid": "bin/uuid" 149 | } 150 | }, 151 | "node_modules/@selderee/plugin-htmlparser2": { 152 | "version": "0.10.0", 153 | "resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.10.0.tgz", 154 | "integrity": "sha512-gW69MEamZ4wk1OsOq1nG1jcyhXIQcnrsX5JwixVw/9xaiav8TCyjESAruu1Rz9yyInhgBXxkNwMeygKnN2uxNA==", 155 | "dependencies": { 156 | "domhandler": "^5.0.3", 157 | "selderee": "^0.10.0" 158 | }, 159 | "funding": { 160 | "url": "https://ko-fi.com/killymxi" 161 | } 162 | }, 163 | "node_modules/bluebird": { 164 | "version": "3.4.7", 165 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", 166 | "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==" 167 | }, 168 | "node_modules/boolbase": { 169 | "version": "1.0.0", 170 | "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", 171 | "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" 172 | }, 173 | "node_modules/cheerio": { 174 | "version": "1.0.0-rc.12", 175 | "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", 176 | "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", 177 | "dependencies": { 178 | "cheerio-select": "^2.1.0", 179 | "dom-serializer": "^2.0.0", 180 | "domhandler": "^5.0.3", 181 | "domutils": "^3.0.1", 182 | "htmlparser2": "^8.0.1", 183 | "parse5": "^7.0.0", 184 | "parse5-htmlparser2-tree-adapter": "^7.0.0" 185 | }, 186 | "engines": { 187 | "node": ">= 6" 188 | }, 189 | "funding": { 190 | "url": "https://github.com/cheeriojs/cheerio?sponsor=1" 191 | } 192 | }, 193 | "node_modules/cheerio-select": { 194 | "version": "2.1.0", 195 | "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", 196 | "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", 197 | "dependencies": { 198 | "boolbase": "^1.0.0", 199 | "css-select": "^5.1.0", 200 | "css-what": "^6.1.0", 201 | "domelementtype": "^2.3.0", 202 | "domhandler": "^5.0.3", 203 | "domutils": "^3.0.1" 204 | }, 205 | "funding": { 206 | "url": "https://github.com/sponsors/fb55" 207 | } 208 | }, 209 | "node_modules/cli-color": { 210 | "version": "0.3.2", 211 | "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-0.3.2.tgz", 212 | "integrity": "sha512-Ys/nDhHNRcxrS4EUI2RS/QCUE+61AMuEOj3sWDX+EIHkJWj+4XkRbOdwdxJteAJKjXYBbeFJMtfaEPd1MBF9pQ==", 213 | "dependencies": { 214 | "d": "~0.1.1", 215 | "es5-ext": "~0.10.2", 216 | "memoizee": "0.3.x", 217 | "timers-ext": "0.1.x" 218 | } 219 | }, 220 | "node_modules/clui": { 221 | "version": "0.3.6", 222 | "resolved": "https://registry.npmjs.org/clui/-/clui-0.3.6.tgz", 223 | "integrity": "sha512-Z4UbgZILlIAjkEkZiDOa2aoYjohKx7fa6DxIh6cE9A6WNWZ61iXfQc6CmdC9SKdS5nO0P0UyQ+WfoXfB65e3HQ==", 224 | "dependencies": { 225 | "cli-color": "0.3.2" 226 | } 227 | }, 228 | "node_modules/css-select": { 229 | "version": "5.1.0", 230 | "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", 231 | "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", 232 | "dependencies": { 233 | "boolbase": "^1.0.0", 234 | "css-what": "^6.1.0", 235 | "domhandler": "^5.0.2", 236 | "domutils": "^3.0.1", 237 | "nth-check": "^2.0.1" 238 | }, 239 | "funding": { 240 | "url": "https://github.com/sponsors/fb55" 241 | } 242 | }, 243 | "node_modules/css-what": { 244 | "version": "6.1.0", 245 | "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", 246 | "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", 247 | "engines": { 248 | "node": ">= 6" 249 | }, 250 | "funding": { 251 | "url": "https://github.com/sponsors/fb55" 252 | } 253 | }, 254 | "node_modules/d": { 255 | "version": "0.1.1", 256 | "resolved": "https://registry.npmjs.org/d/-/d-0.1.1.tgz", 257 | "integrity": "sha512-0SdM9V9pd/OXJHoWmTfNPTAeD+lw6ZqHg+isPyBFuJsZLSE0Ygg1cYZ/0l6DrKQXMOqGOu1oWupMoOfoRfMZrQ==", 258 | "dependencies": { 259 | "es5-ext": "~0.10.2" 260 | } 261 | }, 262 | "node_modules/decodeuricomponent": { 263 | "version": "0.3.1", 264 | "resolved": "https://registry.npmjs.org/decodeuricomponent/-/decodeuricomponent-0.3.1.tgz", 265 | "integrity": "sha512-8PuirnWM7fdE54vRKoVT60RQlkFXgKsi7ToV4pdVbj9jc9qGSFt4P10Oi3UK+FPHZi+lhqRdPyG2QqhSfIhtQQ==", 266 | "dependencies": { 267 | "iconv-lite": "^0.4.19" 268 | } 269 | }, 270 | "node_modules/deepmerge": { 271 | "version": "4.3.0", 272 | "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.0.tgz", 273 | "integrity": "sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og==", 274 | "engines": { 275 | "node": ">=0.10.0" 276 | } 277 | }, 278 | "node_modules/dom-serializer": { 279 | "version": "2.0.0", 280 | "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", 281 | "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", 282 | "dependencies": { 283 | "domelementtype": "^2.3.0", 284 | "domhandler": "^5.0.2", 285 | "entities": "^4.2.0" 286 | }, 287 | "funding": { 288 | "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" 289 | } 290 | }, 291 | "node_modules/dom-serializer/node_modules/entities": { 292 | "version": "4.4.0", 293 | "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", 294 | "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==", 295 | "engines": { 296 | "node": ">=0.12" 297 | }, 298 | "funding": { 299 | "url": "https://github.com/fb55/entities?sponsor=1" 300 | } 301 | }, 302 | "node_modules/domelementtype": { 303 | "version": "2.3.0", 304 | "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", 305 | "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", 306 | "funding": [ 307 | { 308 | "type": "github", 309 | "url": "https://github.com/sponsors/fb55" 310 | } 311 | ] 312 | }, 313 | "node_modules/domhandler": { 314 | "version": "5.0.3", 315 | "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", 316 | "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", 317 | "dependencies": { 318 | "domelementtype": "^2.3.0" 319 | }, 320 | "engines": { 321 | "node": ">= 4" 322 | }, 323 | "funding": { 324 | "url": "https://github.com/fb55/domhandler?sponsor=1" 325 | } 326 | }, 327 | "node_modules/domino": { 328 | "version": "2.1.6", 329 | "resolved": "https://registry.npmjs.org/domino/-/domino-2.1.6.tgz", 330 | "integrity": "sha512-3VdM/SXBZX2omc9JF9nOPCtDaYQ67BGp5CoLpIQlO2KCAPETs8TcDHacF26jXadGbvUteZzRTeos2fhID5+ucQ==" 331 | }, 332 | "node_modules/domutils": { 333 | "version": "3.0.1", 334 | "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", 335 | "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", 336 | "dependencies": { 337 | "dom-serializer": "^2.0.0", 338 | "domelementtype": "^2.3.0", 339 | "domhandler": "^5.0.1" 340 | }, 341 | "funding": { 342 | "url": "https://github.com/fb55/domutils?sponsor=1" 343 | } 344 | }, 345 | "node_modules/ent": { 346 | "version": "2.2.0", 347 | "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", 348 | "integrity": "sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA==" 349 | }, 350 | "node_modules/entities": { 351 | "version": "2.2.0", 352 | "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", 353 | "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", 354 | "funding": { 355 | "url": "https://github.com/fb55/entities?sponsor=1" 356 | } 357 | }, 358 | "node_modules/es5-ext": { 359 | "version": "0.10.62", 360 | "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", 361 | "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", 362 | "hasInstallScript": true, 363 | "dependencies": { 364 | "es6-iterator": "^2.0.3", 365 | "es6-symbol": "^3.1.3", 366 | "next-tick": "^1.1.0" 367 | }, 368 | "engines": { 369 | "node": ">=0.10" 370 | } 371 | }, 372 | "node_modules/es6-iterator": { 373 | "version": "2.0.3", 374 | "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", 375 | "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", 376 | "dependencies": { 377 | "d": "1", 378 | "es5-ext": "^0.10.35", 379 | "es6-symbol": "^3.1.1" 380 | } 381 | }, 382 | "node_modules/es6-iterator/node_modules/d": { 383 | "version": "1.0.1", 384 | "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", 385 | "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", 386 | "dependencies": { 387 | "es5-ext": "^0.10.50", 388 | "type": "^1.0.1" 389 | } 390 | }, 391 | "node_modules/es6-iterator/node_modules/type": { 392 | "version": "1.2.0", 393 | "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", 394 | "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" 395 | }, 396 | "node_modules/es6-symbol": { 397 | "version": "3.1.3", 398 | "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", 399 | "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", 400 | "dependencies": { 401 | "d": "^1.0.1", 402 | "ext": "^1.1.2" 403 | } 404 | }, 405 | "node_modules/es6-symbol/node_modules/d": { 406 | "version": "1.0.1", 407 | "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", 408 | "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", 409 | "dependencies": { 410 | "es5-ext": "^0.10.50", 411 | "type": "^1.0.1" 412 | } 413 | }, 414 | "node_modules/es6-symbol/node_modules/type": { 415 | "version": "1.2.0", 416 | "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", 417 | "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" 418 | }, 419 | "node_modules/es6-weak-map": { 420 | "version": "0.1.4", 421 | "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-0.1.4.tgz", 422 | "integrity": "sha512-P+N5Cd2TXeb7G59euFiM7snORspgbInS29Nbf3KNO2JQp/DyhvMCDWd58nsVAXwYJ6W3Bx7qDdy6QQ3PCJ7jKQ==", 423 | "dependencies": { 424 | "d": "~0.1.1", 425 | "es5-ext": "~0.10.6", 426 | "es6-iterator": "~0.1.3", 427 | "es6-symbol": "~2.0.1" 428 | } 429 | }, 430 | "node_modules/es6-weak-map/node_modules/es6-iterator": { 431 | "version": "0.1.3", 432 | "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-0.1.3.tgz", 433 | "integrity": "sha512-6TOmbFM6OPWkTe+bQ3ZuUkvqcWUjAnYjKUCLdbvRsAUz2Pr+fYIibwNXNkLNtIK9PPFbNMZZddaRNkyJhlGJhA==", 434 | "dependencies": { 435 | "d": "~0.1.1", 436 | "es5-ext": "~0.10.5", 437 | "es6-symbol": "~2.0.1" 438 | } 439 | }, 440 | "node_modules/es6-weak-map/node_modules/es6-symbol": { 441 | "version": "2.0.1", 442 | "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-2.0.1.tgz", 443 | "integrity": "sha512-wjobO4zO8726HVU7mI2OA/B6QszqwHJuKab7gKHVx+uRfVVYGcWJkCIFxV2Madqb9/RUSrhJ/r6hPfG7FsWtow==", 444 | "dependencies": { 445 | "d": "~0.1.1", 446 | "es5-ext": "~0.10.5" 447 | } 448 | }, 449 | "node_modules/event-emitter": { 450 | "version": "0.3.5", 451 | "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", 452 | "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", 453 | "dependencies": { 454 | "d": "1", 455 | "es5-ext": "~0.10.14" 456 | } 457 | }, 458 | "node_modules/event-emitter/node_modules/d": { 459 | "version": "1.0.1", 460 | "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", 461 | "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", 462 | "dependencies": { 463 | "es5-ext": "^0.10.50", 464 | "type": "^1.0.1" 465 | } 466 | }, 467 | "node_modules/event-emitter/node_modules/type": { 468 | "version": "1.2.0", 469 | "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", 470 | "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" 471 | }, 472 | "node_modules/ext": { 473 | "version": "1.7.0", 474 | "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", 475 | "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", 476 | "dependencies": { 477 | "type": "^2.7.2" 478 | } 479 | }, 480 | "node_modules/follow-redirects": { 481 | "version": "1.15.5", 482 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", 483 | "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", 484 | "funding": [ 485 | { 486 | "type": "individual", 487 | "url": "https://github.com/sponsors/RubenVerborgh" 488 | } 489 | ], 490 | "engines": { 491 | "node": ">=4.0" 492 | }, 493 | "peerDependenciesMeta": { 494 | "debug": { 495 | "optional": true 496 | } 497 | } 498 | }, 499 | "node_modules/html-entities": { 500 | "version": "2.4.0", 501 | "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.4.0.tgz", 502 | "integrity": "sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ==", 503 | "funding": [ 504 | { 505 | "type": "github", 506 | "url": "https://github.com/sponsors/mdevils" 507 | }, 508 | { 509 | "type": "patreon", 510 | "url": "https://patreon.com/mdevils" 511 | } 512 | ] 513 | }, 514 | "node_modules/html-to-text": { 515 | "version": "9.0.3", 516 | "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-9.0.3.tgz", 517 | "integrity": "sha512-hxDF1kVCF2uw4VUJ3vr2doc91pXf2D5ngKcNviSitNkhP9OMOaJkDrFIFL6RMvko7NisWTEiqGpQ9LAxcVok1w==", 518 | "dependencies": { 519 | "@selderee/plugin-htmlparser2": "^0.10.0", 520 | "deepmerge": "^4.2.2", 521 | "dom-serializer": "^2.0.0", 522 | "htmlparser2": "^8.0.1", 523 | "selderee": "^0.10.0" 524 | }, 525 | "engines": { 526 | "node": ">=14" 527 | } 528 | }, 529 | "node_modules/htmlparser2": { 530 | "version": "8.0.1", 531 | "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz", 532 | "integrity": "sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==", 533 | "funding": [ 534 | "https://github.com/fb55/htmlparser2?sponsor=1", 535 | { 536 | "type": "github", 537 | "url": "https://github.com/sponsors/fb55" 538 | } 539 | ], 540 | "dependencies": { 541 | "domelementtype": "^2.3.0", 542 | "domhandler": "^5.0.2", 543 | "domutils": "^3.0.1", 544 | "entities": "^4.3.0" 545 | } 546 | }, 547 | "node_modules/htmlparser2/node_modules/entities": { 548 | "version": "4.4.0", 549 | "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", 550 | "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==", 551 | "engines": { 552 | "node": ">=0.12" 553 | }, 554 | "funding": { 555 | "url": "https://github.com/fb55/entities?sponsor=1" 556 | } 557 | }, 558 | "node_modules/iconv-lite": { 559 | "version": "0.4.24", 560 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 561 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 562 | "dependencies": { 563 | "safer-buffer": ">= 2.1.2 < 3" 564 | }, 565 | "engines": { 566 | "node": ">=0.10.0" 567 | } 568 | }, 569 | "node_modules/inherits": { 570 | "version": "2.0.4", 571 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 572 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 573 | }, 574 | "node_modules/ip-regex": { 575 | "version": "4.3.0", 576 | "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", 577 | "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==", 578 | "engines": { 579 | "node": ">=8" 580 | } 581 | }, 582 | "node_modules/leac": { 583 | "version": "0.6.0", 584 | "resolved": "https://registry.npmjs.org/leac/-/leac-0.6.0.tgz", 585 | "integrity": "sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==", 586 | "funding": { 587 | "url": "https://ko-fi.com/killymxi" 588 | } 589 | }, 590 | "node_modules/li": { 591 | "version": "1.3.0", 592 | "resolved": "https://registry.npmjs.org/li/-/li-1.3.0.tgz", 593 | "integrity": "sha512-z34TU6GlMram52Tss5mt1m//ifRIpKH5Dqm7yUVOdHI+BQCs9qGPHFaCUTIzsWX7edN30aa2WrPwR7IO10FHaw==" 594 | }, 595 | "node_modules/lodash.assignin": { 596 | "version": "4.2.0", 597 | "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz", 598 | "integrity": "sha512-yX/rx6d/UTVh7sSVWVSIMjfnz95evAgDFdb1ZozC35I9mSFCkmzptOzevxjgbQUsc78NR44LVHWjsoMQXy9FDg==" 599 | }, 600 | "node_modules/lodash.bind": { 601 | "version": "4.2.1", 602 | "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz", 603 | "integrity": "sha512-lxdsn7xxlCymgLYo1gGvVrfHmkjDiyqVv62FAeF2i5ta72BipE1SLxw8hPEPLhD4/247Ijw07UQH7Hq/chT5LA==" 604 | }, 605 | "node_modules/lodash.defaults": { 606 | "version": "4.2.0", 607 | "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", 608 | "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" 609 | }, 610 | "node_modules/lodash.filter": { 611 | "version": "4.6.0", 612 | "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz", 613 | "integrity": "sha512-pXYUy7PR8BCLwX5mgJ/aNtyOvuJTdZAo9EQFUvMIYugqmJxnrYaANvTbgndOzHSCSR0wnlBBfRXJL5SbWxo3FQ==" 614 | }, 615 | "node_modules/lodash.flatten": { 616 | "version": "4.4.0", 617 | "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", 618 | "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==" 619 | }, 620 | "node_modules/lodash.foreach": { 621 | "version": "4.5.0", 622 | "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", 623 | "integrity": "sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==" 624 | }, 625 | "node_modules/lodash.map": { 626 | "version": "4.6.0", 627 | "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", 628 | "integrity": "sha512-worNHGKLDetmcEYDvh2stPCrrQRkP20E4l0iIS7F8EvzMqBBi7ltvFN5m1HvTf1P7Jk1txKhvFcmYsCr8O2F1Q==" 629 | }, 630 | "node_modules/lodash.merge": { 631 | "version": "4.6.2", 632 | "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", 633 | "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" 634 | }, 635 | "node_modules/lodash.pick": { 636 | "version": "4.4.0", 637 | "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", 638 | "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==" 639 | }, 640 | "node_modules/lodash.reduce": { 641 | "version": "4.6.0", 642 | "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", 643 | "integrity": "sha512-6raRe2vxCYBhpBu+B+TtNGUzah+hQjVdu3E17wfusjyrXBka2nBS8OH/gjVZ5PvHOhWmIZTYri09Z6n/QfnNMw==" 644 | }, 645 | "node_modules/lodash.reject": { 646 | "version": "4.6.0", 647 | "resolved": "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz", 648 | "integrity": "sha512-qkTuvgEzYdyhiJBx42YPzPo71R1aEr0z79kAv7Ixg8wPFEjgRgJdUsGMG3Hf3OYSF/kHI79XhNlt+5Ar6OzwxQ==" 649 | }, 650 | "node_modules/lodash.some": { 651 | "version": "4.6.0", 652 | "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz", 653 | "integrity": "sha512-j7MJE+TuT51q9ggt4fSgVqro163BEFjAt3u97IqU+JA2DkWl80nFTrowzLpZ/BnpN7rrl0JA/593NAdd8p/scQ==" 654 | }, 655 | "node_modules/lru-queue": { 656 | "version": "0.1.0", 657 | "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", 658 | "integrity": "sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==", 659 | "dependencies": { 660 | "es5-ext": "~0.10.2" 661 | } 662 | }, 663 | "node_modules/memoizee": { 664 | "version": "0.3.10", 665 | "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.3.10.tgz", 666 | "integrity": "sha512-LLzVUuWwGBKK188spgOK/ukrp5zvd9JGsiLDH41pH9vt5jvhZfsu5pxDuAnYAMG8YEGce72KO07sSBy9KkvOfw==", 667 | "dependencies": { 668 | "d": "~0.1.1", 669 | "es5-ext": "~0.10.11", 670 | "es6-weak-map": "~0.1.4", 671 | "event-emitter": "~0.3.4", 672 | "lru-queue": "0.1", 673 | "next-tick": "~0.2.2", 674 | "timers-ext": "0.1" 675 | } 676 | }, 677 | "node_modules/memoizee/node_modules/next-tick": { 678 | "version": "0.2.2", 679 | "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-0.2.2.tgz", 680 | "integrity": "sha512-f7h4svPtl+QidoBv4taKXUjJ70G2asaZ8G28nS0OkqaalX8dwwrtWtyxEDPK62AC00ur/+/E0pUwBwY5EPn15Q==" 681 | }, 682 | "node_modules/microformat-node": { 683 | "version": "2.0.1", 684 | "resolved": "https://registry.npmjs.org/microformat-node/-/microformat-node-2.0.1.tgz", 685 | "integrity": "sha512-xMVKlQnCNP7DZyyEP3cpwMF3uahJ0NdTN+SnK+PuFpRtUglTvbIjX4ZbvmqF4U9nuMdW2F3Nh5aya4OjmDyEpQ==", 686 | "dependencies": { 687 | "bluebird": "3.4.x", 688 | "cheerio": "0.22.x", 689 | "ent": "^2.2.0", 690 | "microformat-shiv": "^2.0.0" 691 | }, 692 | "engines": { 693 | "node": ">= 0.10.x" 694 | } 695 | }, 696 | "node_modules/microformat-node/node_modules/cheerio": { 697 | "version": "0.22.0", 698 | "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz", 699 | "integrity": "sha512-8/MzidM6G/TgRelkzDG13y3Y9LxBjCb+8yOEZ9+wwq5gVF2w2pV0wmHvjfT0RvuxGyR7UEuK36r+yYMbT4uKgA==", 700 | "dependencies": { 701 | "css-select": "~1.2.0", 702 | "dom-serializer": "~0.1.0", 703 | "entities": "~1.1.1", 704 | "htmlparser2": "^3.9.1", 705 | "lodash.assignin": "^4.0.9", 706 | "lodash.bind": "^4.1.4", 707 | "lodash.defaults": "^4.0.1", 708 | "lodash.filter": "^4.4.0", 709 | "lodash.flatten": "^4.2.0", 710 | "lodash.foreach": "^4.3.0", 711 | "lodash.map": "^4.4.0", 712 | "lodash.merge": "^4.4.0", 713 | "lodash.pick": "^4.2.1", 714 | "lodash.reduce": "^4.4.0", 715 | "lodash.reject": "^4.4.0", 716 | "lodash.some": "^4.4.0" 717 | }, 718 | "engines": { 719 | "node": ">= 0.6" 720 | } 721 | }, 722 | "node_modules/microformat-node/node_modules/css-select": { 723 | "version": "1.2.0", 724 | "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", 725 | "integrity": "sha512-dUQOBoqdR7QwV90WysXPLXG5LO7nhYBgiWVfxF80DKPF8zx1t/pUd2FYy73emg3zrjtM6dzmYgbHKfV2rxiHQA==", 726 | "dependencies": { 727 | "boolbase": "~1.0.0", 728 | "css-what": "2.1", 729 | "domutils": "1.5.1", 730 | "nth-check": "~1.0.1" 731 | } 732 | }, 733 | "node_modules/microformat-node/node_modules/css-what": { 734 | "version": "2.1.3", 735 | "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", 736 | "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", 737 | "engines": { 738 | "node": "*" 739 | } 740 | }, 741 | "node_modules/microformat-node/node_modules/dom-serializer": { 742 | "version": "0.1.1", 743 | "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", 744 | "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", 745 | "dependencies": { 746 | "domelementtype": "^1.3.0", 747 | "entities": "^1.1.1" 748 | } 749 | }, 750 | "node_modules/microformat-node/node_modules/domelementtype": { 751 | "version": "1.3.1", 752 | "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", 753 | "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" 754 | }, 755 | "node_modules/microformat-node/node_modules/domhandler": { 756 | "version": "2.4.2", 757 | "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", 758 | "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", 759 | "dependencies": { 760 | "domelementtype": "1" 761 | } 762 | }, 763 | "node_modules/microformat-node/node_modules/domutils": { 764 | "version": "1.5.1", 765 | "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", 766 | "integrity": "sha512-gSu5Oi/I+3wDENBsOWBiRK1eoGxcywYSqg3rR960/+EfY0CF4EX1VPkgHOZ3WiS/Jg2DtliF6BhWcHlfpYUcGw==", 767 | "dependencies": { 768 | "dom-serializer": "0", 769 | "domelementtype": "1" 770 | } 771 | }, 772 | "node_modules/microformat-node/node_modules/entities": { 773 | "version": "1.1.2", 774 | "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", 775 | "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" 776 | }, 777 | "node_modules/microformat-node/node_modules/htmlparser2": { 778 | "version": "3.10.1", 779 | "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", 780 | "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", 781 | "dependencies": { 782 | "domelementtype": "^1.3.1", 783 | "domhandler": "^2.3.0", 784 | "domutils": "^1.5.1", 785 | "entities": "^1.1.1", 786 | "inherits": "^2.0.1", 787 | "readable-stream": "^3.1.1" 788 | } 789 | }, 790 | "node_modules/microformat-node/node_modules/nth-check": { 791 | "version": "1.0.2", 792 | "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", 793 | "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", 794 | "dependencies": { 795 | "boolbase": "~1.0.0" 796 | } 797 | }, 798 | "node_modules/microformat-shiv": { 799 | "version": "2.0.3", 800 | "resolved": "https://registry.npmjs.org/microformat-shiv/-/microformat-shiv-2.0.3.tgz", 801 | "integrity": "sha512-pktHqCmZsrmogS1egCryvZlAqDdvIQPxsDqa7hZ56nFkovqrszMXWJ94CUE9iWRjQRKOUKqLp62iCVO0+iwM2Q==" 802 | }, 803 | "node_modules/minimist": { 804 | "version": "0.0.10", 805 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", 806 | "integrity": "sha512-iotkTvxc+TwOm5Ieim8VnSNvCDjCK9S8G3scJ50ZthspSxa7jx50jkhYduuAtAjvfDUwSgOwf8+If99AlOEhyw==" 807 | }, 808 | "node_modules/ms": { 809 | "version": "2.1.3", 810 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 811 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 812 | }, 813 | "node_modules/next-tick": { 814 | "version": "1.1.0", 815 | "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", 816 | "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" 817 | }, 818 | "node_modules/node-fetch": { 819 | "version": "2.7.0", 820 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", 821 | "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", 822 | "dependencies": { 823 | "whatwg-url": "^5.0.0" 824 | }, 825 | "engines": { 826 | "node": "4.x || >=6.0.0" 827 | }, 828 | "peerDependencies": { 829 | "encoding": "^0.1.0" 830 | }, 831 | "peerDependenciesMeta": { 832 | "encoding": { 833 | "optional": true 834 | } 835 | } 836 | }, 837 | "node_modules/nth-check": { 838 | "version": "2.1.1", 839 | "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", 840 | "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", 841 | "dependencies": { 842 | "boolbase": "^1.0.0" 843 | }, 844 | "funding": { 845 | "url": "https://github.com/fb55/nth-check?sponsor=1" 846 | } 847 | }, 848 | "node_modules/optimist": { 849 | "version": "0.6.1", 850 | "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", 851 | "integrity": "sha512-snN4O4TkigujZphWLN0E//nQmm7790RYaE53DdL7ZYwee2D8DDo9/EyYiKUfN3rneWUjhJnueija3G9I2i0h3g==", 852 | "dependencies": { 853 | "minimist": "~0.0.1", 854 | "wordwrap": "~0.0.2" 855 | } 856 | }, 857 | "node_modules/parse5": { 858 | "version": "7.1.2", 859 | "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", 860 | "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", 861 | "dependencies": { 862 | "entities": "^4.4.0" 863 | }, 864 | "funding": { 865 | "url": "https://github.com/inikulin/parse5?sponsor=1" 866 | } 867 | }, 868 | "node_modules/parse5-htmlparser2-tree-adapter": { 869 | "version": "7.0.0", 870 | "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", 871 | "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", 872 | "dependencies": { 873 | "domhandler": "^5.0.2", 874 | "parse5": "^7.0.0" 875 | }, 876 | "funding": { 877 | "url": "https://github.com/inikulin/parse5?sponsor=1" 878 | } 879 | }, 880 | "node_modules/parse5/node_modules/entities": { 881 | "version": "4.4.0", 882 | "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", 883 | "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==", 884 | "engines": { 885 | "node": ">=0.12" 886 | }, 887 | "funding": { 888 | "url": "https://github.com/fb55/entities?sponsor=1" 889 | } 890 | }, 891 | "node_modules/parseley": { 892 | "version": "0.11.0", 893 | "resolved": "https://registry.npmjs.org/parseley/-/parseley-0.11.0.tgz", 894 | "integrity": "sha512-VfcwXlBWgTF+unPcr7yu3HSSA6QUdDaDnrHcytVfj5Z8azAyKBDrYnSIfeSxlrEayndNcLmrXzg+Vxbo6DWRXQ==", 895 | "dependencies": { 896 | "leac": "^0.6.0", 897 | "peberminta": "^0.8.0" 898 | }, 899 | "funding": { 900 | "url": "https://ko-fi.com/killymxi" 901 | } 902 | }, 903 | "node_modules/peberminta": { 904 | "version": "0.8.0", 905 | "resolved": "https://registry.npmjs.org/peberminta/-/peberminta-0.8.0.tgz", 906 | "integrity": "sha512-YYEs+eauIjDH5nUEGi18EohWE0nV2QbGTqmxQcqgZ/0g+laPCQmuIqq7EBLVi9uim9zMgfJv0QBZEnQ3uHw/Tw==", 907 | "funding": { 908 | "url": "https://ko-fi.com/killymxi" 909 | } 910 | }, 911 | "node_modules/readable-stream": { 912 | "version": "3.6.2", 913 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", 914 | "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", 915 | "dependencies": { 916 | "inherits": "^2.0.3", 917 | "string_decoder": "^1.1.1", 918 | "util-deprecate": "^1.0.1" 919 | }, 920 | "engines": { 921 | "node": ">= 6" 922 | } 923 | }, 924 | "node_modules/rss-parser": { 925 | "version": "3.13.0", 926 | "resolved": "https://registry.npmjs.org/rss-parser/-/rss-parser-3.13.0.tgz", 927 | "integrity": "sha512-7jWUBV5yGN3rqMMj7CZufl/291QAhvrrGpDNE4k/02ZchL0npisiYYqULF71jCEKoIiHvK/Q2e6IkDwPziT7+w==", 928 | "dependencies": { 929 | "entities": "^2.0.3", 930 | "xml2js": "^0.5.0" 931 | } 932 | }, 933 | "node_modules/safe-buffer": { 934 | "version": "5.2.1", 935 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 936 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 937 | "funding": [ 938 | { 939 | "type": "github", 940 | "url": "https://github.com/sponsors/feross" 941 | }, 942 | { 943 | "type": "patreon", 944 | "url": "https://www.patreon.com/feross" 945 | }, 946 | { 947 | "type": "consulting", 948 | "url": "https://feross.org/support" 949 | } 950 | ] 951 | }, 952 | "node_modules/safer-buffer": { 953 | "version": "2.1.2", 954 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 955 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 956 | }, 957 | "node_modules/sax": { 958 | "version": "1.3.0", 959 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", 960 | "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==" 961 | }, 962 | "node_modules/selderee": { 963 | "version": "0.10.0", 964 | "resolved": "https://registry.npmjs.org/selderee/-/selderee-0.10.0.tgz", 965 | "integrity": "sha512-DEL/RW/f4qLw/NrVg97xKaEBC8IpzIG2fvxnzCp3Z4yk4jQ3MXom+Imav9wApjxX2dfS3eW7x0DXafJr85i39A==", 966 | "dependencies": { 967 | "parseley": "^0.11.0" 968 | }, 969 | "funding": { 970 | "url": "https://ko-fi.com/killymxi" 971 | } 972 | }, 973 | "node_modules/string_decoder": { 974 | "version": "1.3.0", 975 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 976 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 977 | "dependencies": { 978 | "safe-buffer": "~5.2.0" 979 | } 980 | }, 981 | "node_modules/timers-ext": { 982 | "version": "0.1.7", 983 | "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz", 984 | "integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==", 985 | "dependencies": { 986 | "es5-ext": "~0.10.46", 987 | "next-tick": "1" 988 | } 989 | }, 990 | "node_modules/tlds": { 991 | "version": "1.248.0", 992 | "resolved": "https://registry.npmjs.org/tlds/-/tlds-1.248.0.tgz", 993 | "integrity": "sha512-noj0KdpWTBhwsKxMOXk0rN9otg4kTgLm4WohERRHbJ9IY+kSDKr3RmjitaQ3JFzny+DyvBOQKlFZhp0G0qNSfg==", 994 | "bin": { 995 | "tlds": "bin.js" 996 | } 997 | }, 998 | "node_modules/tr46": { 999 | "version": "0.0.3", 1000 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", 1001 | "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" 1002 | }, 1003 | "node_modules/turndown": { 1004 | "version": "7.1.1", 1005 | "resolved": "https://registry.npmjs.org/turndown/-/turndown-7.1.1.tgz", 1006 | "integrity": "sha512-BEkXaWH7Wh7e9bd2QumhfAXk5g34+6QUmmWx+0q6ThaVOLuLUqsnkq35HQ5SBHSaxjSfSM7US5o4lhJNH7B9MA==", 1007 | "dependencies": { 1008 | "domino": "^2.1.6" 1009 | } 1010 | }, 1011 | "node_modules/type": { 1012 | "version": "2.7.2", 1013 | "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", 1014 | "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" 1015 | }, 1016 | "node_modules/url-regex": { 1017 | "version": "5.0.0", 1018 | "resolved": "https://registry.npmjs.org/url-regex/-/url-regex-5.0.0.tgz", 1019 | "integrity": "sha512-O08GjTiAFNsSlrUWfqF1jH0H1W3m35ZyadHrGv5krdnmPPoxP27oDTqux/579PtaroiSGm5yma6KT1mHFH6Y/g==", 1020 | "dependencies": { 1021 | "ip-regex": "^4.1.0", 1022 | "tlds": "^1.203.0" 1023 | }, 1024 | "engines": { 1025 | "node": ">=8" 1026 | } 1027 | }, 1028 | "node_modules/util-deprecate": { 1029 | "version": "1.0.2", 1030 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1031 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" 1032 | }, 1033 | "node_modules/uuid": { 1034 | "version": "9.0.0", 1035 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", 1036 | "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", 1037 | "bin": { 1038 | "uuid": "dist/bin/uuid" 1039 | } 1040 | }, 1041 | "node_modules/webidl-conversions": { 1042 | "version": "3.0.1", 1043 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", 1044 | "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" 1045 | }, 1046 | "node_modules/whatwg-url": { 1047 | "version": "5.0.0", 1048 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", 1049 | "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", 1050 | "dependencies": { 1051 | "tr46": "~0.0.3", 1052 | "webidl-conversions": "^3.0.0" 1053 | } 1054 | }, 1055 | "node_modules/wordwrap": { 1056 | "version": "0.0.3", 1057 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", 1058 | "integrity": "sha512-1tMA907+V4QmxV7dbRvb4/8MaRALK6q9Abid3ndMYnbyo8piisCmeONVqVSXqQA3KaP4SLt5b7ud6E2sqP8TFw==", 1059 | "engines": { 1060 | "node": ">=0.4.0" 1061 | } 1062 | }, 1063 | "node_modules/xml2js": { 1064 | "version": "0.5.0", 1065 | "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", 1066 | "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", 1067 | "dependencies": { 1068 | "sax": ">=0.6.0", 1069 | "xmlbuilder": "~11.0.0" 1070 | }, 1071 | "engines": { 1072 | "node": ">=4.0.0" 1073 | } 1074 | }, 1075 | "node_modules/xmlbuilder": { 1076 | "version": "11.0.1", 1077 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", 1078 | "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", 1079 | "engines": { 1080 | "node": ">=4.0" 1081 | } 1082 | } 1083 | }, 1084 | "dependencies": { 1085 | "@remy/webmention": { 1086 | "version": "1.5.0", 1087 | "resolved": "https://registry.npmjs.org/@remy/webmention/-/webmention-1.5.0.tgz", 1088 | "integrity": "sha512-50HD/WfXlykMgQxX5Oz9Kzl8WqBbkdXP58mbd7VkBh4ZcKBixNoObyeCeomtTPW36kUMBe9icF4MofF4FC42mw==", 1089 | "requires": { 1090 | "cheerio": "^0.22.0", 1091 | "clui": "^0.3.6", 1092 | "decodeuricomponent": "^0.3.1", 1093 | "follow-redirects": "^1.7.0", 1094 | "li": "^1.3.0", 1095 | "microformat-node": "^2.0.1", 1096 | "ms": "^2.1.2", 1097 | "node-fetch": "^2.6.1", 1098 | "optimist": "^0.6.1", 1099 | "rss-parser": "^3.7.0", 1100 | "uuid": "^3.3.2" 1101 | }, 1102 | "dependencies": { 1103 | "cheerio": { 1104 | "version": "0.22.0", 1105 | "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz", 1106 | "integrity": "sha512-8/MzidM6G/TgRelkzDG13y3Y9LxBjCb+8yOEZ9+wwq5gVF2w2pV0wmHvjfT0RvuxGyR7UEuK36r+yYMbT4uKgA==", 1107 | "requires": { 1108 | "css-select": "~1.2.0", 1109 | "dom-serializer": "~0.1.0", 1110 | "entities": "~1.1.1", 1111 | "htmlparser2": "^3.9.1", 1112 | "lodash.assignin": "^4.0.9", 1113 | "lodash.bind": "^4.1.4", 1114 | "lodash.defaults": "^4.0.1", 1115 | "lodash.filter": "^4.4.0", 1116 | "lodash.flatten": "^4.2.0", 1117 | "lodash.foreach": "^4.3.0", 1118 | "lodash.map": "^4.4.0", 1119 | "lodash.merge": "^4.4.0", 1120 | "lodash.pick": "^4.2.1", 1121 | "lodash.reduce": "^4.4.0", 1122 | "lodash.reject": "^4.4.0", 1123 | "lodash.some": "^4.4.0" 1124 | } 1125 | }, 1126 | "css-select": { 1127 | "version": "1.2.0", 1128 | "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", 1129 | "integrity": "sha512-dUQOBoqdR7QwV90WysXPLXG5LO7nhYBgiWVfxF80DKPF8zx1t/pUd2FYy73emg3zrjtM6dzmYgbHKfV2rxiHQA==", 1130 | "requires": { 1131 | "boolbase": "~1.0.0", 1132 | "css-what": "2.1", 1133 | "domutils": "1.5.1", 1134 | "nth-check": "~1.0.1" 1135 | } 1136 | }, 1137 | "css-what": { 1138 | "version": "2.1.3", 1139 | "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", 1140 | "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==" 1141 | }, 1142 | "dom-serializer": { 1143 | "version": "0.1.1", 1144 | "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", 1145 | "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", 1146 | "requires": { 1147 | "domelementtype": "^1.3.0", 1148 | "entities": "^1.1.1" 1149 | } 1150 | }, 1151 | "domelementtype": { 1152 | "version": "1.3.1", 1153 | "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", 1154 | "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" 1155 | }, 1156 | "domhandler": { 1157 | "version": "2.4.2", 1158 | "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", 1159 | "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", 1160 | "requires": { 1161 | "domelementtype": "1" 1162 | } 1163 | }, 1164 | "domutils": { 1165 | "version": "1.5.1", 1166 | "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", 1167 | "integrity": "sha512-gSu5Oi/I+3wDENBsOWBiRK1eoGxcywYSqg3rR960/+EfY0CF4EX1VPkgHOZ3WiS/Jg2DtliF6BhWcHlfpYUcGw==", 1168 | "requires": { 1169 | "dom-serializer": "0", 1170 | "domelementtype": "1" 1171 | } 1172 | }, 1173 | "entities": { 1174 | "version": "1.1.2", 1175 | "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", 1176 | "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" 1177 | }, 1178 | "htmlparser2": { 1179 | "version": "3.10.1", 1180 | "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", 1181 | "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", 1182 | "requires": { 1183 | "domelementtype": "^1.3.1", 1184 | "domhandler": "^2.3.0", 1185 | "domutils": "^1.5.1", 1186 | "entities": "^1.1.1", 1187 | "inherits": "^2.0.1", 1188 | "readable-stream": "^3.1.1" 1189 | } 1190 | }, 1191 | "nth-check": { 1192 | "version": "1.0.2", 1193 | "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", 1194 | "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", 1195 | "requires": { 1196 | "boolbase": "~1.0.0" 1197 | } 1198 | }, 1199 | "uuid": { 1200 | "version": "3.4.0", 1201 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", 1202 | "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" 1203 | } 1204 | } 1205 | }, 1206 | "@selderee/plugin-htmlparser2": { 1207 | "version": "0.10.0", 1208 | "resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.10.0.tgz", 1209 | "integrity": "sha512-gW69MEamZ4wk1OsOq1nG1jcyhXIQcnrsX5JwixVw/9xaiav8TCyjESAruu1Rz9yyInhgBXxkNwMeygKnN2uxNA==", 1210 | "requires": { 1211 | "domhandler": "^5.0.3", 1212 | "selderee": "^0.10.0" 1213 | } 1214 | }, 1215 | "bluebird": { 1216 | "version": "3.4.7", 1217 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", 1218 | "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==" 1219 | }, 1220 | "boolbase": { 1221 | "version": "1.0.0", 1222 | "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", 1223 | "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" 1224 | }, 1225 | "cheerio": { 1226 | "version": "1.0.0-rc.12", 1227 | "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", 1228 | "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", 1229 | "requires": { 1230 | "cheerio-select": "^2.1.0", 1231 | "dom-serializer": "^2.0.0", 1232 | "domhandler": "^5.0.3", 1233 | "domutils": "^3.0.1", 1234 | "htmlparser2": "^8.0.1", 1235 | "parse5": "^7.0.0", 1236 | "parse5-htmlparser2-tree-adapter": "^7.0.0" 1237 | } 1238 | }, 1239 | "cheerio-select": { 1240 | "version": "2.1.0", 1241 | "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", 1242 | "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", 1243 | "requires": { 1244 | "boolbase": "^1.0.0", 1245 | "css-select": "^5.1.0", 1246 | "css-what": "^6.1.0", 1247 | "domelementtype": "^2.3.0", 1248 | "domhandler": "^5.0.3", 1249 | "domutils": "^3.0.1" 1250 | } 1251 | }, 1252 | "cli-color": { 1253 | "version": "0.3.2", 1254 | "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-0.3.2.tgz", 1255 | "integrity": "sha512-Ys/nDhHNRcxrS4EUI2RS/QCUE+61AMuEOj3sWDX+EIHkJWj+4XkRbOdwdxJteAJKjXYBbeFJMtfaEPd1MBF9pQ==", 1256 | "requires": { 1257 | "d": "~0.1.1", 1258 | "es5-ext": "~0.10.2", 1259 | "memoizee": "0.3.x", 1260 | "timers-ext": "0.1.x" 1261 | } 1262 | }, 1263 | "clui": { 1264 | "version": "0.3.6", 1265 | "resolved": "https://registry.npmjs.org/clui/-/clui-0.3.6.tgz", 1266 | "integrity": "sha512-Z4UbgZILlIAjkEkZiDOa2aoYjohKx7fa6DxIh6cE9A6WNWZ61iXfQc6CmdC9SKdS5nO0P0UyQ+WfoXfB65e3HQ==", 1267 | "requires": { 1268 | "cli-color": "0.3.2" 1269 | } 1270 | }, 1271 | "css-select": { 1272 | "version": "5.1.0", 1273 | "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", 1274 | "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", 1275 | "requires": { 1276 | "boolbase": "^1.0.0", 1277 | "css-what": "^6.1.0", 1278 | "domhandler": "^5.0.2", 1279 | "domutils": "^3.0.1", 1280 | "nth-check": "^2.0.1" 1281 | } 1282 | }, 1283 | "css-what": { 1284 | "version": "6.1.0", 1285 | "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", 1286 | "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==" 1287 | }, 1288 | "d": { 1289 | "version": "0.1.1", 1290 | "resolved": "https://registry.npmjs.org/d/-/d-0.1.1.tgz", 1291 | "integrity": "sha512-0SdM9V9pd/OXJHoWmTfNPTAeD+lw6ZqHg+isPyBFuJsZLSE0Ygg1cYZ/0l6DrKQXMOqGOu1oWupMoOfoRfMZrQ==", 1292 | "requires": { 1293 | "es5-ext": "~0.10.2" 1294 | } 1295 | }, 1296 | "decodeuricomponent": { 1297 | "version": "0.3.1", 1298 | "resolved": "https://registry.npmjs.org/decodeuricomponent/-/decodeuricomponent-0.3.1.tgz", 1299 | "integrity": "sha512-8PuirnWM7fdE54vRKoVT60RQlkFXgKsi7ToV4pdVbj9jc9qGSFt4P10Oi3UK+FPHZi+lhqRdPyG2QqhSfIhtQQ==", 1300 | "requires": { 1301 | "iconv-lite": "^0.4.19" 1302 | } 1303 | }, 1304 | "deepmerge": { 1305 | "version": "4.3.0", 1306 | "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.0.tgz", 1307 | "integrity": "sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og==" 1308 | }, 1309 | "dom-serializer": { 1310 | "version": "2.0.0", 1311 | "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", 1312 | "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", 1313 | "requires": { 1314 | "domelementtype": "^2.3.0", 1315 | "domhandler": "^5.0.2", 1316 | "entities": "^4.2.0" 1317 | }, 1318 | "dependencies": { 1319 | "entities": { 1320 | "version": "4.4.0", 1321 | "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", 1322 | "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==" 1323 | } 1324 | } 1325 | }, 1326 | "domelementtype": { 1327 | "version": "2.3.0", 1328 | "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", 1329 | "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" 1330 | }, 1331 | "domhandler": { 1332 | "version": "5.0.3", 1333 | "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", 1334 | "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", 1335 | "requires": { 1336 | "domelementtype": "^2.3.0" 1337 | } 1338 | }, 1339 | "domino": { 1340 | "version": "2.1.6", 1341 | "resolved": "https://registry.npmjs.org/domino/-/domino-2.1.6.tgz", 1342 | "integrity": "sha512-3VdM/SXBZX2omc9JF9nOPCtDaYQ67BGp5CoLpIQlO2KCAPETs8TcDHacF26jXadGbvUteZzRTeos2fhID5+ucQ==" 1343 | }, 1344 | "domutils": { 1345 | "version": "3.0.1", 1346 | "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", 1347 | "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", 1348 | "requires": { 1349 | "dom-serializer": "^2.0.0", 1350 | "domelementtype": "^2.3.0", 1351 | "domhandler": "^5.0.1" 1352 | } 1353 | }, 1354 | "ent": { 1355 | "version": "2.2.0", 1356 | "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", 1357 | "integrity": "sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA==" 1358 | }, 1359 | "entities": { 1360 | "version": "2.2.0", 1361 | "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", 1362 | "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" 1363 | }, 1364 | "es5-ext": { 1365 | "version": "0.10.62", 1366 | "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", 1367 | "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", 1368 | "requires": { 1369 | "es6-iterator": "^2.0.3", 1370 | "es6-symbol": "^3.1.3", 1371 | "next-tick": "^1.1.0" 1372 | } 1373 | }, 1374 | "es6-iterator": { 1375 | "version": "2.0.3", 1376 | "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", 1377 | "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", 1378 | "requires": { 1379 | "d": "1", 1380 | "es5-ext": "^0.10.35", 1381 | "es6-symbol": "^3.1.1" 1382 | }, 1383 | "dependencies": { 1384 | "d": { 1385 | "version": "1.0.1", 1386 | "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", 1387 | "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", 1388 | "requires": { 1389 | "es5-ext": "^0.10.50", 1390 | "type": "^1.0.1" 1391 | } 1392 | }, 1393 | "type": { 1394 | "version": "1.2.0", 1395 | "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", 1396 | "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" 1397 | } 1398 | } 1399 | }, 1400 | "es6-symbol": { 1401 | "version": "3.1.3", 1402 | "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", 1403 | "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", 1404 | "requires": { 1405 | "d": "^1.0.1", 1406 | "ext": "^1.1.2" 1407 | }, 1408 | "dependencies": { 1409 | "d": { 1410 | "version": "1.0.1", 1411 | "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", 1412 | "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", 1413 | "requires": { 1414 | "es5-ext": "^0.10.50", 1415 | "type": "^1.0.1" 1416 | } 1417 | }, 1418 | "type": { 1419 | "version": "1.2.0", 1420 | "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", 1421 | "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" 1422 | } 1423 | } 1424 | }, 1425 | "es6-weak-map": { 1426 | "version": "0.1.4", 1427 | "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-0.1.4.tgz", 1428 | "integrity": "sha512-P+N5Cd2TXeb7G59euFiM7snORspgbInS29Nbf3KNO2JQp/DyhvMCDWd58nsVAXwYJ6W3Bx7qDdy6QQ3PCJ7jKQ==", 1429 | "requires": { 1430 | "d": "~0.1.1", 1431 | "es5-ext": "~0.10.6", 1432 | "es6-iterator": "~0.1.3", 1433 | "es6-symbol": "~2.0.1" 1434 | }, 1435 | "dependencies": { 1436 | "es6-iterator": { 1437 | "version": "0.1.3", 1438 | "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-0.1.3.tgz", 1439 | "integrity": "sha512-6TOmbFM6OPWkTe+bQ3ZuUkvqcWUjAnYjKUCLdbvRsAUz2Pr+fYIibwNXNkLNtIK9PPFbNMZZddaRNkyJhlGJhA==", 1440 | "requires": { 1441 | "d": "~0.1.1", 1442 | "es5-ext": "~0.10.5", 1443 | "es6-symbol": "~2.0.1" 1444 | } 1445 | }, 1446 | "es6-symbol": { 1447 | "version": "2.0.1", 1448 | "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-2.0.1.tgz", 1449 | "integrity": "sha512-wjobO4zO8726HVU7mI2OA/B6QszqwHJuKab7gKHVx+uRfVVYGcWJkCIFxV2Madqb9/RUSrhJ/r6hPfG7FsWtow==", 1450 | "requires": { 1451 | "d": "~0.1.1", 1452 | "es5-ext": "~0.10.5" 1453 | } 1454 | } 1455 | } 1456 | }, 1457 | "event-emitter": { 1458 | "version": "0.3.5", 1459 | "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", 1460 | "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", 1461 | "requires": { 1462 | "d": "1", 1463 | "es5-ext": "~0.10.14" 1464 | }, 1465 | "dependencies": { 1466 | "d": { 1467 | "version": "1.0.1", 1468 | "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", 1469 | "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", 1470 | "requires": { 1471 | "es5-ext": "^0.10.50", 1472 | "type": "^1.0.1" 1473 | } 1474 | }, 1475 | "type": { 1476 | "version": "1.2.0", 1477 | "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", 1478 | "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" 1479 | } 1480 | } 1481 | }, 1482 | "ext": { 1483 | "version": "1.7.0", 1484 | "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", 1485 | "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", 1486 | "requires": { 1487 | "type": "^2.7.2" 1488 | } 1489 | }, 1490 | "follow-redirects": { 1491 | "version": "1.15.5", 1492 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", 1493 | "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==" 1494 | }, 1495 | "html-entities": { 1496 | "version": "2.4.0", 1497 | "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.4.0.tgz", 1498 | "integrity": "sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ==" 1499 | }, 1500 | "html-to-text": { 1501 | "version": "9.0.3", 1502 | "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-9.0.3.tgz", 1503 | "integrity": "sha512-hxDF1kVCF2uw4VUJ3vr2doc91pXf2D5ngKcNviSitNkhP9OMOaJkDrFIFL6RMvko7NisWTEiqGpQ9LAxcVok1w==", 1504 | "requires": { 1505 | "@selderee/plugin-htmlparser2": "^0.10.0", 1506 | "deepmerge": "^4.2.2", 1507 | "dom-serializer": "^2.0.0", 1508 | "htmlparser2": "^8.0.1", 1509 | "selderee": "^0.10.0" 1510 | } 1511 | }, 1512 | "htmlparser2": { 1513 | "version": "8.0.1", 1514 | "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz", 1515 | "integrity": "sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==", 1516 | "requires": { 1517 | "domelementtype": "^2.3.0", 1518 | "domhandler": "^5.0.2", 1519 | "domutils": "^3.0.1", 1520 | "entities": "^4.3.0" 1521 | }, 1522 | "dependencies": { 1523 | "entities": { 1524 | "version": "4.4.0", 1525 | "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", 1526 | "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==" 1527 | } 1528 | } 1529 | }, 1530 | "iconv-lite": { 1531 | "version": "0.4.24", 1532 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 1533 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 1534 | "requires": { 1535 | "safer-buffer": ">= 2.1.2 < 3" 1536 | } 1537 | }, 1538 | "inherits": { 1539 | "version": "2.0.4", 1540 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 1541 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 1542 | }, 1543 | "ip-regex": { 1544 | "version": "4.3.0", 1545 | "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", 1546 | "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==" 1547 | }, 1548 | "leac": { 1549 | "version": "0.6.0", 1550 | "resolved": "https://registry.npmjs.org/leac/-/leac-0.6.0.tgz", 1551 | "integrity": "sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==" 1552 | }, 1553 | "li": { 1554 | "version": "1.3.0", 1555 | "resolved": "https://registry.npmjs.org/li/-/li-1.3.0.tgz", 1556 | "integrity": "sha512-z34TU6GlMram52Tss5mt1m//ifRIpKH5Dqm7yUVOdHI+BQCs9qGPHFaCUTIzsWX7edN30aa2WrPwR7IO10FHaw==" 1557 | }, 1558 | "lodash.assignin": { 1559 | "version": "4.2.0", 1560 | "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz", 1561 | "integrity": "sha512-yX/rx6d/UTVh7sSVWVSIMjfnz95evAgDFdb1ZozC35I9mSFCkmzptOzevxjgbQUsc78NR44LVHWjsoMQXy9FDg==" 1562 | }, 1563 | "lodash.bind": { 1564 | "version": "4.2.1", 1565 | "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz", 1566 | "integrity": "sha512-lxdsn7xxlCymgLYo1gGvVrfHmkjDiyqVv62FAeF2i5ta72BipE1SLxw8hPEPLhD4/247Ijw07UQH7Hq/chT5LA==" 1567 | }, 1568 | "lodash.defaults": { 1569 | "version": "4.2.0", 1570 | "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", 1571 | "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" 1572 | }, 1573 | "lodash.filter": { 1574 | "version": "4.6.0", 1575 | "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz", 1576 | "integrity": "sha512-pXYUy7PR8BCLwX5mgJ/aNtyOvuJTdZAo9EQFUvMIYugqmJxnrYaANvTbgndOzHSCSR0wnlBBfRXJL5SbWxo3FQ==" 1577 | }, 1578 | "lodash.flatten": { 1579 | "version": "4.4.0", 1580 | "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", 1581 | "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==" 1582 | }, 1583 | "lodash.foreach": { 1584 | "version": "4.5.0", 1585 | "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", 1586 | "integrity": "sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==" 1587 | }, 1588 | "lodash.map": { 1589 | "version": "4.6.0", 1590 | "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", 1591 | "integrity": "sha512-worNHGKLDetmcEYDvh2stPCrrQRkP20E4l0iIS7F8EvzMqBBi7ltvFN5m1HvTf1P7Jk1txKhvFcmYsCr8O2F1Q==" 1592 | }, 1593 | "lodash.merge": { 1594 | "version": "4.6.2", 1595 | "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", 1596 | "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" 1597 | }, 1598 | "lodash.pick": { 1599 | "version": "4.4.0", 1600 | "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", 1601 | "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==" 1602 | }, 1603 | "lodash.reduce": { 1604 | "version": "4.6.0", 1605 | "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", 1606 | "integrity": "sha512-6raRe2vxCYBhpBu+B+TtNGUzah+hQjVdu3E17wfusjyrXBka2nBS8OH/gjVZ5PvHOhWmIZTYri09Z6n/QfnNMw==" 1607 | }, 1608 | "lodash.reject": { 1609 | "version": "4.6.0", 1610 | "resolved": "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz", 1611 | "integrity": "sha512-qkTuvgEzYdyhiJBx42YPzPo71R1aEr0z79kAv7Ixg8wPFEjgRgJdUsGMG3Hf3OYSF/kHI79XhNlt+5Ar6OzwxQ==" 1612 | }, 1613 | "lodash.some": { 1614 | "version": "4.6.0", 1615 | "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz", 1616 | "integrity": "sha512-j7MJE+TuT51q9ggt4fSgVqro163BEFjAt3u97IqU+JA2DkWl80nFTrowzLpZ/BnpN7rrl0JA/593NAdd8p/scQ==" 1617 | }, 1618 | "lru-queue": { 1619 | "version": "0.1.0", 1620 | "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", 1621 | "integrity": "sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==", 1622 | "requires": { 1623 | "es5-ext": "~0.10.2" 1624 | } 1625 | }, 1626 | "memoizee": { 1627 | "version": "0.3.10", 1628 | "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.3.10.tgz", 1629 | "integrity": "sha512-LLzVUuWwGBKK188spgOK/ukrp5zvd9JGsiLDH41pH9vt5jvhZfsu5pxDuAnYAMG8YEGce72KO07sSBy9KkvOfw==", 1630 | "requires": { 1631 | "d": "~0.1.1", 1632 | "es5-ext": "~0.10.11", 1633 | "es6-weak-map": "~0.1.4", 1634 | "event-emitter": "~0.3.4", 1635 | "lru-queue": "0.1", 1636 | "next-tick": "~0.2.2", 1637 | "timers-ext": "0.1" 1638 | }, 1639 | "dependencies": { 1640 | "next-tick": { 1641 | "version": "0.2.2", 1642 | "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-0.2.2.tgz", 1643 | "integrity": "sha512-f7h4svPtl+QidoBv4taKXUjJ70G2asaZ8G28nS0OkqaalX8dwwrtWtyxEDPK62AC00ur/+/E0pUwBwY5EPn15Q==" 1644 | } 1645 | } 1646 | }, 1647 | "microformat-node": { 1648 | "version": "2.0.1", 1649 | "resolved": "https://registry.npmjs.org/microformat-node/-/microformat-node-2.0.1.tgz", 1650 | "integrity": "sha512-xMVKlQnCNP7DZyyEP3cpwMF3uahJ0NdTN+SnK+PuFpRtUglTvbIjX4ZbvmqF4U9nuMdW2F3Nh5aya4OjmDyEpQ==", 1651 | "requires": { 1652 | "bluebird": "3.4.x", 1653 | "cheerio": "0.22.x", 1654 | "ent": "^2.2.0", 1655 | "microformat-shiv": "^2.0.0" 1656 | }, 1657 | "dependencies": { 1658 | "cheerio": { 1659 | "version": "0.22.0", 1660 | "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz", 1661 | "integrity": "sha512-8/MzidM6G/TgRelkzDG13y3Y9LxBjCb+8yOEZ9+wwq5gVF2w2pV0wmHvjfT0RvuxGyR7UEuK36r+yYMbT4uKgA==", 1662 | "requires": { 1663 | "css-select": "~1.2.0", 1664 | "dom-serializer": "~0.1.0", 1665 | "entities": "~1.1.1", 1666 | "htmlparser2": "^3.9.1", 1667 | "lodash.assignin": "^4.0.9", 1668 | "lodash.bind": "^4.1.4", 1669 | "lodash.defaults": "^4.0.1", 1670 | "lodash.filter": "^4.4.0", 1671 | "lodash.flatten": "^4.2.0", 1672 | "lodash.foreach": "^4.3.0", 1673 | "lodash.map": "^4.4.0", 1674 | "lodash.merge": "^4.4.0", 1675 | "lodash.pick": "^4.2.1", 1676 | "lodash.reduce": "^4.4.0", 1677 | "lodash.reject": "^4.4.0", 1678 | "lodash.some": "^4.4.0" 1679 | } 1680 | }, 1681 | "css-select": { 1682 | "version": "1.2.0", 1683 | "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", 1684 | "integrity": "sha512-dUQOBoqdR7QwV90WysXPLXG5LO7nhYBgiWVfxF80DKPF8zx1t/pUd2FYy73emg3zrjtM6dzmYgbHKfV2rxiHQA==", 1685 | "requires": { 1686 | "boolbase": "~1.0.0", 1687 | "css-what": "2.1", 1688 | "domutils": "1.5.1", 1689 | "nth-check": "~1.0.1" 1690 | } 1691 | }, 1692 | "css-what": { 1693 | "version": "2.1.3", 1694 | "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", 1695 | "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==" 1696 | }, 1697 | "dom-serializer": { 1698 | "version": "0.1.1", 1699 | "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", 1700 | "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", 1701 | "requires": { 1702 | "domelementtype": "^1.3.0", 1703 | "entities": "^1.1.1" 1704 | } 1705 | }, 1706 | "domelementtype": { 1707 | "version": "1.3.1", 1708 | "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", 1709 | "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" 1710 | }, 1711 | "domhandler": { 1712 | "version": "2.4.2", 1713 | "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", 1714 | "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", 1715 | "requires": { 1716 | "domelementtype": "1" 1717 | } 1718 | }, 1719 | "domutils": { 1720 | "version": "1.5.1", 1721 | "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", 1722 | "integrity": "sha512-gSu5Oi/I+3wDENBsOWBiRK1eoGxcywYSqg3rR960/+EfY0CF4EX1VPkgHOZ3WiS/Jg2DtliF6BhWcHlfpYUcGw==", 1723 | "requires": { 1724 | "dom-serializer": "0", 1725 | "domelementtype": "1" 1726 | } 1727 | }, 1728 | "entities": { 1729 | "version": "1.1.2", 1730 | "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", 1731 | "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" 1732 | }, 1733 | "htmlparser2": { 1734 | "version": "3.10.1", 1735 | "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", 1736 | "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", 1737 | "requires": { 1738 | "domelementtype": "^1.3.1", 1739 | "domhandler": "^2.3.0", 1740 | "domutils": "^1.5.1", 1741 | "entities": "^1.1.1", 1742 | "inherits": "^2.0.1", 1743 | "readable-stream": "^3.1.1" 1744 | } 1745 | }, 1746 | "nth-check": { 1747 | "version": "1.0.2", 1748 | "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", 1749 | "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", 1750 | "requires": { 1751 | "boolbase": "~1.0.0" 1752 | } 1753 | } 1754 | } 1755 | }, 1756 | "microformat-shiv": { 1757 | "version": "2.0.3", 1758 | "resolved": "https://registry.npmjs.org/microformat-shiv/-/microformat-shiv-2.0.3.tgz", 1759 | "integrity": "sha512-pktHqCmZsrmogS1egCryvZlAqDdvIQPxsDqa7hZ56nFkovqrszMXWJ94CUE9iWRjQRKOUKqLp62iCVO0+iwM2Q==" 1760 | }, 1761 | "minimist": { 1762 | "version": "0.0.10", 1763 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", 1764 | "integrity": "sha512-iotkTvxc+TwOm5Ieim8VnSNvCDjCK9S8G3scJ50ZthspSxa7jx50jkhYduuAtAjvfDUwSgOwf8+If99AlOEhyw==" 1765 | }, 1766 | "ms": { 1767 | "version": "2.1.3", 1768 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 1769 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 1770 | }, 1771 | "next-tick": { 1772 | "version": "1.1.0", 1773 | "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", 1774 | "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" 1775 | }, 1776 | "node-fetch": { 1777 | "version": "2.7.0", 1778 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", 1779 | "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", 1780 | "requires": { 1781 | "whatwg-url": "^5.0.0" 1782 | } 1783 | }, 1784 | "nth-check": { 1785 | "version": "2.1.1", 1786 | "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", 1787 | "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", 1788 | "requires": { 1789 | "boolbase": "^1.0.0" 1790 | } 1791 | }, 1792 | "optimist": { 1793 | "version": "0.6.1", 1794 | "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", 1795 | "integrity": "sha512-snN4O4TkigujZphWLN0E//nQmm7790RYaE53DdL7ZYwee2D8DDo9/EyYiKUfN3rneWUjhJnueija3G9I2i0h3g==", 1796 | "requires": { 1797 | "minimist": "~0.0.1", 1798 | "wordwrap": "~0.0.2" 1799 | } 1800 | }, 1801 | "parse5": { 1802 | "version": "7.1.2", 1803 | "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", 1804 | "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", 1805 | "requires": { 1806 | "entities": "^4.4.0" 1807 | }, 1808 | "dependencies": { 1809 | "entities": { 1810 | "version": "4.4.0", 1811 | "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", 1812 | "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==" 1813 | } 1814 | } 1815 | }, 1816 | "parse5-htmlparser2-tree-adapter": { 1817 | "version": "7.0.0", 1818 | "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", 1819 | "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", 1820 | "requires": { 1821 | "domhandler": "^5.0.2", 1822 | "parse5": "^7.0.0" 1823 | } 1824 | }, 1825 | "parseley": { 1826 | "version": "0.11.0", 1827 | "resolved": "https://registry.npmjs.org/parseley/-/parseley-0.11.0.tgz", 1828 | "integrity": "sha512-VfcwXlBWgTF+unPcr7yu3HSSA6QUdDaDnrHcytVfj5Z8azAyKBDrYnSIfeSxlrEayndNcLmrXzg+Vxbo6DWRXQ==", 1829 | "requires": { 1830 | "leac": "^0.6.0", 1831 | "peberminta": "^0.8.0" 1832 | } 1833 | }, 1834 | "peberminta": { 1835 | "version": "0.8.0", 1836 | "resolved": "https://registry.npmjs.org/peberminta/-/peberminta-0.8.0.tgz", 1837 | "integrity": "sha512-YYEs+eauIjDH5nUEGi18EohWE0nV2QbGTqmxQcqgZ/0g+laPCQmuIqq7EBLVi9uim9zMgfJv0QBZEnQ3uHw/Tw==" 1838 | }, 1839 | "readable-stream": { 1840 | "version": "3.6.2", 1841 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", 1842 | "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", 1843 | "requires": { 1844 | "inherits": "^2.0.3", 1845 | "string_decoder": "^1.1.1", 1846 | "util-deprecate": "^1.0.1" 1847 | } 1848 | }, 1849 | "rss-parser": { 1850 | "version": "3.13.0", 1851 | "resolved": "https://registry.npmjs.org/rss-parser/-/rss-parser-3.13.0.tgz", 1852 | "integrity": "sha512-7jWUBV5yGN3rqMMj7CZufl/291QAhvrrGpDNE4k/02ZchL0npisiYYqULF71jCEKoIiHvK/Q2e6IkDwPziT7+w==", 1853 | "requires": { 1854 | "entities": "^2.0.3", 1855 | "xml2js": "^0.5.0" 1856 | } 1857 | }, 1858 | "safe-buffer": { 1859 | "version": "5.2.1", 1860 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 1861 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" 1862 | }, 1863 | "safer-buffer": { 1864 | "version": "2.1.2", 1865 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1866 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 1867 | }, 1868 | "sax": { 1869 | "version": "1.3.0", 1870 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", 1871 | "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==" 1872 | }, 1873 | "selderee": { 1874 | "version": "0.10.0", 1875 | "resolved": "https://registry.npmjs.org/selderee/-/selderee-0.10.0.tgz", 1876 | "integrity": "sha512-DEL/RW/f4qLw/NrVg97xKaEBC8IpzIG2fvxnzCp3Z4yk4jQ3MXom+Imav9wApjxX2dfS3eW7x0DXafJr85i39A==", 1877 | "requires": { 1878 | "parseley": "^0.11.0" 1879 | } 1880 | }, 1881 | "string_decoder": { 1882 | "version": "1.3.0", 1883 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 1884 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 1885 | "requires": { 1886 | "safe-buffer": "~5.2.0" 1887 | } 1888 | }, 1889 | "timers-ext": { 1890 | "version": "0.1.7", 1891 | "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz", 1892 | "integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==", 1893 | "requires": { 1894 | "es5-ext": "~0.10.46", 1895 | "next-tick": "1" 1896 | } 1897 | }, 1898 | "tlds": { 1899 | "version": "1.248.0", 1900 | "resolved": "https://registry.npmjs.org/tlds/-/tlds-1.248.0.tgz", 1901 | "integrity": "sha512-noj0KdpWTBhwsKxMOXk0rN9otg4kTgLm4WohERRHbJ9IY+kSDKr3RmjitaQ3JFzny+DyvBOQKlFZhp0G0qNSfg==" 1902 | }, 1903 | "tr46": { 1904 | "version": "0.0.3", 1905 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", 1906 | "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" 1907 | }, 1908 | "turndown": { 1909 | "version": "7.1.1", 1910 | "resolved": "https://registry.npmjs.org/turndown/-/turndown-7.1.1.tgz", 1911 | "integrity": "sha512-BEkXaWH7Wh7e9bd2QumhfAXk5g34+6QUmmWx+0q6ThaVOLuLUqsnkq35HQ5SBHSaxjSfSM7US5o4lhJNH7B9MA==", 1912 | "requires": { 1913 | "domino": "^2.1.6" 1914 | } 1915 | }, 1916 | "type": { 1917 | "version": "2.7.2", 1918 | "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", 1919 | "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" 1920 | }, 1921 | "url-regex": { 1922 | "version": "5.0.0", 1923 | "resolved": "https://registry.npmjs.org/url-regex/-/url-regex-5.0.0.tgz", 1924 | "integrity": "sha512-O08GjTiAFNsSlrUWfqF1jH0H1W3m35ZyadHrGv5krdnmPPoxP27oDTqux/579PtaroiSGm5yma6KT1mHFH6Y/g==", 1925 | "requires": { 1926 | "ip-regex": "^4.1.0", 1927 | "tlds": "^1.203.0" 1928 | } 1929 | }, 1930 | "util-deprecate": { 1931 | "version": "1.0.2", 1932 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1933 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" 1934 | }, 1935 | "uuid": { 1936 | "version": "9.0.0", 1937 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", 1938 | "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==" 1939 | }, 1940 | "webidl-conversions": { 1941 | "version": "3.0.1", 1942 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", 1943 | "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" 1944 | }, 1945 | "whatwg-url": { 1946 | "version": "5.0.0", 1947 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", 1948 | "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", 1949 | "requires": { 1950 | "tr46": "~0.0.3", 1951 | "webidl-conversions": "^3.0.0" 1952 | } 1953 | }, 1954 | "wordwrap": { 1955 | "version": "0.0.3", 1956 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", 1957 | "integrity": "sha512-1tMA907+V4QmxV7dbRvb4/8MaRALK6q9Abid3ndMYnbyo8piisCmeONVqVSXqQA3KaP4SLt5b7ud6E2sqP8TFw==" 1958 | }, 1959 | "xml2js": { 1960 | "version": "0.5.0", 1961 | "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", 1962 | "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", 1963 | "requires": { 1964 | "sax": ">=0.6.0", 1965 | "xmlbuilder": "~11.0.0" 1966 | } 1967 | }, 1968 | "xmlbuilder": { 1969 | "version": "11.0.1", 1970 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", 1971 | "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" 1972 | } 1973 | } 1974 | } 1975 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "@remy/webmention": "^1.5.0", 4 | "cheerio": "^1.0.0-rc.12", 5 | "html-entities": "^2.4.0", 6 | "html-to-text": "^9.0.3", 7 | "rss-parser": "^3.12.0", 8 | "turndown": "^7.1.1", 9 | "url-regex": "^5.0.0", 10 | "uuid": "^9.0.0" 11 | }, 12 | "type": "module" 13 | } 14 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Echo - RSS Cross Poster 2 | 3 | ![Echo screenshot](screenshot.png) 4 | 5 | ## What is it? 6 | 7 | Echo is a node script to post new items from an RSS feed to various services including Micro.blog and Mastodon. 8 | 9 | ### Why "Echo"? 10 | 11 | It does RSS feeds, so Feeder. Feeder are a band with an album called Echo Park. Echo is a good name because the album link AND the meaning of the word echo. So there. 12 | 13 | ## Requirements 14 | 15 | - Node 19 (it might work with earlier versions but that's what I used) 16 | - A server/computer/potato to run it on 17 | 18 | ## Installation 19 | 20 | 1. Clone this repository 21 | 2. Run `npm install` to install the node module 22 | 3. Run `cp config.example.js config.js` to create a new config file 23 | 4. Setup your RSS feeds and services (see [configuration](#configuration) for options) 24 | 5. Run `node index.js init` to setup - this will store the latest ID so only new posts going forward will be posted. If you want to post _some_ items from a feed, add the ID of the latest item you don't want to post in `data/nameofsite.txt`. 25 | 6. Setup a cron to run `node index.js` regularly 26 | 27 | 🚨 **Warning**: If you don't run `node index.js init` first, the script will post **all** the posts in the RSS feeds. You _probably_ don't want this. 28 | 29 | You can also run `node index.js dry`. This will log which posts will be created, but _will not_ post anything. 30 | 31 | Echo keeps track of the last item posted so on subsequent runs it will only post new posts. 32 | 33 | You can also run Echo with GitHub actions. [See Lewis' blog post for more info](https://lewisdale.dev/post/using-gitea-github-actions-for-triggering-echo/) 34 | 35 | ## Configuration 36 | 37 | There are two parts to configure: `sites` and `services`. `sites` is the RSS feeds you want to cross-post and `services` is the services you want to cross-post to. 38 | 39 | Go to [the Echo website](https://echo.rknight.me) to use the config generator and paste the generated config into `config.js` or see below for setting it up manually. 40 | 41 | ### Sites 42 | 43 | `config.sites` is an array of RSS feeds you wish to cross-post. A site has five attribute: 44 | 45 | - `name` (required): this can be anything (this is used in a filename so probably don't use special characters). 46 | - `feed` (required): the feed URL you want to post to Micro.blog (e.g. ). 47 | - `categories` (optional - only for Micro.blog): An array of categories to assign to you posts for the site (e.g. `["Cat One", "Cat Two"]`). 48 | - `services`: The services you want to cross-post to. See [services](#services). 49 | - `transform`: this is an object with two functions (see below for preset transforms): 50 | - `getId`: This tells Echo which attribute to use for the ID of each feed item. Most feeds use `id` or `guid` but if it's something different you can set that here. 51 | - `format`: This is how you format the title, body, and date of the post. This returns an object with content, date, and an optional title. 52 | - `filter` (optional): Use this if you need to filter out specific items in a feed. For example, Letterboxd includes items for lists being updated which I don't want to be posted. 53 | 54 | #### Example Site Configuration 55 | 56 | ```js 57 | { 58 | name: "example.com", 59 | feed: "http://example.com/feed", 60 | categories: ["my category"], 61 | services: [SERVICES.MICROBLOG, SERVICES.WEBHOOK] 62 | transform: { 63 | getId: (data) => { 64 | return data.id 65 | }, 66 | format: (data) => { 67 | return { 68 | content: data.content, 69 | date: data.isoDate, 70 | title: data.title, // optional 71 | } 72 | }, 73 | filter: (items) => { 74 | return items.filter(item => { 75 | return !item.link.includes('/list/') 76 | }) 77 | } 78 | } 79 | } 80 | ``` 81 | 82 | ### Preset Transforms 83 | 84 | Echo has a few presets you can use instead of having to write the `getId` and `format` functions for every site. These can be seen in [`presets.js`](lib/presets.js). For example, to use the Letterboxd or status.lol preset you can do the following: 85 | 86 | ```js 87 | { 88 | name: "letterboxd.com", 89 | feed: "http://letterboxd.com/exampleuser/rss", 90 | categories: ["movies"], 91 | transform: presets.letterboxd, 92 | }, 93 | { 94 | name: "status.lol", 95 | feed: "http://exampleuser.status.lol/feed", 96 | categories: ["status"], 97 | transform: presets.statuslol, 98 | } 99 | ``` 100 | 101 | You can define the body of your post in `format` to make your posts look exactly how you want. For ease, Echo includes `helpers.js` with some helper libraries. 102 | 103 | - To convert HTML to markdown, use `helpers.toMarkdown(text)` 104 | - To use [Cheerio](https://cheerio.js.org/) use `helpers.cheerioLoad(text)` 105 | - To generate a UUID use `helpers.generateUuid()` 106 | - Get the length of the post based on how Mastodon calculates this (links are always 23 characters, for example) `helpers.getMastodonLength(string)` 107 | - Get all links as an array `helpers.getLinks(string)` 108 | - Encode and decode HTML entities with `helpers.decode(string)` and `helpers.encode(string)` 109 | 110 | ```js 111 | format: (data) => { 112 | const formatted = presets.default.format(data) 113 | 114 | // get the first link with Cheerio 115 | // and append it to the content 116 | const $ = helpers.cheerioLoad(formatted.content) 117 | const firstLink = $('a:first').attr('href') 118 | formatted.content += ` ${firstLink}` 119 | 120 | // format to markdown 121 | formatted.content = helpers.toMarkdown(formatted.content) 122 | return formatted 123 | } 124 | ``` 125 | 126 | ### Services 127 | 128 | Each service requires a different set of values depending on how the API works. 129 | 130 | #### Micro.blog 131 | 132 | |Key|Value|Notes| 133 | |---|---|---| 134 | |`siteUrl`|The Micro.blog site you're posting to|e.g. `https://coolsite.micro.blog`| 135 | |`apiKey`|A Micro.blog API key|Get an API key from [https://micro.blog/account/apps](https://micro.blog/account/apps)| 136 | 137 | #### Mastodon 138 | 139 | |Key|Value|Notes| 140 | |---|---|---| 141 | |`instance`|Your Mastodon instance|e.g. `https://social.lol`| 142 | |`accessToken`|Go to `Preferences > Development > New Application` on your Mastodon instance and grab the access token| 143 | |`visibility` (optional, default `public`)|`public` `unlisted` `private` `direct`| 144 | |`sensitive` (optional, default `false`)|`false` `true`| 145 | 146 | #### Webhooks 147 | 148 | The webhook service will send a `POST` request with the result of `transform.format` (as set in your site config) to a given url. 149 | 150 | |Key|Value|Notes| 151 | |---|---|---| 152 | |`url`|The URL to post to| 153 | 154 | #### Omnivore 155 | 156 | The Omnivore service will save a URL to your Omnivore account. 157 | 158 | |Key|Value|Notes| 159 | |---|---|---| 160 | |`apiKey`|Your Omnivore API key| 161 | 162 | 163 | #### GitHub 164 | 165 | Create a new file on a GitHub repository. 166 | 167 | | Key | Value | Notes | 168 | | -------- | --------------------- | ----- | 169 | | `token` | Your GitHub token | 170 | | `repo` | The repository to commit to | e.g. `rknightuk/echo` 171 | | `branch` | The branch to commit to | 172 | | `committer` | An object with `name` and `email` values | e.g. `{ name: 'Robb', email: 'robb@example.com }` 173 | 174 | For posting to Github your `format` function must return `content` and `filePath` where `filePath` is the path to where the file will be in the Github repository, for example `src/posts/movies/2024-02-09.md`. It can optionally return a `commit` message, which will fallback to `New post` if none is set. Example `format` function for GitHub: 175 | 176 | ```js 177 | format: (data) => { 178 | return { 179 | content: data.title, 180 | date: new Date(data.isoDate).toISOString(), 181 | filePath: `src/posts/movies/${new Date().getFullYear()}/${new Date().toISOString().split('T').md`, 182 | commit: `Add ${data.title}`, 183 | } 184 | } 185 | ``` 186 | 187 | #### LinkAce 188 | 189 | |Key|Value|Notes| 190 | |---|---|---| 191 | |`domain`|The LinkAce domain where you have it installed|e.g.`https://links.example.com`| 192 | |`apiKey`|A LinkAce API key|| 193 | 194 | `content` returns from `transform.format` should be a link. Categories on the site will be converted to tags. Any tags included from `format` will be merged with `categories`. 195 | 196 | ```js 197 | { 198 | name: 'mycoollinkfeed', 199 | feed: 'https://example.com/linkfeed.xml', 200 | categories: ['ATag'], 201 | transform: { 202 | getId: presets.default.getId, 203 | format: (data) => { 204 | return { 205 | content: data.external_url, 206 | date: data.date_published, 207 | tags: data._custom.tags, 208 | } 209 | } 210 | }, 211 | services: [SERVICES.LINKACE], 212 | }, 213 | ``` 214 | 215 | #### Webmentions 216 | 217 | | Key | Value | Notes | 218 | | -------- | ---------------------------------------------- | ------------------------------- | 219 | | no config is required for webmentions | | | 220 | 221 | `content` returns from `transform.format` should be a link. 222 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rknightuk/echo/8a86e4b0af8b72fa76077851b5c0ed2e44f94e16/screenshot.png --------------------------------------------------------------------------------