use crate::wire::WireSegment;
use nagisa_types::prelude::*;
use nagisa_types::segment::{ContactKind, Forward, ForwardNode, MusicShare};
use serde_json::{Map, Value};
pub(crate) fn encode_forward_node(n: &ForwardNode) -> Value {
let content = encode_segments(&n.content);
let content_json = serde_json::to_value(&content).unwrap_or(Value::Array(vec![]));
let mut node_data = obj(vec![
("user_id", Value::String(n.user.0.to_string())),
("nickname", Value::String(n.name.clone())),
("content", content_json),
]);
if let Some(t) = n.time {
node_data.insert("time".into(), Value::from(t));
}
Value::Object(obj(vec![("type", Value::String("node".into())), ("data", Value::Object(node_data))]))
}
pub(crate) fn encode_source(src: &ResourceSource) -> String {
match src {
ResourceSource::Url(u) => u.clone(),
ResourceSource::Path(p) => format!("file://{}", p.display()),
ResourceSource::Bytes(b) => {
format!("base64://{}", nagisa_core::wire::base64_encode(b))
}
}
}
fn media_file(media: &Media) -> Option<String> {
if let Some(src) = &media.source {
return Some(encode_source(src));
}
if let Some(recv) = &media.recv {
return recv.id.clone().or_else(|| recv.url.clone());
}
None
}
fn obj(pairs: Vec<(&str, Value)>) -> Map<String, Value> {
pairs.into_iter().map(|(k, v)| (k.to_string(), v)).collect()
}
fn apply_send_hints(data: &mut Map<String, Value>, hints: &nagisa_types::segment::MediaSendHints) {
if let Some(c) = hints.cache {
data.insert("cache".into(), Value::from(if c { 1 } else { 0 }));
}
if let Some(p) = hints.proxy {
data.insert("proxy".into(), Value::from(if p { 1 } else { 0 }));
}
if let Some(t) = hints.timeout {
data.insert("timeout".into(), Value::from(t));
}
}
pub fn encode_segments(segs: &[Segment]) -> Vec<WireSegment> {
segs.iter().filter_map(encode_segment).collect()
}
fn encode_segment(seg: &Segment) -> Option<WireSegment> {
let ws = match seg {
Segment::Text(t) => WireSegment::new("text", obj(vec![("text", Value::String(t.clone()))])),
Segment::Mention { user, .. } => WireSegment::new("at", obj(vec![("qq", Value::String(user.0.to_string()))])),
Segment::MentionAll => WireSegment::new("at", obj(vec![("qq", Value::String("all".into()))])),
Segment::Face { id, large, result_id, chain_count, sub_type } => {
let mut data = obj(vec![("id", Value::String(id.clone()))]);
if *large {
data.insert("large".into(), Value::Bool(true));
}
if let Some(rid) = result_id {
data.insert("resultId".into(), Value::String(rid.clone()));
}
if let Some(cc) = chain_count {
data.insert("chainCount".into(), Value::from(*cc));
}
if let Some(st) = sub_type {
data.insert("sub_type".into(), Value::from(*st));
}
WireSegment::new("face", data)
}
Segment::Reply { id, .. } => {
let v = id.onebot_id.map(|o| Value::String(o.to_string())).unwrap_or(Value::String(id.seq.to_string()));
let mut data = obj(vec![("id", v)]);
if id.seq != 0 {
data.insert("seq".into(), Value::String(id.seq.to_string()));
}
WireSegment::new("reply", data)
}
Segment::Image { res, sub_type, hints } => {
let file = media_file(res)?;
let mut data = obj(vec![("file", Value::String(file))]);
match sub_type {
ImageSubType::Sticker => {
data.insert("subType".into(), Value::from(1));
}
ImageSubType::Flash => {
data.insert("type".into(), Value::String("flash".into()));
}
ImageSubType::Normal => {}
}
if let Some(s) = &res.summary {
data.insert("summary".into(), Value::String(s.clone()));
}
apply_send_hints(&mut data, hints);
WireSegment::new("image", data)
}
Segment::Record { res, magic, hints } => {
let file = media_file(res)?;
let mut data = obj(vec![("file", Value::String(file))]);
if let Some(m) = magic {
data.insert("magic".into(), Value::from(*m));
}
apply_send_hints(&mut data, hints);
WireSegment::new("record", data)
}
Segment::Video { res, hints, thumb } => {
let file = media_file(res)?;
let mut data = obj(vec![("file", Value::String(file))]);
if let Some(t) = thumb {
data.insert("thumb".into(), Value::String(encode_source(t)));
}
apply_send_hints(&mut data, hints);
WireSegment::new("video", data)
}
Segment::File { id, name, size, .. } => {
let mut data = obj(vec![("file", Value::String(id.clone())), ("name", Value::String(name.clone()))]);
if *size != 0 {
data.insert("file_size".into(), Value::String(size.to_string()));
}
WireSegment::new("file", data)
}
Segment::Forward(fwd) => encode_forward(fwd),
Segment::MarketFace { emoji_id, key, package_id, summary, .. } => {
let mut data = obj(vec![
("emoji_id", Value::String(emoji_id.clone())),
("key", Value::String(key.clone())),
("emoji_package_id", Value::from(*package_id)),
]);
if let Some(s) = summary {
data.insert("summary".into(), Value::String(s.clone()));
}
WireSegment::new("mface", data)
}
Segment::LightApp { payload, .. } => {
WireSegment::new("json", obj(vec![("data", Value::String(payload.clone()))]))
}
Segment::Xml { service_id, payload } => {
let mut data = obj(vec![("data", Value::String(payload.clone()))]);
if let Some(id) = service_id {
data.insert("service_id".into(), Value::from(*id));
}
WireSegment::new("xml", data)
}
Segment::Poke { kind, id, strength, name } => {
let mut data = obj(vec![("type", Value::String(kind.to_string())), ("id", Value::String(id.to_string()))]);
if let Some(s) = strength {
data.insert("strength".into(), Value::String(s.to_string()));
}
if let Some(n) = name {
data.insert("name".into(), Value::String(n.clone()));
}
WireSegment::new("poke", data)
}
Segment::Contact { kind, id } => {
let ty = match kind {
ContactKind::Friend => "qq",
ContactKind::Group => "group",
};
WireSegment::new(
"contact",
obj(vec![("type", Value::String(ty.into())), ("id", Value::String(id.0.to_string()))]),
)
}
Segment::Location { lat, lon, title, content } => {
let mut data = obj(vec![("lat", Value::String(lat.to_string())), ("lon", Value::String(lon.to_string()))]);
if let Some(t) = title {
data.insert("title".into(), Value::String(t.clone()));
}
if let Some(c) = content {
data.insert("content".into(), Value::String(c.clone()));
}
WireSegment::new("location", data)
}
Segment::Music(share) => match share {
MusicShare::Platform { ty, id } => WireSegment::new(
"music",
obj(vec![("type", Value::String(ty.clone())), ("id", Value::String(id.clone()))]),
),
MusicShare::Custom { url, audio, title, content, image } => {
let mut data = obj(vec![
("type", Value::String("custom".into())),
("url", Value::String(url.clone())),
("audio", Value::String(audio.clone())),
("title", Value::String(title.clone())),
]);
if let Some(c) = content {
data.insert("content".into(), Value::String(c.clone()));
}
if let Some(i) = image {
data.insert("image".into(), Value::String(i.clone()));
}
WireSegment::new("music", data)
}
},
Segment::Share { url, title, content, image } => {
let mut data = obj(vec![("url", Value::String(url.clone())), ("title", Value::String(title.clone()))]);
if let Some(c) = content {
data.insert("content".into(), Value::String(c.clone()));
}
if let Some(i) = image {
data.insert("image".into(), Value::String(i.clone()));
}
WireSegment::new("share", data)
}
Segment::Rps { result } => {
let mut data = Map::new();
if let Some(r) = result {
data.insert("result".into(), Value::from(*r));
}
WireSegment::new("rps", data)
}
Segment::Dice { result } => {
let mut data = Map::new();
if let Some(r) = result {
data.insert("result".into(), Value::from(*r));
}
WireSegment::new("dice", data)
}
Segment::Shake => WireSegment::new("shake", Map::new()),
Segment::Anonymous { ignore } => {
let mut data = Map::new();
if let Some(ig) = ignore {
data.insert("ignore".into(), Value::Bool(*ig));
}
WireSegment::new("anonymous", data)
}
Segment::Keyboard { content } => WireSegment::new("keyboard", obj(vec![("content", content.clone())])),
Segment::Markdown { content } => {
WireSegment::new("markdown", obj(vec![("content", Value::String(content.clone()))]))
}
Segment::LongMsg { id } => WireSegment::new("longmsg", obj(vec![("id", Value::String(id.clone()))])),
Segment::FlashFile { title, file_set_id, scene_type } => {
let mut data = obj(vec![
("file_set_id", Value::String(file_set_id.clone())),
("scene_type", Value::from(*scene_type)),
]);
if let Some(t) = title {
data.insert("title".into(), Value::String(t.clone()));
}
WireSegment::new("flash_file", data)
}
Segment::MiniApp { payload } => {
WireSegment::new("miniapp", obj(vec![("data", Value::String(payload.clone()))]))
}
Segment::OnlineFile { msg_id, element_id, file_name, file_size, is_dir } => WireSegment::new(
"onlinefile",
obj(vec![
("msgId", Value::String(msg_id.clone())),
("elementId", Value::String(element_id.clone())),
("fileName", Value::String(file_name.clone())),
("fileSize", Value::String(file_size.clone())),
("isDir", Value::Bool(*is_dir)),
]),
),
Segment::FlashTransfer { file_set_id } => {
WireSegment::new("flashtransfer", obj(vec![("fileSetId", Value::String(file_set_id.clone()))]))
}
Segment::Raw { protocol, kind, data } if *protocol == Protocol::OneBot11 => {
WireSegment::new(kind.clone(), data.clone())
}
Segment::Raw { .. } => return None,
_ => return None,
};
Some(ws)
}
fn encode_forward(fwd: &Forward) -> WireSegment {
match fwd {
Forward::Ref { id, .. } => WireSegment::new("forward", obj(vec![("id", Value::String(id.clone()))])),
Forward::Nodes { nodes, summary, prompt, news, source, .. } => {
let node_vals: Vec<Value> = nodes.iter().map(encode_forward_node).collect();
let mut data = obj(vec![("nodes", Value::Array(node_vals))]);
if let Some(s) = source {
data.insert("source".into(), Value::String(s.clone()));
}
if let Some(s) = summary {
data.insert("summary".into(), Value::String(s.clone()));
}
if let Some(p) = prompt {
data.insert("prompt".into(), Value::String(p.clone()));
}
if !news.is_empty() {
let arr: Vec<Value> =
news.iter().map(|line| Value::Object(obj(vec![("text", Value::String(line.clone()))]))).collect();
data.insert("news".into(), Value::Array(arr));
}
WireSegment::new("forward", data)
}
}
}