atagen

macltok

public
Star0
Watch0

Commit 2656c1d26d6b

tarball

atagen <boss@atagen.co>2026-10-25 05:56 UTC

3 files changed+148-9
packs: preserve clone usage and outgoing sticker names
Modified file: src/engine/packs.rs+108 additions−9 deletions
@@ -220,8 +220,10 @@ pub(super) async fn run_detached(
220220 from_pack,
221221 shortcode,
222222 to,
223+ usage,
223224 } => {
224- let image = pack_image(&client, &from_pack, &shortcode).await?;
225+ let mut image = pack_image(&client, &from_pack, &shortcode).await?;
226+ apply_usage(&mut image, usage);
225227 let image = rehost_image(&client, image, max_bytes).await?;
226228 insert(&mutations, to, vec![(shortcode, image)], None, Vec::new()).await
227229 }
@@ -230,8 +232,12 @@ pub(super) async fn run_detached(
230232 event_id,
231233 to,
232234 shortcode,
235+ usage,
233236 } => {
234- let image = sticker_image(&client, room_id, event_id).await?;
237+ let mut image = sticker_image(&client, room_id, event_id).await?;
238+ let body = sticker_body(&image, &shortcode);
239+ image.insert("body".to_owned(), body.into());
240+ apply_usage(&mut image, usage);
235241 let image = rehost_image(&client, image, max_bytes).await?;
236242 insert(&mutations, to, vec![(shortcode, image)], None, Vec::new()).await
237243 }
@@ -239,7 +245,19 @@ pub(super) async fn run_detached(
239245 from_pack,
240246 to,
241247 display_name,
242- } => clone_pack(&client, &mutations, max_bytes, &from_pack, to, display_name).await,
248+ usage,
249+ } => {
250+ clone_pack(
251+ &client,
252+ &mutations,
253+ max_bytes,
254+ &from_pack,
255+ to,
256+ display_name,
257+ usage,
258+ )
259+ .await
260+ }
243261 _ => Err(Error::new(ErrorKind::Other, "pack command was misrouted")),
244262 }
245263 }
@@ -251,6 +269,7 @@ async fn clone_pack(
251269 from_pack: &str,
252270 to: ImagePackSource,
253271 display_name: Option<String>,
272+ usage: Option<Vec<PackUsage>>,
254273 ) -> Result<CommandOutput, Error> {
255274 let source = resolve_pack_id(client, from_pack)
256275 .ok_or_else(|| Error::new(ErrorKind::Invalid, "no pack has that id"))?;
@@ -265,7 +284,8 @@ async fn clone_pack(
265284 Some(JsonValue::Object(images)) => images
266285 .iter()
267286 .filter_map(|(shortcode, image)| {
268- let image = with_pack_usage(image.as_object()?.clone(), &meta);
287+ let mut image = with_pack_usage(image.as_object()?.clone(), &meta);
288+ apply_usage(&mut image, usage.clone());
269289 Some((shortcode.clone(), image))
270290 })
271291 .collect(),
@@ -340,7 +360,7 @@ async fn send_sticker(
340360 .and_then(mxc)
341361 .ok_or_else(|| Error::new(ErrorKind::Invalid, "that image has no usable url"))?;
342362 let content = json!({
343- "body": image.get("body").and_then(JsonValue::as_str).unwrap_or(shortcode),
363+ "body": sticker_body(&image, shortcode),
344364 "url": url,
345365 "info": image.get("info").cloned().unwrap_or_else(|| json!({})),
346366 });
@@ -487,12 +507,43 @@ async fn write_pack(
487507 }
488508
489509 fn with_pack_usage(mut image: JsonObject, meta: &JsonObject) -> JsonObject {
490- if !image.contains_key("usage")
491- && let Some(usage) = meta.get("usage")
492- {
493- image.insert("usage".to_owned(), usage.clone());
510+ let usage = known_usage(image.get("usage"))
511+ .or_else(|| known_usage(meta.get("usage")))
512+ .unwrap_or_else(|| vec![PackUsage::Emoticon, PackUsage::Sticker]);
513+ image.insert("usage".to_owned(), usage_json(usage));
514+ image
515+}
516+
517+fn known_usage(value: Option<&JsonValue>) -> Option<Vec<PackUsage>> {
518+ let values = value?.as_array()?;
519+ let usage = [
520+ ("emoticon", PackUsage::Emoticon),
521+ ("sticker", PackUsage::Sticker),
522+ ]
523+ .into_iter()
524+ .filter_map(|(name, usage)| {
525+ values
526+ .iter()
527+ .any(|value| value.as_str() == Some(name))
528+ .then_some(usage)
529+ })
530+ .collect::<Vec<_>>();
531+ (!usage.is_empty()).then_some(usage)
532+}
533+
534+fn apply_usage(image: &mut JsonObject, usage: Option<Vec<PackUsage>>) {
535+ if let Some(usage) = usage {
536+ image.insert("usage".to_owned(), usage_json(usage));
494537 }
538+}
539+
540+fn sticker_body(image: &JsonObject, shortcode: &str) -> String {
495541 image
542+ .get("body")
543+ .and_then(JsonValue::as_str)
544+ .filter(|body| !body.trim().is_empty())
545+ .unwrap_or(shortcode)
546+ .to_owned()
496547 }
497548
498549 fn free_shortcode(images: &JsonObject, wanted: &str) -> String {
@@ -573,6 +624,54 @@ fn object(value: JsonValue) -> JsonObject {
573624 }
574625 }
575626
627+#[cfg(test)]
628+mod tests {
629+ use super::*;
630+
631+ #[test]
632+ fn pack_usage_inherits_known_values_then_defaults() {
633+ let pack = object(json!({ "usage": ["sticker", "unknown"] }));
634+ let inherited = with_pack_usage(object(json!({ "usage": [] })), &pack);
635+ assert_eq!(inherited.get("usage"), Some(&json!(["sticker"])));
636+
637+ let pack = object(json!({ "usage": ["emoticon"] }));
638+ let inherited = with_pack_usage(object(json!({ "usage": ["unknown"] })), &pack);
639+ assert_eq!(inherited.get("usage"), Some(&json!(["emoticon"])));
640+
641+ let pack = object(json!({ "usage": ["sticker"] }));
642+ let inherited = with_pack_usage(object(json!({ "usage": ["emoticon", "unknown"] })), &pack);
643+ assert_eq!(inherited.get("usage"), Some(&json!(["emoticon"])));
644+
645+ let pack = object(json!({ "usage": [] }));
646+ let inherited = with_pack_usage(object(json!({})), &pack);
647+ assert_eq!(
648+ inherited.get("usage"),
649+ Some(&json!(["emoticon", "sticker"]))
650+ );
651+
652+ let pack = object(json!({ "usage": ["unknown"] }));
653+ let inherited = with_pack_usage(object(json!({})), &pack);
654+ assert_eq!(
655+ inherited.get("usage"),
656+ Some(&json!(["emoticon", "sticker"]))
657+ );
658+ }
659+
660+ #[test]
661+ fn sticker_body_falls_back_when_missing_or_blank() {
662+ assert_eq!(sticker_body(&object(json!({})), "wave"), "wave");
663+ assert_eq!(sticker_body(&object(json!({ "body": "" })), "wave"), "wave");
664+ assert_eq!(
665+ sticker_body(&object(json!({ "body": " \t\n " })), "wave"),
666+ "wave"
667+ );
668+ assert_eq!(
669+ sticker_body(&object(json!({ "body": " hello " })), "wave"),
670+ " hello "
671+ );
672+ }
673+}
674+
576675 fn parse(json: &str) -> Result<JsonObject, Error> {
577676 serde_json::from_str(json)
578677 .map_err(|source| Error::with_source(ErrorKind::Server, "the pack is malformed", source))
Modified file: src/model.rs+3 additions
@@ -455,12 +455,14 @@ pub enum PackCommand {
455455 from_pack: String,
456456 shortcode: String,
457457 to: ImagePackSource,
458+ usage: Option<Vec<PackUsage>>,
458459 },
459460 CloneSticker {
460461 room_id: OwnedRoomId,
461462 event_id: OwnedEventId,
462463 to: ImagePackSource,
463464 shortcode: String,
465+ usage: Option<Vec<PackUsage>>,
464466 },
465467 /// makes the room pack if its state key is free, else merges. images that can't be
466468 /// fetched are skipped and listed, the rest are written
@@ -468,6 +470,7 @@ pub enum PackCommand {
468470 from_pack: String,
469471 to: ImagePackSource,
470472 display_name: Option<String>,
473+ usage: Option<Vec<PackUsage>>,
471474 },
472475 }
473476
Modified file: src/transport.rs+37 additions
@@ -1097,11 +1097,23 @@ impl ClientHandle {
10971097 shortcode: String,
10981098 to: ImagePackSource,
10991099 ) -> Result<String, Error> {
1100+ self.clone_pack_image_with_usage(from_pack, shortcode, to, None)
1101+ .await
1102+ }
1103+
1104+ pub async fn clone_pack_image_with_usage(
1105+ &self,
1106+ from_pack: String,
1107+ shortcode: String,
1108+ to: ImagePackSource,
1109+ usage: Option<Vec<PackUsage>>,
1110+ ) -> Result<String, Error> {
11001111 let (mut shortcodes, _) = self
11011112 .run_clone(PackCommand::CloneImage {
11021113 from_pack,
11031114 shortcode,
11041115 to,
1116+ usage,
11051117 })
11061118 .await?;
11071119 shortcodes
@@ -1117,12 +1129,25 @@ impl ClientHandle {
11171129 to: ImagePackSource,
11181130 shortcode: String,
11191131 ) -> Result<String, Error> {
1132+ self.clone_sticker_with_usage(room_id, event_id, to, shortcode, None)
1133+ .await
1134+ }
1135+
1136+ pub async fn clone_sticker_with_usage(
1137+ &self,
1138+ room_id: OwnedRoomId,
1139+ event_id: OwnedEventId,
1140+ to: ImagePackSource,
1141+ shortcode: String,
1142+ usage: Option<Vec<PackUsage>>,
1143+ ) -> Result<String, Error> {
11201144 let (mut shortcodes, _) = self
11211145 .run_clone(PackCommand::CloneSticker {
11221146 room_id,
11231147 event_id,
11241148 to,
11251149 shortcode,
1150+ usage,
11261151 })
11271152 .await?;
11281153 shortcodes
@@ -1137,11 +1162,23 @@ impl ClientHandle {
11371162 to: ImagePackSource,
11381163 display_name: Option<String>,
11391164 ) -> Result<Vec<String>, Error> {
1165+ self.clone_pack_with_usage(from_pack, to, display_name, None)
1166+ .await
1167+ }
1168+
1169+ pub async fn clone_pack_with_usage(
1170+ &self,
1171+ from_pack: String,
1172+ to: ImagePackSource,
1173+ display_name: Option<String>,
1174+ usage: Option<Vec<PackUsage>>,
1175+ ) -> Result<Vec<String>, Error> {
11401176 let (_, skipped) = self
11411177 .run_clone(PackCommand::ClonePack {
11421178 from_pack,
11431179 to,
11441180 display_name,
1181+ usage,
11451182 })
11461183 .await?;
11471184 Ok(skipped)