15 个稳定版本
3.3.0 | 2024 年 4 月 22 日 |
---|---|
3.2.0 | 2024 年 3 月 11 日 |
3.1.0 | 2024 年 2 月 5 日 |
3.0.2 | 2023 年 12 月 25 日 |
0.2.1 |
|
#84 in 网页开发
每月下载量 937
在 2 crates 中使用
1MB
28K SLoC
mangadex-api
重要
此 Git 仓库仅是从 gondolyr/mangadex-api 分支的 fork,但由于项目和 crate 已被删除,所以我将现在维护此 crate 以供 special-eureka 和 eureka-manager 使用
mangadex-api
crate 提供了一个方便、高级的包装,客户端 用于 MangaDex API,使用 Rust 编写。
它涵盖了 其文档 中涵盖的所有公共端点。
请注意,由于 MangaDex 仍在测试中,此 SDK 可能会经历突然的重大更改(很多)。
免责声明
mangadex-api
与 MangaDex 无关。
目录
工作区详细信息
mangadex-api
是 SDK 本身mangadex-api-types
包含所有枚举和静态数据。默认情况下,这些是non_exhaustive
mangadex-api-schema
包含所有所需的响应结构体。这些支持与serialize
功能的序列化mangadex-api-inputs-types
包含端点输入类型。请注意,这个输入存储库只包含具有多个参数的类型。
需求
如何安装
将 mangadex-api
添加到您的依赖项
[dependencies]
# ...
# Types and schemas are always required
mangadex-api-types-rust = "0.9"
mangadex-api-schema-rust = "0.9"
mangadex-api = "3.3"
如果您正在使用 cargo-edit
,请运行
cargo add mangadex-api
依赖项解释
依赖项 | 用途 | 包含 |
---|---|---|
anyhow |
捕获意外错误。 | always |
mangadex-api-types-rust |
Mangadex API 的枚举和静态数据 | always |
mangadex-api-schema-rust |
Mangadex API 使用的类型 | always |
clap |
演示库功能的示例 | 开发构建 |
derive_builder |
方便为 API 端点构建器生成设置器。 | always |
fake |
为单元测试生成随机数据。 | 开发构建 |
futures |
异步请求处理。 | always |
reqwest |
向 MangaDex API 发送 HTTP 请求。 | always |
serde |
将 HTTP 响应体序列化为结构体。 | always |
serde_json |
为单元测试创建 JSON 对象。 | 开发构建 |
serde_qs |
HTTP 请求的查询字符串序列化。 | always |
thiserror |
自定义错误处理。 | always |
time |
处理时间字段的便利类型。 | always |
tokio |
异步运行时,用于处理示例中的 futures 以及章节报告中的 utils 功能(仅在 tokio-mutli-thread 和 rw-mutli-thread 中)。 |
开发构建 + utils 功能 |
url |
方便的 Url 类型,用于验证和包含 URL。 |
always |
uuid |
方便的 Uuid 类型,用于验证和包含请求和响应的 UUID。还用于为测试随机生成 UUID。 |
always |
wiremock |
HTTP 模拟以测试 MangaDex API。 | 开发构建 |
功能
默认情况下不包含所有功能。要启用它们,请将以下任何一个添加到您的项目 Cargo.toml
文件中。
-
multi-thread
启用
MangaDexClient
的线程安全,但代价是操作稍微昂贵。 -
legacy-auth
已弃用启用 SDK 中使用
< 5.9.0
登录系统。请访问 Mangadex Discord 获取更多信息 -
utils
启用使用
MangaDexClient::download()
。允许您在不流泪和长代码的情况下下载章节或封面图片。 -
tokio-multi-thread
启用对
tokio::sync::Mutex
的使用,而不是futures::lock::Mutex
-
rw-mutli-thread
在客户端中启用对
tokio::sync::RwLock
的使用,而不是futures::lock::Mutex
。如果您希望实现灵活的并发多线程,这可能很有用。 -
legacy-user-delete
已弃用启用使用用户删除端点。
-
oauth
(默认启用)启用使用 MangaDex API 5.9.0 中引入的全新 OAuth 2.0 登录。
快速提示: 此
oauth
功能使用 个人客户端 方法,这意味着您需要注册一个个人客户端并等待其验证。更多详情请参阅 此处 -
custom_list_v2
: 启用即将推出的自定义列表系统。请注意,这些端点尚未在api.mangadex.org
上部署,但您可以在api.mangadex.dev
(他们的实时开发API)上使用它们。更多信息请参阅 MangaDex 论坛上的Follows/CustomList API Changelog - BREAKING CHANGES
例如,要启用 multi-thread
功能,请将以下内容添加到您的 Cargo.toml
文件中
mangadex-api = { version = "3.2.0", features = ["multi-thread"] }
HTTP 客户端
mangadex_api::MangaDexClient
是异步的,使用 reqwest
作为 HTTP 客户端。
响应结构体
响应结构体可以在 mangadex-api-schema-rust
包 中找到,并包含 JSON 响应中的字段。
入门
此示例演示了如何获取随机漫画。
use mangadex_api::v5::MangaDexClient;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let client = MangaDexClient::default();
let random_manga = client
.manga()
.random()
.get()
.send()
.await?;
println!("{:?}", random_manga);
Ok(())
}
此示例演示了如何获取 Mangadex 人气标题
use mangadex_api::MangaDexClient;
use mangadex_api_schema_rust::v5::RelatedAttributes;
use mangadex_api_types_rust::{
Language, MangaDexDateTime, MangaSortOrder, OrderDirection, ReferenceExpansionResource,
};
use time::{Duration, OffsetDateTime};
use url::Url;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let client = MangaDexClient::default();
// Take the local date and put substract it with 30 days
let created_at_since = OffsetDateTime::now_utc()
.checked_sub(Duration::days(30))
.unwrap();
let created_at_since = MangaDexDateTime::new(&created_at_since);
let res = client
.manga()
.get()
// We pick up all manga that has been created during these last 30 days
.created_at_since(created_at_since)
// Mangadex Popular Titles is ordered by followedCount descending
.order(MangaSortOrder::FollowedCount(OrderDirection::Descending))
// We include the author data
.include(ReferenceExpansionResource::Author)
// We include the arstits data
.include(ReferenceExpansionResource::Artist)
.send()
.await?;
let not_found = String::from("Not found");
// Just a simple index :3
let mut index = 1;
for manga in res.data {
// Find the English title
let title = manga
.attributes
.title
.get(&Language::English)
.unwrap_or(¬_found);
println!("{index} ~ {title}");
// Find the author name
let author = manga
.find_first_relationships(mangadex_api_types::RelationshipType::Author)
.and_then(|e| {
e.attributes.clone().map(|rel| match rel {
RelatedAttributes::Author(a) => a.name,
_ => not_found.clone(),
})
})
.unwrap_or(not_found.clone());
println!("\tAuthor: {author}");
// Find the author name
let artist = manga
.find_first_relationships(mangadex_api_types::RelationshipType::Artist)
.and_then(|e| {
e.attributes.clone().map(|rel| match rel {
RelatedAttributes::Author(a) => a.name,
_ => not_found.clone(),
})
})
.unwrap_or(not_found.clone());
// Print the artist name if it's different of the author
if artist != author {
println!("\tArtist: {artist}");
}
// We generate the link that goes to the Mangadex page
let title_link =
Url::parse("https://mangadex.org/title/")?.join(manga.id.to_string().as_str())?;
println!("\tLink: {title_link}");
println!();
index += 1;
}
println!("Done :3");
Ok(())
}
使用自定义 reqwest 客户端
默认情况下,mangadex_api::MangaDexClient
将使用默认的 reqwest::Client
设置。
您可以提供自己的 reqwest::Client
来自定义选项,例如请求超时。
use reqwest::Client;
use mangadex_api::v5::MangaDexClient;
# async fn run() -> anyhow::Result<()> {
let reqwest_client = Client::builder()
.timeout(std::time::Duration::from_secs(10))
.build()?;
let client = MangaDexClient::new(reqwest_client);
# Ok(())
# }
按标题搜索漫画
参考:https://api.mangadex.org/swagger.html#/Manga/get-search-manga
use mangadex_api::v5::MangaDexClient;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let client = MangaDexClient::default();
let manga_results = client
.manga()
.get()
.title("full metal")
.send()
.await?;
println!("manga results = {:?}", manga_results);
Ok(())
}
使用参考扩展按标题搜索漫画
每次获取都将包括所有关系,但仅包含最小信息,例如关系类型和ID。参考扩展将包括请求中添加的类型的结果中的完整JSON对象。
在下面的示例中,关系列表中的任何相关作者都会在结果中提供详细的信息,例如作者的名字、传记和网站。
参考文献
- https://api.mangadex.org/docs/01-concepts/reference-expansion/
- 端点: https://api.mangadex.org/docs/swagger.html#/Manga/get-search-manga
- 作者对象: https://api.mangadex.org/docs/swagger.html#/Author/get-author-id
use mangadex_api::v5::schema::RelatedAttributes;
use mangadex_api::v5::MangaDexClient;
// use mangadex_api_types_rust::{ReferenceExpansionResource, RelationshipType};
use mangadex_api_types::{ReferenceExpansionResource, RelationshipType};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let client = MangaDexClient::default();
let manga_results = client
.manga()
.get()
.title("full metal")
.include(&ReferenceExpansionResource::Author)
.send()
.await?;
println!("manga results = {:?}", manga_results);
let authors = manga_results.data.iter().filter_map(|manga| {
manga
.relationships
.iter()
.find(|&rel| rel.type_ == RelationshipType::Author)
});
for author in authors {
if let Some(RelatedAttributes::Author(author_attributes)) = &author.attributes {
println!("{} - {}", author.id, author_attributes.name);
}
}
Ok(())
}
下载章节页面
参考文献: https://api.mangadex.org/docs/reading-chapter/
使用旧方法
// Imports used for downloading the pages to a file.
// They are not used because we're just printing the raw bytes.
// use std::fs::File;
// use std::io::Write;
use uuid::Uuid;
use mangadex_api::v5::MangaDexClient;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let client = MangaDexClient::default();
// Yeah, i'm a [`100 girlfriend`](https://mangadex.org/title/efb4278c-a761-406b-9d69-19603c5e4c8b/the-100-girlfriends-who-really-really-really-really-really-love-you) simp and what! >:)
let chapter_id = Uuid::parse_str("f2a09509-3c09-4371-a810-ecb99242bd90")?;
let at_home = client
.at_home()
.server()
.id(chapter_id)
.get()
.send()
.await?;
let http_client = reqwest::Client::new();
// Original quality. Use `.data.attributes.data_saver` for smaller, compressed images.
let page_filenames = &at_home.chapter.data;
for filename in page_filenames {
// If using the data-saver option, use "/data-saver/" instead of "/data/" in the URL.
let page_url = at_home
.base_url
.join(&format!(
"/{quality_mode}/{chapter_hash}/{page_filename}",
quality_mode = "data",
chapter_hash = at_home.chapter.hash,
page_filename = filename
))
.unwrap();
let res = http_client.get(page_url).send().await?;
// The data should be streamed rather than downloading the data all at once.
let bytes = res.bytes().await?;
// This is where you would download the file but for this example,
// we're just printing the raw data.
// let mut file = File::create(&filename)?;
// let _ = file.write_all(&bytes);
println!("Chunk: {:?}", bytes);
}
Ok(())
}
使用 utils
功能
基于 (filename, Result<bytes>)
向量
如果您想要处理每个响应错误,则不建议使用此方法。
use anyhow::Result;
use mangadex_api::{utils::download::chapter::DownloadMode, MangaDexClient};
/// used for file exporting
use std::{
fs::{create_dir_all, File},
io::Write,
};
/// It's from this manga called [`The Grim Reaper Falls In Love With A Human`](https://mangadex.org/title/be2efc56-1669-4e42-9f27-3bd232bca8ea/the-grim-reaper-falls-in-love-with-a-human)
///
/// [Chapter 1 English](https://mangadex.org/chapter/2b4e39a5-fba0-4055-a176-8b7e19faacdb) by [`Kredim`](https://mangadex.org/group/0b870e54-c75f-4d2e-8068-c40f939135fd/kredim)
#[tokio::main]
async fn main() -> Result<()> {
let output_dir = "your-output-dir";
let client = MangaDexClient::default();
let chapter_id = uuid::Uuid::parse_str("32b229f6-e9bf-41a0-9694-63c11191704c")?;
let chapter_files = client
// We use the download builder
.download()
// Chapter id (accept uuid::Uuid)
.chapter(chapter_id)
// You also use `DownloadMode::Normal` if you want some the original quality
//
// Default : Normal
.mode(DownloadMode::DataSaver)
// Enable the [`The MangaDex@Home report`](https://api.mangadex.org/docs/retrieving-chapter/#the-mangadexhome-report-endpoint) if true
//
// Default : false
.report(true)
// Something that i don`t really know about
//
// More details at : https://api.mangadex.org/docs/retrieving-chapter/#basics
.force_port_443(false)
.build()?
.download_element_vec()
.await?;
create_dir_all(format!("{}{}", output_dir, chapter_id))?;
for (filename, bytes_) in chapter_files {
if let Ok(bytes) = bytes_ {
let mut file: File =
File::create(format!("{}{}/{}", output_dir, chapter_id, filename))?;
file.write_all(&bytes)?;
} else if let Err(e) = bytes_ {
eprintln!("{}", e);
}
}
Ok(())
}
通过 tokio-stream
使用 tokio-stream
,您可以为每个响应结果进行处理。
不带检查器
use anyhow::Result;
use mangadex_api::{utils::download::chapter::DownloadMode, MangaDexClient};
use std::{
fs::{create_dir_all, File},
io::Write,
};
use tokio::pin;
use tokio_stream::StreamExt;
/// It's from this manga called [`Keiken Zumi na Kimi to, Keiken Zero na Ore ga, Otsukiai Suru Hanashi`](https://mangadex.org/title/1c8f0358-d663-4d60-8590-b5e82890a1e3/keiken-zumi-na-kimi-to-keiken-zero-na-ore-ga-otsukiai-suru-hanashi)
///
/// [Chapter 13 English](https://mangadex.org/chapter/250f091f-4166-4831-9f45-89ff54bf433b) by [`Galaxy Degen Scans`](https://mangadex.org/group/ab24085f-b16c-4029-8c05-38fe16592a85/galaxy-degen-scans)
#[tokio::main]
async fn main() -> Result<()> {
let output_dir = "./test-outputs";
let client = MangaDexClient::default();
let chapter_id = uuid::Uuid::parse_str("250f091f-4166-4831-9f45-89ff54bf433b")?;
create_dir_all(format!("{}/{}", output_dir, chapter_id))?;
let download = client
// We use the download builder
.download()
// Chapter id (accept uuid::Uuid)
.chapter(chapter_id)
// You also use `DownloadMode::Normal` if you want some the original quality
//
// Default : Normal
.mode(DownloadMode::DataSaver)
// Enable the [`The MangaDex@Home report`](https://api.mangadex.org/docs/04-chapter/retrieving-chapter/) if true
//
// Default : false
.report(true)
// Something that i don`t really know about
//
// More details at : https://api.mangadex.org/docs/04-chapter/retrieving-chapter/
.force_port_443(false)
.build()?;
let chapter_files = download.download_stream().await?;
// `pin!` Required for iteration
pin!(chapter_files);
while let Some((data, index, total)) = chapter_files.next().await {
let (filename, bytes_) = data;
// Prin the progression in the standart output
println!("{index} / {total} : {filename} ");
if let Ok(bytes) = bytes_ {
let mut file: File =
File::create(format!("{}/{}/{}", output_dir, chapter_id, filename))?;
file.write_all(&bytes)?;
println!("downloaded");
} else if let Err(e) = bytes_ {
eprintln!("{e}");
}
}
Ok(())
}
带检查器
检查器是在获取响应之后、检索字节内容之前调用的函数。示例
/// Some code here
let download = client
.download()
.chapter(chapter_id)
.mode(DownloadMode::DataSaver)
.report(true)
.build()?;
let chapter_files = download
.download_stream_with_checker(move |filename, response| {
/// if this function return `true`, the current response will be skipped
true
})
.await?;
/// Some code here too
实际示例
如果已创建具有响应内容长度的文件,则检查器将返回 true
。
use anyhow::Result;
use mangadex_api::{utils::download::chapter::DownloadMode, MangaDexClient};
use std::{
fs::{create_dir_all, File},
io::Write,
};
use tokio::pin;
use tokio_stream::StreamExt;
/// It's from this manga called [`Keiken Zumi na Kimi to, Keiken Zero na Ore ga, Otsukiai Suru Hanashi`](https://mangadex.org/title/1c8f0358-d663-4d60-8590-b5e82890a1e3/keiken-zumi-na-kimi-to-keiken-zero-na-ore-ga-otsukiai-suru-hanashi)
///
/// [Chapter 13 English](https://mangadex.org/chapter/250f091f-4166-4831-9f45-89ff54bf433b) by [`Galaxy Degen Scans`](https://mangadex.org/group/ab24085f-b16c-4029-8c05-38fe16592a85/galaxy-degen-scans)
#[tokio::main]
async fn main() -> Result<()> {
let output_dir = "./test-outputs";
let client = MangaDexClient::default();
let chapter_id = uuid::Uuid::parse_str("250f091f-4166-4831-9f45-89ff54bf433b")?;
create_dir_all(format!("{}/{}", output_dir, chapter_id))?;
let download = client
// We use the download builder
.download()
// Chapter id (accept uuid::Uuid)
.chapter(chapter_id)
// You also use `DownloadMode::Normal` if you want some the original quality
//
// Default : Normal
.mode(DownloadMode::DataSaver)
// Enable the [`The MangaDex@Home report`](https://api.mangadex.org/docs/04-chapter/retrieving-chapter/) if true
//
// Default : false
.report(true)
// Something that i don`t really know about
//
// More details at : https://api.mangadex.org/docs/04-chapter/retrieving-chapter/
.force_port_443(false)
.build()?;
let chapter_files = download
.download_stream_with_checker(move |filename, response| {
let is_skip: bool = {
// Get the response content length
let content_length = match response.content_length() {
None => return false,
Some(d) => d,
};
// open the chapter image file
File::open(format!(
"{}/{}/{}",
output_dir,
chapter_id,
filename.filename.clone()
))
.map(|pre_file| {
pre_file
.metadata()
.map(|metadata| metadata.len() == content_length)
.unwrap_or(false)
})
.unwrap_or(false)
};
is_skip
})
.await?;
// `pin!` Required for iteration
pin!(chapter_files);
while let Some((data, index, total)) = chapter_files.next().await {
let (filename, bytes_) = data;
// Prin the progression in the standart output
println!("{index} / {total} : {filename} ");
if let Ok(bytes) = bytes_ {
let mut file: File =
File::create(format!("{}/{}/{}", output_dir, chapter_id, filename))?;
file.write_all(&bytes)?;
println!("downloaded");
} else if let Err(e) = bytes_ {
eprintln!("{e}");
}
}
Ok(())
}
下载漫画的主要封面图像
使用旧方法
虽然这个示例可以通过传递封面ID直接获取封面信息,但通常情况下,人们不会手头上有ID,所以最常见的方法是从漫画结果中获取。
如果您想要获取所有漫画的封面图片,则需要使用 封面列表端点 并使用 manga[]
查询参数。
// Imports used for downloading the cover to a file.
// They are not used because we're just printing the raw bytes.
// use std::fs::File;
// use std::io::Write;
use reqwest::Url;
use uuid::Uuid;
use mangadex_api::v5::MangaDexClient;
use mangadex_api::CDN_URL;
// use mangadex_api_types_rust::RelationshipType;
use mangadex_api_types::RelationshipType;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let client = MangaDexClient::default();
let manga_id = Uuid::new_v4();
let manga = client.manga().id(manga_id).get().send().await?;
let cover_id = manga
.data
.relationships
.iter()
.find(|related| related.type_ == RelationshipType::CoverArt)
.expect("no cover art found for manga")
.id;
let cover = client.cover().cover_id(cover_id).get().send().await?;
// This uses the best quality image.
// To use smaller, thumbnail-sized images, append any of the following:
//
// - .512.jpg
// - .256.jpg
//
// For example, "https://uploads.mangadex.org/covers/8f3e1818-a015-491d-bd81-3addc4d7d56a/4113e972-d228-4172-a885-cb30baffff97.jpg.512.jpg"
let cover_url = Url::parse(&format!(
"{}/covers/{}/{}",
CDN_URL, manga_id, cover.data.attributes.file_name
))
.unwrap();
let http_client = reqwest::Client::new();
let res = http_client.get(cover_url).send().await?;
// The data should be streamed rather than downloading the data all at once.
let bytes = res.bytes().await?;
// This is where you would download the file but for this example, we're just printing the raw data.
// let mut file = File::create(&filename)?;
// let _ = file.write_all(&bytes);
println!("Chunk: {:?}", bytes);
Ok(())
}
使用 utils
功能(推荐)
通过封面 ID
use anyhow::Result;
use uuid::Uuid;
use crate::MangaDexClient;
use std::{io::Write, fs::File};
/// Download the volume 2 cover of [Lycoris Recoil](https://mangadex.org/title/9c21fbcd-e22e-4e6d-8258-7d580df9fc45/lycoris-recoil)
#[tokio::main]
async fn main() -> Result<()>{
let cover_id : Uuid = Uuid::parse_str("0bc12ff4-3cec-4244-8582-965b8be496ea")?;
let client : MangaDexClient = MangaDexClient::default();
let (filename, bytes) = client.download().cover().build()?.via_cover_id(cover_id).await?;
let mut file = File::create(format!("{}/{}", "your-output-dir", filename))?;
file.write_all(&bytes)?;
Ok(())
}
通过漫画 ID
use anyhow::Result;
use mangadex_api::MangaDexClient;
use std::{fs::File, io::Write};
use uuid::Uuid;
/// Download the [Kimi tte Watashi no Koto Suki Nandesho?](https://mangadex.org/title/f75c2845-0241-4e69-87c7-b93575b532dd/kimi-tte-watashi-no-koto-suki-nandesho) cover
///
/// For test... of course :3
#[tokio::main]
async fn main() -> Result<()> {
let output_dir = String::from("test-outputs");
let manga_id: Uuid = Uuid::parse_str("f75c2845-0241-4e69-87c7-b93575b532dd")?;
let client: MangaDexClient = MangaDexClient::default();
let (filename, bytes) = client
.download()
.cover()
// you can use
//
// ```rust
// .quality(CoverQuality::Size512)
// ``` for 512
// or
// ```rust
// .quality(CoverQuality::Size256)
// ``` for 256
.build()?
.via_manga_id(manga_id)
.await?;
let bytes = bytes?;
let mut file = File::create(format!("{}/{}", output_dir, filename))?;
file.write_all(&bytes)?;
println!("donwloaded :3");
Ok(())
}
身份验证(通过 oauth
功能)
在复制示例代码之前,我建议您阅读Mangadex 认证部分
首先,在Mangadex 个人资料设置注册个人客户端,并等待工作人员批准。目前可能需要2到3天,所以请耐心等待 :>
经过一段时间,您现在可以通过 oauth
功能进行 login
。
登录
use mangadex_api::MangaDexClient;
use mangadex_api_schema::v5::oauth::ClientInfo;
use mangadex_api_types::{Password, Username};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let mut client = MangaDexClient::default();
client
.set_client_info(&ClientInfo {
client_id: String::from("<SET YOUR CLIENT ID HERE>"),
client_secret: String::from("<SET YOUR CLIENT INFO HERE>"),
})
.await?;
let response = client
.oauth()
.login()
.username(Username::parse("<YOUR USERNAME HERE>")?)
.password(Password::parse("<YOUR PASSWORD HERE>")?)
.send()
.await?;
/*
println!("Access Token: {}", response.access_token);
*/
println!("Expires in {} minutes", response.expires_in / 60);
Ok(())
}
刷新您的令牌
您只需调用 mangadex_api::MangaDexClient::oauth().refresh()
...
client
.oauth()
.refresh()
.send()
.await?;
...
示例
use mangadex_api::MangaDexClient;
// use mangadex_api_schema_rust::v5::oauth::ClientInfo;
// use mangadex_api_types_rust::{Password, Username};
use mangadex_api_schema::v5::oauth::ClientInfo;
use mangadex_api_types::{Password, Username};
use tokio::time::{sleep, Duration};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let mut client = MangaDexClient::default();
// Register your client info
client
.set_client_info(&ClientInfo {
client_id: String::from("<SET YOUR CLIENT ID HERE>"),
client_secret: String::from("<SET YOUR CLIENT INFO HERE>"),
})
.await?;
// Login to your account
let response = client
.oauth()
.login()
.username(Username::parse("<YOUR USERNAME HERE>")?)
.password(Password::parse("<YOUR PASSWORD HERE>")?)
.send()
.await?;
/*
println!("Access Token: {}", response.access_token);
*/
println!("Expires in {} minutes", response.expires_in / 60);
// Wait until the token expires
sleep(Duration::from_secs(<u64 as TryFrom<usize>>::try_from(
response.expires_in,
)?))
.await;
// Refresh the session token
let response = client.oauth().refresh().send().await?;
/*
println!("Access Token: {}", response.access_token);
*/
println!("Expires in {} minutes", response.expires_in / 60);
Ok(())
}
许可证
许可于以下之一
- Apache License, Version 2.0 (LICENSE-APACHE 或 https://apache.ac.cn/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT 或 http://opensource.org/licenses/MIT)
任选其一。
贡献
除非您明确表示,否则您根据Apache-2.0许可证定义的任何旨在包含在作品中的有意贡献,都应如上所述双重许可,而无需任何额外条款或条件。
贡献者
我们欢迎所有人的贡献。有许多方式可以做出贡献,而CONTRIBUTING.md 文档解释了您如何做出贡献并开始。
依赖项
~5–20MB
~255K SLoC