~krathalan/wtwitch-rust

da9a5431f0103660e01ac3895081aef57956cd7c — Hunter Peavey 3 years ago
Initial commit
5 files changed, 177 insertions(+), 0 deletions(-)

A .gitignore
A Cargo.lock
A Cargo.toml
A TODO
A src/main.rs
A  => .gitignore +1 -0
@@ 1,1 @@
/target

A  => Cargo.lock +5 -0
@@ 1,5 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "wtwitch-rust"
version = "0.1.0"

A  => Cargo.toml +9 -0
@@ 1,9 @@
[package]
name = "wtwitch-rust"
version = "0.1.0"
authors = ["Hunter Peavey <srht@krathalan.net>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]

A  => TODO +22 -0
@@ 1,22 @@
Commands:
  - watch
  - sub
  - unsub
  - check
  - e: search games/categories
  - n: search streamers/channels
  - game
  - l
  - player
  - quality
  - block
    - implemented: adding to blocklist, printing to blocklist
    - unimplemented: remove from blocklist
  - version
  - help

Misc:
  - colors
  - debug/logging facilities
  - translations
  - environment.sh?
\ No newline at end of file

A  => src/main.rs +140 -0
@@ 1,140 @@
use std::env;
use std::fs;
use std::io::prelude::*;
use std::path::Path;

// Twitch API information
// This API key is intended to be used for wtwitch only -- do not use this API
// key for any other reason!
const TWITCH_API_URL: &str = "https://api.twitch.tv/helix";
const TWITCH_ID_URL: &str = "https://id.twitch.tv/oauth2/token";
const TWITCH_API_KEY: &str = "cotxsalhlctv8z572f7fant4b0sc3u";
const TWITCH_API_SECRET: &str = "gaofxvult280l3sbz8n6btvk5fdswp";

// Number of characters always present in print_game_info() output
// 5 spaces, 1 colon, 2 parantheses
const TITLE_CHARACTERS: &str = "8";

fn main() {
    // Get args
    let cli_args: Vec<String> = env::args().collect();

    if cli_args.len() < 2 {
        println!("Please specify a command, e.g. wtwitch c");
        return;
    }

    // Set vars
    let home_path = env::var("HOME").unwrap();

    // Config
    let config_path = format!("{}/wtwitch", load_config_path(&home_path));
    let config_file = format!("{}/config.json", config_path);
    let blocklist_file = format!("{}/blocklist", config_path);

    // Cache
    let cache_path = format!("{}/wtwitch", load_cache_path(&home_path));
    let cache_offline_text_file = format!("{}/stream_offline.txt", cache_path);
    let cache_online_text_file = format!("{}/stream_online.txt", cache_path);
    let cache_game_id_file = format!("{}/gameids.json", cache_path);
    let last_seen_path = format!("{}/lastSeen", cache_path);

    // Create missing dirs, files
    if let Err(why) = fs::create_dir_all(&config_path) {
        panic!("Couldn't create config directory {}: {}", config_path, why);
    }
    // Creating last_seen_path also creates cache_path
    if let Err(why) = fs::create_dir_all(&last_seen_path) {
        panic!("Couldn't create config directory {}: {}", cache_path, why);
    }

    // Load data
    let blocklist = load_blocklist(&blocklist_file);

    // Parse user command
    let command = &cli_args[1].chars().next().unwrap();
    match command {
        'b' => block(cli_args, blocklist_file, blocklist),
        _ => help()
    }
}

// Loading, checking

fn load_config_path(path_to_home: &str) -> String {
    if let Ok(xdg_config_home) = env::var("XDG_CONFIG_HOME") {
        xdg_config_home
    } else {
        format!("{}/.config", path_to_home)
    }
}

fn load_cache_path(path_to_home: &str) -> String {
    if let Ok(xdg_cache_home) = env::var("XDG_CACHE_HOME") {
        xdg_cache_home
    } else {
        format!("{}/.cache", path_to_home)
    }
}

fn load_blocklist(path_to_blocklist: &str) -> String {
    let path = Path::new(&path_to_blocklist);

    // Create blocklist file if not exists
    if !path.exists() {
        if let Err(why) = fs::File::create(&path) {
            panic!("Couldn't create blocklist file {}: {}", path_to_blocklist, why);
        }
    }

    // Open the path in read-only mode
    let mut file = match fs::File::open(&path) {
        Err(why) => panic!("couldn't open {}: {}", path.display(), why),
        Ok(file) => file,
    };

    // Read the file contents into a string
    let mut s = String::new();
    match file.read_to_string(&mut s) {
        Err(why) => panic!("couldn't read {}: {}", path.display(), why),
        Ok(_) => s
    }
}

// Commands

fn block(args: Vec<String>, blocklist_file: String, blocklist: String) {
    // args
    // 0: path to binary
    // 1: command ([b]lock)
    // 2.. : all streamers to block

    if args.len() == 2 {
        // Print blocklist 
        println!("Blocklist:\n{}", blocklist);
    } else {
        let mut counter = 2;
        let mut file_to_write = fs::OpenOptions::new()
            .append(true)
            .open(&blocklist_file)
            .unwrap();
        while counter < args.len() {
            // Add args[counter] to blocklist file if not present,
            // Delete args[counter] from blocklist if present
            if !blocklist.contains(&args[counter]) {
                if let Err(why) = writeln!(file_to_write, "{}", args[counter]) {
                    eprintln!("Couldn't write to file {}: {}", blocklist_file, why);
                }

                println!("Added {} to blocklist", args[counter]);
            } else {
                println!("Skipping {}, already in blocklist", args[counter]);
            }
            counter += 1;
        }
    }
}

fn help() {
    unimplemented!("TODO: Implement print help")
}
\ No newline at end of file