Skip to content

Instantly share code, notes, and snippets.

@thehackersbrain
Created February 14, 2023 18:01
Show Gist options
  • Select an option

  • Save thehackersbrain/13b9fb9646f61554d2fa142f837ec633 to your computer and use it in GitHub Desktop.

Select an option

Save thehackersbrain/13b9fb9646f61554d2fa142f837ec633 to your computer and use it in GitHub Desktop.
A rust program to list upcoming events from ctftime.org
[package]
name = "ctftime"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
reqwest = {version = "0.11", features = ["blocking"]}
scraper = "0.12.0"
tabled = "0.9.0"
tokio = { version = "1", features = ["full"] }
select = "0.6"
colored = "2.0.0"
prettytable-rs = "0.8.0"
use scraper::{Html, Selector};
use std::error::Error;
use reqwest::header::{HeaderMap, USER_AGENT};
use colored::*;
use prettytable::{Table, row, cell};
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let url = "https://ctftime.org/event/list/upcoming";
let headers = create_headers();
let data = extract_data(url, headers).await?;
print_data(data);
Ok(())
}
async fn extract_data(url: &str, headers: HeaderMap) -> Result<Vec<Vec<String>>, Box<dyn Error>> {
let response = reqwest::Client::new()
.get(url)
.headers(headers)
.send()
.await?
.text()
.await?;
let fragment = Html::parse_document(&response);
let events_table = fragment.select(&Selector::parse("#content > div > div > div.col-md-12 > div > div > div > div > div.panel-body > table").unwrap()).next();
let mut names = Vec::new();
let mut dates = Vec::new();
let mut locations = Vec::new();
if let Some(table) = events_table {
for row in table.select(&Selector::parse("tr").unwrap()).skip(1) {
let columns = row.select(&Selector::parse("td").unwrap()).collect::<Vec<_>>();
let name = match columns[0].select(&Selector::parse("a").unwrap()).next() {
Some(name_element) => name_element.text().collect(),
None => "".to_string(), // handle the None case
};
let date = match columns[1].text().next() {
Some(date_text) => date_text.chars().collect(),
None => "".to_string(), // handle the None case
};
let location = match columns[2].text().next() {
Some(location_text) => location_text.chars().collect(),
None => "".to_string(), // handle the None case
};
names.push(name);
dates.push(date);
locations.push(location);
}
}
let data = vec![names, dates, locations];
Ok(data)
}
fn print_data(data: Vec<Vec<String>>) {
let mut table = Table::new();
table.add_row(row![b -> "Name", b -> "Date", b -> "Location"]);
for i in 0..data[0].len() {
table.add_row(row![data[0][i].cyan(), data[1][i].cyan(), data[2][i].cyan()]);
}
table.printstd();
}
fn create_headers() -> HeaderMap {
let mut headers = HeaderMap::new();
headers.insert(USER_AGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3".parse().unwrap());
headers
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment