// This file is part of Locus . // Copyright (C) 2022-2022 Fuwn // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, version 3. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // // Copyright (C) 2022-2022 Fuwn // SPDX-License-Identifier: GPL-3.0-only use std::lazy::SyncLazy; use serde::Deserialize; use crate::{route::track_mount, success}; static REFERRALS: SyncLazy> = SyncLazy::new(|| { serde_json::from_str(include_str!("../../content/json/stock_referrals.json")) .unwrap() }); #[derive(Deserialize)] struct Referral { name: String, description: String, url: String, } #[derive(Deserialize, Debug)] struct Quote { c: f64, d: Option, dp: Option, h: f64, l: f64, o: f64, pc: f64, #[allow(unused)] t: i64, } impl Quote { pub fn try_to_string(&self) -> Result { Ok(format!( "Current Price: ${}\nDaily Change: ${} ({}%)\nHigh of the Day: ${}\nLow \ of the Day: ${}\nOpening Price: ${}\nYesterday's Closing Price: ${}", self.c, if let Some(d) = self.d { d } else { return Err(()); }, if let Some(dp) = self.dp { dp } else { return Err(()); }, self.l, self.h, self.o, self.pc, )) } } fn symbol_to_string(symbol: &str) -> String { let mut quote = None; if let Ok(response) = reqwest::blocking::get(format!( "https://finnhub.io/api/v1/quote?symbol={}&token={}", symbol, std::env::var("FINNHUB_TOKEN") .expect("could not locate FINNHUB_TOKEN environment variable") )) { if let Ok(response_content) = response.json::() { if response_content.dp.is_some() { quote = Some(response_content); } else { return "You have searched for an invalid symbol.".to_string(); } } } quote.as_ref().map_or_else( || { format!( "An API error has occurred while looking up the {} symbol... Take \ this up with Finnhub.", symbol ) }, |quote| quote.try_to_string().unwrap(), ) } pub fn module(router: &mut windmark::Router) { track_mount( router, "/stocks/referrals", "Want to start investing? Support me by using one of my referral links!", Box::new(|context| { success!( format!( "# Referrals\n\n=> /stocks Home\n=> /stocks/search?action=go \ Search!\n\nWant to start investing? Support me by using one of my \ referral links!\n\n{}", REFERRALS .iter() .map(|r| { format!("## {}\n\n{}\n\n=> {} {0}", r.name, r.description, r.url) }) .collect::>() .join("\n") ), context ) }), ); track_mount( router, "/stocks", "Explore and search the stock market using Gemini!", Box::new(|context| { success!( format!( "# Stocks\n\n=> /stocks/search Symbol Search\n=> /stocks/referrals Referrals\n\n## Popular \ Symbols\n\n### AAPL\n\n{}\n\n### TSLA\n\n{}\n\n## Credits\n\nFinancial data provided by\n\n=> https://finnhub.io/ Finnhub", symbol_to_string("AAPL"), symbol_to_string("TSLA") ), context ) }), ); track_mount( router, "/stocks/search", "Search for a specific symbol", Box::new(|context| { let mut symbol = context.url.query().unwrap_or("Symbol Search"); if symbol.is_empty() { symbol = "Symbol Search"; } let mut response = format!( "# {}\n\n=> /stocks Home\n=> /stocks/search?action=go Search!", symbol ); if symbol != "Symbol Search" { if let Some(query) = context.url.query_pairs().next() { if query.0 == "action" && query.1 == "go" { return windmark::Response::Input( "Which symbol would you like to explore?".to_string(), ); } let symbol = query.0; if symbol != "Symbol Search" { response = format!( "{}\n\nYou searched for \"{}\"!\n\n## Key Statistics\n\n{}", response, symbol, symbol_to_string(&symbol) ); } } } success!( format!( "{}\n\n## Credits\n\nFinancial data provided by\n\n=> https://finnhub.io/ Finnhub", response ), context ) }), ); }