use std::fs;
use std::path::Path;
use super::{
AreaResult, LegacyConfig, MigrationArea, err, qt, resolve_channel_token_for_export,
store_in_keystore,
};
pub(crate) fn import_channels(oc_root: &Path, ic_root: &Path) -> AreaResult {
let ks_path = ic_root.join("keystore.enc");
let config_path = oc_root.join("legacy.json");
if !config_path.exists() {
return AreaResult {
area: MigrationArea::Channels,
success: true,
items_processed: 0,
warnings: vec!["No legacy.json found; skipping channel import".into()],
error: None,
};
}
let content = match fs::read_to_string(&config_path) {
Ok(c) => c,
Err(e) => {
return err(
MigrationArea::Channels,
format!("Failed to read legacy.json: {e}"),
);
}
};
let oc_cfg: LegacyConfig = match serde_json::from_str(&content) {
Ok(c) => c,
Err(e) => {
return err(
MigrationArea::Channels,
format!("Failed to parse legacy.json: {e}"),
);
}
};
let mut items = 0;
let mut warnings = Vec::new();
let mut lines = vec!["[channels]".to_string()];
if let Some(channels) = &oc_cfg.channels {
if let Some(tg) = &channels.telegram {
lines.push(String::new());
lines.push("[channels.telegram]".into());
lines.push(format!("enabled = {}", tg.enabled.unwrap_or(false)));
if let Some(token) = &tg.token {
match store_in_keystore("telegram_bot_token", token, &ks_path) {
Ok(()) => {
lines.push("token_ref = \"keystore:telegram_bot_token\"".into());
warnings.push(
"Telegram token stored in encrypted keystore as \"telegram_bot_token\""
.into(),
);
}
Err(e) => {
lines.push("token_env = \"TELEGRAM_BOT_TOKEN\"".into());
warnings.push(format!(
"Keystore unavailable ({e}); set env var TELEGRAM_BOT_TOKEN=<token>"
));
}
}
}
items += 1;
}
if let Some(wa) = &channels.whatsapp {
lines.push(String::new());
lines.push("[channels.whatsapp]".into());
lines.push(format!("enabled = {}", wa.enabled.unwrap_or(false)));
if let Some(token) = &wa.token {
match store_in_keystore("whatsapp_token", token, &ks_path) {
Ok(()) => {
lines.push("token_ref = \"keystore:whatsapp_token\"".into());
warnings.push(
"WhatsApp token stored in encrypted keystore as \"whatsapp_token\""
.into(),
);
}
Err(e) => {
lines.push("token_env = \"WHATSAPP_TOKEN\"".into());
warnings.push(format!(
"Keystore unavailable ({e}); set env var WHATSAPP_TOKEN=<token>"
));
}
}
}
if let Some(phone) = &wa.phone_id {
lines.push(format!("phone_id = {}", qt(phone)));
}
items += 1;
}
}
if items == 0 {
return AreaResult {
area: MigrationArea::Channels,
success: true,
items_processed: 0,
warnings: vec!["No channel configuration found in Legacy config".into()],
error: None,
};
}
if let Err(e) = fs::create_dir_all(ic_root) {
return err(
MigrationArea::Channels,
format!("Failed to create dir: {e}"),
);
}
if let Err(e) = fs::write(ic_root.join("channels.toml"), lines.join("\n") + "\n") {
return err(
MigrationArea::Channels,
format!("Failed to write channels.toml: {e}"),
);
}
AreaResult {
area: MigrationArea::Channels,
success: true,
items_processed: items,
warnings,
error: None,
}
}
pub(crate) fn export_channels(ic_root: &Path, oc_root: &Path) -> AreaResult {
let ks_path = ic_root.join("keystore.enc");
let channels_path = ic_root.join("channels.toml");
let config_path = ic_root.join("roboticus.toml");
let mut warnings = Vec::new();
let channel_toml = if channels_path.exists() {
match fs::read_to_string(&channels_path) {
Ok(c) => c,
Err(e) => {
return err(
MigrationArea::Channels,
format!("Failed to read channels.toml: {e}"),
);
}
}
} else if config_path.exists() {
match fs::read_to_string(&config_path) {
Ok(c) => c,
Err(e) => {
return err(
MigrationArea::Channels,
format!("Failed to read roboticus.toml: {e}"),
);
}
}
} else {
return AreaResult {
area: MigrationArea::Channels,
success: true,
items_processed: 0,
warnings: vec!["No channel configuration found".into()],
error: None,
};
};
let parsed: toml::Value = match toml::from_str(&channel_toml) {
Ok(v) => v,
Err(_) => {
return AreaResult {
area: MigrationArea::Channels,
success: true,
items_processed: 0,
warnings: vec!["Could not parse channel config".into()],
error: None,
};
}
};
let mut oc_channels = serde_json::Map::new();
let mut items = 0;
if let Some(channels) = parsed.get("channels").and_then(|v| v.as_table()) {
if let Some(tg) = channels.get("telegram").and_then(|v| v.as_table()) {
let mut obj = serde_json::Map::new();
if let Some(e) = tg.get("enabled").and_then(|v| v.as_bool()) {
obj.insert("enabled".into(), serde_json::Value::Bool(e));
}
if !resolve_channel_token_for_export(tg, &mut obj, &mut warnings, "telegram", &ks_path)
{
}
oc_channels.insert("telegram".into(), serde_json::Value::Object(obj));
items += 1;
}
if let Some(wa) = channels.get("whatsapp").and_then(|v| v.as_table()) {
let mut obj = serde_json::Map::new();
if let Some(e) = wa.get("enabled").and_then(|v| v.as_bool()) {
obj.insert("enabled".into(), serde_json::Value::Bool(e));
}
if !resolve_channel_token_for_export(wa, &mut obj, &mut warnings, "whatsapp", &ks_path)
{
}
if let Some(phone) = wa.get("phone_id").and_then(|v| v.as_str()) {
obj.insert("phone_id".into(), serde_json::Value::String(phone.into()));
}
oc_channels.insert("whatsapp".into(), serde_json::Value::Object(obj));
items += 1;
}
}
if items == 0 {
return AreaResult {
area: MigrationArea::Channels,
success: true,
items_processed: 0,
warnings: vec!["No channel definitions found to export".into()],
error: None,
};
}
let oc_config_path = oc_root.join("legacy.json");
let mut oc_config: serde_json::Map<String, serde_json::Value> = if oc_config_path.exists() {
match fs::read_to_string(&oc_config_path) {
Ok(c) => match serde_json::from_str(&c) {
Ok(map) => map,
Err(e) => {
warnings.push(format!(
"Could not parse existing legacy.json: {e}; starting fresh"
));
serde_json::Map::new()
}
},
Err(e) => {
warnings.push(format!(
"Could not read existing legacy.json: {e}; starting fresh"
));
serde_json::Map::new()
}
}
} else {
serde_json::Map::new()
};
oc_config.insert("channels".into(), serde_json::Value::Object(oc_channels));
if let Err(e) = fs::create_dir_all(oc_root) {
return err(
MigrationArea::Channels,
format!("Failed to create output dir: {e}"),
);
}
let serialized = match serde_json::to_string_pretty(&oc_config) {
Ok(s) => s,
Err(e) => {
return err(
MigrationArea::Channels,
format!("Failed to serialize legacy.json: {e}"),
);
}
};
if let Err(e) = fs::write(&oc_config_path, &serialized) {
return err(
MigrationArea::Channels,
format!("Failed to write legacy.json: {e}"),
);
}
AreaResult {
area: MigrationArea::Channels,
success: true,
items_processed: items,
warnings,
error: None,
}
}