Skip to content

Commit 890bba5

Browse files
committed
fix: resolve clippy warnings and missing fields in gateway adapters
1 parent 5d76f5b commit 890bba5

6 files changed

Lines changed: 58 additions & 38 deletions

File tree

gateway/src/adapters/line.rs

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::media::{resize_and_compress, IMAGE_MAX_DOWNLOAD};
1+
use crate::media::{resize_and_compress, AUDIO_MAX_DOWNLOAD, IMAGE_MAX_DOWNLOAD};
22
use crate::schema::*;
33
use axum::extract::State;
44
use serde::Deserialize;
@@ -92,13 +92,14 @@ pub async fn webhook(
9292
continue;
9393
};
9494
let is_text = msg.message_type == "text";
95+
let is_image = msg.message_type == "image";
9596
let is_audio = msg.message_type == "audio";
9697

9798
if !is_text && !is_image && !is_audio {
9899
continue;
99100
}
100101

101-
let mut text = msg.text.clone().unwrap_or_default();
102+
let text = msg.text.clone().unwrap_or_default();
102103
if is_text && text.trim().is_empty() {
103104
continue;
104105
}
@@ -316,6 +317,13 @@ async fn download_line_media(
316317
}
317318
}
318319

320+
let content_type = resp
321+
.headers()
322+
.get(reqwest::header::CONTENT_TYPE)
323+
.and_then(|h| h.to_str().ok())
324+
.unwrap_or(if attachment_type == "image" { "image/jpeg" } else { "audio/x-m4a" })
325+
.to_string();
326+
319327
let bytes = resp.bytes().await.ok()?;
320328
if bytes.len() as u64 > max_size {
321329
warn!(message_id, size = bytes.len(), "LINE {} exceeds limit", attachment_type);
@@ -324,7 +332,7 @@ async fn download_line_media(
324332

325333
let (data_bytes, mime, filename) = if attachment_type == "image" {
326334
match resize_and_compress(&bytes) {
327-
Ok((c, m)) => (c, m, format!("{}.jpg", message_id)),
335+
Ok((c, _m)) => (c, content_type, format!("{}.jpg", message_id)),
328336
Err(e) => {
329337
error!(err = %e, "LINE image processing failed");
330338
return None;
@@ -333,14 +341,7 @@ async fn download_line_media(
333341
} else {
334342
// For audio, we don't process, just send as is.
335343
// LINE audio is usually m4a.
336-
let mime = resp
337-
.headers()
338-
.get(reqwest::header::CONTENT_TYPE)
339-
.and_then(|h| h.to_str().ok())
340-
.unwrap_or("audio/x-m4a")
341-
.to_string();
342-
let ext = if mime.contains("mp3") { "mp3" } else { "m4a" };
343-
(bytes.to_vec(), mime, format!("{}.{}", message_id, ext))
344+
(bytes.to_vec(), content_type, format!("{}.m4a", message_id))
344345
};
345346

346347
use base64::Engine;

gateway/src/adapters/telegram.rs

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::media::{resize_and_compress, FILE_MAX_DOWNLOAD, IMAGE_MAX_DOWNLOAD};
1+
use crate::media::{resize_and_compress, AUDIO_MAX_DOWNLOAD, FILE_MAX_DOWNLOAD, IMAGE_MAX_DOWNLOAD};
22
use crate::schema::*;
33
use axum::extract::State;
44
use axum::Json;
@@ -32,6 +32,8 @@ struct TelegramMessage {
3232
#[serde(default)]
3333
photo: Vec<TelegramPhoto>,
3434
document: Option<TelegramDocument>,
35+
voice: Option<TelegramVoice>,
36+
audio: Option<TelegramAudio>,
3537
}
3638

3739
#[derive(Debug, Deserialize)]
@@ -45,6 +47,20 @@ struct TelegramPhoto {
4547
struct TelegramDocument {
4648
file_id: String,
4749
file_name: Option<String>,
50+
mime_type: Option<String>,
51+
}
52+
53+
#[derive(Debug, Deserialize)]
54+
struct TelegramVoice {
55+
file_id: String,
56+
mime_type: Option<String>,
57+
}
58+
59+
#[derive(Debug, Deserialize)]
60+
struct TelegramAudio {
61+
file_id: String,
62+
file_name: Option<String>,
63+
mime_type: Option<String>,
4864
}
4965

5066
#[derive(Debug, Deserialize)]
@@ -93,6 +109,8 @@ pub async fn webhook(
93109
let Some(msg) = update.message else {
94110
return axum::http::StatusCode::OK;
95111
};
112+
let is_photo = !msg.photo.is_empty();
113+
let is_document = msg.document.is_some();
96114
let is_voice = msg.voice.is_some();
97115
let is_audio = msg.audio.is_some();
98116
let text = msg.text.as_deref().or(msg.caption.as_deref()).unwrap_or("");
@@ -356,6 +374,13 @@ async fn download_telegram_media(
356374
}
357375
}
358376

377+
let content_type = resp
378+
.headers()
379+
.get(reqwest::header::CONTENT_TYPE)
380+
.and_then(|h| h.to_str().ok())
381+
.unwrap_or(if attachment_type == "image" { "image/jpeg" } else { "audio/ogg" })
382+
.to_string();
383+
359384
let bytes = resp.bytes().await.ok()?;
360385
if bytes.len() as u64 > max_size {
361386
warn!(file_id, size = bytes.len(), "Telegram {} exceeds limit", attachment_type);
@@ -364,28 +389,22 @@ async fn download_telegram_media(
364389

365390
let (data_bytes, mime, filename) = if attachment_type == "image" {
366391
match resize_and_compress(&bytes) {
367-
Ok((c, m)) => (c, m, format!("{}.jpg", file_id)),
392+
Ok((c, _m)) => (c, content_type, format!("{}.jpg", file_id)),
368393
Err(e) => {
369394
error!(err = %e, "Telegram image processing failed");
370395
return None;
371396
}
372397
}
373398
} else {
374399
// For audio/voice, we don't process.
375-
let mime = resp
376-
.headers()
377-
.get(reqwest::header::CONTENT_TYPE)
378-
.and_then(|h| h.to_str().ok())
379-
.unwrap_or("audio/ogg") // Default for Telegram voice
380-
.to_string();
381-
let ext = if mime.contains("mpeg") || mime.contains("mp3") {
400+
let ext = if content_type.contains("mpeg") || content_type.contains("mp3") {
382401
"mp3"
383-
} else if mime.contains("m4a") {
402+
} else if content_type.contains("m4a") {
384403
"m4a"
385404
} else {
386405
"ogg"
387406
};
388-
(bytes.to_vec(), mime, format!("{}.{}", file_id, ext))
407+
(bytes.to_vec(), content_type, format!("{}.{}", file_id, ext))
389408
};
390409

391410
use base64::Engine;

pr_checks.txt

Whitespace-only changes.

pr_reviews.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"comments":[{"id":"IC_kwDOR40_Sc8AAAABBY_30Q","author":{"login":"shaun-agent"},"authorAssociation":"CONTRIBUTOR","body":"<!-- openab-project-screening -->\n## OpenAB PR Screening\nThis is auto-generated by the OpenAB project-screening flow for context collection and reviewer handoff.\nClick 👍 if you find this useful. Human review will be done within 24 hours. We appreciate your support and contribution 🙏\n- Title: feat(gateway): implement multimodal (image/document/audio) inbound support for LINE and Telegram\n- Source: https://github.com/openabdev/openab/pull/757\n- Status: moved to PR-Screening\n- Generated at: 2026-05-06T13:01:48.169Z\n- Discord thread: https://discord.com/channels/1488041051187974246/1501569407673962546\n<details>\n<summary>Screening report</summary>\n## Intent\n\nPR #757 aims to add inbound multimodal message handling to the OpenAB custom gateway for LINE and Telegram. The user-visible problem is that users on those platforms can currently interact reliably with text, but images, voice/audio, and Telegram text documents are not fully accepted, normalized, and forwarded into the agent/core flow.\n\n## Feat\n\nThis is a gateway feature PR.\n\nIt adds inbound support for:\n\n- LINE images and audio/voice\n- Telegram images, audio/voice, and small text documents\n- Shared image preprocessing through `gateway/src/media.rs`\n- Core-side delivery of downloaded media so configured STT can transcribe audio into text for the agent\n- Documentation updates for LINE and Telegram behavior\n\nOutbound multimodal support is explicitly out of scope: text remains supported, while outbound images/audio are not implemented.\n\n## Who It Serves\n\nPrimary beneficiaries:\n\n- Discord/LINE/Telegram end users who expect bots to handle media-rich messages\n- Deployers operating OpenAB gateways for LINE or Telegram communities\n- Agent runtime operators who need inbound media normalized before it reaches the core\n- Maintainers who want shared gateway media handling instead of one-off adapter logic\n\n## Rewritten Prompt\n\nImplement inbound multimodal support for LINE and Telegram in the custom gateway.\n\nRequirements:\n\n- Accept inbound image messages from LINE and Telegram.\n- Download media through each platform’s authenticated media APIs.\n- Normalize images through a shared gateway media helper: resize large images to a maximum dimension of 1200px and JPEG-compress them before forwarding.\n- Accept inbound LINE and Telegram audio/voice messages and forward them to core so the configured STT provider can transcribe them for the agent.\n- Accept Telegram text documents up to 512KB and pass their contents to the agent as Markdown code blocks.\n- Keep outbound multimodal delivery out of scope.\n- Preserve existing text-message behavior across all gateway adapters.\n- Add or update docs for LINE and Telegram.\n- Add tests or focused verification covering media download, size limits, image normalization, and unsupported media cases.\n\nReview carefully for unrelated adapter churn outside LINE, Telegram, shared media, gateway wiring, and docs.\n\n## Merge Pitch\n\nThis PR moves OpenAB closer to parity with real messaging-platform usage: users commonly send screenshots, photos, voice notes, and small documents instead of plain text. Inbound-only support is a sensible first step because it unlocks richer agent context without committing to platform-specific outbound media delivery.\n\nRisk profile is moderate. The main reviewer concern should be scope control: the file list includes substantial changes to Feishu, Google Chat, and Teams even though the PR title is LINE and Telegram focused. Review should determine whether those changes are necessary gateway-interface adjustments or unrelated refactor noise.\n\n## Best-Practice Comparison\n\nOpenClaw principles that apply:\n\n- Explicit delivery routing is relevant because downloaded media must be associated with the correct platform conversation and forwarded into the correct core/session path.\n- Isolated executions are relevant if media processing or STT handoff can be expensive or failure-prone.\n- Run logs and retry/backoff are partially relevant for media downloads, especially platform API failures and transient network errors.\n\nOpenClaw principles that are less central here:\n\n- Gateway-owned scheduling and durable job persistence are not the main concern unless media processing is moved async or retried after webhook acknowledgement.\n\nHermes Agent principles that apply:\n\n- Fresh session per scheduled run is not directly relevant, but the broader principle of self-contained prompts does apply: document contents and transcribed audio should arrive with enough context for the agent to understand the user action.\n- Atomic writes and file locking are relevant only if downloaded media or intermediate state is persisted to disk.\n\nHermes Agent principles that are less central here:\n\n- Gateway daemon tick model is not a natural fit for webhook-driven LINE and Telegram inbound media unless the implementation evolves toward queued background processing.\n\nOverall, the most relevant best practices are explicit routing, bounded media handling, failure logging, and keeping expensive processing isolated from fragile webhook request paths.\n\n## Implementation Options\n\nOption 1: Conservative, LINE/Telegram-only inbound support \nKeep changes tightly scoped to `line.rs`, `telegram.rs`, shared `media.rs`, core gateway payload wiring, and docs. Reject or ignore unsupported media types with clear logs. Avoid broad adapter refactors.\n\nOption 2: Balanced, shared gateway media abstraction \nIntroduce a common inbound media representation used by LINE and Telegram now, with adapter-safe compatibility for other platforms. Centralize image normalization, document limits, MIME checks, and audio forwarding while minimizing changes to unrelated adapters.\n\nOption 3: Ambitious, durable async media pipeline \nTreat inbound media as gateway jobs: acknowledge webhooks quickly, persist media metadata, process downloads/transforms asynchronously, retry transient failures, and emit structured run logs. This would align more closely with OpenClaw-style durable execution but is a larger architectural step.\n\n## Comparison Table\n\n| Option | Speed to ship | Complexity | Reliability | Maintainability | User impact | Fit for OpenAB right now |\n|---|---:|---:|---:|---:|---:|---|\n| Conservative LINE/Telegram-only | High | Low-Medium | Medium | Medium | High for LINE/Telegram users | Strong if PR scope needs tightening |\n| Shared gateway media abstraction | Medium | Medium | Medium-High | High | High, with future adapter reuse | Best balance for this PR |\n| Durable async media pipeline | Low | High | High | Medium-High | High, especially at scale | Likely too large for this PR |\n\n## Recommendation\n\nAdvance this item, but steer review toward the balanced option.\n\nThe feature is valuable and user-facing, but the merge discussion should focus on scope discipline: confirm that non-LINE/Telegram adapter changes are required by a shared gateway interface rather than incidental churn. If they are not required, split them out.\n\nRecommended sequencing:\n\n1. Merge inbound LINE/Telegram media support with shared media helpers and clear limits.\n2. Keep outbound media out of scope.\n3. Add follow-up issues for async/durable media processing, richer retry logging, and outbound multimodal support.\n</details>","createdAt":"2026-05-06T13:01:48Z","includesCreatedEdit":false,"isMinimized":false,"minimizedReason":"","reactionGroups":[],"url":"https://github.com/openabdev/openab/pull/757#issuecomment-4388288465","viewerDidAuthor":false}],"reviews":[{"id":"PRR_kwDOR40_Sc78f5et","author":{"login":"copilot-pull-request-reviewer"},"authorAssociation":"NONE","body":"## Pull request overview\n\nThis PR adds inbound multimodal support (images, text documents, and audio) for the Custom Gateway’s LINE and Telegram adapters, including shared gateway-side image resizing/compression and Core-side audio transcription via the configured STT.\n\n**Changes:**\n- Add gateway-side media utilities (`resize_and_compress`, size limits) and wire them into adapters.\n- Implement Telegram + LINE inbound attachment downloading/encoding and inclusion in `GatewayEvent` attachments.\n- Extend Core gateway adapter to decode attachments and (optionally) transcribe inbound audio when STT is enabled.\n\n### Reviewed changes\n\nCopilot reviewed 11 out of 12 changed files in this pull request and generated 7 comments.\n\n<details>\n<summary>Show a summary per file</summary>\n\n| File | Description |\r\n| ---- | ----------- |\r\n| src/main.rs | Pass STT config into the Core gateway adapter params. |\r\n| src/gateway.rs | Convert gateway attachments into Core `ContentBlock`s, including audio transcription support. |\r\n| gateway/src/media.rs | New shared media module for image resize/compress + download size limits. |\r\n| gateway/src/main.rs | Register the new `media` module; minor formatting changes. |\r\n| gateway/src/adapters/telegram.rs | Add inbound photo/document/audio handling and media download helpers (currently has compile/logic issues). |\r\n| gateway/src/adapters/line.rs | Add inbound image/audio handling and LINE media download helper (currently has compile issues). |\r\n| gateway/src/adapters/feishu.rs | Refactor to reuse shared media module; mostly formatting. |\r\n| gateway/src/adapters/googlechat.rs | Formatting and test fixture updates to include empty attachments. |\r\n| gateway/src/adapters/teams.rs | Formatting only. |\r\n| gateway/Cargo.lock | Bump `openab-gateway` lockfile version entry. |\r\n| docs/telegram.md | Document Telegram inbound file/image/audio support. |\r\n| docs/line.md | Document LINE inbound image/audio support. |\n</details>\n\n\n\n\n\n\n---\n\n💡 <a href=\"/openabdev/openab/new/main?filename=.github/instructions/*.instructions.md\" class=\"Link--inTextBlock\" target=\"_blank\" rel=\"noopener noreferrer\">Add Copilot custom instructions</a> for smarter, more guided reviews. <a href=\"https://docs.github.com/en/copilot/customizing-copilot/adding-repository-custom-instructions-for-github-copilot\" class=\"Link--inTextBlock\" target=\"_blank\" rel=\"noopener noreferrer\">Learn how to get started</a>.","submittedAt":"2026-05-06T12:59:19Z","includesCreatedEdit":false,"reactionGroups":[],"state":"COMMENTED","commit":{"oid":"5d76f5b2dd39f2a4035c71b50ae3b94d4a5c2481"}}]}

pr_status.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"statusCheckRollup":[{"__typename":"CheckRun","completedAt":"2026-05-06T12:54:14Z","conclusion":"FAILURE","detailsUrl":"https://github.com/openabdev/openab/actions/runs/25436423459/job/74615516424","name":"check","startedAt":"2026-05-06T12:53:09Z","status":"COMPLETED","workflowName":"CI"},{"__typename":"CheckRun","completedAt":"2026-05-06T12:56:52Z","conclusion":"SUCCESS","detailsUrl":"https://github.com/openabdev/openab/actions/runs/25436423409/job/74615516284","name":"smoke-test (Dockerfile, kiro-cli, acp --trust-all-tools)","startedAt":"2026-05-06T12:53:16Z","status":"COMPLETED","workflowName":"Docker Smoke Test"},{"__typename":"CheckRun","completedAt":"2026-05-06T12:53:21Z","conclusion":"SUCCESS","detailsUrl":"https://github.com/openabdev/openab/actions/runs/25436423365/job/74615516734","name":"check","startedAt":"2026-05-06T12:53:16Z","status":"COMPLETED","workflowName":"PR Discussion URL Check"},{"__typename":"CheckRun","completedAt":"2026-05-06T12:59:23Z","conclusion":"SUCCESS","detailsUrl":"https://github.com/openabdev/openab/actions/runs/25436736434/job/74616613803","name":"check","startedAt":"2026-05-06T12:59:18Z","status":"COMPLETED","workflowName":"PR Discussion URL Check"},{"__typename":"CheckRun","completedAt":"2026-05-06T13:00:39Z","conclusion":"SUCCESS","detailsUrl":"https://github.com/openabdev/openab/actions/runs/25436803265/job/74616852998","name":"check","startedAt":"2026-05-06T13:00:35Z","status":"COMPLETED","workflowName":"PR Discussion URL Check"},{"__typename":"CheckRun","completedAt":"2026-05-06T12:53:15Z","conclusion":"SUCCESS","detailsUrl":"https://github.com/openabdev/openab/actions/runs/25436423353/job/74615516365","name":"add-label","startedAt":"2026-05-06T12:53:09Z","status":"COMPLETED","workflowName":"PR Pending Screening"},{"__typename":"CheckRun","completedAt":"2026-05-06T12:56:53Z","conclusion":"SUCCESS","detailsUrl":"https://github.com/openabdev/openab/actions/runs/25436423409/job/74615516251","name":"smoke-test (Dockerfile.claude, -claude, claude-agent-acp)","startedAt":"2026-05-06T12:53:16Z","status":"COMPLETED","workflowName":"Docker Smoke Test"},{"__typename":"CheckRun","completedAt":"2026-05-06T12:56:49Z","conclusion":"SUCCESS","detailsUrl":"https://github.com/openabdev/openab/actions/runs/25436423409/job/74615516325","name":"smoke-test (Dockerfile.codex, -codex, codex-acp)","startedAt":"2026-05-06T12:53:15Z","status":"COMPLETED","workflowName":"Docker Smoke Test"},{"__typename":"CheckRun","completedAt":"2026-05-06T12:56:38Z","conclusion":"SUCCESS","detailsUrl":"https://github.com/openabdev/openab/actions/runs/25436423409/job/74615516329","name":"smoke-test (Dockerfile.gemini, -gemini, gemini, --acp)","startedAt":"2026-05-06T12:53:09Z","status":"COMPLETED","workflowName":"Docker Smoke Test"},{"__typename":"CheckRun","completedAt":"2026-05-06T12:56:47Z","conclusion":"SUCCESS","detailsUrl":"https://github.com/openabdev/openab/actions/runs/25436423409/job/74615516285","name":"smoke-test (Dockerfile.copilot, -copilot, copilot, --acp)","startedAt":"2026-05-06T12:53:16Z","status":"COMPLETED","workflowName":"Docker Smoke Test"},{"__typename":"CheckRun","completedAt":"2026-05-06T12:56:53Z","conclusion":"SUCCESS","detailsUrl":"https://github.com/openabdev/openab/actions/runs/25436423409/job/74615516287","name":"smoke-test (Dockerfile.opencode, -opencode, opencode, acp)","startedAt":"2026-05-06T12:53:15Z","status":"COMPLETED","workflowName":"Docker Smoke Test"},{"__typename":"CheckRun","completedAt":"2026-05-06T12:57:08Z","conclusion":"SUCCESS","detailsUrl":"https://github.com/openabdev/openab/actions/runs/25436423409/job/74615516328","name":"smoke-test (Dockerfile.cursor, -cursor, cursor-agent, acp)","startedAt":"2026-05-06T12:53:09Z","status":"COMPLETED","workflowName":"Docker Smoke Test"}]}

0 commit comments

Comments
 (0)