Verified Commit e9f6d9a8 authored by Fabio Manganiello's avatar Fabio Manganiello
Browse files

Refactored RSS integration into its own `rss` plugin [closes #199]

parent 3e4b91cd
Pipeline #135 passed with stages
in 5 minutes and 59 seconds
......@@ -50,6 +50,7 @@ Events
platypush/events/ping.rst
platypush/events/pushbullet.rst
platypush/events/qrcode.rst
platypush/events/rss.rst
platypush/events/scard.rst
platypush/events/sensor.rst
platypush/events/sensor.ir.rst
......
``platypush.message.event.rss``
===============================
.. automodule:: platypush.message.event.rss
:members:
``rss``
=======
.. automodule:: platypush.plugins.rss
:members:
......@@ -103,6 +103,7 @@ Plugins
platypush/plugins/pwm.pca9685.rst
platypush/plugins/qrcode.rst
platypush/plugins/redis.rst
platypush/plugins/rss.rst
platypush/plugins/rtorrent.rst
platypush/plugins/serial.rst
platypush/plugins/shell.rst
......
......@@ -7,6 +7,9 @@ from platypush.backend.http.request import HttpRequest
class HttpPollBackend(Backend):
"""
WARNING: This integration is deprecated, since it was practically only used for RSS subscriptions.
RSS feeds integration has been replaced by :class:`platypush.plugins.rss.RSSPlugin`.
This backend will poll multiple HTTP endpoints/services and return events
the bus whenever something new happened. Supported types:
:class:`platypush.backend.http.request.JsonHttpRequest` (for polling updates on
......
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/favicon.ico"><title>platypush</title><link href="/static/css/chunk-0127d4fc.e2dcb450.css" rel="prefetch"><link href="/static/css/chunk-01c1b3b0.1c564b4b.css" rel="prefetch"><link href="/static/css/chunk-06539e5d.d6ebdd59.css" rel="prefetch"><link href="/static/css/chunk-07773226.2850d1a9.css" rel="prefetch"><link href="/static/css/chunk-0827360a.31e3bd05.css" rel="prefetch"><link href="/static/css/chunk-09eaa919.fe0ff27c.css" rel="prefetch"><link href="/static/css/chunk-0b6bec58.8cc2943a.css" rel="prefetch"><link href="/static/css/chunk-14965a61.8d205da0.css" rel="prefetch"><link href="/static/css/chunk-1b24f8bf.5cf6a693.css" rel="prefetch"><link href="/static/css/chunk-31bc5041.af71a98e.css" rel="prefetch"><link href="/static/css/chunk-35986630.7e5f6e19.css" rel="prefetch"><link href="/static/css/chunk-36760726.acebcec3.css" rel="prefetch"><link href="/static/css/chunk-37779247.4e1f8040.css" rel="prefetch"><link href="/static/css/chunk-3cd152ac.d08d23e5.css" rel="prefetch"><link href="/static/css/chunk-40ee55e4.6c1a49c0.css" rel="prefetch"><link href="/static/css/chunk-41adab28.880375c8.css" rel="prefetch"><link href="/static/css/chunk-43a68b48.02d462d3.css" rel="prefetch"><link href="/static/css/chunk-44b22f6e.9028babf.css" rel="prefetch"><link href="/static/css/chunk-49f94906.6da56ca1.css" rel="prefetch"><link href="/static/css/chunk-57f8147d.80e6ff90.css" rel="prefetch"><link href="/static/css/chunk-5c4852cd.260670a9.css" rel="prefetch"><link href="/static/css/chunk-5d57b054.b6b906be.css" rel="prefetch"><link href="/static/css/chunk-5d632024.0af01962.css" rel="prefetch"><link href="/static/css/chunk-5d73ace1.d080786c.css" rel="prefetch"><link href="/static/css/chunk-60dbbc82.e4f0a304.css" rel="prefetch"><link href="/static/css/chunk-6ee47cbe.dd59375f.css" rel="prefetch"><link href="/static/css/chunk-74fb6755.944603b9.css" rel="prefetch"><link href="/static/css/chunk-75e68c24.b2a693b6.css" rel="prefetch"><link href="/static/css/chunk-9684cd10.cd250be8.css" rel="prefetch"><link href="/static/css/chunk-972487d6.0b7528ab.css" rel="prefetch"><link href="/static/css/chunk-a018ff46.12719ccd.css" rel="prefetch"><link href="/static/css/chunk-b6886800.988a3584.css" rel="prefetch"><link href="/static/css/chunk-c4aee99e.77e9d3dc.css" rel="prefetch"><link href="/static/css/chunk-cb35d852.ddb70879.css" rel="prefetch"><link href="/static/css/chunk-cc8a6536.1db53995.css" rel="prefetch"><link href="/static/css/chunk-d8561e02.8c29ed5e.css" rel="prefetch"><link href="/static/css/chunk-da0a28d2.032a907c.css" rel="prefetch"><link href="/static/js/chunk-0127d4fc.a10585f1.js" rel="prefetch"><link href="/static/js/chunk-01c1b3b0.e53aa42f.js" rel="prefetch"><link href="/static/js/chunk-06539e5d.71e8e73b.js" rel="prefetch"><link href="/static/js/chunk-07773226.8df3ca2d.js" rel="prefetch"><link href="/static/js/chunk-0827360a.832f08b4.js" rel="prefetch"><link href="/static/js/chunk-09eaa919.22084f8d.js" rel="prefetch"><link href="/static/js/chunk-0b6bec58.dae9d413.js" rel="prefetch"><link href="/static/js/chunk-14965a61.1d7339b2.js" rel="prefetch"><link href="/static/js/chunk-1b24f8bf.da3d43c1.js" rel="prefetch"><link href="/static/js/chunk-2d0aa612.d6557933.js" rel="prefetch"><link href="/static/js/chunk-2d0ac54d.d666dc77.js" rel="prefetch"><link href="/static/js/chunk-2d0b21a7.59ca0ac9.js" rel="prefetch"><link href="/static/js/chunk-2d0b270c.76b89971.js" rel="prefetch"><link href="/static/js/chunk-2d0c1eb0.7d3a54c7.js" rel="prefetch"><link href="/static/js/chunk-2d0c229a.b202b7c9.js" rel="prefetch"><link href="/static/js/chunk-2d0cc2be.7b1ad454.js" rel="prefetch"><link href="/static/js/chunk-2d0d5f97.4a7eb0b7.js" rel="prefetch"><link href="/static/js/chunk-2d0d6b06.c30e3dbb.js" rel="prefetch"><link href="/static/js/chunk-2d0da3df.a451aa44.js" rel="prefetch"><link href="/static/js/chunk-2d208116.a219a6df.js" rel="prefetch"><link href="/static/js/chunk-2d2091df.2eea6c5c.js" rel="prefetch"><link href="/static/js/chunk-2d21b0dc.548e37ff.js" rel="prefetch"><link href="/static/js/chunk-2d21da1a.228a810a.js" rel="prefetch"><link href="/static/js/chunk-2d22495e.ab1a60c2.js" rel="prefetch"><link href="/static/js/chunk-2d231217.c20a3396.js" rel="prefetch"><link href="/static/js/chunk-2d237d41.7d190124.js" rel="prefetch"><link href="/static/js/chunk-31bc5041.150e0d32.js" rel="prefetch"><link href="/static/js/chunk-35986630.2deb9299.js" rel="prefetch"><link href="/static/js/chunk-36760726.5007ff45.js" rel="prefetch"><link href="/static/js/chunk-37779247.042dd9b7.js" rel="prefetch"><link href="/static/js/chunk-3cd152ac.7ba8b690.js" rel="prefetch"><link href="/static/js/chunk-40ee55e4.5487788f.js" rel="prefetch"><link href="/static/js/chunk-41adab28.c070d036.js" rel="prefetch"><link href="/static/js/chunk-43a68b48.cee68265.js" rel="prefetch"><link href="/static/js/chunk-44b22f6e.9b730fcd.js" rel="prefetch"><link href="/static/js/chunk-49f94906.b96fc78b.js" rel="prefetch"><link href="/static/js/chunk-57f8147d.392752be.js" rel="prefetch"><link href="/static/js/chunk-5c4852cd.a1e4850b.js" rel="prefetch"><link href="/static/js/chunk-5d57b054.8fb7b99f.js" rel="prefetch"><link href="/static/js/chunk-5d632024.1b5ac971.js" rel="prefetch"><link href="/static/js/chunk-5d73ace1.e7dea834.js" rel="prefetch"><link href="/static/js/chunk-60dbbc82.1e9259bf.js" rel="prefetch"><link href="/static/js/chunk-6ee47cbe.3d51c5e6.js" rel="prefetch"><link href="/static/js/chunk-746dfdf7.996f7b5a.js" rel="prefetch"><link href="/static/js/chunk-74fb6755.adfe5442.js" rel="prefetch"><link href="/static/js/chunk-75e68c24.6b249468.js" rel="prefetch"><link href="/static/js/chunk-9684cd10.8ab232e2.js" rel="prefetch"><link href="/static/js/chunk-972487d6.50c26261.js" rel="prefetch"><link href="/static/js/chunk-a018ff46.f5ec4b17.js" rel="prefetch"><link href="/static/js/chunk-b6886800.1a2c1829.js" rel="prefetch"><link href="/static/js/chunk-c4aee99e.b70ad5c7.js" rel="prefetch"><link href="/static/js/chunk-cb35d852.b66b8b63.js" rel="prefetch"><link href="/static/js/chunk-cc8a6536.9cd55708.js" rel="prefetch"><link href="/static/js/chunk-d8561e02.24122872.js" rel="prefetch"><link href="/static/js/chunk-da0a28d2.e49720f2.js" rel="prefetch"><link href="/static/css/app.3cf9567c.css" rel="preload" as="style"><link href="/static/css/chunk-vendors.5dad8b00.css" rel="preload" as="style"><link href="/static/js/app.ea14fcd4.js" rel="preload" as="script"><link href="/static/js/chunk-vendors.9ad0b6d3.js" rel="preload" as="script"><link href="/static/css/chunk-vendors.5dad8b00.css" rel="stylesheet"><link href="/static/css/app.3cf9567c.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but platypush doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="/static/js/chunk-vendors.9ad0b6d3.js"></script><script src="/static/js/app.ea14fcd4.js"></script></body></html>
\ No newline at end of file
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/favicon.ico"><title>platypush</title><link href="/static/css/chunk-0127d4fc.e2dcb450.css" rel="prefetch"><link href="/static/css/chunk-01c1b3b0.1c564b4b.css" rel="prefetch"><link href="/static/css/chunk-06539e5d.d6ebdd59.css" rel="prefetch"><link href="/static/css/chunk-07773226.2850d1a9.css" rel="prefetch"><link href="/static/css/chunk-0827360a.31e3bd05.css" rel="prefetch"><link href="/static/css/chunk-09eaa919.fe0ff27c.css" rel="prefetch"><link href="/static/css/chunk-0b6bec58.8cc2943a.css" rel="prefetch"><link href="/static/css/chunk-14965a61.8d205da0.css" rel="prefetch"><link href="/static/css/chunk-1b24f8bf.5cf6a693.css" rel="prefetch"><link href="/static/css/chunk-31bc5041.af71a98e.css" rel="prefetch"><link href="/static/css/chunk-35986630.7e5f6e19.css" rel="prefetch"><link href="/static/css/chunk-36760726.acebcec3.css" rel="prefetch"><link href="/static/css/chunk-37779247.4e1f8040.css" rel="prefetch"><link href="/static/css/chunk-3cd152ac.d08d23e5.css" rel="prefetch"><link href="/static/css/chunk-40ee55e4.6c1a49c0.css" rel="prefetch"><link href="/static/css/chunk-41adab28.880375c8.css" rel="prefetch"><link href="/static/css/chunk-43a68b48.02d462d3.css" rel="prefetch"><link href="/static/css/chunk-44b22f6e.9028babf.css" rel="prefetch"><link href="/static/css/chunk-49f94906.6da56ca1.css" rel="prefetch"><link href="/static/css/chunk-57f8147d.80e6ff90.css" rel="prefetch"><link href="/static/css/chunk-5c4852cd.260670a9.css" rel="prefetch"><link href="/static/css/chunk-5d57b054.b6b906be.css" rel="prefetch"><link href="/static/css/chunk-5d632024.0af01962.css" rel="prefetch"><link href="/static/css/chunk-5d73ace1.d080786c.css" rel="prefetch"><link href="/static/css/chunk-60dbbc82.e4f0a304.css" rel="prefetch"><link href="/static/css/chunk-6ee47cbe.dd59375f.css" rel="prefetch"><link href="/static/css/chunk-74fb6755.944603b9.css" rel="prefetch"><link href="/static/css/chunk-7babe442.e5958b5e.css" rel="prefetch"><link href="/static/css/chunk-9684cd10.cd250be8.css" rel="prefetch"><link href="/static/css/chunk-972487d6.0b7528ab.css" rel="prefetch"><link href="/static/css/chunk-a018ff46.12719ccd.css" rel="prefetch"><link href="/static/css/chunk-b6886800.988a3584.css" rel="prefetch"><link href="/static/css/chunk-c4aee99e.77e9d3dc.css" rel="prefetch"><link href="/static/css/chunk-cb35d852.ddb70879.css" rel="prefetch"><link href="/static/css/chunk-cc8a6536.1db53995.css" rel="prefetch"><link href="/static/css/chunk-d8561e02.8c29ed5e.css" rel="prefetch"><link href="/static/css/chunk-da0a28d2.032a907c.css" rel="prefetch"><link href="/static/js/chunk-0127d4fc.a10585f1.js" rel="prefetch"><link href="/static/js/chunk-01c1b3b0.e53aa42f.js" rel="prefetch"><link href="/static/js/chunk-06539e5d.71e8e73b.js" rel="prefetch"><link href="/static/js/chunk-07773226.8df3ca2d.js" rel="prefetch"><link href="/static/js/chunk-0827360a.832f08b4.js" rel="prefetch"><link href="/static/js/chunk-09eaa919.22084f8d.js" rel="prefetch"><link href="/static/js/chunk-0b6bec58.dae9d413.js" rel="prefetch"><link href="/static/js/chunk-14965a61.1d7339b2.js" rel="prefetch"><link href="/static/js/chunk-1b24f8bf.da3d43c1.js" rel="prefetch"><link href="/static/js/chunk-2d0aa612.d6557933.js" rel="prefetch"><link href="/static/js/chunk-2d0ac54d.d666dc77.js" rel="prefetch"><link href="/static/js/chunk-2d0b21a7.59ca0ac9.js" rel="prefetch"><link href="/static/js/chunk-2d0b270c.76b89971.js" rel="prefetch"><link href="/static/js/chunk-2d0c1eb0.7d3a54c7.js" rel="prefetch"><link href="/static/js/chunk-2d0c229a.b202b7c9.js" rel="prefetch"><link href="/static/js/chunk-2d0cc2be.7b1ad454.js" rel="prefetch"><link href="/static/js/chunk-2d0d5f97.4a7eb0b7.js" rel="prefetch"><link href="/static/js/chunk-2d0d6b06.c30e3dbb.js" rel="prefetch"><link href="/static/js/chunk-2d0da3df.a451aa44.js" rel="prefetch"><link href="/static/js/chunk-2d208116.a219a6df.js" rel="prefetch"><link href="/static/js/chunk-2d2091df.2eea6c5c.js" rel="prefetch"><link href="/static/js/chunk-2d21b0dc.548e37ff.js" rel="prefetch"><link href="/static/js/chunk-2d21da1a.228a810a.js" rel="prefetch"><link href="/static/js/chunk-2d22495e.ab1a60c2.js" rel="prefetch"><link href="/static/js/chunk-2d231217.c20a3396.js" rel="prefetch"><link href="/static/js/chunk-2d237d41.7d190124.js" rel="prefetch"><link href="/static/js/chunk-31bc5041.150e0d32.js" rel="prefetch"><link href="/static/js/chunk-35986630.2deb9299.js" rel="prefetch"><link href="/static/js/chunk-36760726.5007ff45.js" rel="prefetch"><link href="/static/js/chunk-37779247.042dd9b7.js" rel="prefetch"><link href="/static/js/chunk-3cd152ac.7ba8b690.js" rel="prefetch"><link href="/static/js/chunk-40ee55e4.5487788f.js" rel="prefetch"><link href="/static/js/chunk-41adab28.c070d036.js" rel="prefetch"><link href="/static/js/chunk-43a68b48.cee68265.js" rel="prefetch"><link href="/static/js/chunk-44b22f6e.9b730fcd.js" rel="prefetch"><link href="/static/js/chunk-49f94906.b96fc78b.js" rel="prefetch"><link href="/static/js/chunk-57f8147d.392752be.js" rel="prefetch"><link href="/static/js/chunk-5c4852cd.a1e4850b.js" rel="prefetch"><link href="/static/js/chunk-5d57b054.8fb7b99f.js" rel="prefetch"><link href="/static/js/chunk-5d632024.1b5ac971.js" rel="prefetch"><link href="/static/js/chunk-5d73ace1.e7dea834.js" rel="prefetch"><link href="/static/js/chunk-60dbbc82.1e9259bf.js" rel="prefetch"><link href="/static/js/chunk-6ee47cbe.3d51c5e6.js" rel="prefetch"><link href="/static/js/chunk-746dfdf7.996f7b5a.js" rel="prefetch"><link href="/static/js/chunk-74fb6755.adfe5442.js" rel="prefetch"><link href="/static/js/chunk-7babe442.e3a7971d.js" rel="prefetch"><link href="/static/js/chunk-9684cd10.8ab232e2.js" rel="prefetch"><link href="/static/js/chunk-972487d6.50c26261.js" rel="prefetch"><link href="/static/js/chunk-a018ff46.f5ec4b17.js" rel="prefetch"><link href="/static/js/chunk-b6886800.1a2c1829.js" rel="prefetch"><link href="/static/js/chunk-c4aee99e.b70ad5c7.js" rel="prefetch"><link href="/static/js/chunk-cb35d852.b66b8b63.js" rel="prefetch"><link href="/static/js/chunk-cc8a6536.9cd55708.js" rel="prefetch"><link href="/static/js/chunk-d8561e02.24122872.js" rel="prefetch"><link href="/static/js/chunk-da0a28d2.e49720f2.js" rel="prefetch"><link href="/static/css/app.3cf9567c.css" rel="preload" as="style"><link href="/static/css/chunk-vendors.5dad8b00.css" rel="preload" as="style"><link href="/static/js/app.20e7b565.js" rel="preload" as="script"><link href="/static/js/chunk-vendors.9ad0b6d3.js" rel="preload" as="script"><link href="/static/css/chunk-vendors.5dad8b00.css" rel="stylesheet"><link href="/static/css/app.3cf9567c.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but platypush doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="/static/js/chunk-vendors.9ad0b6d3.js"></script><script src="/static/js/app.20e7b565.js"></script></body></html>
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-75e68c24"],{"0709":function(e,t,r){"use strict";r("84c2")},"84c2":function(e,t,r){},c306:function(e,t,r){"use strict";r.r(t);var n=r("7a23"),s=Object(n["K"])("data-v-1b599aef");Object(n["u"])("data-v-1b599aef");var i={class:"rss-news"},c={key:0,class:"article"};Object(n["s"])();var u=s((function(e,t,r,s,u,a){return Object(n["r"])(),Object(n["e"])("div",i,[e.currentArticle?(Object(n["r"])(),Object(n["e"])("div",c,[Object(n["h"])("div",{class:"source",textContent:Object(n["C"])(e.currentArticle.source)},null,8,["textContent"]),Object(n["h"])("div",{class:"title",textContent:Object(n["C"])(e.currentArticle.title)},null,8,["textContent"]),Object(n["h"])("div",{class:"published",textContent:Object(n["C"])(new Date(e.currentArticle.published).toDateString()+", "+new Date(e.currentArticle.published).toTimeString().substring(0,5))},null,8,["textContent"])])):Object(n["f"])("",!0)])})),a=r("2909"),l=r("1da1"),o=(r("96cf"),r("a9e3"),r("b680"),r("3e54")),d={name:"RssNews",mixins:[o["a"]],props:{db:{type:String,required:!0},limit:{type:Number,required:!1,default:25},refreshSeconds:{type:Number,required:!1,default:15}},data:function(){return{articles:[],queue:[],currentArticle:void 0}},methods:{refresh:function(){var e=Object(l["a"])(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){while(1)switch(e.prev=e.next){case 0:if(this.queue.length){e.next=5;break}return e.next=3,this.request("db.select",{engine:this.db,query:"\n select s.title as source, e.title, e.summary,\n strftime('%Y-%m-%dT%H:%M:%fZ', e.published) as published\n from FeedEntry e join FeedSource s\n on e.source_id = s.id order by e.published desc limit ".concat(this.limit)});case 3:this.articles=e.sent,this.queue=Object(a["a"])(this.articles);case 5:if(this.queue.length){e.next=7;break}return e.abrupt("return");case 7:this.currentArticle=this.queue.pop();case 8:case"end":return e.stop()}}),e,this)})));function t(){return e.apply(this,arguments)}return t}()},mounted:function(){this.refresh(),setInterval(this.refresh,parseInt((1e3*this.refreshSeconds).toFixed(0)))}};r("0709");d.render=u,d.__scopeId="data-v-1b599aef";t["default"]=d}}]);
//# sourceMappingURL=chunk-75e68c24.6b249468.js.map
\ No newline at end of file
{"version":3,"sources":["webpack:///./src/components/widgets/RssNews/Index.vue?5220","webpack:///./src/components/widgets/RssNews/Index.vue","webpack:///./src/components/widgets/RssNews/Index.vue?6001"],"names":["class","currentArticle","source","title","Date","published","toDateString","toTimeString","substring","name","mixins","Utils","props","db","type","String","required","limit","Number","default","refreshSeconds","data","articles","queue","undefined","methods","refresh","this","length","request","engine","query","pop","mounted","setInterval","parseInt","toFixed","render","__scopeId"],"mappings":"kHAAA,W,+JCCOA,MAAM,Y,SACJA,MAAM,W,wEADb,eAMM,MANN,EAMM,CALuB,EAAAC,gB,iBAA3B,eAIM,MAJN,EAIM,CAHJ,eAAyD,OAApDD,MAAM,S,YAAS,eAA8B,EAAR,eAACE,S,wBAC3C,eAAuD,OAAlDF,MAAM,Q,YAAQ,eAA6B,EAAP,eAACG,Q,wBAC1C,eAAkK,OAA7JH,MAAM,Y,YAAY,eAAoI,IAAxHI,KAAK,EAAAH,eAAeI,WAAWC,eAAY,SAAgBF,KAAK,EAAAH,eAAeI,WAAWE,eAAeC,UAAS,O,0HAY5I,GACbC,KAAM,UACNC,OAAQ,CAACC,EAAA,MACTC,MAAO,CAILC,GAAI,CACFC,KAAMC,OACNC,UAAU,GAIZC,MAAO,CACLH,KAAMI,OACNF,UAAU,EACVG,QAAS,IAIXC,eAAgB,CACdN,KAAMI,OACNF,UAAU,EACVG,QAAS,KAIbE,KAAM,WACJ,MAAO,CACLC,SAAU,GACVC,MAAO,GACPtB,oBAAgBuB,IAIpBC,QAAS,CACPC,QAAS,WAAF,8CAAE,iGACFC,KAAKJ,MAAMK,OADT,gCAEiBD,KAAKE,QAAQ,YAAa,CAC9CC,OAAQH,KAAKd,GACbkB,MAAO,iQAAF,OAIuDJ,KAAKV,SAR9D,OAELU,KAAKL,SAFA,OAWLK,KAAKJ,MAAL,eAAiBI,KAAKL,UAXjB,UAcFK,KAAKJ,MAAMK,OAdT,iDAiBPD,KAAK1B,eAAiB0B,KAAKJ,MAAMS,MAjB1B,gDAAF,qDAAE,IAqBXC,QAAS,WACPN,KAAKD,UACLQ,YAAYP,KAAKD,QAASS,UAA8B,IAApBR,KAAKP,gBAAqBgB,QAAQ,O,UCvE1E,EAAOC,OAASA,EAChB,EAAOC,UAAY,kBAEJ","file":"static/js/chunk-75e68c24.6b249468.js","sourcesContent":["export * from \"-!../../../../node_modules/mini-css-extract-plugin/dist/loader.js??ref--8-oneOf-1-0!../../../../node_modules/css-loader/dist/cjs.js??ref--8-oneOf-1-1!../../../../node_modules/vue-loader-v16/dist/stylePostLoader.js!../../../../node_modules/postcss-loader/src/index.js??ref--8-oneOf-1-2!../../../../node_modules/sass-loader/dist/cjs.js??ref--8-oneOf-1-3!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader-v16/dist/index.js??ref--0-1!./Index.vue?vue&type=style&index=0&id=1b599aef&lang=scss&scoped=true\"","<template>\n <div class=\"rss-news\">\n <div class=\"article\" v-if=\"currentArticle\">\n <div class=\"source\" v-text=\"currentArticle.source\"></div>\n <div class=\"title\" v-text=\"currentArticle.title\"></div>\n <div class=\"published\" v-text=\"new Date(currentArticle.published).toDateString() + ', ' + new Date(currentArticle.published).toTimeString().substring(0,5)\"></div>\n </div>\n </div>\n</template>\n\n<script>\nimport Utils from \"@/Utils\";\n\n/**\n * In order to use this widget you need to configure the `backend.http.poll` backend to\n * poll a list of RSS sources.\n */\nexport default {\n name: \"RssNews\",\n mixins: [Utils],\n props: {\n // Database engine string pointing to the source of the RSS feeds.\n // If not otherwise configured, you should set this to\n // `sqlite:///<HOME>/.local/share/platypush/feeds/rss.db`.\n db: {\n type: String,\n required: true,\n },\n\n // Maximum number of items to be shown in a cycle.\n limit: {\n type: Number,\n required: false,\n default: 25,\n },\n\n // How long an entry should be displayed before moving to the next one.\n refreshSeconds: {\n type: Number,\n required: false,\n default: 15,\n },\n },\n\n data: function() {\n return {\n articles: [],\n queue: [],\n currentArticle: undefined,\n }\n },\n\n methods: {\n refresh: async function() {\n if (!this.queue.length) {\n this.articles = await this.request('db.select', {\n engine: this.db,\n query: `\n select s.title as source, e.title, e.summary,\n strftime('%Y-%m-%dT%H:%M:%fZ', e.published) as published\n from FeedEntry e join FeedSource s\n on e.source_id = s.id order by e.published desc limit ${this.limit}`,\n })\n\n this.queue = [...this.articles]\n }\n\n if (!this.queue.length)\n return\n\n this.currentArticle = this.queue.pop()\n },\n },\n\n mounted: function() {\n this.refresh()\n setInterval(this.refresh, parseInt((this.refreshSeconds*1000).toFixed(0)))\n },\n}\n</script>\n\n<style lang=\"scss\" scoped>\n.rss-news {\n width: 100%;\n height: 100%;\n display: flex;\n align-items: center;\n letter-spacing: .025em;\n\n .article {\n width: 90%;\n padding: 0 2em;\n\n .source {\n font-size: 1.7em;\n font-weight: bold;\n margin-bottom: .5em;\n }\n\n .title {\n font-size: 1.8em;\n font-weight: normal;\n margin-bottom: .5em;\n }\n\n .published {\n text-align: right;\n font-size: 1.1em;\n }\n }\n}\n</style>\n","import { render } from \"./Index.vue?vue&type=template&id=1b599aef&scoped=true\"\nimport script from \"./Index.vue?vue&type=script&lang=js\"\nexport * from \"./Index.vue?vue&type=script&lang=js\"\n\nimport \"./Index.vue?vue&type=style&index=0&id=1b599aef&lang=scss&scoped=true\"\nscript.render = render\nscript.__scopeId = \"data-v-1b599aef\"\n\nexport default script"],"sourceRoot":""}
\ No newline at end of file
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-7babe442"],{"0d43":function(e,t,r){"use strict";r("ddbd")},c306:function(e,t,r){"use strict";r.r(t);var n=r("7a23"),s=Object(n["K"])("data-v-52a823f4");Object(n["u"])("data-v-52a823f4");var i={class:"rss-news"},c={key:0,class:"article"};Object(n["s"])();var u=s((function(e,t,r,s,u,a){return Object(n["r"])(),Object(n["e"])("div",i,[e.currentArticle?(Object(n["r"])(),Object(n["e"])("div",c,[Object(n["h"])("div",{class:"source",textContent:Object(n["C"])(e.currentArticle.feed_title||e.currentArticle.feed_url)},null,8,["textContent"]),Object(n["h"])("div",{class:"title",textContent:Object(n["C"])(e.currentArticle.title)},null,8,["textContent"]),Object(n["h"])("div",{class:"published",textContent:Object(n["C"])(new Date(e.currentArticle.published).toDateString()+", "+new Date(e.currentArticle.published).toTimeString().substring(0,5))},null,8,["textContent"])])):Object(n["f"])("",!0)])})),a=r("2909"),l=r("1da1"),d=(r("96cf"),r("a9e3"),r("b680"),r("3e54")),o={name:"RssNews",mixins:[d["a"]],props:{limit:{type:Number,required:!1,default:25},refreshSeconds:{type:Number,required:!1,default:15}},data:function(){return{articles:[],queue:[],currentArticle:void 0}},methods:{refresh:function(){var e=Object(l["a"])(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){while(1)switch(e.prev=e.next){case 0:if(this.queue.length){e.next=5;break}return e.next=3,this.request("rss.get_latest_entries",{limit:this.limit});case 3:this.articles=e.sent,this.queue=Object(a["a"])(this.articles).reverse();case 5:if(this.queue.length){e.next=7;break}return e.abrupt("return");case 7:this.currentArticle=this.queue.pop();case 8:case"end":return e.stop()}}),e,this)})));function t(){return e.apply(this,arguments)}return t}()},mounted:function(){this.refresh(),setInterval(this.refresh,parseInt((1e3*this.refreshSeconds).toFixed(0)))}};r("0d43");o.render=u,o.__scopeId="data-v-52a823f4";t["default"]=o},ddbd:function(e,t,r){}}]);
//# sourceMappingURL=chunk-7babe442.e3a7971d.js.map
\ No newline at end of file
{"version":3,"sources":["webpack:///./src/components/widgets/RssNews/Index.vue?a9df","webpack:///./src/components/widgets/RssNews/Index.vue","webpack:///./src/components/widgets/RssNews/Index.vue?6001"],"names":["class","currentArticle","feed_title","feed_url","title","Date","published","toDateString","toTimeString","substring","name","mixins","Utils","props","limit","type","Number","required","default","refreshSeconds","data","articles","queue","undefined","methods","refresh","this","length","request","reverse","pop","mounted","setInterval","parseInt","toFixed","render","__scopeId"],"mappings":"kHAAA,W,sICCOA,MAAM,Y,SACJA,MAAM,W,wEADb,eAMM,MANN,EAMM,CALuB,EAAAC,gB,iBAA3B,eAIM,MAJN,EAIM,CAHJ,eAAwF,OAAnFD,MAAM,S,YAAS,eAA6D,EAAvC,eAACE,YAAc,EAAAD,eAAeE,W,wBACxE,eAAuD,OAAlDH,MAAM,Q,YAAQ,eAA6B,EAAP,eAACI,Q,wBAC1C,eAAkK,OAA7JJ,MAAM,Y,YAAY,eAAoI,IAAxHK,KAAK,EAAAJ,eAAeK,WAAWC,eAAY,SAAgBF,KAAK,EAAAJ,eAAeK,WAAWE,eAAeC,UAAS,O,0HAY5I,GACbC,KAAM,UACNC,OAAQ,CAACC,EAAA,MACTC,MAAO,CAELC,MAAO,CACLC,KAAMC,OACNC,UAAU,EACVC,QAAS,IAIXC,eAAgB,CACdJ,KAAMC,OACNC,UAAU,EACVC,QAAS,KAIbE,KAAM,WACJ,MAAO,CACLC,SAAU,GACVC,MAAO,GACPrB,oBAAgBsB,IAIpBC,QAAS,CACPC,QAAS,WAAF,8CAAE,iGACFC,KAAKJ,MAAMK,OADT,gCAEiBD,KAAKE,QAAQ,yBAA0B,CAC3Dd,MAAOY,KAAKZ,QAHT,OAELY,KAAKL,SAFA,OAMLK,KAAKJ,MAAQ,eAAII,KAAKL,UAAUQ,UAN3B,UASFH,KAAKJ,MAAMK,OATT,iDAYPD,KAAKzB,eAAiByB,KAAKJ,MAAMQ,MAZ1B,gDAAF,qDAAE,IAgBXC,QAAS,WACPL,KAAKD,UACLO,YAAYN,KAAKD,QAASQ,UAA8B,IAApBP,KAAKP,gBAAqBe,QAAQ,O,UC1D1E,EAAOC,OAASA,EAChB,EAAOC,UAAY,kBAEJ,gB","file":"static/js/chunk-7babe442.e3a7971d.js","sourcesContent":["export * from \"-!../../../../node_modules/mini-css-extract-plugin/dist/loader.js??ref--8-oneOf-1-0!../../../../node_modules/css-loader/dist/cjs.js??ref--8-oneOf-1-1!../../../../node_modules/vue-loader-v16/dist/stylePostLoader.js!../../../../node_modules/postcss-loader/src/index.js??ref--8-oneOf-1-2!../../../../node_modules/sass-loader/dist/cjs.js??ref--8-oneOf-1-3!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader-v16/dist/index.js??ref--0-1!./Index.vue?vue&type=style&index=0&id=52a823f4&lang=scss&scoped=true\"","<template>\n <div class=\"rss-news\">\n <div class=\"article\" v-if=\"currentArticle\">\n <div class=\"source\" v-text=\"currentArticle.feed_title || currentArticle.feed_url\"></div>\n <div class=\"title\" v-text=\"currentArticle.title\"></div>\n <div class=\"published\" v-text=\"new Date(currentArticle.published).toDateString() + ', ' + new Date(currentArticle.published).toTimeString().substring(0,5)\"></div>\n </div>\n </div>\n</template>\n\n<script>\nimport Utils from \"@/Utils\";\n\n/**\n * In order to use this widget you need to configure the `backend.http.poll` backend to\n * poll a list of RSS sources.\n */\nexport default {\n name: \"RssNews\",\n mixins: [Utils],\n props: {\n // Maximum number of items to be shown in a cycle.\n limit: {\n type: Number,\n required: false,\n default: 25,\n },\n\n // How long an entry should be displayed before moving to the next one.\n refreshSeconds: {\n type: Number,\n required: false,\n default: 15,\n },\n },\n\n data: function() {\n return {\n articles: [],\n queue: [],\n currentArticle: undefined,\n }\n },\n\n methods: {\n refresh: async function() {\n if (!this.queue.length) {\n this.articles = await this.request('rss.get_latest_entries', {\n limit: this.limit\n })\n\n this.queue = [...this.articles].reverse()\n }\n\n if (!this.queue.length)\n return\n\n this.currentArticle = this.queue.pop()\n },\n },\n\n mounted: function() {\n this.refresh()\n setInterval(this.refresh, parseInt((this.refreshSeconds*1000).toFixed(0)))\n },\n}\n</script>\n\n<style lang=\"scss\" scoped>\n.rss-news {\n width: 100%;\n height: 100%;\n display: flex;\n align-items: center;\n letter-spacing: .025em;\n\n .article {\n width: 90%;\n padding: 0 2em;\n\n .source {\n font-size: 1.7em;\n font-weight: bold;\n margin-bottom: .5em;\n }\n\n .title {\n font-size: 1.8em;\n font-weight: normal;\n margin-bottom: .5em;\n }\n\n .published {\n text-align: right;\n font-size: 1.1em;\n }\n }\n}\n</style>\n","import { render } from \"./Index.vue?vue&type=template&id=52a823f4&scoped=true\"\nimport script from \"./Index.vue?vue&type=script&lang=js\"\nexport * from \"./Index.vue?vue&type=script&lang=js\"\n\nimport \"./Index.vue?vue&type=style&index=0&id=52a823f4&lang=scss&scoped=true\"\nscript.render = render\nscript.__scopeId = \"data-v-52a823f4\"\n\nexport default script"],"sourceRoot":""}
\ No newline at end of file
<template>
<div class="rss-news">
<div class="article" v-if="currentArticle">
<div class="source" v-text="currentArticle.source"></div>
<div class="source" v-text="currentArticle.feed_title || currentArticle.feed_url"></div>
<div class="title" v-text="currentArticle.title"></div>
<div class="published" v-text="new Date(currentArticle.published).toDateString() + ', ' + new Date(currentArticle.published).toTimeString().substring(0,5)"></div>
</div>
......@@ -12,21 +12,13 @@
import Utils from "@/Utils";
/**
* In order to use this widget you need to configure the `backend.http.poll` backend to
* poll a list of RSS sources.
* In order to use this widget you need to configure the `rss` plugin
* with a list of subscriptions.
*/
export default {
name: "RssNews",
mixins: [Utils],
props: {
// Database engine string pointing to the source of the RSS feeds.
// If not otherwise configured, you should set this to
// `sqlite:///<HOME>/.local/share/platypush/feeds/rss.db`.
db: {
type: String,
required: true,
},
// Maximum number of items to be shown in a cycle.
limit: {
type: Number,
......@@ -53,16 +45,11 @@ export default {
methods: {
refresh: async function() {
if (!this.queue.length) {
this.articles = await this.request('db.select', {
engine: this.db,
query: `
select s.title as source, e.title, e.summary,
strftime('%Y-%m-%dT%H:%M:%fZ', e.published) as published
from FeedEntry e join FeedSource s
on e.source_id = s.id order by e.published desc limit ${this.limit}`,
this.articles = await this.request('rss.get_latest_entries', {
limit: this.limit
})
this.queue = [...this.articles]
this.queue = [...this.articles].reverse()
}
if (!this.queue.length)
......
from datetime import datetime
from typing import Optional
from platypush.message.event import Event
class NewFeedEntryEvent(Event):
"""
Event triggered when a new (RSS/Atom) feed entry is received.
"""
def __init__(
self, *, feed_url: str, url: str, title: Optional[str] = None, id: Optional[str] = None,
feed_title: Optional[str] = None, published: Optional[datetime] = None, summary: Optional[str] = None,
content: Optional[str] = None, **kwargs
):
super().__init__(
feed_url=feed_url, url=url, title=title, id=id, feed_title=feed_title,
published=published, summary=summary, content=content, **kwargs
)
......@@ -105,7 +105,6 @@ class DbPlugin(Plugin):
return table, engine
@action
def select(self, query=None, table=None, filter=None, engine=None, *args, **kwargs):
"""
......@@ -179,10 +178,9 @@ class DbPlugin(Plugin):
with engine.connect() as connection:
result = connection.execute(query)
columns = result.keys()
rows = [
{ columns[i]: row[i] for i in range(0, len(columns)) }
{col: row[i] for i, col in enumerate(list(columns))}
for row in result.fetchall()
]
......
import datetime
import queue
import threading
import time
from typing import Optional, Collection
import dateutil.parser
import requests
from platypush.context import get_bus, get_plugin
from platypush.message.event.rss import NewFeedEntryEvent
from platypush.plugins import RunnablePlugin, action
from platypush.schemas.rss import RssFeedEntrySchema
class RssPlugin(RunnablePlugin):
"""
A plugin for parsing and subscribing to RSS feeds.
Triggers:
- :class:`platypush.message.event.rss.NewFeedEntryEvent` when a new entry is received on a subscribed feed.
Requires:
* **feedparser** (``pip install feedparser``)
"""
user_agent = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) ' + \
'Chrome/62.0.3202.94 Safari/537.36'
def __init__(
self, subscriptions: Optional[Collection[str]] = None, poll_seconds: int = 300,
user_agent: str = user_agent, **kwargs
):
"""
:param subscriptions: List of feeds to monitor for updates, as URLs.
:param poll_seconds: How often we should check for updates (default: 300 seconds).
:param user_agent: Custom user agent to use for the requests.
"""
super().__init__(**kwargs)
self.subscriptions = subscriptions or []
self.poll_seconds = poll_seconds
self.user_agent = user_agent
self._latest_timestamps = self._get_latest_timestamps()
self._feed_worker_queues = [queue.Queue()] * 5
self._feed_response_queue = queue.Queue()
self._feed_workers = []
self._latest_entries = []
@staticmethod
def _get_feed_latest_timestamp_varname(url: str) -> str:
return f'LATEST_FEED_TIMESTAMP[{url}]'
def _get_latest_timestamps(self) -> dict:
variable = get_plugin('variable')
return {
url: dateutil.parser.isoparse(
variable.get(
self._get_feed_latest_timestamp_varname(url)
).output.get(self._get_feed_latest_timestamp_varname(url))
)
for url in self.subscriptions
}
def _update_latest_timestamps(self) -> None:
variable = get_plugin('variable')
variable.set(**{
self._get_feed_latest_timestamp_varname(url): latest_timestamp
for url, latest_timestamp in self._latest_timestamps.items()
})
@staticmethod
def _parse_content(entry) -> Optional[str]:
content = getattr(entry, 'content', None)
if not content:
return
if isinstance(content, list):
return content[0]['value']
return content
@action
def parse_feed(self, url: str):
"""
Parse a feed URL.
:param url: Feed URL.
:return: .. schema:: rss.RssFeedEntrySchema(many=True)
"""
import feedparser
feed = feedparser.parse(requests.get(url, headers={'User-Agent': self.user_agent}).text)
return RssFeedEntrySchema().dump(
sorted([
{
'feed_url': url,
'feed_title': getattr(feed.feed, 'title', None),
'id': entry.id,
'url': entry.link,
'published': datetime.datetime.fromtimestamp(time.mktime(entry.published_parsed)),
'title': entry.title,
'summary': getattr(entry, 'summary', None),
'content': self._parse_content(entry),
}
for entry in feed.entries
if getattr(entry, 'published_parsed', None)
], key=lambda e: e['published']),
many=True
)
@action
def get_latest_entries(self, limit: int = 20):
"""
Get the latest entries from the subscribed feeds, sorted by descending published date.
:param limit: Maximum number of entries to return (default: 20).
:return: .. schema:: rss.RssFeedEntrySchema(many=True)
"""
return sorted(self._latest_entries, key=lambda e: e['published'], reverse=True)[:limit]
def _feed_worker(self, q: queue.Queue):
while not self.should_stop():
try:
url = q.get(block=True, timeout=1)
except queue.Empty:
continue
try:
self._feed_response_queue.put({
'url': url,
'content': self.parse_feed(url).output,
})
except Exception as e:
self._feed_response_queue.put({
'url': url,
'error': e,
})
self._feed_response_queue.put(None)
def main(self):
self._feed_workers = [
threading.Thread(target=self._feed_worker, args=(q,))
for q in self._feed_worker_queues
]
for worker in self._feed_workers:
worker.start()
self.logger.info(f'Initialized RSS plugin with {len(self.subscriptions)} subscriptions')
while not self.should_stop():
responses = {}
for i, url in enumerate(self.subscriptions):
worker_queue = self._feed_worker_queues[i % len(self._feed_worker_queues)]
worker_queue.put(url)
time_start = time.time()
timeout = 60
max_time = time_start + timeout
new_entries = []
while (
not self.should_stop() and
len(responses) < len(self.subscriptions) and
time.time() - time_start <= timeout
):
try:
response = self._feed_response_queue.get(block=True, timeout=max_time-time_start)
except queue.Empty:
self.logger.warning('RSS parse timeout')
break
if not response:
continue
url = response['url']
error = response.get('error')
if error:
self.logger.error(f'Could not parse feed {url}: {error}')
responses[url] = error
else:
responses[url] = response['content']
responses = {k: v for k, v in responses.items() if not isinstance(v, Exception)}
for url, response in responses.items():
latest_timestamp = self._latest_timestamps.get(url)
new_entries += response
for entry in response:
published = datetime.datetime.fromisoformat(entry['published'])
if not latest_timestamp or published > latest_timestamp:
latest_timestamp = published
get_bus().post(NewFeedEntryEvent(**entry))
self._latest_timestamps[url] = latest_timestamp
self._update_latest_timestamps()
self._latest_entries = new_entries
time.sleep(self.poll_seconds)
def stop(self):
super().stop()
for worker in self._feed_workers:
worker.join(timeout=60)
self.logger.info('RSS integration stopped')
# vim:sw=4:ts=4:et:
manifest:
events:
platypush.message.event.rss.NewFeedEntryEvent: when a new entry is received on a subscribed feed.
install:
pip:
- feedparser
package: platypush.plugins.rss
type: plugin
from marshmallow import fields
from marshmallow.schema import Schema
from platypush.schemas import DateTime
class RssFeedEntrySchema(Schema):
feed_title = fields.String(metadata=dict(description='Feed title'))
feed_url = fields.URL(
required=True,
metadata=dict(
description='URL of the feed',
example='https://some-website/rss',
)
)
id = fields.String(
required=True,
metadata=dict(
description='Feed entry ID',
example='1234',
)
)
url = fields.URL(
required=True,
metadata=dict(
description='URL of the feed entry',
example='https://some-website/articles/1234',
)
)
published = DateTime(
required=True,
metadata=dict(description='Entry published time')
)
title = fields.String(metadata=dict(description='Feed entry title'))
summary = fields.String(metadata=dict(description='Feed entry summary'))
content = fields.String(metadata=dict(description='Feed entry content'))
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment