mirror of https://github.com/MedzikUser/imgurs
check CHANGELOG.md for a list of variables
This commit is contained in:
parent
b83847ff20
commit
b1bfe52a4c
13
CHANGELOG.md
13
CHANGELOG.md
|
@ -8,6 +8,19 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
<!-- next-header -->
|
<!-- next-header -->
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
### CLI
|
||||||
|
- completions: changed type from String to Shell
|
||||||
|
- removed `&` from `cli.commands` (line 54 in [parse.rs](./src/cli/parse.rs))
|
||||||
|
|
||||||
|
### Library
|
||||||
|
- removed `.map_err(anyhow::Error::new)` when function returns error
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- commands in the code
|
||||||
|
- api functions to `impl` in `ImgurClient`
|
||||||
|
|
||||||
|
### Breaking Changes
|
||||||
|
- lib: moved everything to the main package with api submodules (before `imgurs::api::ImgurClient`, after `imgurs::api::ImgurClient`)
|
||||||
|
|
||||||
## [0.6.0] - 2022-03-14
|
## [0.6.0] - 2022-03-14
|
||||||
### CLI
|
### CLI
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
macro_rules! api_url (
|
||||||
|
($path: expr) => (
|
||||||
|
format!("{}{}", "https://api.imgur.com/3/", $path)
|
||||||
|
);
|
||||||
|
);
|
||||||
|
|
||||||
|
use std::{fmt, fs, io, path::Path};
|
||||||
|
|
||||||
|
use anyhow::Error;
|
||||||
|
pub(crate) use api_url;
|
||||||
|
use reqwest::Client;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub struct ImgurClient {
|
||||||
|
pub client_id: String,
|
||||||
|
pub client: Client,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for ImgurClient {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "ImgurClient - client_id: {}", self.client_id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ImgurClient {
|
||||||
|
pub fn new(client_id: String) -> Self {
|
||||||
|
let client = Client::new();
|
||||||
|
ImgurClient { client_id, client }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn upload_image(&self, path: String) -> Result<ImageInfo, Error> {
|
||||||
|
let mut image: String = path.clone();
|
||||||
|
|
||||||
|
// check if the specified file exists if not then check if it is a url
|
||||||
|
if Path::new(&path).exists() {
|
||||||
|
image = fs::read_to_string(&path)
|
||||||
|
.map_err(|err| err.to_string())
|
||||||
|
.expect("read file");
|
||||||
|
} else if !validator::validate_url(&path) {
|
||||||
|
let err = io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
format!("{path} is not url or file path"),
|
||||||
|
);
|
||||||
|
|
||||||
|
Err(anyhow::Error::from(err))?
|
||||||
|
}
|
||||||
|
|
||||||
|
upload_image(self, image).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn delete_image(&self, delete_hash: String) -> Result<(), Error> {
|
||||||
|
delete_image(self, delete_hash).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn rate_limit(&self) -> Result<RateLimitInfo, Error> {
|
||||||
|
rate_limit(self).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn image_info(&self, id: String) -> Result<ImageInfo, Error> {
|
||||||
|
get_image(self, id).await
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,28 +0,0 @@
|
||||||
use reqwest::Client;
|
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
macro_rules! api_url (
|
|
||||||
($path: expr) => (
|
|
||||||
format!("{}{}", "https://api.imgur.com/3/", $path)
|
|
||||||
);
|
|
||||||
);
|
|
||||||
|
|
||||||
pub(crate) use api_url;
|
|
||||||
|
|
||||||
pub struct ImgurClient {
|
|
||||||
pub client_id: String,
|
|
||||||
pub client: Client,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for ImgurClient {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
write!(f, "ImgurClient - client_id: {}", self.client_id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ImgurClient {
|
|
||||||
pub fn new(client_id: String) -> Self {
|
|
||||||
let client = Client::new();
|
|
||||||
ImgurClient { client_id, client }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,29 +1,35 @@
|
||||||
use super::send_api_request;
|
use std::io;
|
||||||
use crate::api::configuration::{api_url, ImgurClient};
|
|
||||||
|
|
||||||
|
use anyhow::Error;
|
||||||
use reqwest::Method;
|
use reqwest::Method;
|
||||||
use std::io::{Error, ErrorKind};
|
|
||||||
|
|
||||||
pub async fn delete_image(c: ImgurClient, delete_hash: String) -> Result<String, anyhow::Error> {
|
use super::{client::api_url, send_api_request, ImgurClient};
|
||||||
|
|
||||||
|
pub async fn delete_image(client: &ImgurClient, delete_hash: String) -> Result<(), Error> {
|
||||||
|
// get imgur api url
|
||||||
let uri = api_url!(format!("image/{delete_hash}"));
|
let uri = api_url!(format!("image/{delete_hash}"));
|
||||||
let res = send_api_request(&c, Method::DELETE, uri, None).await?;
|
|
||||||
|
|
||||||
|
// send request to imgur api
|
||||||
|
let res = send_api_request(client, Method::DELETE, uri, None).await?;
|
||||||
|
|
||||||
|
// get response http code
|
||||||
let status = res.status();
|
let status = res.status();
|
||||||
|
|
||||||
|
// check if an error has occurred
|
||||||
if status.is_client_error() || status.is_server_error() {
|
if status.is_client_error() || status.is_server_error() {
|
||||||
let mut body = res.text().await.map_err(anyhow::Error::new)?;
|
let mut body = res.text().await?;
|
||||||
|
|
||||||
if body.chars().count() > 30 {
|
if body.chars().count() > 30 {
|
||||||
body = "body is too length".to_string()
|
body = "body is too length".to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
let err = Error::new(
|
let err = io::Error::new(
|
||||||
ErrorKind::Other,
|
io::ErrorKind::Other,
|
||||||
format!("server returned non-successful status code = {status}, body = {body}"),
|
format!("server returned non-successful status code = {status}, body = {body}"),
|
||||||
);
|
);
|
||||||
|
|
||||||
Err(anyhow::Error::from(err))
|
Err(err)?
|
||||||
} else {
|
|
||||||
Ok("If the delete hash was correct the image was deleted!".to_string())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,30 @@
|
||||||
use super::send_api_request;
|
use std::io;
|
||||||
use crate::api::configuration::{api_url, ImgurClient};
|
|
||||||
use crate::api::ImageInfo;
|
|
||||||
|
|
||||||
|
use anyhow::Error;
|
||||||
use reqwest::Method;
|
use reqwest::Method;
|
||||||
use std::io::{Error, ErrorKind};
|
|
||||||
|
|
||||||
pub async fn get_image(c: ImgurClient, image: &str) -> Result<ImageInfo, anyhow::Error> {
|
use super::{client::api_url, send_api_request, ImageInfo, ImgurClient};
|
||||||
|
|
||||||
|
pub async fn get_image(client: &ImgurClient, image: String) -> Result<ImageInfo, Error> {
|
||||||
|
// get imgur api url
|
||||||
let uri = api_url!(format!("image/{image}"));
|
let uri = api_url!(format!("image/{image}"));
|
||||||
let res = send_api_request(&c, Method::GET, uri, None).await?;
|
|
||||||
|
|
||||||
|
// send request to imgur api
|
||||||
|
let res = send_api_request(client, Method::GET, uri, None).await?;
|
||||||
|
|
||||||
|
// get response http code
|
||||||
let status = res.status();
|
let status = res.status();
|
||||||
|
|
||||||
|
// check if an error has occurred
|
||||||
if status.is_client_error() || status.is_server_error() {
|
if status.is_client_error() || status.is_server_error() {
|
||||||
let err = Error::new(
|
let err = io::Error::new(
|
||||||
ErrorKind::Other,
|
io::ErrorKind::Other,
|
||||||
format!("server returned non-successful status code = {status}"),
|
format!("server returned non-successful status code = {status}"),
|
||||||
);
|
);
|
||||||
|
|
||||||
Err(anyhow::Error::from(err))
|
Err(err)?
|
||||||
} else {
|
} else {
|
||||||
let content: ImageInfo = res.json().await.map_err(anyhow::Error::new)?;
|
let content: ImageInfo = res.json().await?;
|
||||||
Ok(content)
|
Ok(content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct ImageInfo {
|
pub struct ImageInfo {
|
||||||
pub data: ImageInfoData,
|
pub data: ImageInfoData,
|
||||||
pub success: bool,
|
pub success: bool,
|
||||||
pub status: i32,
|
pub status: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct ImageInfoData {
|
pub struct ImageInfoData {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub title: Option<String>,
|
pub title: Option<String>,
|
||||||
|
|
|
@ -1,41 +1,56 @@
|
||||||
|
mod delete_image;
|
||||||
|
mod get_image;
|
||||||
mod image_type;
|
mod image_type;
|
||||||
|
mod rate_limit;
|
||||||
|
mod upload_image;
|
||||||
|
|
||||||
pub mod configuration;
|
pub mod client;
|
||||||
pub mod delete_image;
|
|
||||||
pub mod get_image;
|
|
||||||
pub mod rate_limit;
|
|
||||||
pub mod upload_image;
|
|
||||||
|
|
||||||
pub use configuration::ImgurClient;
|
pub use client::ImgurClient;
|
||||||
|
pub use delete_image::*;
|
||||||
|
pub use get_image::*;
|
||||||
pub use image_type::*;
|
pub use image_type::*;
|
||||||
|
pub use rate_limit::*;
|
||||||
|
pub use upload_image::*;
|
||||||
|
|
||||||
use reqwest::Method;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use reqwest::{Response, Method};
|
||||||
|
use anyhow::Error;
|
||||||
|
|
||||||
|
// send request to imgur api
|
||||||
pub async fn send_api_request(
|
pub async fn send_api_request(
|
||||||
config: &ImgurClient,
|
config: &ImgurClient,
|
||||||
method: Method,
|
method: Method,
|
||||||
uri: String,
|
uri: String,
|
||||||
form: Option<HashMap<&str, String>>,
|
form: Option<HashMap<&str, String>>,
|
||||||
) -> Result<reqwest::Response, anyhow::Error> {
|
) -> Result<Response, Error> {
|
||||||
|
// get request client
|
||||||
let client = &config.client;
|
let client = &config.client;
|
||||||
|
|
||||||
|
// create request buidler
|
||||||
let mut req = client.request(method, uri.as_str());
|
let mut req = client.request(method, uri.as_str());
|
||||||
|
|
||||||
const VERSION: Option<&str> = option_env!("CARGO_PKG_VERSION");
|
// get program version
|
||||||
|
let version: Option<&str> = option_env!("CARGO_PKG_VERSION");
|
||||||
|
let version = version.unwrap_or("unknown");
|
||||||
|
|
||||||
|
// add `Authorization` and `User-Agent` to request
|
||||||
req = req
|
req = req
|
||||||
.header("Authorization", format!("Client-ID {}", config.client_id))
|
.header("Authorization", format!("Client-ID {}", config.client_id))
|
||||||
.header(
|
.header(
|
||||||
"User-Agent",
|
"User-Agent",
|
||||||
format!("Imgur/{:?}", VERSION.unwrap_or("unknown")),
|
format!("Imgur/{:?}", version),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// if exists add hashmap to request
|
||||||
if form != None {
|
if form != None {
|
||||||
req = req.form(&form.unwrap())
|
req = req.form(&form.unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// build request
|
||||||
let req = req.build()?;
|
let req = req.build()?;
|
||||||
|
|
||||||
client.execute(req).await.map_err(anyhow::Error::from)
|
// send request
|
||||||
|
Ok(client.execute(req).await?)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use super::send_api_request;
|
use std::io;
|
||||||
use crate::api::configuration::{api_url, ImgurClient};
|
|
||||||
|
|
||||||
|
use anyhow::Error;
|
||||||
use reqwest::Method;
|
use reqwest::Method;
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::io::{Error, ErrorKind};
|
|
||||||
|
use super::{client::api_url, send_api_request, ImgurClient};
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct RateLimitInfo {
|
pub struct RateLimitInfo {
|
||||||
|
@ -26,22 +27,28 @@ pub struct RateLimitData {
|
||||||
pub client_remaining: i32,
|
pub client_remaining: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn rate_limit(c: ImgurClient) -> Result<RateLimitInfo, anyhow::Error> {
|
pub async fn rate_limit(client: &ImgurClient) -> Result<RateLimitInfo, Error> {
|
||||||
|
// get imgur api url
|
||||||
let uri = api_url!("credits");
|
let uri = api_url!("credits");
|
||||||
let res = send_api_request(&c, Method::GET, uri, None).await?;
|
|
||||||
|
|
||||||
|
// send request to imgur api
|
||||||
|
let res = send_api_request(client, Method::GET, uri, None).await?;
|
||||||
|
|
||||||
|
// get response http code
|
||||||
let status = res.status();
|
let status = res.status();
|
||||||
|
|
||||||
|
// check if an error has occurred
|
||||||
if status.is_client_error() || status.is_server_error() {
|
if status.is_client_error() || status.is_server_error() {
|
||||||
let body = res.text().await.map_err(anyhow::Error::new)?;
|
let body = res.text().await?;
|
||||||
let err = Error::new(
|
|
||||||
ErrorKind::Other,
|
let err = io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
format!("server returned non-successful status code = {status}, body = {body}"),
|
format!("server returned non-successful status code = {status}, body = {body}"),
|
||||||
);
|
);
|
||||||
|
|
||||||
Err(anyhow::Error::from(err))
|
Err(err)?
|
||||||
} else {
|
} else {
|
||||||
let content: RateLimitInfo = res.json().await.map_err(anyhow::Error::new)?;
|
let content = res.json::<RateLimitInfo>().await?;
|
||||||
Ok(content)
|
Ok(content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,40 +1,41 @@
|
||||||
use super::send_api_request;
|
use std::{collections::HashMap, io};
|
||||||
use crate::api::{
|
|
||||||
configuration::{api_url, ImgurClient},
|
|
||||||
ImageInfo,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
use anyhow::Error;
|
||||||
use reqwest::Method;
|
use reqwest::Method;
|
||||||
use std::{
|
|
||||||
collections::HashMap,
|
|
||||||
io::{Error, ErrorKind},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub async fn upload_image(c: ImgurClient, image: &str) -> Result<ImageInfo, anyhow::Error> {
|
use super::{client::api_url, send_api_request, ImageInfo, ImgurClient};
|
||||||
|
|
||||||
|
pub async fn upload_image(c: &ImgurClient, image: String) -> Result<ImageInfo, Error> {
|
||||||
|
// create http form (hashmap)
|
||||||
let mut form = HashMap::new();
|
let mut form = HashMap::new();
|
||||||
|
// insert image to form
|
||||||
|
form.insert("image", image);
|
||||||
|
|
||||||
form.insert("image", image.to_string());
|
// get imgur api url
|
||||||
|
|
||||||
let uri = api_url!("image");
|
let uri = api_url!("image");
|
||||||
|
|
||||||
|
// send request to imgur api
|
||||||
let res = send_api_request(&c, Method::POST, uri, Some(form)).await?;
|
let res = send_api_request(&c, Method::POST, uri, Some(form)).await?;
|
||||||
|
|
||||||
|
// get response http code
|
||||||
let status = res.status();
|
let status = res.status();
|
||||||
|
|
||||||
|
// check if an error has occurred
|
||||||
if status.is_client_error() || status.is_server_error() {
|
if status.is_client_error() || status.is_server_error() {
|
||||||
let mut body = res.text().await.map_err(anyhow::Error::new)?;
|
let mut body = res.text().await?;
|
||||||
|
|
||||||
if body.chars().count() > 30 {
|
if body.chars().count() > 200 {
|
||||||
body = "body is too length".to_string()
|
body = "server returned too long".to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
let err = Error::new(
|
let err = io::Error::new(
|
||||||
ErrorKind::Other,
|
io::ErrorKind::Other,
|
||||||
format!("server returned non-successful status code = {status}, body = {body}"),
|
format!("server returned non-successful status code = {status}, body = {body}"),
|
||||||
);
|
);
|
||||||
|
|
||||||
Err(anyhow::Error::from(err))
|
Err(err)?
|
||||||
} else {
|
} else {
|
||||||
let content: ImageInfo = res.json().await.map_err(anyhow::Error::new)?;
|
let content: ImageInfo = res.json().await?;
|
||||||
Ok(content)
|
Ok(content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,25 +2,20 @@
|
||||||
unix,
|
unix,
|
||||||
not(any(target_os = "macos", target_os = "android", target_os = "emscripten"))
|
not(any(target_os = "macos", target_os = "android", target_os = "emscripten"))
|
||||||
))]
|
))]
|
||||||
fn is_program_in_path(program: &str) -> bool {
|
// use xclip (or a similar program that is installed) because the kernel deletes the clipboard after the process ends
|
||||||
use std::{env, fs};
|
pub fn set_clipboard(content: String) {
|
||||||
|
fn is_program_in_path(program: &str) -> bool {
|
||||||
if let Ok(path) = env::var("PATH") {
|
if let Ok(path) = std::env::var("PATH") {
|
||||||
for p in path.split(':') {
|
for p in path.split(':') {
|
||||||
let p_str = format!("{}/{}", p, program);
|
let p_str = format!("{}/{}", p, program);
|
||||||
if fs::metadata(p_str).is_ok() {
|
if std::fs::metadata(p_str).is_ok() {
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
false
|
||||||
}
|
}
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(
|
|
||||||
unix,
|
|
||||||
not(any(target_os = "macos", target_os = "android", target_os = "emscripten"))
|
|
||||||
))]
|
|
||||||
pub fn set_clipboard(content: String) {
|
|
||||||
use std::{
|
use std::{
|
||||||
io::Write,
|
io::Write,
|
||||||
process::{Command, Stdio},
|
process::{Command, Stdio},
|
||||||
|
@ -38,6 +33,7 @@ pub fn set_clipboard(content: String) {
|
||||||
.stdin(Stdio::piped())
|
.stdin(Stdio::piped())
|
||||||
.spawn()
|
.spawn()
|
||||||
.expect("execute command xsel")
|
.expect("execute command xsel")
|
||||||
|
|
||||||
// xclip
|
// xclip
|
||||||
} else if is_program_in_path("xclip") {
|
} else if is_program_in_path("xclip") {
|
||||||
child = Command::new("xclip")
|
child = Command::new("xclip")
|
||||||
|
@ -47,41 +43,43 @@ pub fn set_clipboard(content: String) {
|
||||||
.stdin(Stdio::piped())
|
.stdin(Stdio::piped())
|
||||||
.spawn()
|
.spawn()
|
||||||
.expect("execute command xclip")
|
.expect("execute command xclip")
|
||||||
|
|
||||||
// termux
|
// termux
|
||||||
} else if is_program_in_path("termux-clipboard-set") {
|
} else if is_program_in_path("termux-clipboard-set") {
|
||||||
child = Command::new("termux-clipboard-set")
|
child = Command::new("termux-clipboard-set")
|
||||||
.stdin(Stdio::piped())
|
.stdin(Stdio::piped())
|
||||||
.spawn()
|
.spawn()
|
||||||
.expect("execute command termux-clipboard-set")
|
.expect("execute command termux-clipboard-set")
|
||||||
|
|
||||||
|
// the above programs responsible for the clipboard were not found
|
||||||
} else {
|
} else {
|
||||||
println!(
|
println!(
|
||||||
"{} {}",
|
"{} {}",
|
||||||
"WARN".yellow(),
|
"WARN".yellow(),
|
||||||
"command for clipboard not found".magenta()
|
"command for clipboard not found".magenta()
|
||||||
);
|
);
|
||||||
return;
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// copy the content (send it to stdin command)
|
||||||
child
|
child
|
||||||
.stdin
|
.stdin
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.write_all(content.as_bytes())
|
.write_all(content.as_bytes())
|
||||||
.expect("execute command");
|
.expect("execute command");
|
||||||
child.wait_with_output().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(all(
|
child
|
||||||
unix,
|
.wait_with_output()
|
||||||
not(any(target_os = "macos", target_os = "android", target_os = "emscripten"))
|
.expect("wait for clipboard command output");
|
||||||
)))]
|
}
|
||||||
use arboard::Clipboard;
|
|
||||||
|
|
||||||
#[cfg(not(all(
|
#[cfg(not(all(
|
||||||
unix,
|
unix,
|
||||||
not(any(target_os = "macos", target_os = "android", target_os = "emscripten"))
|
not(any(target_os = "macos", target_os = "android", target_os = "emscripten"))
|
||||||
)))]
|
)))]
|
||||||
pub fn set_clipboard(content: String) {
|
pub fn set_clipboard(content: String) {
|
||||||
let mut clipboard = Clipboard::new().unwrap();
|
let mut clipboard = arboard::Clipboard::new().unwrap();
|
||||||
clipboard.set_text(content).unwrap();
|
clipboard.set_text(content).execute(format!("set clipboard to '{content}'"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
use chrono::{prelude::DateTime, Utc};
|
use chrono::{prelude::DateTime, Utc};
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use imgurs::api::{rate_limit::rate_limit, ImgurClient};
|
use imgurs::ImgurClient;
|
||||||
use std::time::{Duration, UNIX_EPOCH};
|
use std::time::{Duration, UNIX_EPOCH};
|
||||||
|
|
||||||
pub async fn credits(client: ImgurClient) {
|
pub async fn credits(client: ImgurClient) {
|
||||||
let i = rate_limit(client).await.expect("send api request");
|
// get client ratelimit from imgur api
|
||||||
|
let i = client
|
||||||
|
.rate_limit()
|
||||||
|
.await
|
||||||
|
.expect("send request to imgur api");
|
||||||
|
|
||||||
|
// format image upload date
|
||||||
let date = UNIX_EPOCH + Duration::from_secs(i.data.user_reset.try_into().unwrap());
|
let date = UNIX_EPOCH + Duration::from_secs(i.data.user_reset.try_into().unwrap());
|
||||||
let datetime = DateTime::<Utc>::from(date);
|
let datetime = DateTime::<Utc>::from(date);
|
||||||
let timestamp_str = datetime.format("%Y-%m-%d %H:%M:%S").to_string();
|
let timestamp_str = datetime.format("%Y-%m-%d %H:%M:%S").to_string();
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
|
use imgurs::ImgurClient;
|
||||||
use imgurs::api::{delete_image::delete_image as del_img, ImgurClient};
|
|
||||||
|
|
||||||
pub async fn delete_image(client: ImgurClient, delete_hash: String) {
|
pub async fn delete_image(client: ImgurClient, delete_hash: String) {
|
||||||
let i = del_img(client, delete_hash)
|
// delete image from imgur
|
||||||
|
client
|
||||||
|
.delete_image(delete_hash)
|
||||||
.await
|
.await
|
||||||
.expect("send api request");
|
.expect("send api request");
|
||||||
|
|
||||||
println!("{}", i.magenta());
|
println!(
|
||||||
|
"{}",
|
||||||
|
"If Delete Hash was correct the image was deleted!".magenta()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,14 @@
|
||||||
use imgurs::api::{get_image::get_image, ImgurClient};
|
use imgurs::ImgurClient;
|
||||||
|
|
||||||
use super::print_image_info;
|
use super::print_image_info;
|
||||||
|
|
||||||
pub async fn image_info(client: ImgurClient, id: &str) {
|
pub async fn image_info(client: ImgurClient, id: String) {
|
||||||
let info = get_image(client, id).await.expect("send api request");
|
// get a image info from imgur
|
||||||
|
let info = client
|
||||||
|
.image_info(id)
|
||||||
|
.await
|
||||||
|
.expect("send request to imfur api");
|
||||||
|
|
||||||
|
// print image information from imgur
|
||||||
print_image_info(info);
|
print_image_info(info);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,14 +8,17 @@ pub mod webhook;
|
||||||
|
|
||||||
use chrono::{prelude::DateTime, Utc};
|
use chrono::{prelude::DateTime, Utc};
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use imgurs::api::ImageInfo;
|
use imgurs::ImageInfo;
|
||||||
use std::time::{Duration, UNIX_EPOCH};
|
use std::time::{Duration, UNIX_EPOCH};
|
||||||
|
|
||||||
|
// print image information from imgur
|
||||||
pub fn print_image_info(i: ImageInfo) {
|
pub fn print_image_info(i: ImageInfo) {
|
||||||
|
// format image upload date
|
||||||
let d = UNIX_EPOCH + Duration::from_secs(i.data.datetime.try_into().unwrap());
|
let d = UNIX_EPOCH + Duration::from_secs(i.data.datetime.try_into().unwrap());
|
||||||
let datetime = DateTime::<Utc>::from(d);
|
let datetime = DateTime::<Utc>::from(d);
|
||||||
let timestamp_str = datetime.format("%Y-%m-%d %H:%M:%S").to_string();
|
let timestamp_str = datetime.format("%Y-%m-%d %H:%M:%S").to_string();
|
||||||
|
|
||||||
|
// image title
|
||||||
if i.data.title != None {
|
if i.data.title != None {
|
||||||
println!(
|
println!(
|
||||||
"{} {}",
|
"{} {}",
|
||||||
|
@ -26,6 +29,8 @@ pub fn print_image_info(i: ImageInfo) {
|
||||||
.magenta()
|
.magenta()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// image description
|
||||||
if i.data.description != None {
|
if i.data.description != None {
|
||||||
println!(
|
println!(
|
||||||
"{} {}",
|
"{} {}",
|
||||||
|
@ -36,6 +41,8 @@ pub fn print_image_info(i: ImageInfo) {
|
||||||
.magenta()
|
.magenta()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// image deletehas
|
||||||
if i.data.deletehash != None {
|
if i.data.deletehash != None {
|
||||||
println!(
|
println!(
|
||||||
"{} {}",
|
"{} {}",
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
use clap::{Command, IntoApp, Parser, Subcommand};
|
use clap::{Command, IntoApp, Parser, Subcommand};
|
||||||
use clap_complete::{generate, Generator, Shell};
|
use clap_complete::{generate, Generator, Shell};
|
||||||
use imgurs::api::ImgurClient;
|
use imgurs::ImgurClient;
|
||||||
use std::io::{self, stdout};
|
use std::io::{self, stdout};
|
||||||
|
|
||||||
use crate::cli::{credits::*, delete_image::*, info_image::*, upload_image::*};
|
use crate::cli::{credits::*, delete_image::*, info_image::*, upload_image::*};
|
||||||
|
|
||||||
|
// get program name and varsion from Cargo.toml
|
||||||
const VERSION: Option<&str> = option_env!("CARGO_PKG_VERSION");
|
const VERSION: Option<&str> = option_env!("CARGO_PKG_VERSION");
|
||||||
const NAME: Option<&str> = option_env!("CARGO_PKG_NAME");
|
const NAME: Option<&str> = option_env!("CARGO_PKG_NAME");
|
||||||
|
|
||||||
|
@ -21,7 +22,7 @@ struct Cli {
|
||||||
|
|
||||||
#[derive(Subcommand, Debug)]
|
#[derive(Subcommand, Debug)]
|
||||||
enum Commands {
|
enum Commands {
|
||||||
#[clap(about = "Print API Rate Limit", display_order = 1)]
|
#[clap(about = "Print Client Rate Limit", display_order = 1)]
|
||||||
Credits,
|
Credits,
|
||||||
|
|
||||||
#[clap(about = "Upload image to Imgur", display_order = 2)]
|
#[clap(about = "Upload image to Imgur", display_order = 2)]
|
||||||
|
@ -37,7 +38,7 @@ enum Commands {
|
||||||
about = "Generate completion file for a shell [bash, elvish, fish, powershell, zsh]",
|
about = "Generate completion file for a shell [bash, elvish, fish, powershell, zsh]",
|
||||||
display_order = 5
|
display_order = 5
|
||||||
)]
|
)]
|
||||||
Completions { shell: String },
|
Completions { shell: Shell },
|
||||||
|
|
||||||
#[clap(about = "Generate man page", display_order = 6)]
|
#[clap(about = "Generate man page", display_order = 6)]
|
||||||
Manpage,
|
Manpage,
|
||||||
|
@ -50,41 +51,26 @@ fn print_completions<G: Generator>(gen: G, app: &mut Command) {
|
||||||
pub async fn parse(client: ImgurClient) {
|
pub async fn parse(client: ImgurClient) {
|
||||||
let args = Cli::parse();
|
let args = Cli::parse();
|
||||||
|
|
||||||
match &args.command {
|
match args.command {
|
||||||
Commands::Credits => {
|
Commands::Credits => credits(client).await,
|
||||||
credits(client).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
Commands::Upload { path } => {
|
Commands::Upload { path } => upload_image(client, path.to_string()).await,
|
||||||
upload_image(client, path).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
Commands::Delete { delete_hash } => {
|
Commands::Delete { delete_hash } => delete_image(client, delete_hash.to_string()).await,
|
||||||
delete_image(client, delete_hash.to_string()).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
Commands::Info { id } => {
|
Commands::Info { id } => image_info(client, id.to_string()).await,
|
||||||
image_info(client, id).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
Commands::Completions { shell } => {
|
Commands::Completions { shell } => {
|
||||||
let mut app = Cli::command();
|
let mut app = Cli::command();
|
||||||
|
|
||||||
match shell.as_str() {
|
print_completions(shell, &mut app)
|
||||||
"bash" => print_completions(Shell::Bash, &mut app),
|
|
||||||
"elvish" => print_completions(Shell::Elvish, &mut app),
|
|
||||||
"fish" => print_completions(Shell::Fish, &mut app),
|
|
||||||
"powershell" => print_completions(Shell::PowerShell, &mut app),
|
|
||||||
"zsh" => print_completions(Shell::Zsh, &mut app),
|
|
||||||
|
|
||||||
_ => panic!("Completions to shell `{shell}`, not found!"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Commands::Manpage => {
|
Commands::Manpage => {
|
||||||
let clap_app = Cli::command();
|
let clap_app = Cli::command();
|
||||||
let man = clap_mangen::Man::new(clap_app);
|
let man = clap_mangen::Man::new(clap_app);
|
||||||
man.render(&mut io::stdout()).unwrap();
|
|
||||||
|
man.render(&mut io::stdout()).expect("generate manpage")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
use super::clipboard::set_clipboard;
|
use super::clipboard::set_clipboard;
|
||||||
use imgurs::api::{upload_image::upload_image as upload_img, ImgurClient};
|
use imgurs::ImgurClient;
|
||||||
use notify_rust::Notification;
|
use notify_rust::Notification;
|
||||||
|
|
||||||
use crate::{cli::webhook::send_discord_webhook, config::toml};
|
use crate::{cli::webhook::send_discord_webhook, config::toml};
|
||||||
|
|
||||||
use super::print_image_info;
|
use super::print_image_info;
|
||||||
|
|
||||||
use base64::encode as base64_encode;
|
// show notification
|
||||||
use std::{fs::read as fs_read, path::Path};
|
|
||||||
|
|
||||||
macro_rules! notify (
|
macro_rules! notify (
|
||||||
($notification: expr) => (
|
($notification: expr) => (
|
||||||
if toml::parse().notification.enabled {
|
if toml::parse().notification.enabled {
|
||||||
|
@ -17,43 +15,39 @@ macro_rules! notify (
|
||||||
);
|
);
|
||||||
);
|
);
|
||||||
|
|
||||||
pub async fn upload_image(client: ImgurClient, path: &str) {
|
pub async fn upload_image(client: ImgurClient, path: String) {
|
||||||
let mut image: String = path.to_string();
|
// parse configuration file
|
||||||
|
|
||||||
let config = toml::parse();
|
let config = toml::parse();
|
||||||
|
|
||||||
if Path::new(path).exists() {
|
// upload a image to imgur
|
||||||
let bytes = fs_read(path)
|
let mut i = client.upload_image(path).await.unwrap_or_else(|err| {
|
||||||
.map_err(|err| err.to_string())
|
|
||||||
.expect("read file");
|
|
||||||
image = base64_encode(bytes);
|
|
||||||
} else if !validator::validate_url(path) {
|
|
||||||
panic!("{path} is not a url")
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut i = upload_img(client, &image).await.unwrap_or_else(|err| {
|
|
||||||
notify!(Notification::new()
|
notify!(Notification::new()
|
||||||
.summary("Error!")
|
.summary("Error!")
|
||||||
.body(&format!("Error: {}", &err.to_string()))
|
.body(&format!("Error: {}", &err.to_string()))
|
||||||
.appname("Imgurs")); // I don't think you can set it to error
|
.appname("Imgurs")); // I don't think you can set it to error
|
||||||
|
|
||||||
panic!("{}", err)
|
panic!("send request to imagur api: {}", err)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// change domain to proxy (to be set in config)
|
||||||
if config.imgur.image_cdn != "i.imgur.com" {
|
if config.imgur.image_cdn != "i.imgur.com" {
|
||||||
i.data.link = i.data.link.replace("i.imgur.com", "cdn.magicuser.cf")
|
i.data.link = i.data.link.replace("i.imgur.com", &config.imgur.image_cdn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// print image information from imgur
|
||||||
print_image_info(i.clone());
|
print_image_info(i.clone());
|
||||||
|
|
||||||
let body = format!("Uploaded {}", i.data.link);
|
// send notification that the image has been uploaded
|
||||||
|
notify!(Notification::new()
|
||||||
notify!(Notification::new().summary("Imgurs").body(&body));
|
.summary("Imgurs")
|
||||||
|
.body(&format!("Uploaded {}", i.data.link)));
|
||||||
|
|
||||||
|
// if enabled copy link to clipboard
|
||||||
if config.clipboard.enabled {
|
if config.clipboard.enabled {
|
||||||
set_clipboard(i.data.link.clone())
|
set_clipboard(i.data.link.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if enabled send embed with link and deletehash to discord (something like logger)
|
||||||
if config.discord_webhook.enabled {
|
if config.discord_webhook.enabled {
|
||||||
send_discord_webhook(i.data.link, i.data.deletehash.unwrap())
|
send_discord_webhook(i.data.link, i.data.deletehash.unwrap())
|
||||||
.await
|
.await
|
||||||
|
|
|
@ -3,13 +3,21 @@ use std::error::Error;
|
||||||
|
|
||||||
use crate::config::toml;
|
use crate::config::toml;
|
||||||
|
|
||||||
|
// send embed with link and deletehash to discord (something like logger)
|
||||||
pub async fn send_discord_webhook(
|
pub async fn send_discord_webhook(
|
||||||
link: String,
|
link: String,
|
||||||
deletehash: String,
|
deletehash: String,
|
||||||
) -> Result<bool, Box<dyn Error + Send + Sync>> {
|
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||||
|
// get discord webhook uri from config
|
||||||
let url = toml::parse().discord_webhook.uri;
|
let url = toml::parse().discord_webhook.uri;
|
||||||
|
|
||||||
|
// create WebhookClient
|
||||||
let client: WebhookClient = WebhookClient::new(&url);
|
let client: WebhookClient = WebhookClient::new(&url);
|
||||||
|
|
||||||
|
// get program version
|
||||||
|
let version = option_env!("CARGO_PKG_VERSION").unwrap_or("unknown");
|
||||||
|
|
||||||
|
// send discord webhook
|
||||||
client
|
client
|
||||||
.send(|message| {
|
.send(|message| {
|
||||||
message.username("Imgurs").embed(|embed| {
|
message.username("Imgurs").embed(|embed| {
|
||||||
|
@ -17,14 +25,10 @@ pub async fn send_discord_webhook(
|
||||||
.title(&link)
|
.title(&link)
|
||||||
.description(&format!("Delete Hash ||{deletehash}||"))
|
.description(&format!("Delete Hash ||{deletehash}||"))
|
||||||
.image(&link)
|
.image(&link)
|
||||||
.footer(
|
.footer(&format!("Imgurs v{version}"), None)
|
||||||
&format!(
|
|
||||||
"Imgurs v{}",
|
|
||||||
option_env!("CARGO_PKG_VERSION").unwrap_or("unknown")
|
|
||||||
),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.await
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1,3 @@
|
||||||
pub mod api;
|
mod api;
|
||||||
|
|
||||||
|
pub use api::*;
|
||||||
|
|
|
@ -2,7 +2,7 @@ mod cli;
|
||||||
mod config;
|
mod config;
|
||||||
|
|
||||||
use cli::parse::parse;
|
use cli::parse::parse;
|
||||||
use imgurs::api::ImgurClient;
|
use imgurs::ImgurClient;
|
||||||
|
|
||||||
use simple_logger::SimpleLogger;
|
use simple_logger::SimpleLogger;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue