How I Used a Japanese Phone Number in Taiwan to Pass eplus and PIA Ticket Verification — My ifmobile Experience

Note: This article is written from the perspective of a user in Taiwan. I’m not sure how the experience may differ in other countries outside of Taiwan or Japan.

Recently, I needed a Japanese phone number that could receive SMS for logging into PIA and eplus ticketing websites.

After doing some research, I chose ifmobile instead of Hanacell or Rakuten's resold SIM cards. This post documents the purchase process and my experience using it—hopefully helpful for those with similar needs.


Why I Chose ifmobile

Although ifmobile is a Chinese-owned company (which may raise privacy concerns for some), I decided to try it out due to several practical advantages:

  • The most important reason:
    Hanacell requires you to physically be in Japan to use the number—so while it’s cheaper, it’s not usable immediately.
    According to users on Plurk, Rakuten-based SIMs can no longer be used for PIA or eplus phone call verification as of 2025.
  • Like Hanacell, ifmobile supports both physical SIM and eSIM (eSIM must be requested through customer support).
  • You can purchase directly on ifmobile’s website using a credit card.
  • The signup process only requires a photo of you holding your passport (ID page).
  • Customer service is responsive—even during Japan’s Golden Week holidays.
  • Once the number is activated, you can receive SMS or make calls right away—Japanese website verification (PIA, eplus, LINE) works without issues.

Some online posts suggest you have to wait 30 minutes after roaming is activated via support—but in my case, and confirmed with their staff, it worked immediately with no need for manual activation.


Signup Process and Customer Support

Here’s a quick overview of how I signed up:

  1. Fill in your info on their website. Japanese name doesn't have to match precisely (stroke count can differ), but your English name should match your passport.

  2. Use your English address, and list your nationality as “Taiwan” (no need to write "ROC").

  3. Upload a selfie with your passport and a clear photo of the ID page. The background doesn’t need to be professional—as long as your face is visible and you’re not wearing a mask, it’s fine.

  4. Make the payment (first-month fee + ¥5,500 activation fee).

  5. You must manually contact customer service (WeChat recommended—they reply much faster than LINE). You can request an eSIM and get a refund for the shipping fee.

    During Golden Week, I contacted them via LINE on Wednesday (4/30) and only got a reply on Saturday (5/03).
    Meanwhile, I messaged them on WeChat the same Wednesday and received a reply that day, plus follow-up help on Saturday (5/03).

  6. Shipping is via EMS. International shipping costs ¥2,200.

    Note: Each time you transfer the eSIM to a new phone, there’s a ¥1,100 re-issue fee.

Customer service sometimes replies with voice messages, almost like a casual WeChat voice chat. I’m not sure if it’s just personal preference or situational, but it feels pretty chill . Still, they were helpful and resolved my questions quickly.


Shipping Speed

