#include "RSS.hpp" #include #include #include #include #include #include #define DT_TEMPLATES_LEN 2 const char *DT_TEMPLATES[] = { "%Y-%m-%dT%H:%M:%SZ", "%a, %d %b %Y %H:%M:%S GMT", }; std::tm ParseTime(std::string original) { std::tm parsed{}; for (auto i = 0; i < DT_TEMPLATES_LEN; i++) { strptime(original.c_str(), DT_TEMPLATES[i], &parsed); if (parsed.tm_year != 0) break; } return parsed; } RSS::RSS(std::string url) { URL = url; channelInfo = ChannelInfo(url); parse(request()); } size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* userp) { size_t totalSize = size * nmemb; userp->append((char*)contents, totalSize); return totalSize; } std::string RSS::request() { std::unique_ptr curl ( curl_easy_init(), &curl_easy_cleanup); if (!curl) { throw std::runtime_error("Failed to initialize CURL"); } std::string data; curl_easy_setopt(curl.get(), CURLOPT_URL, URL.c_str()); curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, WriteCallback); curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &data); curl_easy_setopt(curl.get(), CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl.get(), CURLOPT_CONNECTTIMEOUT, 2L); try { auto res = curl_easy_perform(curl.get()); if (res != CURLE_OK) { // throw std::runtime_error(std::string("CURL request failed: ") + curl_easy_strerror(res)); return ""; } return data; } catch (const std::exception& e) { return ""; } } void RSS::parse(std::string contents) { // std::cout << "starting tokenization..." << std::endl; if (contents.length() == 0) return; try { auto leaf = XML_leaf(contents); if (leaf.Name != "channel") leaf = leaf.GetChild("channel"); if (leaf.Raw == "<>") throw new std::runtime_error("The feed does not contain element"); this->channelInfo.Title = leaf.GetChild("title").Value; auto items = leaf.GetChildren("item"); for (auto item : items) Entries.push_back(RSS_Entry(item)); } catch (std::exception& err) { std::cout << "Failed to parse feed '" << URL << "' :" << err.what() << std::endl; return; } } void RSS::print() { channelInfo.print(); int i = 0; for (auto entry : Entries) { if (i > 3) break; entry.print(); ++i; } } void RSS::print_latest(int minutesSpan) { std::time_t now = std::time(nullptr); std::tm* gmt_time = std::gmtime(&now); gmt_time->tm_min -= minutesSpan; auto ticks = std::mktime(gmt_time); // std::cout << "starting time: " << std::put_time(gmt_time, "%Y-%m-%d %H:%M:%S") << std::endl; for (auto entry : Entries) { if (entry.pubDate.tm_year == 0 || std::mktime(&entry.pubDate) < ticks) break; entry.print(); } } RSS_Entry::RSS_Entry(XML_leaf node) { Title = node.GetChild("title").GetValue(); URL = node.GetChild("link").GetValue(); Contents = node.GetChild("description").GetValue(); auto pubDateField = node.GetChild("pubDate"); if (pubDateField.Value.length() == 0) return; pubDate = ParseTime(pubDateField.Value); } void RSS_Entry::print() { std::cout << "\033[1m" << Title << "\033[0m" << '\n' << std::put_time(&pubDate, "%Y-%m-%d %H:%M:%S") << '\n' << Contents << '\n' << URL << '\n' << "==============================\n"; }