mirror of
https://framagit.org/veretcle/scootaloo.git
synced 2025-07-20 17:11:19 +02:00
feature: make thread in Twitter thread in Mastodon
This commit is contained in:
@@ -1,7 +1,4 @@
|
||||
// std
|
||||
use std::fs::read_to_string;
|
||||
|
||||
// toml
|
||||
use serde::Deserialize;
|
||||
|
||||
/// General configuration Struct
|
||||
|
32
src/lib.rs
32
src/lib.rs
@@ -1,4 +1,3 @@
|
||||
// auto-imports
|
||||
mod error;
|
||||
use crate::error::ScootalooError;
|
||||
|
||||
@@ -19,22 +18,13 @@ mod state;
|
||||
use state::{read_state, write_state, TweetToToot};
|
||||
pub use state::init_db;
|
||||
|
||||
// std
|
||||
use std::borrow::Cow;
|
||||
|
||||
// tokio
|
||||
use tokio::fs::remove_file;
|
||||
|
||||
// elefren
|
||||
use elefren::{
|
||||
prelude::*,
|
||||
status_builder::StatusBuilder,
|
||||
};
|
||||
|
||||
// log
|
||||
use log::{info, warn, error, debug};
|
||||
|
||||
// rusqlite
|
||||
use rusqlite::Connection;
|
||||
|
||||
/// This is where the magic happens
|
||||
@@ -72,6 +62,8 @@ pub async fn run(config: Config) {
|
||||
|
||||
for tweet in &feed {
|
||||
debug!("Treating Tweet {} inside feed", tweet.id);
|
||||
// initiate the toot_reply_id var
|
||||
let mut toot_reply_id: Option<String> = None;
|
||||
// determine if the tweet is part of a thread (response to self) or a standard response
|
||||
if let Some(r) = &tweet.in_reply_to_screen_name {
|
||||
if &r.to_lowercase() != &config.twitter.username.to_lowercase() {
|
||||
@@ -79,6 +71,11 @@ pub async fn run(config: Config) {
|
||||
info!("Tweet is a direct response, skipping");
|
||||
continue;
|
||||
}
|
||||
|
||||
let searched_toot = read_state(&conn, tweet.in_reply_to_status_id).unwrap_or(None);
|
||||
if let Some(i) = searched_toot {
|
||||
toot_reply_id = Some(i.toot_id);
|
||||
};
|
||||
};
|
||||
|
||||
// build basic status by just yielding text and dereferencing contained urls
|
||||
@@ -125,10 +122,17 @@ pub async fn run(config: Config) {
|
||||
// finished reuploading attachments, now let’s do the toot baby!
|
||||
|
||||
debug!("Building corresponding Mastodon status");
|
||||
let status = StatusBuilder::new()
|
||||
.status(&status_text)
|
||||
.media_ids(status_medias)
|
||||
.build()
|
||||
|
||||
let mut status_builder = StatusBuilder::new();
|
||||
|
||||
status_builder.status(&status_text)
|
||||
.media_ids(status_medias);
|
||||
|
||||
if let Some(i) = toot_reply_id {
|
||||
status_builder.in_reply_to(&i);
|
||||
}
|
||||
|
||||
let status = status_builder.build()
|
||||
.expect(&format!("Cannot build status with text {}", &status_text));
|
||||
|
||||
// publish status
|
||||
|
11
src/main.rs
11
src/main.rs
@@ -1,14 +1,7 @@
|
||||
// self
|
||||
use scootaloo::*;
|
||||
|
||||
// clap
|
||||
use clap::{App, Arg, SubCommand};
|
||||
|
||||
// log
|
||||
use log::{LevelFilter, error};
|
||||
use simple_logger::SimpleLogger;
|
||||
|
||||
// std
|
||||
use std::str::FromStr;
|
||||
|
||||
const DEFAULT_CONFIG_PATH: &'static str = "/usr/local/etc/scootaloo.toml";
|
||||
@@ -21,7 +14,7 @@ fn main() {
|
||||
.short("c")
|
||||
.long("config")
|
||||
.value_name("CONFIG_FILE")
|
||||
.help(&*format!("TOML config file for scootaloo (default {})", DEFAULT_CONFIG_PATH))
|
||||
.help(&format!("TOML config file for scootaloo (default {})", DEFAULT_CONFIG_PATH))
|
||||
.takes_value(true)
|
||||
.display_order(1))
|
||||
.arg(Arg::with_name("log_level")
|
||||
@@ -49,7 +42,7 @@ fn main() {
|
||||
.short("c")
|
||||
.long("config")
|
||||
.value_name("CONFIG_FILE")
|
||||
.help(&*format!("TOML config file for scootaloo (default {})", DEFAULT_CONFIG_PATH))
|
||||
.help(&format!("TOML config file for scootaloo (default {})", DEFAULT_CONFIG_PATH))
|
||||
.takes_value(true)
|
||||
.display_order(1)))
|
||||
.get_matches();
|
||||
|
@@ -1,24 +1,16 @@
|
||||
// auto imports
|
||||
use crate::config::MastodonConfig;
|
||||
|
||||
// std
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
error::Error,
|
||||
collections::HashMap,
|
||||
io::stdin,
|
||||
};
|
||||
|
||||
// htmlescape
|
||||
use htmlescape::decode_html;
|
||||
|
||||
// egg-mode
|
||||
use egg_mode::{
|
||||
tweet::Tweet,
|
||||
entities::{UrlEntity, MentionEntity},
|
||||
};
|
||||
|
||||
// elefren
|
||||
use elefren::{
|
||||
prelude::*,
|
||||
apps::App,
|
||||
|
174
src/state.rs
174
src/state.rs
@@ -1,13 +1,7 @@
|
||||
// auto-imports
|
||||
use crate::config::ScootalooConfig;
|
||||
|
||||
// std
|
||||
use std::error::Error;
|
||||
|
||||
// log
|
||||
use log::debug;
|
||||
|
||||
// rusqlite
|
||||
use rusqlite::{Connection, params, OptionalExtension};
|
||||
|
||||
/// Struct for each query line
|
||||
@@ -50,9 +44,6 @@ pub fn write_state(conn: &Connection, t: TweetToToot) -> Result<(), Box<dyn Erro
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/*********
|
||||
* Main functions
|
||||
*********/
|
||||
/// Initiates the DB from path
|
||||
pub fn init_db(config: &ScootalooConfig) -> Result<(), Box<dyn Error>> {
|
||||
debug!("Initializing DB for Scootaloo");
|
||||
@@ -78,7 +69,7 @@ mod tests {
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_db() {
|
||||
fn test_init_db() {
|
||||
let scootaloo_config = ScootalooConfig {
|
||||
db_path: String::from("/tmp/test_init_db.sqlite"),
|
||||
cache_path: String::from("/tmp/scootaloo"),
|
||||
@@ -96,57 +87,136 @@ mod tests {
|
||||
[],
|
||||
).unwrap();
|
||||
|
||||
// write a state to DB
|
||||
let t = TweetToToot {
|
||||
remove_file(scootaloo_config.db_path).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_write_state() {
|
||||
let scootaloo_config = ScootalooConfig {
|
||||
db_path: String::from("/tmp/test_write_state.sqlite"),
|
||||
cache_path: String::from("/tmp/scootaloo"),
|
||||
};
|
||||
|
||||
init_db(&scootaloo_config).unwrap();
|
||||
|
||||
let conn = Connection::open(&scootaloo_config.db_path).unwrap();
|
||||
|
||||
let t_in = TweetToToot {
|
||||
tweet_id: 123456789,
|
||||
toot_id: String::from("987654321"),
|
||||
};
|
||||
write_state(&conn, t).unwrap();
|
||||
|
||||
let mut stmt = conn.prepare("SELECT * FROM tweet_to_toot limit 1;").unwrap();
|
||||
let mut rows = stmt.query([]).unwrap();
|
||||
write_state(&conn, t_in).unwrap();
|
||||
|
||||
while let Some(row) = rows.next().unwrap() {
|
||||
assert_eq!(123456789 as u64, row.get::<_, u64>(0).unwrap());
|
||||
assert_eq!("987654321", row.get::<_, String>(1).unwrap());
|
||||
}
|
||||
let mut stmt = conn.prepare("SELECT * FROM tweet_to_toot;").unwrap();
|
||||
|
||||
// write several other states
|
||||
let (t1, t2) = (
|
||||
TweetToToot {
|
||||
tweet_id: 11111111,
|
||||
toot_id: String::from("tamerelol"),
|
||||
},
|
||||
TweetToToot {
|
||||
tweet_id: 1123456789,
|
||||
toot_id: String::from("tonperemdr"),
|
||||
});
|
||||
let t_out = stmt.query_row([], |row| {
|
||||
Ok(TweetToToot {
|
||||
tweet_id: row.get(0).unwrap(),
|
||||
toot_id: row.get(1).unwrap(),
|
||||
})
|
||||
}).unwrap();
|
||||
|
||||
write_state(&conn, t1).unwrap();
|
||||
write_state(&conn, t2).unwrap();
|
||||
|
||||
match read_state(&conn, None).unwrap() {
|
||||
Some(i) => {
|
||||
assert_eq!(1123456789, i.tweet_id);
|
||||
assert_eq!("tonperemdr", &i.toot_id);
|
||||
},
|
||||
None => panic!("This should not happen!"),
|
||||
}
|
||||
|
||||
match read_state(&conn, Some(11111111)).unwrap() {
|
||||
Some(i) => {
|
||||
assert_eq!(11111111, i.tweet_id);
|
||||
assert_eq!("tamerelol", &i.toot_id);
|
||||
},
|
||||
None => panic!("This should not happen!"),
|
||||
}
|
||||
|
||||
match read_state(&conn, Some(0000000)).unwrap() {
|
||||
Some(_) => panic!("This should not happen"),
|
||||
_ => (),
|
||||
}
|
||||
assert_eq!(t_out.tweet_id, 123456789);
|
||||
assert_eq!(t_out.toot_id, String::from("987654321"));
|
||||
|
||||
remove_file(&scootaloo_config.db_path).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_none_to_tweet_id_read_state() {
|
||||
let scootaloo_config = ScootalooConfig {
|
||||
db_path: String::from("/tmp/test_none_to_tweet_id_read_state.sqlite"),
|
||||
cache_path: String::from("/tmp/scootaloo"),
|
||||
};
|
||||
|
||||
init_db(&scootaloo_config).unwrap();
|
||||
|
||||
let conn = Connection::open(&scootaloo_config.db_path).unwrap();
|
||||
|
||||
conn.execute(
|
||||
"INSERT INTO tweet_to_toot (tweet_id, toot_id)
|
||||
VALUES
|
||||
(101, 'A'),
|
||||
(102, 'B');",
|
||||
[],
|
||||
).unwrap();
|
||||
|
||||
let t_out = read_state(&conn, None).unwrap().unwrap();
|
||||
|
||||
remove_file(&scootaloo_config.db_path).unwrap();
|
||||
|
||||
assert_eq!(t_out.tweet_id, 102);
|
||||
assert_eq!(t_out.toot_id, "B");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_none_to_none_read_state() {
|
||||
let scootaloo_config = ScootalooConfig {
|
||||
db_path: String::from("/tmp/test_none_to_none_read_state.sqlite"),
|
||||
cache_path: String::from("/tmp/scootaloo"),
|
||||
};
|
||||
|
||||
init_db(&scootaloo_config).unwrap();
|
||||
|
||||
let conn = Connection::open(&scootaloo_config.db_path).unwrap();
|
||||
|
||||
let t_out = read_state(&conn, None).unwrap();
|
||||
|
||||
remove_file(&scootaloo_config.db_path).unwrap();
|
||||
|
||||
assert!(t_out.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tweet_id_to_none_read_state() {
|
||||
let scootaloo_config = ScootalooConfig {
|
||||
db_path: String::from("/tmp/test_tweet_id_to_none_read_state.sqlite"),
|
||||
cache_path: String::from("/tmp/scootaloo"),
|
||||
};
|
||||
|
||||
init_db(&scootaloo_config).unwrap();
|
||||
|
||||
let conn = Connection::open(&scootaloo_config.db_path).unwrap();
|
||||
|
||||
conn.execute(
|
||||
"INSERT INTO tweet_to_toot (tweet_id, toot_id)
|
||||
VALUES
|
||||
(100, 'A');",
|
||||
[],
|
||||
).unwrap();
|
||||
|
||||
let t_out = read_state(&conn, Some(101)).unwrap();
|
||||
|
||||
remove_file(&scootaloo_config.db_path).unwrap();
|
||||
|
||||
assert!(t_out.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tweet_id_to_tweet_id_read_state() {
|
||||
let scootaloo_config = ScootalooConfig {
|
||||
db_path: String::from("/tmp/test_tweet_id_to_tweet_id_read_state.sqlite"),
|
||||
cache_path: String::from("/tmp/scootaloo"),
|
||||
};
|
||||
|
||||
init_db(&scootaloo_config).unwrap();
|
||||
|
||||
let conn = Connection::open(&scootaloo_config.db_path).unwrap();
|
||||
|
||||
conn.execute(
|
||||
"INSERT INTO tweet_to_toot (tweet_id, toot_id)
|
||||
VALUES
|
||||
(100, 'A');",
|
||||
[],
|
||||
).unwrap();
|
||||
|
||||
let t_out = read_state(&conn, Some(100)).unwrap().unwrap();
|
||||
|
||||
remove_file(&scootaloo_config.db_path).unwrap();
|
||||
|
||||
assert_eq!(t_out.tweet_id, 100);
|
||||
assert_eq!(t_out.toot_id, "A");
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user