+++ date = "2022-02-06" tags = ["rust", "mastodon"] title = "mammutのexampleをまとめてみる" slug = "rust-mammut" +++ mastodon apiのrust libである[mammut](https://docs.rs/mammut/latest/mammut/)ですが、exampleがあるとわかりやすいと思ったので、作ってみました。 まず、mastodonへのrequestはこんな感じになります。 ```sh curl -H "Content-Type: application/json" -X POST -Ss https://$host/oauth/token \ -d "{ \"client_id\": \"$client_id\", \"client_secret\": \"$client_secret\", \"grant_type\": \"password\", \"username\": \"$username\", \"password\": \"$password\", \"scope\": \"$scope\" }" ``` mammutでは`Data`として認証に必要な要素がまとめられてますので、各種要素を入れて`Mastodon::from_data(data)`すると認証できます。 ```toml:Cargo.toml [package] name = "test" version = "0.0.1" [dependencies] dotenv = "0.15" mammut = "0.13.0" ``` ```rust:src/main.rs extern crate mammut; extern crate dotenv; use std::env; use mammut::{Data, Mastodon}; fn main() -> mammut::Result<()> { let data = Data { base: env::var("BASE").unwrap().into(), client_id: env::var("CLIENT_ID").unwrap().into(), client_secret: env::var("CLIENT_SECRET").unwrap().into(), redirect: env::var("REDIRECT").unwrap().into(), token: env::var("TOKEN").unwrap().into(), }; let mastodon = Mastodon::from_data(data); let t = mastodon.verify_credentials(); println!("{:?}", t); Ok(()) } ``` ```sh:.env export TOKEN='' export CLIENT_ID='' export CLIENT_SECRET='' export REDIRECT='' export BASE='https://mstdn.syui.ai' ``` ```sh $ cargo run $ ./target/debug/test ``` Data : https://docs.rs/mammut/latest/mammut/struct.Data.html ### src/data ちなみに、`struct Data`はmammutが用意してるので、作る必要がありませんが、ファイルを分けて、何度も呼び出す場合、下記のようになります。`env`をやめて`config`にtokenなどを書いて読み込む例です。 なお、`edition`を`2021`にすることで、様々な記法を省略できたりします。 ```toml:Cargo.toml [package] name = "test" version = "0.0.1" edition = "2021" [dependencies] dotenv = "0.15" mammut = "0.13.0" serde_derive = "*" serde = "*" config = { git = "https://github.com/mehcode/config-rs", branch = "master" } ``` ```rust:src/data.rs use config::{Config, ConfigError, File}; use serde_derive::Deserialize; use std::borrow::Cow; #[derive(Debug, Deserialize)] #[allow(unused)] pub struct Data { pub base: Cow<'static, str>, pub token: Cow<'static, str>, pub client_id: Cow<'static, str>, pub client_secret: Cow<'static, str>, pub redirect: Cow<'static, str>, } impl Data { pub fn new() -> Result { let s = Config::builder() .add_source(File::with_name("config")) .build()?; s.try_deserialize() } } ``` ```rust:src/main.rs pub mod data; use data::Data as Datas; use mammut::{Data, Mastodon}; fn token() -> Mastodon { let data = Datas::new().unwrap(); let data = Data { base: data.base, token: data.token, client_id: data.client_id, client_secret: data.client_secret, redirect: data.redirect, }; let t = Mastodon::from_data(data); return t; } fn main() { let mastodon = token(); let t = mastodon.verify_credentials(); println!("{:#?}", t); } ``` ```sh:config.toml token = "" client_id = "" client_secret = "" redirect = "localhost" base = "https://mstdn.syui.ai" ``` ```sh $ cargo run ``` ### home dir ここからは簡潔に紹介します。 `$HOME`を使うには`shellexpand`が便利です。 ```rust:Cargo.toml [dependencies] shellexpand = "*" ``` ```rust impl Data { pub fn new() -> Result { let d = shellexpand::tilde("~") + "/.config/msr/config.toml"; let s = Config::builder() .add_source(File::with_name(&d)) .add_source(config::Environment::with_prefix("APP")) .build()?; s.try_deserialize() } } ``` ### toot post ```rust fn post() { let mastodon = token(); let message = "test".to_string(); let status_b = StatusBuilder::new(format!("{}", message)); let post = mastodon.new_status(status_b); println!("{:?}", post); } ``` ### timeline ```rust fn timeline() -> mammut::Result<()> { let mastodon = token(); let t = mastodon.get_home_timeline()?.initial_items; println!("{:?}", t); Ok(()) } ``` ### toot delete ```rust #[allow(unused_must_use)] fn delete() -> mammut::Result<()> { let mastodon = token(); let id = &mastodon.get_home_timeline()?.initial_items[0].id; mastodon.delete_status(id); Ok(()) } ``` ### upload media `media_ids`が必要になるので、手順としては、画像をmastodonのmedia serverにuploadする処理と、その出力にある`media_ids`をtootとしてpostする処理が必要になります。 ```rust use mammut::{Data, Mastodon, StatusBuilder, MediaBuilder}; fn media() { let mastodon = token(); let file = "./test.png".to_string(); let t = mastodon.media(file.into()); println!("{:?}", t); let id = t.as_ref().unwrap(); let mid = Some(vec![id.id.to_string()]); let status = "#media".to_string(); let status_b = StatusBuilder { status: status, in_reply_to_id: None, media_ids: mid, sensitive: None, spoiler_text: None, visibility: None, }; let post = mastodon.new_status(status_b); println!("{:?}", post); } } ``` StatusBuilder : https://docs.rs/mammut/0.11.0/mammut/status_builder/struct.StatusBuilder.html `text`も同時に投稿するのはこっち。 ```rust use std::borrow::Cow; let text = "test"; let s = Cow::Owned(String::from(text)); let status_b = StatusBuilder { status: s, in_reply_to_id: None, media_ids: mid, sensitive: None, spoiler_text: None, visibility: None, }; ``` ref : https://github.com/syui/msr