raccoon/main.go

251 lines
6.8 KiB
Go

package main
import (
"bufio"
"encoding/json"
"fmt"
"github.com/grokify/html-strip-tags-go"
"github.com/mmcdole/gofeed"
"github.com/writeas/go-strip-markdown"
"golang.org/x/net/proxy"
"io/ioutil"
"log"
"net/http"
"net/url"
"os"
"path"
"strconv"
"strings"
"time"
)
func makeTorifiedClient() *http.Client {
torDialer, err := proxy.SOCKS5("tcp", "127.0.0.1:9050", nil, proxy.Direct)
if err != nil {
log.Fatalf("Could not connect to Tor Proxy: %v", err)
}
transportConfig := &http.Transport{
Dial: torDialer.Dial,
}
client := new(http.Client)
client.Transport = transportConfig
client.CheckRedirect = func(r *http.Request, via []*http.Request) error {
r.URL.Opaque = r.URL.Path
return nil
}
return client
}
func fetch(url string, cachepath string) {
fmt.Printf("Fetching [%v]\n", url)
fp := gofeed.NewParser()
fp.Client = makeTorifiedClient()
feed, err := fp.ParseURL(url)
fmt.Printf("Feed %v %v\n", feed, err)
cache, _ := json.Marshal(feed)
ioutil.WriteFile(cachepath, cache, 0644)
}
func report() map[string]gofeed.Feed {
feedMap := make(map[string]gofeed.Feed)
items, _ := ioutil.ReadDir(".")
for _, item := range items {
if item.IsDir() {
//subitems, _ := ioutil.ReadDir(item.Name())
//for _, subitem := range subitems {
cachepath := path.Join(".", item.Name(), "latest")
var feed gofeed.Feed
data, err := ioutil.ReadFile(cachepath)
if err == nil {
json.Unmarshal(data, &feed)
// fmt.Printf("Feed %v\n", feed)
}
feedMap[item.Name()] = feed
//}
}
}
return feedMap
}
func download(url string, cachepath string) {
fmt.Printf("Fetching [%v]\n", url)
client := makeTorifiedClient()
req, err := http.NewRequest("GET", url, nil)
if err != nil {
log.Fatalln(err)
}
// Set User Agent to be Tor Browser (it likely won't be hard for a site to determine these requests are partially automated, but a little more obfuscation is never a bad idea)
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 6.1; rv:60.0) Gecko/20100101 Firefox/60.0")
resp, err := client.Do(req)
if err == nil {
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
ioutil.WriteFile(cachepath, body, 0644)
}
}
func update() {
items, _ := ioutil.ReadDir(".")
for _, item := range items {
if item.IsDir() {
subitems, _ := ioutil.ReadDir(item.Name())
for _, subitem := range subitems {
if subitem.Name() == "feedinfo" {
// handle file there
filepath := path.Join(".", item.Name(), subitem.Name())
fmt.Println("Processing: " + filepath)
rawfeedinfo, _ := ioutil.ReadFile(filepath)
feedinfo := strings.Split(strings.TrimSpace(string(rawfeedinfo)), " ")
cachepath := path.Join(".", item.Name(), "latest")
info, err := os.Stat(cachepath)
if err == nil {
duration := time.Since(info.ModTime())
cron, _ := strconv.Atoi(feedinfo[1])
// If it has been greater than <cron> minutes since the last fetch, we fetch
if (time.Duration(cron) * time.Minute).Minutes() < duration.Minutes() {
fetch(feedinfo[0], cachepath)
} else {
}
} else {
// First time
fetch(feedinfo[0], cachepath)
}
} else if subitem.Name() == "images" {
filepath := path.Join(".", item.Name(), subitem.Name())
file, err := os.Open(filepath)
if err != nil {
log.Fatal(err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
image := scanner.Text()
imageinfo := strings.Split(image, " ")
uri, _ := url.Parse(imageinfo[0])
cachepath := path.Join(".", item.Name(), "latest-"+path.Base(uri.Path))
info, err := os.Stat(cachepath)
if err == nil {
duration := time.Since(info.ModTime())
cron, _ := strconv.Atoi(imageinfo[1])
// If it has been greater than <cron> minutes since the last fetch, we fetch
if (time.Duration(cron) * time.Minute).Minutes() < duration.Minutes() {
download(imageinfo[0], cachepath)
} else {
}
} else {
// First time
download(imageinfo[0], cachepath)
}
}
}
}
}
}
}
func processItems(format []string, items []*gofeed.Item, dateCheck time.Duration) {
num := 0
for _, item := range items {
if dateCheck == 0 {
processItem(format, *item)
num = 100
} else {
if item.PublishedParsed != nil {
if time.Since(*item.PublishedParsed) < dateCheck {
processItem(format, *item)
num++
}
} else if item.UpdatedParsed != nil {
if time.Since(*item.UpdatedParsed) < dateCheck {
processItem(format, *item)
num++
}
}
}
}
if num == 0 {
fmt.Printf("* Nothing new this %v From %v\n", format[1], format[0])
}
}
// stripString removes html, then potential markdown characters, and then some additional potential markdown
func stripString(input string) string {
return strings.Replace(stripmd.Strip(strip.StripTags(input)), "`", "", -1)
}
func processItem(format []string, item gofeed.Item) {
fmt.Printf("* ")
for i := 2; i < len(format); i++ {
switch format[i] {
case "Title":
fmt.Printf("%v ", stripString(item.Title))
case "Link":
fmt.Printf("[%v](%v)", stripString(item.Link), stripString(item.Link))
case "Description":
fmt.Printf("\n * %v <hr/>", strings.Replace(stripString(item.Description), "\n", "", -1))
}
}
fmt.Printf("\n")
}
func main() {
if len(os.Args) >= 2 {
cmd := os.Args[1]
switch cmd {
case "update":
update()
case "report":
if len(os.Args) == 3 {
feeds := report()
file, err := os.Open(os.Args[2])
if err != nil {
log.Fatal(err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
reportLine := strings.Split(line, " ")
if strings.HasPrefix(reportLine[0], "%") {
// Ignore, this is a comment
} else if strings.HasPrefix(reportLine[0], "#") || strings.HasPrefix(reportLine[0], "<") {
fmt.Printf("%v\n", strings.TrimSpace(line))
} else if len(reportLine) > 2 {
_, exists := feeds[reportLine[0]]
if exists {
if reportLine[1] == "ALL" {
processItems(reportLine, feeds[reportLine[0]].Items, 0)
} else if reportLine[1] == "DAY" { // Only output entries from the last Day
processItems(reportLine, feeds[reportLine[0]].Items, time.Hour*24)
} else if reportLine[1] == "WEEK" { // Only output entries from the last Week
processItems(reportLine, feeds[reportLine[0]].Items, time.Hour*24*7)
} else {
index, _ := strconv.Atoi(reportLine[1])
processItem(reportLine, *feeds[reportLine[0]].Items[index])
}
} else {
log.Fatalf("Report Template Contains Non-Existent Feed %v\n", reportLine[0])
}
} else {
fmt.Printf("\n")
}
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
}
}
}
os.Exit(0)
}