Here's how it went for me:

  • April 30: I placed the order and inquired.
  • May 1: Shipped in the afternoon (you'll need to ask support for the EMS tracking number manually).
  • May 2 (morning): Arrived at Tokyo International Exchange Center.
  • May 2 (afternoon): Arrived in Taiwan.
  • May 3 (11 AM): Delivered to my home in Taipei (I requested afternoon delivery, but the courier called and delivered it earlier).

For international shipping from Japan to Taiwan, I’d say that’s impressively fast.


Cost Breakdown

Monthly plans vary based on the amount of data you choose. Here's a basic fee overview:

  • Activation Fee: ¥5,500
  • Emergency call service (110/119/120): ¥4/month
  • Incoming SMS while abroad: Free
  • Sending SMS while abroad: ¥220/message
  • Domestic Japan calls: ¥24/30 sec
  • Calls from Japan to overseas: ¥275/30 sec
  • Calls made/received abroad: ¥275/30 sec
  • eSIM transfer (each time): ¥1,100
  • International shipping: ¥2,200 (Domestic Japan shipping: ¥880)

I chose the 1GB/month plan with no additional voice package.


Real-World Usage Tests

The SIM arrived in a large Japanese EMS envelope and included a physical SIM and a Japanese-language instruction sheet.

Tested devices:

  • ASUS Zenfone 8: Could receive SMS, but VoLTE couldn’t be enabled → couldn’t make calls.
  • Apple iPhone 6s (2017): No signal—might be a device-specific issue.
  • Apple iPhone 6+ (2015): Connected to Chunghwa Telecom with full bars right after reboot. Could call Japan/Taiwan and receive/send SMS without issue.

You don’t need to configure APN or install profiles when using it in Taiwan—those are only necessary when you’re in Japan.

Bonus: Ticket Site Verification

  • Successfully passed PIA and eplus concert site phone verification.

    Reminder: When dialing from Taiwan, drop the initial "0" and use Japan’s country code +81.

  • Japanese LINE account binding worked.
    However, LINE age verification still requires a personal visit to the ifmobile office in Tokyo, so you can’t use the LINE ID search feature—only QR code invites will work for now.

Final Thoughts: Is it Worth It?

If you’re just looking for a cheap number to keep long-term ("number parking"), ifmobile isn’t the most cost-effective choice.
But if you need a quick setup, easy application, responsive support, and a number that can receive SMS and make calls, then ifmobile is a lifesaver—especially if you're in a rush for something like PIA or eplus authentication.

For me, getting everything set up within a week and being able to choose between eSIM and physical SIM made it absolutely worth it. The only real downside is the price.
If you’re not in a rush, can physically go to Japan, or don’t need last-minute login verification, then Hanacell is probably the better (and much cheaper) choice.


So if you need a Japanese number for receiving SMS or binding Japanese LINE, consider giving ifmobile a try!

The Lazy Dev’s Guide to Bulk Insert: Efficiently Sync Data from Access MDB to SQL Server

Background

  1. The target computer system is outdated, running Windows Server 2003 (which is essentially Windows XP), and the latest framework it can support is .NET Framework 4.0.3.
  2. The goal is to create a headless application that executes automatically upon startup and provides simple file-based outputs, allowing easy access for other legacy programming languages.

Prerequisites

You need to install the following NuGet packages in Visual Studio. The choice of packages may depend on your specific needs.
(For details on how to install NuGet packages, please refer to online documentation.)

  1. CommandLineParser [v2.9.1]
  2. NLog [v5.4.0]
  3. Dapper [v1.50.2]
  4. DataBooster.SqlServer [v1.8.4]

Additionally, since we want Windows XP compatibility, the project is created as a .NET Framework 4 Console Application.


Logging Setup

Create an nlog.config file in the project root directory:

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

        <!-- Define NLog Targets -->
        <targets>
                <!-- Console Output -->
                <target name="console" xsi:type="Console" layout="${longdate} | ${level:uppercase=true} | ${message}" />

                <!-- File Output -->
                <target name="file" xsi:type="File"
                fileName="logs/SyncDemox.log"
                archiveFileName="logs/SyncDemox.{#}.log"
                archiveNumbering="Rolling"
                archiveEvery="Day"
                maxArchiveFiles="7"
                layout="${longdate} | ${level:uppercase=true} | ${message}" />

                <!-- LastError.txt (Only keeps the latest error) -->
                <target name="lastError" xsi:type="File"
                fileName="user/LastError.txt"
                writeMode="Overwrite"
                layout="${longdate} | ${level:uppercase=true} | ${message}" />
        </targets>

        <!-- Define Logger Rules -->
        <rules>
                <!-- Console and File Logging for INFO and above -->
                <logger name="*" minlevel="Info" writeTo="console, file" />
                <logger name="*" minlevel="Error" writeTo="lastError" />
        </rules>
</nlog>

What this setup does:

  • Last error messages are written to LastError.txt.
  • All log messages are output to both the console and logs/SyncDemo.log.
  • Logs are automatically managed (rolling over daily and retaining only the last 7 files).

Example Usage:

internal class Program
{
    private static readonly Logger Logger = LogManager.GetCurrentClassLogger();

    static void Main(string[] args)
    {
        // ...
        try 
        {
            Logger.Info("Start doing work!");
        }
        catch (Exception ex)
        {
            Logger.Error("SyncMdbToSQLServer Error: " + ex.Message);
        }
        // ...
    }
}

Main Functionality: Copying MDB Data to SQL Server

Program.cs

try
{
    using (var mdbconn = new OleDbConnection(mdbConnStr))
    {
        mdbconn.Open();
        Logger.Info("Database is not locked, starting to read MDB.");

        var result = SyncMdbToSQLServer(a, ini, mdbconn, sqlConnStr, targetTableName);

        WriteLastResult(a, result);
    }
}
catch (OleDbException ex)
{
    Logger.Error("Unable to open MDB, it may be locked! ErrorMsg: " + ex.Message);
    WriteLastResult(a, false);
}

Core Function: SyncMdbToSQLServer

private static bool SyncMdbToSQLServer(ParserResult<CLOptions> a, IniReader ini, OleDbConnection mdbConn, string sqlConnStr, string targetTableName)
{
    try
    {
        var clo = a.Value;

        if (!int.TryParse(ini.Read("MaxRecentMonths", "Sync"), out int maxRecentMonths))
        {
            maxRecentMonths = -1; // All records
        }
        if (clo.Verbose)
        {
            Logger.Info($"MaxRecentMonths: {maxRecentMonths}");
        }

        // Step 1: Read CardData and store it in a Dictionary for quick lookup
        var cardDataLookup = mdbConn.Query<CardData>("SELECT EmNo, CardID, SiteID FROM CardData")
            .ToDictionary(x => new Tuple<int, int>(x.CardID, x.SiteID), x => x.EmNo);

        string whereDateClause = maxRecentMonths <= -1 ?
            string.Empty :
            $" AND DateT >= DateAdd('m', {-1 * (maxRecentMonths-1)}, Date())";

        // Step 2: Fetch AttRecords and fix EmNo when it is 0
        string sSQL = "SELECT NoTe,DateT,NodeID,CardID,Events,SiteID,AddrID,AttRecords.EmNo,SetID,Name,Left " +
            "FROM [AttRecords] " +
            "LEFT JOIN EmData ON AttRecords.EmNo = EmData.EmNo " +
            "WHERE 1 = 1 And (Events = 3 OR Events = 11)" + whereDateClause;
        var attJoinEmRecords = mdbConn.Query<SimpleAttRecord>(sSQL)
        .Select(record =>
        {
            if (record.EmNo == 0)
            {
                var key = new Tuple<int, int>(record.CardID, record.SiteID);
                if (cardDataLookup.TryGetValue(key, out int correctEmNo))
                {
                    record.EmNo = correctEmNo;
                }
            }
            return record;
        }).ToList();

        Logger.Info($"MDB contains {attJoinEmRecords.Count()} records.");

        // Step 3: Download SQL Server data for comparison
        using var sqlConn = new SqlConnection(sqlConnStr);
        sqlConn.Open();
        var existedSqlItems = sqlConn.Query<MhPCard>($"SELECT * FROM [{targetTableName}]");
        var existedSqlHashItems = new HashSet<Tuple<int?, DateTime>>(existedSqlItems.Select(item => new Tuple<int?, DateTime>(item.Code, item.Date)));

        // Step 4: Find new records
        var newRecords = attJoinEmRecords
            .Where(item => !existedSqlHashItems.Contains(new Tuple<int?, DateTime>(item.EmNo, item.DateT)))
            .ToList();

        Logger.Info($"New records: {newRecords.Count()}");

        // Step 5: Bulk Insert using SqlLauncher
        using (SqlLauncher sqlLaun = new SqlLauncher(sqlConnStr, "dbo." + targetTableName, map =>
        { 
            map.Add(0,0);
            map.Add(1,2);
            map.Add(2,5); // Test
        }))
        {
            foreach (var item in newRecords)
            {
                sqlLaun.Post(item.EmNo, item.DateT, item.NoTe);
            }
        }

        Logger.Info("BulkInsert completed successfully!");
        return true;
    }
    catch (Exception ex)
    {
        Logger.Error("SyncMdbToSQLServer Error: " + ex.Message);
        return false;
    }
}

Conclusion

With this approach, we can keep the old system running while avoiding unnecessary stored procedures in SQL Server.

By leveraging Dapper and SqlLauncher, we:
✅ Efficiently read MDB data
✅ Use HashSet to prevent duplicate inserts
✅ Perform BulkInsert for better performance

Personally, I just prefer keeping SQL Server clean and solely for storing data!

Using AutoHotkey to Emulate macOS Cmd + ` Window Switching on Windows OS

As a long-time Windows user, I experienced the convenience of Cmd+ after getting a MacBook Air M1. Especially when switching between different windows within the same application, this shortcut is incredibly smooth. For Windows users, this feature isn't as straightforward—typically, you need to use Alt+Tab to switch between all applications, without limiting it to just a single app. This guide will show you how to achieve a similar operation using AutoHotkey 1.0, allowing you to quickly switch between windows within the same program.

Download and Install AutoHotkey 1.0

  1. Visit the AutoHotkey Website: Open your browser and go to the AutoHotkey official website.
  2. Download AutoHotkey v1.0: The website provides download links for both v1.0 and v2.0. For this guide, we need v1.0, so download v1.0.
  3. Install AutoHotkey: Once the download is complete, run the installer and follow the prompts to complete the installation.

Create and Save Your Script

  1. Create a New AHK Script File: Anywhere on your computer, right-click and choose "New" > "AutoHotkey Script". Name the script "SwitchWindows.ahk".
    Alternatively, you can open a blank file using Windows Notepad.
  2. Copy and Paste the Script Content: Paste the following script code into the new .ahk file and save it:

    ; Define a hotkey, such as Alt + ` (similar to macOS Cmd + `)
    !`::
       ; Get the PID of the current active window
       WinGet, currentPID, PID, A
       ; Get the process name of the current active window
       WinGet, processName, ProcessName, A
       ; Initialize variables
       nextWindow := ""
       foundCurrent := false
       ; Iterate through all windows
       WinGet, id, list,,, Program Manager
       Loop, %id%
       {
           this_id := id%A_Index%
           ; Get the process name of the window
           WinGet, thisProcess, ProcessName, ahk_id %this_id%
           ; Check if the window belongs to the same process and is visible
           if (thisProcess = processName)
           {
               ; If the current window is found, the next matching window is the target
               if (foundCurrent)
               {
                   nextWindow := this_id
                   break
               }
               ; Check if this is the current active window
               if (this_id = WinExist("A"))
                   foundCurrent := true
               ; If the current window is not yet found, set the first matching window as the target
               if (nextWindow = "")
                   nextWindow := this_id
           }
       }
       ; Switch to the target window
       if (nextWindow != "")
           WinActivate, ahk_id %nextWindow%
    return

Place the AHK File in a Specific Location and Set It to Run on Startup

  1. Place the Script in a Specific Location: Place the newly created "SwitchWindows.ahk" script in the C:\Scripts\AHK1\ directory.
  2. Set It to Run on Startup:
    • Press Win + R, type shell:startup, and press Enter.
    • In the startup folder that opens, right-click and choose "Paste Shortcut" (Windows 11 may require expanding the menu first), and paste a shortcut to the "SwitchWindows.ahk" file.
    • This ensures that the script starts automatically every time you boot your computer, saving you from having to manually run it.

How to Temporarily Disable the AHK Script

If you need to temporarily disable the script, such as when you don't want the hotkeys to interfere, follow these steps:

  1. Find the AutoHotkey Icon in the System Tray: In the system tray at the bottom-right of the screen, you'll see a green "H" icon.
  2. Right-Click the Icon: Right-click the green "H" icon to open the menu, and choose "Suspend hotkeys" to pause the script. The icon will turn into an "S". Alternatively, choose "Exit" to completely close the script.

Conclusion

Using AutoHotkey 1.0, you can achieve the convenience of the macOS Cmd+ operation with just a few simple steps, allowing you to set it up and run it automatically.
I hope this guide helps you experience better multi-window switching in the Windows environment and boosts your productivity. Give this script a try and see how much convenience it can bring to your workflow!

Handling Line Breaks in Home Assistant’s YAML to Send LineMessenger Push Messages (Restful Request)

In this blog post, I won't guide you step-by-step on how to configure Home Assistant to send Gmail notifications to LINE, but focusing on how to correctly handle line breaks (\n). This seemingly simple requirement can become quite challenging due to the complexities between YAML, JSON, and various APIs.

This post will focus on ensuring that line breaks are correctly displayed in LINE messages.繼續閱讀...

Import data to PowertoysRunTOTP from Aegis Authenticator

This article will introduce how to export plain JSON data from Aegis Authenticator and convert it into the format required by PowertoysRunTOTP, helping you import two-factor authentication (2FA) accounts from Aegis into the PowerToys Run TOTP extension.

Warning: Do not keep plain JSON files on your computer for an extended period. It is recommended to store them in encrypted storage, such as pCloud Crypto, or use 7zip to compress and set a secure password to protect the files.

Step 1: Export Plain JSON from Aegis Authenticator

First, export your 2FA account data from Aegis Authenticator. Ensure the exported file is in plain JSON format and save it to a secure location, such as C:\path\to\aegis_export.json.

Step 2: Write a PowerShell Script

Write a PowerShell script to convert the exported Aegis JSON file into the format required by PowertoysRunTOTP. Below is the complete script, which you can copy and paste into Notepad and save as a .ps1 file, for example, convert_aegis_to_powertoysrun.ps1.

$inputFilePath = "P:\Crypto Folder\aegis.json"
$outputFilePath = "$env:LOCALAPPDATA\Microsoft\PowerToys\PowerToys Run\Settings\Plugins\Community.PowerToys.Run.Plugin.TOTP\OTPList.json_new"
try {
    # Read the Aegis JSON file and ensure it uses UTF-8 encoding
    $jsonContent = Get-Content -Raw -Path $inputFilePath -Encoding UTF8

    # Check if the JSON file is empty
    if ($jsonContent -eq $null -or $jsonContent.Trim() -eq "") {
        throw "The Aegis JSON file is empty or contains no content"
    }

    try {
        # Parse the JSON file
        $aegisData = $jsonContent | ConvertFrom-Json
    } catch {
        throw "JSON parsing error: $_"
    }

    # Prepare the JSON structure for PowerToysRunTOTP
    $powerToysRunTOTP = @{
        Version = 2
        Entries = @()
    }

    # Check the structure of the Aegis JSON file
    if ($aegisData.db.entries -ne $null) {
        # Iterate over Aegis entries and extract necessary data
        foreach ($entry in $aegisData.db.entries) {
            $newEntry = @{
                Name = "$($entry.issuer): $($entry.name)"
                Key = $entry.info.secret
                IsEncrypted = $false
            }
            $powerToysRunTOTP.Entries += $newEntry
        }
    } else {
        throw "Entries in the Aegis JSON file are empty or not found"
    }

    # Write the converted data to the PowerToysRunTOTP JSON file
    $powerToysRunTOTP | ConvertTo-Json -Depth 3 | Set-Content -Path $outputFilePath -Encoding UTF8

    Write-Host "Aegis JSON conversion successful and saved to $outputFilePath"
} catch {
    Write-Host "An error occurred during the conversion process: $_"
}

Step 3: Run the PowerShell Script

Method 1: Run via Right-Click on Windows 10 or Later

  1. Ensure PowerToys is closed. This prevents the PowertoysRun OTP extension from overwriting the user-edited file during the process.
  2. Open File Explorer and locate the PowerShell script file you saved, such as convert_aegis_to_powertoysrun.ps1.
  3. Right-click the file and select "Run with PowerShell."
  4. If you see a Windows security warning, select "More info" and then click "Run anyway."

Method 2: Run Using PowerShell Command

  1. Ensure PowerToys is closed. This prevents the PowertoysRun OTP extension from overwriting the user-edited file during the process.
  2. Press Win + X and select "Windows PowerShell (Admin)" or "Windows Terminal (Admin)."
  3. In the PowerShell window, type the following command, without pressing Enter yet (there is a space after -File):
    %%%
    PowerShell -ExecutionPolicy Bypass -File
    %%%
  4. Open File Explorer and locate the PowerShell script file you saved.
  5. Drag and drop the file into the PowerShell window. This will automatically fill in the complete path of the file.
  6. Ensure the command looks like this and then press Enter to execute:
    %%%
    PowerShell -ExecutionPolicy Bypass -File "C:\path\to\convert_aegis_to_powertoysrun.ps1"
    %%%

Step 4: Verify the Import Results

  1. Open PowerToys, which will automatically start the TOTP extension.
  2. Once the PowertoysRun TOTP extension starts, it will automatically encrypt the data in the OTPList.json file.
  3. Open PowerToys Run and check if your 2FA accounts were successfully imported. If everything is correct, you should see your imported accounts and be able to use them for authentication.

Summary

Through the above steps, we successfully converted the plain JSON file exported from Aegis Authenticator and imported it into PowertoysRunTOTP. This method helps you easily manage your 2FA accounts and migrate them between different devices.
If you found this article helpful, please leave a comment, give a thumbs up, or share it with others.

If you have any suggestions, feel free to leave a comment!