2022-02-06 / @syui

rust , mastodon

mammutのexampleをまとめてみる

mastodon apiのrust libであるmammutですが、exampleがあるとわかりやすいと思ったので、作ってみました。

まず、mastodonへのrequestはこんな感じになります。

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)すると認証できます。

[package]
name = "test"
version = "0.0.1"

[dependencies]
dotenv = "0.15"
mammut = "0.13.0"
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(())
}
export TOKEN=''
export CLIENT_ID=''
export CLIENT_SECRET=''
export REDIRECT=''
export BASE='https://mstdn.syui.cf'
$ cargo run
$ ./target/debug/test

Data : https://docs.rs/mammut/latest/mammut/struct.Data.html

src/data

ちなみに、struct Dataはmammutが用意してるので、作る必要がありませんが、ファイルを分けて、何度も呼び出す場合、下記のようになります。envをやめてconfigにtokenなどを書いて読み込む例です。

なお、edition2021にすることで、様々な記法を省略できたりします。

[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" }
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<Self, ConfigError> {
        let s = Config::builder()
            .add_source(File::with_name("config"))
            .build()?;
        s.try_deserialize()
    }
}
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);
}
token = ""
client_id = ""
client_secret = ""
redirect = "localhost"
base = "https://mstdn.syui.cf"
$ cargo run

home dir

ここからは簡潔に紹介します。

$HOMEを使うにはshellexpandが便利です。

[dependencies]
shellexpand = "*"
impl Data {
    pub fn new() -> Result<Self, ConfigError> {
        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

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

fn timeline() -> mammut::Result<()> {
	let mastodon = token();
	let t = mastodon.get_home_timeline()?.initial_items;
	println!("{:?}", t);
	Ok(())
}

toot delete

#[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する処理が必要になります。

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も同時に投稿するのはこっち。

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