Update push

This commit is contained in:
2023-10-28 09:13:32 +00:00
committed by GitHub
parent 2f53a8a35b
commit 289be3a1f0
3 changed files with 296 additions and 22 deletions

View File

@@ -76,6 +76,9 @@ pub struct EveryPushConfig {
#[serde(default = "default_true")]
/// Whether to filter author name
pub filter_author: bool,
#[serde(default = "default_true")]
/// Whether to add artwork link
pub add_link: bool,
}
#[derive(Debug, Serialize, Deserialize)]

View File

@@ -1,3 +1,4 @@
use crate::error::PixivDownloaderError;
use crate::gettext;
use crate::pixiv_link::remove_track;
use html_parser::Dom;
@@ -31,7 +32,23 @@ impl DescriptionNode {
self.attrs.insert(String::from(k), String::from(v))
}
pub fn is_link(&self) -> bool {
pub fn is_em(&self) -> bool {
self.tag == "em" || self.tag == "i"
}
pub fn is_headline(&self) -> bool {
match self.tag.as_str() {
"h1" => true,
"h2" => true,
"h3" => true,
"h4" => true,
"h5" => true,
"h6" => true,
_ => false,
}
}
pub fn is_link(&self, md_mode: bool) -> bool {
if self.tag != "a" {
return false;
}
@@ -39,12 +56,36 @@ impl DescriptionNode {
return false;
}
let href = self.attrs.get("href").unwrap();
if href.as_str() == self.data.as_str() {
if !md_mode && href.as_str() == self.data.as_str() {
return false;
}
true
}
pub fn is_paragraph(&self) -> bool {
self.tag == "p" || self.tag == "paragraph"
}
pub fn is_strong(&self) -> bool {
self.tag == "strong" || self.tag == "b"
}
pub fn to_em(&self) -> String {
format!("*{}*", self.data.as_str())
}
pub fn to_headline(&self) -> String {
let mut s = String::from("#");
let level = self.tag.chars().last().unwrap().to_digit(10).unwrap();
for _ in 1..level {
s += "#";
}
s += " ";
s += self.data.as_str();
s += "\n";
s
}
pub fn to_link(&self) -> String {
format!(
"[{}]({})",
@@ -52,6 +93,16 @@ impl DescriptionNode {
self.attrs.get("href").unwrap()
)
}
pub fn to_paragraph(&self) -> String {
let mut s = self.data.clone();
s += "\n\n";
s
}
pub fn to_strong(&self) -> String {
format!("**{}**", self.data.as_str())
}
}
/// A simple HTML parser to parse description HTML
@@ -60,13 +111,16 @@ pub struct DescriptionParser {
nodes: Vec<DescriptionNode>,
/// Output
pub data: String,
/// Markdown mode
md_mode: bool,
}
impl DescriptionParser {
pub fn new() -> Self {
pub fn new(md_mode: bool) -> Self {
Self {
nodes: Vec::new(),
data: String::from(""),
md_mode,
}
}
@@ -85,10 +139,11 @@ impl DescriptionParser {
if tag == "script" || tag == "style" {
return;
} else if tag == "br" {
let br = if self.md_mode { " \n" } else { "\n" };
if self.nodes.len() == 0 {
self.data += "\n";
self.data += br;
} else {
self.nodes.last_mut().unwrap().data += "\n";
self.nodes.last_mut().unwrap().data += br;
}
return;
}
@@ -109,56 +164,86 @@ impl DescriptionParser {
self.iter(n);
}
let node = self.nodes.pop().unwrap();
let s = if node.is_link() {
let mut is_paragraph = false;
let s = if node.is_link(self.md_mode) {
node.to_link()
} else if self.md_mode && node.is_headline() {
node.to_headline()
} else if self.md_mode && node.is_paragraph() {
is_paragraph = true;
node.to_paragraph()
} else if self.md_mode && node.is_strong() {
node.to_strong()
} else if self.md_mode && node.is_em() {
node.to_em()
} else {
node.data
};
if self.nodes.len() == 0 {
while self.md_mode && is_paragraph && !self.data.ends_with("\n\n") {
self.data += "\n";
}
self.data += s.as_str();
} else {
self.nodes.last_mut().unwrap().data += s.as_str();
let n = self.nodes.last_mut().unwrap();
while self.md_mode && is_paragraph && !n.data.ends_with("\n\n") {
n.data += "\n";
}
n.data += s.as_str();
}
}
}
}
pub fn parse(&mut self, desc: &str) -> Result<(), ()> {
let r = Dom::parse(desc);
pub fn parse<S: AsRef<str> + ?Sized>(&mut self, desc: &S) -> Result<(), PixivDownloaderError> {
let r = Dom::parse(desc.as_ref());
if r.is_err() {
println!("{} {}", gettext("Failed to parse HTML:"), r.unwrap_err());
return Err(());
return Err(format!("{} {}", gettext("Failed to parse HTML:"), r.unwrap_err()).into());
}
let dom = r.unwrap();
if dom.errors.len() > 0 {
println!("{}", gettext("Some errors occured during parsing:"));
let mut s = String::from(gettext("Some errors occured during parsing:"));
for i in dom.errors.iter() {
println!("{}", i);
s += "\n";
s += i;
}
return Err(s.into());
}
for node in dom.children.iter() {
self.iter(node)
}
if self.nodes.len() != 0 {
println!(
println!();
return Err(format!(
"{} {:?}",
gettext("There are some nodes still in stack:"),
self.nodes
);
return Err(());
)
.into());
}
Ok(())
}
}
pub fn parse_description(desc: &str) -> Option<String> {
let mut p = DescriptionParser::new();
pub fn parse_description<S: AsRef<str> + ?Sized>(desc: &S) -> Option<String> {
let mut p = DescriptionParser::new(false);
match p.parse(desc) {
Ok(_) => Some(p.data),
Err(_) => None,
Err(e) => {
println!("{}", e);
None
}
}
}
pub fn convert_description_to_md<S: AsRef<str> + ?Sized>(
desc: &S,
) -> Result<String, PixivDownloaderError> {
let mut p = DescriptionParser::new(true);
p.parse(desc)?;
Ok(p.data)
}
#[test]
fn test_parse_description() {
assert_eq!(
@@ -178,3 +263,26 @@ fn test_parse_description() {
parse_description("<a href=\"/jump.php?https%3A%2F%2Fa.com\">https://a.com</a>")
)
}
#[test]
fn test_convert_description_to_md() {
assert_eq!(
String::from("test \n[https://a.com](https://a.com)"),
convert_description_to_md(
"test<br/><a href=\"/jump.php?https%3A%2F%2Fa.com\">https://a.com</a>"
)
.unwrap()
);
assert_eq!(
String::from("# He\n## Be\ntest"),
convert_description_to_md("<h1>He</h1><h2>Be</h2>test").unwrap()
);
assert_eq!(
String::from("D\n\nHe\n\nBe\n\ntest"),
convert_description_to_md("D<p>He</p><p>Be</p>test").unwrap()
);
assert_eq!(
String::from("# Head\nD\n\nHe\n\nBe\n\nt***e**s*t\n\n[Link](https://a.com)\n\n"),
convert_description_to_md("<h1>Head</h1>D<p>He</p><p>Be</p>t<em><strong>e</strong>s</em>t<p><a href=\"/jump.php?https%3A%2F%2Fa.com\">Link</a></p>").unwrap()
);
}

View File

@@ -1,10 +1,11 @@
use super::super::super::preclude::*;
use crate::db::push_task::{AuthorLocation, EveryPushConfig, PushConfig};
use crate::error::PixivDownloaderError;
use crate::get_helper;
use crate::opt::author_name_filter::AuthorFiler;
use crate::parser::description::DescriptionParser;
use crate::pixivapp::illust::PixivAppIllust;
use crate::push::every_push::{EveryPushClient, EveryPushTextType};
use crate::{get_helper, gettext};
use json::JsonValue;
struct RunContext<'a> {
@@ -29,6 +30,25 @@ impl<'a> RunContext<'a> {
}
}
pub fn id(&self) -> Option<u64> {
match self.illust {
Some(i) => match i.id() {
Some(i) => return Some(i),
None => {}
},
None => {}
}
match self.data {
Some(d) => d["illustId"]
.as_u64()
.or_else(|| match d["illustId"].as_str() {
Some(s) => s.parse().ok(),
None => None,
}),
None => None,
}
}
pub fn _author(&self) -> Option<&str> {
match self.illust {
Some(i) => match i.user_name() {
@@ -58,6 +78,23 @@ impl<'a> RunContext<'a> {
}
}
pub fn user_id(&self) -> Option<u64> {
match self.illust {
Some(i) => match i.user_id() {
Some(u) => return Some(u),
None => {}
},
None => {}
}
match self.data {
Some(d) => d["userId"].as_u64().or_else(|| match d["userId"].as_str() {
Some(s) => s.parse().ok(),
None => None,
}),
None => None,
}
}
/// Whether to filter author name
pub fn filter_author(&self) -> bool {
match self.cfg {
@@ -104,11 +141,137 @@ impl<'a> RunContext<'a> {
}
}
pub fn add_author<S: AsRef<str> + ?Sized>(&self, text: &mut String, author: &S) {
text.push_str(gettext("by "));
let author = author.as_ref();
if let Some(uid) = self.user_id() {
text.push_str(&format!(
"[{}](https://www.pixiv.net/users/{})",
author, uid
));
} else {
text.push_str(author);
}
text.push_str(" \n");
}
pub fn desc(&self) -> Option<&str> {
match self.illust {
Some(i) => {
if !i.caption_is_empty() {
return i.caption();
}
}
None => {}
}
match self.data {
Some(d) => d["description"]
.as_str()
.or_else(|| d["illustComment"].as_str()),
None => None,
}
}
pub async fn send_every_push(&self, cfg: &EveryPushConfig) -> Result<(), PixivDownloaderError> {
let client = EveryPushClient::new(&cfg.push_server);
match cfg.typ {
EveryPushTextType::Text => {}
EveryPushTextType::Markdown => {}
EveryPushTextType::Text => {
let mut title = self.title().map(|s| s.to_owned());
let author = self.author();
if cfg.author_locations.contains(&AuthorLocation::Title) {
if let Some(t) = &title {
if let Some(a) = &author {
title = Some(format!("{} - {}", t, a));
}
}
}
let mut text = String::new();
if cfg.author_locations.contains(&AuthorLocation::Top) {
if let Some(a) = &author {
text.push_str(gettext("by "));
text.push_str(a);
text.push_str("\n");
}
}
if cfg.add_link {
if let Some(id) = self.id() {
text.push_str(&format!("https://www.pixiv.net/artworks/{}\n", id));
}
}
if let Some(desc) = self.desc() {
let mut p = DescriptionParser::new(false);
p.parse(desc)?;
text.push_str(&p.data);
text.push_str("\n");
}
if cfg.author_locations.contains(&AuthorLocation::Bottom) {
if let Some(a) = &author {
text.push_str(gettext("by "));
text.push_str(a);
text.push_str("\n");
}
}
client
.push_message(
&cfg.push_token,
&text,
title.as_ref(),
cfg.topic_id.as_ref(),
Some(EveryPushTextType::Text),
)
.await?;
}
EveryPushTextType::Markdown => {
let mut title = self.title().map(|s| s.to_owned());
let author = self.author();
if cfg.author_locations.contains(&AuthorLocation::Title) {
if let Some(t) = &title {
if let Some(a) = &author {
title = Some(format!("{} - {}", t, a));
}
}
}
let mut text = String::new();
let len = self.len().unwrap_or(1);
for i in 0..len {
if let Some(url) = self.get_image_url(i).await? {
text.push_str(&format!("![​]({})", url));
}
}
if cfg.author_locations.contains(&AuthorLocation::Top) {
if let Some(a) = &author {
self.add_author(&mut text, a);
}
}
if cfg.add_link {
if let Some(id) = self.id() {
let link = format!("https://www.pixiv.net/artworks/{}", id);
text.push_str(&format!("[{}]({}) \n", link, link));
}
}
if let Some(desc) = self.desc() {
let mut p = DescriptionParser::new(true);
p.parse(desc)?;
text.push_str(&p.data);
if !p.data.ends_with("\n\n") {
text.push_str("\n\n");
}
}
if cfg.author_locations.contains(&AuthorLocation::Bottom) {
if let Some(a) = &author {
self.add_author(&mut text, a);
}
}
client
.push_message(
&cfg.push_token,
&text,
title.as_ref(),
cfg.topic_id.as_ref(),
Some(EveryPushTextType::Markdown),
)
.await?;
}
EveryPushTextType::Image => {
let mut title = self.title().map(|s| s.to_owned());
if cfg.author_locations.contains(&AuthorLocation::Title) {