Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Comments

Record outbound phone calls + 2-channel socket#4416

Open
mdmohsin7 wants to merge 14 commits intomain from
phone-calls
Open

Record outbound phone calls + 2-channel socket #4416
mdmohsin7 wants to merge 14 commits intomain from
phone-calls

Conversation

@mdmohsin7
Copy link
Member

@mdmohsin7 mdmohsin7 commented Jan 27, 2026
edited
Loading

End-to-End Flow

1. Phone Number Verification

  • User enters phone number → Backend validates E.164 format
  • Twilio places automated verification call with 6-digit code
  • User answers, enters code → Number stored encrypted in Firestore (users/{uid}/phone_numbers/)
  • Security: SHA256 hash for lookups, AES-GCM encryption for storage, 5-min pending verification TTL

2. Call Initiation

  • App requests Twilio access token (JWT with user's UID as identity)
  • Native SDK (TwilioVoice) initialized with token
  • Microphone permission requested, call placed with unique callId

3. Call Routing (TwiML Webhook)

  • Twilio invokes POST /v1/phone/twiml with request signature
  • Backend validates:
    • Twilio signature
    • User's verified caller ID exists
    • Destination number E.164 format
    • Caller ID still valid in Twilio
  • Returns TwiML: <Dial callerId="+15551234567">+15559876543</Dial>
  • Twilio bridges call: User ↔ Twilio ↔ Recipient

4. Recording & Transcription

Audio Capture

  • Native SDK captures 2-channel PCM16 @ 48kHz:
    • Channel 0x01: User microphone
    • Channel 0x02: Remote party audio
  • Each audio chunk prefixed with channel byte: [0x01|0x02][audio_data]

Streaming

  • WebSocket to wss://api/v1/listen/multi?source=phone_call&channels=2
  • Firebase auth via get_current_user_uid dependency
  • Auto-reconnect with exponential backoff (1s → 16s, max 5 attempts)

Backend Processing

  • Routes each channel to STT
  • Real-time transcription with speaker labels:
    • Channel 1 → SPEAKER_00 (is_user: true)
    • Channel 2 → SPEAKER_01 (is_user: false)
  • Returns: {"type": "phone_transcript", "segment": {...}}

Storage

  • Raw PCM16 uploaded to GCS every 5s
  • Encrypted with user-specific AES-GCM keys
  • Channels saved separately for diarization

5. Call Termination

  • User/remote ends call → Native SDK disconnects
  • WebSocket closes → Backend finalizes conversation
  • Structured output: title, overview, category, action items
  • Saved with source: phone_call
External.Device-trimmed.mp4

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new multi-channel audio transcription WebSocket endpoint, along with supporting utilities for conversation management, Pusher integration, and translation. The changes include creating new files for streaming utilities and modifying the backend router to handle the new endpoint. A critical issue related to error handling and fallback strategy has been identified and needs to be addressed.

Copy link
Member Author

/gemini summary

Copy link
Contributor

Summary of Changes

This pull request adds phone call functionality to the application, leveraging the Twilio Voice SDK for call management and real-time transcription. It incorporates native platform features like CallKit on iOS for a seamless user experience and introduces a new multi-channel audio processing pipeline. The changes include backend endpoints for token generation and phone number verification, as well as Flutter plugins for handling call initiation, control, and audio streaming.

Highlights

  • Twilio Voice SDK Integration: This PR introduces the Twilio Voice SDK to enable outbound phone call functionality within the app.
  • Real-time Transcription: The implementation includes real-time transcription of phone calls, providing users with live transcripts of their conversations.
  • CallKit Integration (iOS): For iOS, the PR integrates with CallKit to provide a native call experience, including handling incoming and outgoing calls through the system's call interface.
  • Multi-Channel Audio Support: The PR implements multi-channel audio support, allowing for the capture and processing of both the user's microphone input and the remote audio stream.
  • Phone Number Verification: The PR includes phone number verification to ensure that users can only make calls from verified numbers.
Changelog
  • app/android/app/build.gradle
    • Added Twilio Voice SDK dependency.
  • app/android/app/src/main/kotlin/com/example/my_project/MainActivity.kt
    • Registered the PhoneCallsPlugin with the Flutter engine.
  • app/android/app/src/main/kotlin/com/friend/ios/PhoneCallsPlugin.kt
    • Added a new Flutter plugin for handling phone call functionality using the Twilio Voice SDK on Android.
    • Implements method channel communication for call initialization, making calls, ending calls, muting, and toggling the speaker.
    • Uses an event channel to stream call state changes to Flutter.
  • app/ios/Podfile
    • Added TwilioVoice pod dependency.
  • app/ios/Podfile.lock
    • Updated to include TwilioVoice and pasteboard dependencies, along with associated checksums and specifications.
  • app/ios/Runner/AppDelegate.swift
    • Registered the PhoneCallsPlugin with the Flutter engine.
  • app/ios/Runner/Info.plist
    • Added voip to the list of UIBackgroundModes.
  • app/ios/Runner/OmiRecordingAudioDevice.swift
    • Added a custom audio device to capture both local and remote audio streams using Twilio Voice SDK and Core Audio.
    • Implements audio unit setup, rendering, and capturing for real-time audio processing.
  • app/ios/Runner/PhoneCallsPlugin.swift
    • Added a Flutter plugin for handling phone call functionality using the Twilio Voice SDK on iOS.
    • Integrates with CallKit for native call management and user interface.
    • Implements method channel communication for call initialization, making calls, ending calls, muting, and toggling the speaker.
    • Uses an event channel to stream call state changes and audio data to Flutter.
  • app/lib/backend/http/api/phone_calls.dart
    • Added API functions for phone number verification, checking verification status, retrieving verified phone numbers, deleting verified phone numbers, and generating Twilio access tokens.
  • app/lib/backend/schema/phone_call.dart
    • Added data models for phone call-related data, including PhoneTranscriptSegment, VerifiedPhoneNumber, and PhoneCallToken.
  • app/lib/backend/schema/schema.dart
    • Exported the new phone_call.dart schema.
  • app/lib/main.dart
    • Added PhoneCallProvider to the list of ChangeNotifierProviders.
  • app/lib/pages/home/page.dart
    • Added a floating action button to navigate to the PhoneCallsPage or PhoneSetupIntroPage based on the user's verification status.
  • app/lib/pages/phone_calls/active_call_page.dart
    • Added a new page for displaying the active phone call UI, including call info, live transcript, and call controls.
  • app/lib/pages/phone_calls/phone_calls_page.dart
    • Added a new page for initiating phone calls, with tabs for contacts and a dialpad.
  • app/lib/pages/phone_calls/phone_setup_intro_page.dart
    • Added a new page to guide users through the phone number verification process.
  • app/lib/pages/phone_calls/phone_setup_number_page.dart
    • Added a new page for entering and verifying the user's phone number.
  • app/lib/pages/phone_calls/phone_setup_verify_page.dart
    • Added a new page for verifying the user's phone number using a validation code.
  • app/lib/pages/settings/phone_call_settings_page.dart
    • Added a new page for managing verified phone numbers.
  • app/lib/pages/settings/settings_drawer.dart
    • Added a navigation item for phone call settings.
  • app/lib/providers/phone_call_provider.dart
    • Added a new provider to manage phone call state, Twilio SDK initialization, call control, and transcription.
  • app/lib/services/phone_call_service.dart
    • Added a new service for handling native method channel calls related to phone call functionality.
  • backend/database/phone_calls.py
    • Added database functions for managing verified phone numbers, including upsert, retrieval, and deletion.
  • backend/main.py
    • Included the phone_calls router in the main FastAPI application.
  • backend/requirements.txt
    • Added the twilio package as a dependency.
  • backend/routers/multichannel.py
    • Added a new router for handling multi-channel audio streams, including phone calls and desktop audio.
    • Implements WebSocket endpoints for receiving audio, processing transcriptions, and managing conversation state.
  • backend/utils/streaming/init.py
    • Added streaming utility modules.
  • backend/utils/streaming/conversation_manager.py
    • Added conversation management utilities.
  • backend/utils/streaming/pusher_handler.py
    • Added Pusher handler utilities.
  • backend/utils/streaming/translator.py
    • Added translation utilities.
  • backend/utils/streaming/usage_tracker.py
    • Added usage tracking utilities.
  • backend/utils/twilio_service.py
    • Added a new module for interacting with the Twilio API, including token generation, caller ID verification, and call management.
Activity
  • mdmohsin7 requested a Gemini summary.
  • gemini-code-assist[bot] suggested a more informative error message or a more intelligent fallback strategy for the Deepgram STT service in backend/routers/multichannel.py.

@mdmohsin7 mdmohsin7 marked this pull request as ready for review January 27, 2026 19:56
Copy link
Collaborator

@mdmohsin7 The TwiML webhook in phone_calls.py:200 lacks Twilio signature validation so anyone can POST and trigger calls, phone verification is account-level not user-bound in phone_calls.py:120 which lets users claim/delete each other's numbers, /verify/check has no rate limiting (1s polling hits Twilio API), multichannel WS accepts unbounded channels in multichannel.py:117 (DoS risk), TwiML To is not validated to E.164 in phone_calls.py:209, PII is logged in plaintext in phone_calls.py:214, and iOS OmiRecordingAudioDevice has a buffer alignment issue with potential leaks; plus there are no tests for the new endpoints or mobile plugins, though the separation of streaming utils (pusher_handler/usage_tracker/conversation_manager) is solid—can you address these and add coverage, and let me know when it's ready for re-review?

Reviewed by @kenji

Copy link
Member Author

/gemini summary

@mdmohsin7 mdmohsin7 changed the title (削除) Record outbound phone calls + n-channel socket (削除ここまで) (追記) Record outbound phone calls + 2-channel socket (追記ここまで) Jan 28, 2026
Copy link
Contributor

Summary of Changes

This pull request introduces phone call functionality to the application, leveraging the Twilio Voice SDK for call management and real-time transcription. It incorporates native platform features like CallKit on iOS for a seamless user experience and introduces a new multi-channel audio processing pipeline. The changes include backend endpoints for token generation and phone number verification, as well as Flutter plugins for handling call initiation, control, and audio streaming.

Highlights

  • Twilio Voice SDK Integration: This PR introduces the Twilio Voice SDK to enable outbound phone call functionality within the app.
  • Real-time Transcription: The implementation includes real-time transcription of phone calls, providing users with live transcripts of their conversations.
  • CallKit Integration (iOS): For iOS, the PR integrates with CallKit to provide a native call experience, including handling incoming and outgoing calls through the system's call interface.
  • Multi-Channel Audio Support: The PR implements multi-channel audio support, allowing for the capture and processing of both the user's microphone input and the remote audio stream.
  • Phone Number Verification: The PR includes phone number verification to ensure that users can only make calls from verified numbers.
Changelog
  • app/android/app/build.gradle
    • Added Twilio Voice SDK dependency.
  • app/android/app/src/main/kotlin/com/example/my_project/MainActivity.kt
    • Registered the PhoneCallsPlugin with the Flutter engine.
  • app/android/app/src/main/kotlin/com/friend/ios/PhoneCallsPlugin.kt
    • Added a new Flutter plugin for handling phone call functionality using the Twilio Voice SDK on Android.
    • Implements method channel communication for call initialization, making calls, ending calls, muting, and toggling the speaker.
    • Uses an event channel to stream call state changes to Flutter.
  • app/ios/Podfile
    • Added TwilioVoice pod dependency.
  • app/ios/Podfile.lock
    • Updated to include TwilioVoice and pasteboard dependencies, along with associated checksums and specifications.
  • app/ios/Runner/AppDelegate.swift
    • Registered the PhoneCallsPlugin with the Flutter engine.
  • app/ios/Runner/Info.plist
    • Added voip to the list of UIBackgroundModes.
  • app/ios/Runner/OmiRecordingAudioDevice.swift
    • Added a custom audio device to capture both local and remote audio streams using Twilio Voice SDK and Core Audio.
    • Implements audio unit setup, rendering, and capturing for real-time audio processing.
  • app/ios/Runner/PhoneCallsPlugin.swift
    • Added a Flutter plugin for handling phone call functionality using the Twilio Voice SDK on iOS.
    • Integrates with CallKit for native call management and user interface.
    • Implements method channel communication for call initialization, making calls, ending calls, muting, and toggling the speaker.
    • Uses an event channel to stream call state changes and audio data to Flutter.
  • app/lib/backend/http/api/phone_calls.dart
    • Added API functions for phone number verification, checking verification status, retrieving verified phone numbers, deleting verified phone numbers, and generating Twilio access tokens.
  • app/lib/backend/schema/phone_call.dart
    • Added data models for phone call-related data, including PhoneTranscriptSegment, VerifiedPhoneNumber, and PhoneCallToken.
  • app/lib/backend/schema/schema.dart
    • Exported the new phone_call.dart schema.
  • app/lib/main.dart
    • Added PhoneCallProvider to the list of ChangeNotifierProviders.
  • app/lib/pages/home/page.dart
    • Added a floating action button to navigate to the PhoneCallsPage or PhoneSetupIntroPage based on the user's verification status.
  • app/lib/pages/phone_calls/active_call_page.dart
    • Added a new page for displaying the active phone call UI, including call info, live transcript, and call controls.
  • app/lib/pages/phone_calls/phone_calls_page.dart
    • Added a new page for initiating phone calls, with tabs for contacts and a dialpad.
  • app/lib/pages/phone_calls/phone_setup_intro_page.dart
    • Added a new page to guide users through the phone number verification process.
  • app/lib/pages/phone_calls/phone_setup_number_page.dart
    • Added a new page for entering and verifying the user's phone number.
  • app/lib/pages/phone_calls/phone_setup_verify_page.dart
    • Added a new page for verifying the user's phone number using a validation code.
  • app/lib/pages/settings/phone_call_settings_page.dart
    • Added a new page for managing verified phone numbers.
  • app/lib/pages/settings/settings_drawer.dart
    • Added a navigation item for phone call settings.
  • app/lib/providers/phone_call_provider.dart
    • Added a new provider to manage phone call state, Twilio SDK initialization, call control, and transcription.
  • app/lib/services/phone_call_service.dart
    • Added a new service for handling native method channel calls related to phone call functionality.
  • backend/database/phone_calls.py
    • Added database functions for managing verified phone numbers, including upsert, retrieval, and deletion.
  • backend/main.py
    • Included the phone_calls router in the main FastAPI application.
  • backend/requirements.txt
    • Added the twilio package as a dependency.
  • backend/routers/multichannel.py
    • Added a new router for handling multi-channel audio streams, including phone calls and desktop audio.
    • Implements WebSocket endpoints for receiving audio, processing transcriptions, and managing conversation state.
  • backend/utils/streaming/init.py
    • Added streaming utility modules.
  • backend/utils/streaming/conversation_manager.py
    • Added conversation management utilities.
  • backend/utils/streaming/pusher_handler.py
    • Added Pusher handler utilities.
  • backend/utils/streaming/translator.py
    • Added translation utilities.
  • backend/utils/streaming/usage_tracker.py
    • Added usage tracking utilities.
  • backend/utils/twilio_service.py
    • Added a new module for interacting with the Twilio API, including token generation, caller ID verification, and call management.
Activity
  • mdmohsin7 requested a Gemini summary.
  • gemini-code-assist[bot] suggested a more informative error message or a more intelligent fallback strategy for the Deepgram STT service in backend/routers/multichannel.py.
  • beastoin noted several potential issues, including lack of Twilio signature validation, account-level phone verification, missing rate limiting, unbounded channels, lack of E.164 validation, PII logging, and a buffer alignment issue, and requested that these be addressed and that tests be added.
  • mdmohsin7 requested a Gemini summary.

Copy link
Member Author

@mdmohsin7 The TwiML webhook in phone_calls.py:200 lacks Twilio signature validation so anyone can POST and trigger calls, phone verification is account-level not user-bound in phone_calls.py:120 which lets users claim/delete each other's numbers, /verify/check has no rate limiting (1s polling hits Twilio API), multichannel WS accepts unbounded channels in multichannel.py:117 (DoS risk), TwiML To is not validated to E.164 in phone_calls.py:209, PII is logged in plaintext in phone_calls.py:214, and iOS OmiRecordingAudioDevice has a buffer alignment issue with potential leaks; plus there are no tests for the new endpoints or mobile plugins, though the separation of streaming utils (pusher_handler/usage_tracker/conversation_manager) is solid—can you address these and add coverage, and let me know when it's ready for re-review?

Reviewed by @kenji

Done, pls check now @beastoin

Copy link
Collaborator

PR Review: Record outbound phone calls + 2-channel socket

Substantial feature adding Twilio-based phone call recording with dual-channel audio streaming. Good test coverage included.

Issues to Address

1. Twilio signature validation bypass when auth_token missing (backend/utils/twilio_service.py:175-177)

if not auth_token:
 return True # <-- Security risk: bypasses validation

This silently allows unauthenticated requests when TWILIO_AUTH_TOKEN is not set. The webhook at /v1/phone/twiml would be exploitable. Fix: Return False or raise an exception when auth_token is missing.

2. Inconsistent package path (app/android/app/src/main/kotlin/com/friend/ios/)
Android Kotlin files are under com/friend/ios/ package path. This is confusing and should be com/friend/android/ or similar.

3. Missing rate limiting on verification endpoints
/v1/phone/numbers/verify could be abused for toll fraud or phone verification spam. Consider adding rate limiting per-user and per-phone-number.

4. Phone number validation could be stricter (backend/routers/phone_calls.py)
The E164 regex allows very long numbers. Consider adding a max length check (E.164 max is 15 digits).

Minor Suggestions

  • multichannel.py:115: Channel count validation rejects >2 channels but build_channel_config has generic N-channel logic. Either support N channels or remove the unused generic code.
  • Consider adding call duration limits to prevent runaway charges.
  • OmiRecordingAudioDevice (both platforms): No explicit resource cleanup on exceptions during capture/render runnables.

Positive Notes

  • Phone number encryption with hash-based lookup is well designed
  • Twilio signature validation on TwiML webhook is correct pattern (when auth_token present)
  • Good unit test coverage for router endpoints
  • Proper pending verification TTL prevents stale verification records

Verdict: Address item #1 (security issue) before merging. Items #2-4 can be follow-up tasks.


by AI for @beastoin

Copy link
Member Author

  1. Inconsistent package path (app/android/app/src/main/kotlin/com/friend/ios/)
    Android Kotlin files are under com/friend/ios/ package path. This is confusing and should be com/friend/android/ or similar.

It's com/friend/ios coz the package name is com.friend.ios

Rest all fixed, pls check now @beastoin

Copy link
Collaborator

@mdmohsin7 In backend/routers/phone_calls.py:141-166, /v1/phone/numbers/verify/check only blocks when pending_uid is set for another user, so a caller can claim any already-verified Twilio caller ID by hitting the check endpoint when there is no pending record; please require pending_uid == uid (return verified=false when missing/expired) before calling check_caller_id_verified and add a regression test for the no-pending case. Also in backend/routers/phone_calls.py:88-91, validation only checks startswith('+') even though E164_PATTERN exists, which will accept invalid numbers and persist bad data—please validate against E164_PATTERN (or normalize) before starting verification.

Can you update the verification flow and add the missing test?


by AI for @beastoin

Copy link
Member Author

final fixes done, pls check again @beastoin's AI 🤖

Copy link
Collaborator

conflicts @mdmohsin7

Copy link
Collaborator

beastoin commented Feb 2, 2026
edited by mdmohsin7
Loading

@mdmohsin7 Before review, quick questions:

  1. Why is this feature important? What user problem does it solve?
  2. Quick checklist for review:
    • iOS working?
    • Android working?
    • User consent flow included?
    • Tested with real calls?

Thanks!


by AI for @beastoin

Copy link
Collaborator

@mdmohsin7 lets roll this out soon pls

Copy link
Collaborator

beastoin commented Feb 6, 2026

Hi @mdmohsin7 — this PR is 10 days old with a minimal description. Could you add:

  • Test steps / demo evidence
  • How outbound call recording works end-to-end

This will help move it through review. Thanks!

Copy link
Collaborator

beastoin commented Feb 6, 2026

Do not refactor the transcripe.py file; please keep it as is.

Copy link
Member Author

End-to-End Flow

1. Phone Number Verification

  • User enters phone number → Backend validates E.164 format
  • Twilio places automated verification call with 6-digit code
  • User answers, enters code → Number stored encrypted in Firestore (users/{uid}/phone_numbers/)
  • Security: SHA256 hash for lookups, AES-GCM encryption for storage, 5-min pending verification TTL

2. Call Initiation

  • App requests Twilio access token (JWT with user's UID as identity)
  • Native SDK (TwilioVoice) initialized with token
  • Microphone permission requested, call placed with unique callId

3. Call Routing (TwiML Webhook)

  • Twilio invokes POST /v1/phone/twiml with request signature
  • Backend validates:
    • Twilio signature
    • User's verified caller ID exists
    • Destination number E.164 format
    • Caller ID still valid in Twilio
  • Returns TwiML: <Dial callerId="+15551234567">+15559876543</Dial>
  • Twilio bridges call: User ↔ Twilio ↔ Recipient

4. Recording & Transcription

Audio Capture

  • Native SDK captures 2-channel PCM16 @ 48kHz:
    • Channel 0x01: User microphone
    • Channel 0x02: Remote party audio
  • Each audio chunk prefixed with channel byte: [0x01|0x02][audio_data]

Streaming

  • WebSocket to wss://api/v1/listen/multi?source=phone_call&channels=2
  • Firebase auth via get_current_user_uid dependency
  • Auto-reconnect with exponential backoff (1s → 16s, max 5 attempts)

Backend Processing

  • Routes each channel to STT
  • Real-time transcription with speaker labels:
    • Channel 1 → SPEAKER_00 (is_user: true)
    • Channel 2 → SPEAKER_01 (is_user: false)
  • Returns: {"type": "phone_transcript", "segment": {...}}

Storage

  • Raw PCM16 uploaded to GCS every 5s
  • Encrypted with user-specific AES-GCM keys
  • Channels saved separately for diarization

5. Call Termination

  • User/remote ends call → Native SDK disconnects
  • WebSocket closes → Backend finalizes conversation
  • Structured output: title, overview, category, action items
  • Saved with source: phone_call

Copy link
Member Author

Do not refactor the transcripe.py file; please keep it as is.

Yes no changes in the transcribe.py file. I duplicated logic from that file into multiple utilities so that I can use them in the new multichannel route. In future we could use the same utilities in transcribe.py file as well (ofcourse once you review it and confirm there's nothing breaking). So right now nothing is changed in the transcribe file

@beastoin

Copy link
Member Author

bump @beastoin, can you pls check this

Copy link
Collaborator

Hey, closing this for now — evidence of testing is really important for PRs to move forward. Screenshots, videos, test results, or a demo showing the feature/fix working goes a long way in helping reviewers feel confident about merging.

Feel free to reopen once you've added thorough testing evidence to the PR description. Thanks for contributing!

Copy link
Contributor

Hey @mdmohsin7 👋

Thank you so much for taking the time to contribute to Omi! We truly appreciate you putting in the effort to submit this pull request.

After careful review, we've decided not to merge this particular PR. Please don't take this personally — we genuinely try to merge as many contributions as possible, but sometimes we have to make tough calls based on:

  • Project standards — Ensuring consistency across the codebase
  • User needs — Making sure changes align with what our users need
  • Code best practices — Maintaining code quality and maintainability
  • Project direction — Keeping aligned with our roadmap and vision

Your contribution is still valuable to us, and we'd love to see you contribute again in the future! If you'd like feedback on how to improve this PR or want to discuss alternative approaches, please don't hesitate to reach out.

Thank you for being part of the Omi community! 💜

Copy link
Member Author

Hey, closing this for now — evidence of testing is really important for PRs to move forward. Screenshots, videos, test results, or a demo showing the feature/fix working goes a long way in helping reviewers feel confident about merging.

Feel free to reopen once you've added thorough testing evidence to the PR description. Thanks for contributing!

There already is a video evidence in the pr description, and also tests. This is the top requested feature on our feature board

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@beastoin beastoin Awaiting requested review from beastoin

1 more reviewer

@gemini-code-assist gemini-code-assist[bot] gemini-code-assist[bot] left review comments

Reviewers whose approvals may not affect merge requirements

At least 1 approving review is required to merge this pull request.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

AltStyle によって変換されたページ (->オリジナル) /