diff options
author | Yorhel <git@yorhel.nl> | 2018-02-02 09:46:55 +0100 |
---|---|---|
committer | Yorhel <git@yorhel.nl> | 2018-02-02 09:46:55 +0100 |
commit | ad3a2228b76c1a29076295dc6fdf879c25340906 (patch) | |
tree | 0afcf66120eb99ac2e7a08a16b4b3a6c8a261a76 |
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | Main.elm | 98 | ||||
-rw-r--r-- | elm-package.json | 16 | ||||
-rwxr-xr-x | gen.pl | 62 | ||||
-rw-r--r-- | index.html | 23 |
5 files changed, 201 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3161d4e --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +elm-stuff +intel.js diff --git a/Main.elm b/Main.elm new file mode 100644 index 0000000..961c13a --- /dev/null +++ b/Main.elm @@ -0,0 +1,98 @@ +import Html exposing (..) +import Html.Attributes exposing (..) +import Http +import Task +import Date +import Time exposing (..) +import Json.Decode as Decode + +main = Html.program + { init = ( + { lastFetch = 0 + , state = Fetching + , feed = [] + }, fetch) + , view = view + , update = update + , subscriptions = \_ -> Time.every minute Tick + } + + +type alias Article = + { title : String + , feed : String + , ts : Time + , url : String + } + +decodeArticle : Decode.Decoder Article +decodeArticle = Decode.map4 Article + (Decode.field "title" Decode.string) + (Decode.field "feed" Decode.string) + (Decode.field "ts" Decode.float) + (Decode.field "url" Decode.string) + + +type State + = Start + | Current + | Fetching + | FetchError + +type alias Model = + { lastFetch : Time + , state : State + , feed : List Article + } + +type Msg + = Tick Time + | Data (Result Http.Error (List Article)) + | SetTime Time + + +fetch : Cmd Msg +fetch = Http.send Data (Http.get "feeds.json" (Decode.list decodeArticle)) + + +update : Msg -> Model -> (Model, Cmd Msg) +update msg model = case msg of + Tick _ -> ({ model | state = Fetching }, fetch) + Data (Err _) -> ({ model | state = FetchError }, Cmd.none) + SetTime t -> ({ model | lastFetch = t }, Cmd.none) + Data (Ok r) -> + ( { model | state = Current, feed = r } + , Task.perform SetTime now + ) + + +-- Surely there must be an easier way to do this +fmtts : Time -> String +fmtts t = let d = Date.fromTime t in + toString (Date.day d) + ++ " " + ++ toString (Date.month d) + ++ " " + ++ toString (Date.year d) + ++ " " + ++ String.padLeft 2 '0' (toString (Date.hour d)) + ++ ":" + ++ String.padLeft 2 '0' (toString (Date.minute d)) + + +view : Model -> Html Msg +view model = + let + state = em [ class (toString model.state) ] + [ if model.lastFetch == 0 + then text "Loading feeds..." + else text (fmtts model.lastFetch) ] + item n = article [] + [ a [href n.url] [ text n.title ] + , i [] + [ text (fmtts (n.ts*1000)) + , text " - " + , text n.feed + ] + ] + in div [] (state :: List.map item model.feed) diff --git a/elm-package.json b/elm-package.json new file mode 100644 index 0000000..04c32fe --- /dev/null +++ b/elm-package.json @@ -0,0 +1,16 @@ +{ + "version": "1.0.0", + "summary": "Blicky.net feed reader", + "repository": "https://g.blicky.net/bintel.git", + "license": "MIT", + "source-directories": [ + "." + ], + "exposed-modules": [], + "dependencies": { + "elm-lang/core": "5.1.1 <= v < 6.0.0", + "elm-lang/html": "2.0.0 <= v < 3.0.0", + "elm-lang/http": "1.0.0 <= v < 2.0.0" + }, + "elm-version": "0.18.0 <= v < 0.19.0" +} @@ -0,0 +1,62 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +my @feeds = ( + [ 'Security.nl', 'https://www.security.nl/rss/headlines.xml' ], + [ 'r/netsec', 'https://www.reddit.com/r/netsec/.rss' ], + [ 'Threatpost', 'https://threatpost.com/feed' ], + [ 'BleepingComputer', 'https://www.bleepingcomputer.com/feed/' ], + #[ 'AusCERT', 'https://www.auscert.org.au/rss.html' ], + [ 'Naked Security', 'https://nakedsecurity.sophos.com/feed/' ], + [ 'NCSC', 'https://www.ncsc.nl/rss/beveiligingsadviezen' ], +); + +my $fn = '/var/www/www/intel/feeds.json'; + + +use POSIX 'strftime'; +use LWP::UserAgent; +use XML::FeedPP; +use JSON::XS; + +sub unescape { local $_ = shift; s/&(amp|quot|#(\d+));/$2 ? chr $2 : +{qw{amp & quot "}}->{$1}/eg; $_ } + + +my @items; + +for my $feed (@feeds) { + #print "Fetching $feed->[0]\n"; + my $ua = LWP::UserAgent->new; + $ua->agent("Yorhel's RSS reader"); + $ua->timeout(30); + my $res = $ua->get($feed->[1]); + #print "got\n"; + + if($res->is_success) { + my $X = eval { XML::FeedPP->new(scalar $res->decoded_content()); }; + if(!$X) { + warn "Parsing $feed->[0]: $@\n"; + next; + } + for my $i ($X->get_item()) { + warn "No date/time in feed '$feed->[0]'\n" if !$i->pubDate(); + push @items, { + feed => $feed->[0], + title => scalar unescape($i->title()), + url => scalar $i->link(), + ts => scalar XML::FeedPP::Util::get_epoch($i->pubDate()), # XXX: Use of internal function + }; + } + } else { + warn "Unable to fetch '$feed->[1]': ".$res->status_line()."\n"; + } +} + +@items = sort { $b->{ts} <=> $a->{ts} } @items; + +open my $F, '>', "$fn~" or die $!; +print $F encode_json \@items; +close $F; +rename "$fn~", $fn or die $!; diff --git a/index.html b/index.html new file mode 100644 index 0000000..f6e18a6 --- /dev/null +++ b/index.html @@ -0,0 +1,23 @@ +<html> + <head> + <title>Security Feeds</title> + <style type="text/css"> + body { background: #1F1F1F; color: #fff; font-family: "Dejavu Sans", "Arial"; overflow: hidden } + h1 { font-size: 60px; margin: 0 0 30px 0 } + article { width: 90%; margin: 10px 3% } + a { display: block; font-size: 30px; color: #fff; text-decoration: none; font-weight: bold } + i { color: #999; font-style: normal; font-size: 14px } + em { position: absolute; top: 5px; right: 5px; font-size: 12px; color: #999; font-style: normal } + em.Fetching { color: #ffff00 } + em.FetchError { color: #f00 } + </style> + </head> +<body> +<h1>Security Feeds</h1> +<main id="main">Loading Javascript...</main> +<script src="intel.js" type="application/javascript"></script> +<script type="application/javascript"> +Elm.Main.embed(document.getElementById('main')); +</script> +</body> +</html> |