Remove emojis from codebase, config, and documentation
This commit is contained in:
parent
6cb649fb8c
commit
be32704914
9 changed files with 36 additions and 36 deletions
|
|
@ -25,7 +25,7 @@ INSTA_NOTE__INTERVAL_SECONDS=120
|
|||
INSTA_NOTE__TIME_FORMAT=%H:%M
|
||||
|
||||
# Note Prefix and Suffix (Max note length on Instagram is 60 characters)
|
||||
INSTA_NOTE__PREFIX="🕒 "
|
||||
INSTA_NOTE__PREFIX=""
|
||||
INSTA_NOTE__SUFFIX=""
|
||||
|
||||
# Rate limiting cap (safety maximum per hour)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
# 🕒 insta-note-clock
|
||||
# insta-note-clock
|
||||
|
||||
A lightweight Rust CLI daemon that updates your Instagram Note with the current time at a configurable, safe interval.
|
||||
|
||||
> ⚠️ **Disclaimer:** Instagram has no public API for Notes. This tool interacts with private mobile endpoints, which violates Instagram's Terms of Service. Frequent updates risk rate limits (HTTP 429) or account suspension. Default interval is 120s with exponential backoff. Use at your own risk.
|
||||
> **Disclaimer:** Instagram has no public API for Notes. This tool interacts with private mobile endpoints, which violates Instagram's Terms of Service. Frequent updates risk rate limits (HTTP 429) or account suspension. Default interval is 120s with exponential backoff. Use at your own risk.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -47,7 +47,7 @@ timezone = "Asia/Kolkata"
|
|||
[note]
|
||||
interval_seconds = 120 # Minimum recommended: 120s
|
||||
time_format = "%H:%M" # strftime syntax
|
||||
prefix = "🕒 "
|
||||
prefix = ""
|
||||
suffix = ""
|
||||
max_requests_per_hour = 30
|
||||
```
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ interval_seconds = 120
|
|||
time_format = "%H:%M"
|
||||
|
||||
# Prefix placed before the formatted time
|
||||
prefix = "🕒 "
|
||||
prefix = ""
|
||||
|
||||
# Suffix placed after the formatted time
|
||||
suffix = ""
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ interval_seconds = 120
|
|||
time_format = "%H:%M"
|
||||
|
||||
# Prefix placed before the formatted time
|
||||
prefix = "🕒 "
|
||||
prefix = ""
|
||||
|
||||
# Suffix placed after the formatted time
|
||||
suffix = ""
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ impl RateLimiter {
|
|||
if let Some(&oldest) = self.request_timestamps.front() {
|
||||
let wait_time = one_hour.saturating_sub(now.duration_since(oldest));
|
||||
warn!(
|
||||
"⏳ Hourly rate limit reached ({}/{} requests in past hour). Pausing for {} seconds...",
|
||||
"Hourly rate limit reached ({}/{} requests in past hour). Pausing for {} seconds...",
|
||||
self.request_timestamps.len(),
|
||||
self.max_per_hour,
|
||||
wait_time.as_secs()
|
||||
|
|
@ -340,10 +340,10 @@ impl InstagramClient {
|
|||
let signed_payload = Self::sign_payload(&json_payload);
|
||||
|
||||
if self.dry_run {
|
||||
info!("🔬 [DRY RUN] Request to: {}", url);
|
||||
info!("🔬 [DRY RUN] Headers: User-Agent, X-IG-App-ID, Cookie: sessionid=***, ds_user_id={}", session.ds_user_id);
|
||||
info!("🔬 [DRY RUN] Payload: {}", json_payload);
|
||||
info!("🔬 [DRY RUN] Signed Form: signed_body={}", signed_payload);
|
||||
info!("[DRY RUN] Request to: {}", url);
|
||||
info!("[DRY RUN] Headers: User-Agent, X-IG-App-ID, Cookie: sessionid=***, ds_user_id={}", session.ds_user_id);
|
||||
info!("[DRY RUN] Payload: {}", json_payload);
|
||||
info!("[DRY RUN] Signed Form: signed_body={}", signed_payload);
|
||||
return Ok(json!({
|
||||
"status": "ok",
|
||||
"id": "dry-run-note-id",
|
||||
|
|
@ -387,7 +387,7 @@ impl InstagramClient {
|
|||
|
||||
// 2. Fatal Auth Errors - Never retry!
|
||||
if status == StatusCode::UNAUTHORIZED || status == StatusCode::FORBIDDEN {
|
||||
error!("🚨 Authentication failure (HTTP {}). Instagram rejected session credentials.", status);
|
||||
error!("Authentication failure (HTTP {}). Instagram rejected session credentials.", status);
|
||||
let err_text = response.text().await.unwrap_or_default();
|
||||
return Err(anyhow!("Authentication failed (HTTP {}): {}. Please re-login.", status, err_text));
|
||||
}
|
||||
|
|
@ -405,7 +405,7 @@ impl InstagramClient {
|
|||
let wait_duration = Duration::from_secs(base_delay_secs + jitter);
|
||||
|
||||
warn!(
|
||||
"⚠️ HTTP 429 (Too Many Requests) received from Instagram!\n\
|
||||
"HTTP 429 (Too Many Requests) received from Instagram!\n\
|
||||
Entering exponential backoff: sleeping for {} seconds (attempt {}/{})...",
|
||||
wait_duration.as_secs(),
|
||||
retry_count + 1,
|
||||
|
|
|
|||
|
|
@ -62,11 +62,11 @@ mod tests {
|
|||
// 2026-09-26 12:00:00 UTC -> 17:30:00 IST (+05:30)
|
||||
let utc_time = Utc.with_ymd_and_hms(2026, 9, 26, 12, 0, 0).unwrap();
|
||||
|
||||
let formatted_24h = format_note_time(utc_time, tz, "%H:%M", "🕒 ", "");
|
||||
assert_eq!(formatted_24h, "🕒 17:30");
|
||||
let formatted_24h = format_note_time(utc_time, tz, "%H:%M", "", "");
|
||||
assert_eq!(formatted_24h, "17:30");
|
||||
|
||||
let formatted_12h = format_note_time(utc_time, tz, "%I:%M %p", "🕒 ", " (IST)");
|
||||
assert_eq!(formatted_12h, "🕒 05:30 PM (IST)");
|
||||
let formatted_12h = format_note_time(utc_time, tz, "%I:%M %p", "", " (IST)");
|
||||
assert_eq!(formatted_12h, "05:30 PM (IST)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ fn default_time_format() -> String {
|
|||
}
|
||||
|
||||
fn default_prefix() -> String {
|
||||
"🕒 ".to_string()
|
||||
"".to_string()
|
||||
}
|
||||
|
||||
fn default_max_requests_per_hour() -> u32 {
|
||||
|
|
@ -111,7 +111,7 @@ impl AppConfig {
|
|||
.set_default("instagram.timezone", "Asia/Kolkata")?
|
||||
.set_default("note.interval_seconds", 120)?
|
||||
.set_default("note.time_format", "%H:%M")?
|
||||
.set_default("note.prefix", "🕒 ")?
|
||||
.set_default("note.prefix", "")?
|
||||
.set_default("note.suffix", "")?
|
||||
.set_default("note.max_requests_per_hour", 30)?;
|
||||
|
||||
|
|
@ -154,7 +154,7 @@ impl AppConfig {
|
|||
pub fn validate(&self) -> Result<()> {
|
||||
if self.note.interval_seconds < MIN_SAFE_INTERVAL_SECONDS {
|
||||
warn!(
|
||||
"⚠️ DANGER: Configured interval is {}s, which is below the minimum safe threshold ({}s)!\n\
|
||||
"DANGER: Configured interval is {}s, which is below the minimum safe threshold ({}s)!\n\
|
||||
Instagram aggressively rate-limits Note updates. Using intervals below {}s will almost\n\
|
||||
certainly trigger HTTP 429 errors, automated bot detection, and temporary or permanent\n\
|
||||
account suspension.",
|
||||
|
|
@ -185,7 +185,7 @@ mod tests {
|
|||
let config = AppConfig::default();
|
||||
assert_eq!(config.note.interval_seconds, 120);
|
||||
assert_eq!(config.note.time_format, "%H:%M");
|
||||
assert_eq!(config.note.prefix, "🕒 ");
|
||||
assert_eq!(config.note.prefix, "");
|
||||
assert_eq!(config.note.max_requests_per_hour, 30);
|
||||
assert_eq!(config.instagram.timezone, "Asia/Kolkata");
|
||||
assert!(config.validate().is_ok());
|
||||
|
|
|
|||
20
src/main.rs
20
src/main.rs
|
|
@ -115,7 +115,7 @@ async fn init_client(config: &AppConfig, dry_run: bool) -> Result<InstagramClien
|
|||
});
|
||||
|
||||
let session = if session.is_none() && dry_run {
|
||||
info!("🔬 [DRY RUN] No saved session found; using simulated dry-run session.");
|
||||
info!("[DRY RUN] No saved session found; using simulated dry-run session.");
|
||||
Some(SessionData::new(
|
||||
"dry_run_session_id".to_string(),
|
||||
"123456789".to_string(),
|
||||
|
|
@ -128,7 +128,7 @@ async fn init_client(config: &AppConfig, dry_run: bool) -> Result<InstagramClien
|
|||
|
||||
if session.is_none() {
|
||||
error!(
|
||||
"❌ No active session found!\n\
|
||||
"No active session found!\n\
|
||||
Please run `insta-note-clock login` to authenticate first,\n\
|
||||
or set session_id and ds_user_id in your config.toml / environment variables."
|
||||
);
|
||||
|
|
@ -174,7 +174,7 @@ async fn handle_login(
|
|||
if config.instagram.username.is_empty() { None } else { Some(config.instagram.username.clone()) },
|
||||
);
|
||||
session.save_to_file(session_path)?;
|
||||
info!("✅ Direct session successfully saved to {}", session_path.display());
|
||||
info!("Direct session successfully saved to {}", session_path.display());
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
|
@ -203,7 +203,7 @@ async fn handle_login(
|
|||
|
||||
let session = SessionData::new(sid, uid, csrf, None);
|
||||
session.save_to_file(session_path)?;
|
||||
info!("✅ Session saved successfully to {}", session_path.display());
|
||||
info!("Session saved successfully to {}", session_path.display());
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
|
@ -228,10 +228,10 @@ async fn handle_login(
|
|||
match client.login(&username, &password).await? {
|
||||
LoginResult::Success(session) => {
|
||||
session.save_to_file(session_path)?;
|
||||
info!("🎉 Successfully logged in as @{}!", username);
|
||||
info!("Successfully logged in as @{}!", username);
|
||||
}
|
||||
LoginResult::TwoFactorRequired { info: two_factor, temp_session } => {
|
||||
println!("\n🔐 Two-Factor Authentication (2FA) is enabled for this account.");
|
||||
println!("\nTwo-Factor Authentication (2FA) is enabled for this account.");
|
||||
if let Some(phone) = two_factor.obfuscated_phone_number {
|
||||
println!("Security code sent to phone ending in {}", phone);
|
||||
}
|
||||
|
|
@ -245,7 +245,7 @@ async fn handle_login(
|
|||
).await?;
|
||||
|
||||
session.save_to_file(session_path)?;
|
||||
info!("🎉 2FA verified successfully! Logged in as @{}.", username);
|
||||
info!("2FA verified successfully! Logged in as @{}.", username);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -266,7 +266,7 @@ async fn update_clock_note(client: &InstagramClient, config: &AppConfig) -> Resu
|
|||
|
||||
let resp = client.create_note(¬e_text).await?;
|
||||
info!(
|
||||
"✅ Note active! Text: \"{}\" (Note ID: {})",
|
||||
"Note active! Text: \"{}\" (Note ID: {})",
|
||||
resp.text.unwrap_or(note_text),
|
||||
resp.id.unwrap_or_else(|| "N/A".to_string())
|
||||
);
|
||||
|
|
@ -280,7 +280,7 @@ async fn run_clock_loop(client: &InstagramClient, config: &AppConfig) -> Result<
|
|||
let interval = Duration::from_secs(config.note.interval_seconds);
|
||||
|
||||
info!("==================================================");
|
||||
info!("🕒 Instagram Note Clock Started");
|
||||
info!("Instagram Note Clock Started");
|
||||
info!("Timezone: {}", config.instagram.timezone);
|
||||
info!("Refresh Interval: {} seconds", config.note.interval_seconds);
|
||||
info!("Format: \"{}{}{}\"", config.note.prefix, config.note.time_format, config.note.suffix);
|
||||
|
|
@ -313,7 +313,7 @@ async fn run_clock_loop(client: &InstagramClient, config: &AppConfig) -> Result<
|
|||
}
|
||||
}
|
||||
_ = tokio::signal::ctrl_c() => {
|
||||
info!("\n🛑 Received Ctrl+C. Shutting down insta-note-clock gracefully. Goodbye!");
|
||||
info!("\nReceived Ctrl+C. Shutting down insta-note-clock gracefully. Goodbye!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ async fn test_create_note_success() {
|
|||
ResponseTemplate::new(200).set_body_json(json!({
|
||||
"status": "ok",
|
||||
"id": "18012345678901234",
|
||||
"text": "🕒 14:30",
|
||||
"text": "14:30",
|
||||
"created_at": 1727364600
|
||||
})),
|
||||
)
|
||||
|
|
@ -31,11 +31,11 @@ async fn test_create_note_success() {
|
|||
|
||||
let client = InstagramClient::new(Some(mock_server.uri()), Some(session), 30, false);
|
||||
|
||||
let resp = client.create_note("🕒 14:30").await.expect("Note creation should succeed");
|
||||
let resp = client.create_note("14:30").await.expect("Note creation should succeed");
|
||||
|
||||
assert_eq!(resp.status, "ok");
|
||||
assert_eq!(resp.id.unwrap(), "18012345678901234");
|
||||
assert_eq!(resp.text.unwrap(), "🕒 14:30");
|
||||
assert_eq!(resp.text.unwrap(), "14:30");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
|
@ -89,7 +89,7 @@ async fn test_unauthorized_does_not_retry() {
|
|||
|
||||
let client = InstagramClient::new(Some(mock_server.uri()), Some(session), 30, false);
|
||||
|
||||
let result = client.create_note("🕒 14:30").await;
|
||||
let result = client.create_note("14:30").await;
|
||||
assert!(result.is_err());
|
||||
let err_msg = result.unwrap_err().to_string();
|
||||
assert!(err_msg.contains("Authentication failed") || err_msg.contains("401"));
|
||||
|
|
@ -116,7 +116,7 @@ async fn test_dry_run_does_not_make_http_calls() {
|
|||
|
||||
let client = InstagramClient::new(Some(mock_server.uri()), Some(session), 30, true);
|
||||
|
||||
let resp = client.create_note("🕒 14:30").await.expect("Dry run should succeed");
|
||||
let resp = client.create_note("14:30").await.expect("Dry run should succeed");
|
||||
assert_eq!(resp.status, "ok");
|
||||
assert_eq!(resp.id.unwrap(), "dry-run-note-id");
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue