40 releases
0.1.39 | Dec 17, 2021 |
---|---|
0.1.38 | Apr 22, 2021 |
0.1.35 | Feb 27, 2021 |
0.1.33 | Nov 16, 2020 |
0.1.15 | Aug 29, 2020 |
#667 in Web programming
67 downloads per month
300KB
7K
SLoC
What is this?
WIP Rust bindings for the Reddit API
This is a WIP, you likely won't find it particularly useful
Set up
There are some hand rolled getting started instructions here: https://github.com/tobymurray/mr_splashy_pants/blob/master/getting-started/getting-started.md. You can use https://tobymurray.github.io/reddit-auth-generator/ to help generate an auth string.
Alternatively, you can follow https://github.com/reddit-archive/reddit/wiki/OAuth2 for more complete set up instructions.
Use
Set up a script with access to a Reddit account, collect the access token, the client ID, and the client secret. Once you have that, get a refresh token and an access token. Once you have that you can do:
let pants = Pants::new(
USER_AGENT,
"<access-token>",
"<refresh_token>",
"<client-id>",
"<client-secret>",
);
For example, if you're using dotenv and reading values from the environment:
let pants = Pants::new(
USER_AGENT,
env::var("ACCESS_TOKEN").unwrap(),
&env::var("REFRESH_TOKEN").unwrap(),
&env::var("CLIENT_ID").unwrap(),
&env::var("CLIENT_SECRET").unwrap(),
);
Then you can invoke things, e.g:
pants.me()
If your access token expires, it should automatically refresh.
Currently implemented with (partially) structured response:
Account:
- GET /api/v1/me
- GET /api/v1/me/karma
- GET /api/v1/me/prefs
- GET /prefs/friends
Currently kind of implemented (no query parameters), with JSON response:
Account:
- GET /api/v1/me/trophies
- GET /prefs/blocked
- GET /prefs/messaging
- GET /prefs/trusted
- GET /api/v1/me/friends
- GET /api/v1/me/blocked
Listing
- GET /api/trending_subreddits
- GET /best
- GET /by_id/names
- GET /comments/article
- GET /controversial
- GET /r/{subreddit}/controversial
- GET /duplicates/article
- GET /hot
- GET /r/{subreddit}/hot
- GET /new
- GET /r/{subreddit}/new
- GET /random
- GET /r/{subreddit}/random
- GET /rising
- GET /r/{subreddit}/rising
- GET /top
- GET /r/{subreddit}/top
Links and Comments
- POST /api/submit
- Crossposting is also implemented (same API, different request body)
- POST /api/del
Moderation
- GET /about/log
- GET /about/reports
- GET /about/spam
- GET /about/modqueue
- GET /about/unmoderated
- GET /about/edited
- GET /stylesheet
- GET /r/{subreddit}/about/log
- GET /r/{subreddit}/about/reports
- GET /r/{subreddit}/about/spam
- GET /r/{subreddit}/about/modqueue
- GET /r/{subreddit}/about/unmoderated
- GET /r/{subreddit}/about/edited
- GET /r/{subreddit}/stylesheet
Users
- GET /user/{username}/about
- GET /user/{username}/overview
- GET /user/{username}/submitted
- GET /user/{username}/comments
- GET /user/{username}/upvoted
- GET /user/{username}/downvoted
- GET /user/{username}/hidden
- GET /user/{username}/saved
- GET /user/{username}/gilded
To submit a post to Reddit:
// Build the submission
let request_body = links_and_comments::ApiSubmit {
ad: "".to_string(),
api_type: "".to_string(),
app: "".to_string(),
collection_id: "".to_string(),
event_end: "".to_string(),
event_start: "".to_string(),
event_tz: "".to_string(),
extension: "".to_string(),
flair_id: "".to_string(),
flair_text: "".to_string(),
g_recaptcha_response: "".to_string(),
kind: "self".to_string(),
nsfw: "".to_string(),
resubmit: "".to_string(),
richtext_json: "".to_string(),
sendreplies: "".to_string(),
spoiler: "".to_string(),
sr: "name_of_subreddit".to_string(),
text: "Here's an example of the post's body".to_string(),
title: "This is the title of the post".to_string(),
uh: "".to_string(),
url: "".to_string(),
video_poster_url: "".to_string(),
};
// then submit the post
let submission_name = pants.submit(request_body).await {
Ok(response) => {
println!("Response to submit is: {}", serde_json::to_string_pretty(&response).unwrap());
response.json.data.name
},
Err(e) => panic!("An error ocurred: {}", e),
};
// remove it if you'd like
let delete_request_body = links_and_comments::ApiDel { id: submission_name };
pants.del(delete_request_body).await;
Streaming support for:
Disclaimer: This implementation of streaming is not compatible with very high traffic subreddits. If more than 25 posts are submitted within any 30 second period, this streaming method will miss some.
use futures_util::pin_mut;
use futures_util::stream::StreamExt;
...
let stream = pants.subreddit("testingground4bots").stream_new();
pin_mut!(stream);
while let Some(value) = tokio_test::block_on(stream.next()) {
match value {
Ok(data) => {
println!("New post: {}", data);
}
Err(e) => {
// Note, this can get noisy if the failure persists
println!("Encountered an error: {}", e);
}
}
}
All other APIs are not implemented
Logging
Should things go sideways, the response bodies are logged at a trace log level. For example, using fern:
fern::Dispatch::new()
.format(|out, message, record| {
out.finish(format_args!(
"{}[{}][{}] {}",
chrono::Local::now().format("[%Y-%m-%d][%H:%M:%S]"),
record.target(),
record.level(),
message
))
})
.level(log::LevelFilter::Trace) // This has to be trace level
.chain(std::io::stdout())
.chain(fern::log_file("output.log")?)
.apply()?;
Path to version 1.0
As it stands, I'm making arbitrary changes to the library to support whatever I happen to be working on at the time. Additionally, I'm new to Rust, so I generally don't have a good sense of what I'm doing. To that end, this library's interface should be considered unstable and often not good. Until 1.0, it's entirely possible that every new release will introduce breaking changes. I'm not clear on where I want to end up with this library - should it be a "low level" API wrapper that just provides types to the Reddit API? Should it try and provide some usability improvement over the raw API? Should it focus on streamlining the most common use cases for bots to interact with Reddit?
Until I have some clarity myself around these questions, this library should be expected to change dramatically.
Dependencies
~7–18MB
~267K SLoC