Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

73 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

line-python

Independent Python client built from the current LINE Android 26.11.0 Thrift contracts.

This is an unofficial, experimental project and is not affiliated with or endorsed by LINE/Yahoo Japan. Use it only with accounts and chats you are authorized to test, at conservative request rates.

Features

  • Secure QR login with link, terminal QR and SVG output
  • Existing access-token login with local session persistence
  • PIN approval flow and reusable certificate login
  • Windows DPAPI-encrypted local session storage
  • getProfile, getAllChatMids, getChats and message history
  • Dedicated /SYNC4 polling contract
  • Named 26.11.0 OpType values and persistent sync revisions
  • Reconnecting background listener and APK-compatible HTTP/2 push mode
  • Lazy access to all APK-extracted service paths
  • Text sendMessage, including automatic E2EE v2 encryption retry
  • Message unsend and /V4 direct/group call route management
  • Incoming private-chat E2EE v1 and chat/group E2EE v2 text decryption
  • Authenticated OBS downloads and verified E2EE media decryption
  • E2EE device-key registration and safe register-first key rotation
  • LIFF view/token operations and modular Square/OpenChat access
  • Explicitly confirmed chat and account-management operations
  • Ready-to-run PublicBot (op 25 + 26) and SelfBot (own op 25) helpers
  • APK-derived Compact Thrift codec and generated protocol schema
  • Pythonic and camelCase-compatible APIs

Credits

Maintained by CyberTKR. The modern client is derived from the MIT-licensed line-new-qr-login-api implementation and retains its contributor attribution.

Install

git clone https://github.com/CyberTKR/line-python.git
cd line-python
python -m pip install -e .

After installation, either invocation style is available:

python -m lineapi --help
simple-line --help

Phone registration

python -m lineapi register

The command asks for the region, mobile number, new password, and SMS or voice PIN. Supported registration regions are TW, TH, JP, KR, and HK. If LINE requests human verification, the official page opens in Chrome and the same registration session continues after completion.

The password must contain at least 8 characters and at least three of these categories: uppercase letter, lowercase letter, number, symbol. Values may also be supplied with --region, --phone, --display-name, --verification-method, LINE_REGISTER_PASSWORD, LINE_SIM_HNI, and LINE_SIM_CARRIER. Registration identity overrides are available through --registration-application, --registration-user-agent, LINE_REGISTER_APPLICATION, and LINE_REGISTER_USER_AGENT. The resulting credentials are saved in the configured .lineapi session and are never printed.

QR login

python -m lineapi qr
python -m lineapi qr --app desktopmac

The command prints the login link, renders a terminal QR when supported and writes lineapi-login-qr.svg. On Windows, credentials and E2EE key-chain metadata are encrypted for the current user in .lineapi/session.dpapi. After approval it reads getProfile, prints the logged-in account name, then performs the initial /SYNC4 sync and persists its revision.

Token login

Use an existing access token interactively, or provide it through the LINE_AUTH_TOKEN environment variable. The token is never printed:

python -m lineapi token
$env:LINE_AUTH_TOKEN = "YOUR_TOKEN"
python -m lineapi token --app desktopwin

Token login also verifies the account with getProfile, prints the account name and performs the initial /SYNC4 sync.

Application profiles

QR and token login support the same selectable LINE application identities:

python -m lineapi qr --app androidsecondary
python -m lineapi qr --app desktopmac
python -m lineapi token --app desktopwin
python -m lineapi token --app chrome

Available values are androidsecondary, desktopmac, desktopwin, and chrome. The selected application header and matching user agent are used for login and all later requests made by that client. Use the same profile when reopening a saved session. Android tracks the project's extracted 26.11.0 protocol; the desktop and Chrome entries are compatibility presets and can be overridden as LINE releases change.

python -m lineapi profile
python -m lineapi chats
python -m lineapi send TARGET_CHATID "hello"
python -m lineapi listen --app androidsecondary --workers 4

Python API

The regular LineClient remains the only main client class. from_config() loads LineConfig and creates the platform-appropriate session storage:

from lineapi import LineClient

client = LineClient.from_config()
profile = client.login_with_qr()

For token login, pass an existing token to client.login_with_token(token). The CLI can instead read LINE_AUTH_TOKEN without placing it in source code.

Direct LineClient construction remains available when full dependency injection or custom storage is needed:

import os

from lineapi import DpapiFileStorage, FileStorage, LineClient

storage = (
    DpapiFileStorage(".lineapi/session.dpapi")
    if os.name == "nt"
    else FileStorage(".lineapi/session.json")
)
client = LineClient(storage=storage)

profile = client.login_with_qr()
print(profile.display_name)

for chat in client.get_chats():
    print(chat.mid, chat.name)

client.send_message("TARGET_CHATID", "hello from Python")
client.unsend_message("MESSAGE_ID")

Modular layout

The project uses a modular service and protocol layout:

lineapi/
├── config.py       # host, app identity, session and sync settings
├── client.py       # messages, chats and operation models
├── cli.py          # QR/token login output and initial sync workflow
├── bots.py         # PublicBot and SelfBot
├── e2ee.py         # E2EE v1/v2 text crypto
├── streaming.py    # continuous push transport
├── services/       # endpoint-specific service bindings
└── protocol/       # Compact/MoreCompact codecs

LineConfig.from_env() supports these optional settings:

  • LINE_API_HOST, LINE_OBS_HOST, LINE_APP_PROFILE, LINE_API_TIMEOUT
  • LINE_APPLICATION, LINE_USER_AGENT for explicit identity overrides
  • LINE_SESSION_PATH, LINE_PUSH, LINE_SYNC_COUNT
  • LINE_AUTH_TOKEN is read only by token login; it is not stored in config.

Example customization:

from lineapi import LineClient, LineConfig

config = LineConfig(push=True, sync_count=100)
client = LineClient.from_config(config)

send_message first uses the normal TalkService contract. If LINE returns E2EE_RETRY_ENCRYPT (82), the client loads the QR-transferred key chain, obtains the current direct or chat shared key and automatically retries with APK-compatible AES-GCM E2EE chunks.

Once an E2EE target is observed, the client sends subsequent replies encrypted immediately instead of spending one request on a known plaintext rejection. Decoded device keys and short-lived group/public keys are cached in memory to reduce round trips; they are never written to logs. Bot examples report per-operation elapsed_ms for local and server-side performance checks. client.listen(..., workers=4) keeps revision polling ordered while processing independent command callbacks concurrently under load.

Verified scope

The following methods were tested against a real authorized account, private chat and group:

  • Secure QR/PIN login and certificate reuse
  • getProfile
  • getAllChatMids
  • getChats
  • getMessageBoxes
  • getPreviousMessagesV2WithRequest
  • sync
  • E2EE text sendMessage
  • Incoming private-chat E2EE v1 and chat/group E2EE v2 text decryption
  • HTTP/2 push operation delivery and reconnect

The generated schema contains many more APK contracts. A method appearing in the schema does not mean it already has a high-level Python binding. Legacy /P4 fetchOps is rejected by the current gateway; use sync.

Media, LIFF, OpenChat and key management

The optional feature services are exposed on the regular client:

message_bytes = client.download_media(message)

view = client.liff.issue_view("YOUR_LIFF_ID", chat_mid="TARGET_CHAT_MID")
joined = client.square.get_joined_squares()
events = client.square.fetch_chat_events("SQUARE_CHAT_MID")

new_key_id = client.register_e2ee_key()
rotated_key_id = client.rotate_e2ee_key(retire_previous=True)

Calls and message unsend

Call routing uses the APK-derived /V4 contract. These methods obtain and manage LINE call routes; audio/video transport and media rendering remain the responsibility of the calling application.

client.unsend_message("MESSAGE_ID")

route = client.acquire_call_route("USER_MID")
group = client.get_group_call("TARGET_CHAT_MID")
group_route = client.acquire_group_call_route("TARGET_CHAT_MID")
client.invite_into_group_call("TARGET_CHAT_MID", ["USER_MID"])

call_url = client.create_group_call_url("Team call")
urls = client.get_group_call_urls()
client.update_group_call_url("CALL_URL_ID", "New title")
client.delete_group_call_url("CALL_URL_ID", confirm=True)

Encrypted OBS media is authenticated with HMAC-SHA256 before AES-CTR decryption. Invalid or truncated objects raise an exception and are never returned as valid media.

Chat-management methods use explicit target lists. Destructive calls require an additional confirmation argument:

client.invite_into_chat("TARGET_CHAT_MID", ["USER_MID"])
client.cancel_chat_invitation("TARGET_CHAT_MID", ["USER_MID"])
ticket = client.reissue_chat_ticket("TARGET_CHAT_MID")
chat = client.find_chat_by_ticket("TICKET_ID")
client.accept_chat_invitation_by_ticket("TARGET_CHAT_MID", "TICKET_ID")
client.reject_chat_invitation("TARGET_CHAT_MID")
client.kick_from_chat("TARGET_CHAT_MID", ["USER_MID"], confirm=True)
client.leave_chat("TARGET_CHAT_MID", confirm=True)

# Permanently unregisters the current account/device and clears its session.
client.unregister_user_and_device("UNREGISTER")

Additional Talk bindings cover contacts, chat creation, announcements, history, followers, configuration and E2EE key lookup:

contact_ids = client.get_contact_ids()
blocked_ids = client.get_blocked_contact_ids()
client.add_friend_by_mid("USER_MID")

chat = client.create_chat("New group", ["USER_MID"])
client.update_chat_name(chat.mid, "Renamed group")
client.update_chat_ticket(chat.mid, enabled=False)

announcements = client.get_chat_announcements(chat.mid)
client.create_chat_announcement(
    chat.mid, "Important notice", "https://example.com/notice"
)

recent = client.get_recent_messages(chat.mid, count=50)
revision = client.get_last_op_revision()
followers = client.get_followers(mid="USER_MID")
public_key = client.get_e2ee_public_key("USER_MID", 2, 1)

Use account and membership operations only in chats and accounts you control.

Continuous operation listener

The conservative listener repeatedly calls the verified /SYNC4 sync contract, persists all revisions and reconnects with bounded exponential backoff:

import os

from lineapi import DpapiFileStorage, FileStorage, LineClient

storage = (
    DpapiFileStorage(".lineapi/session.dpapi")
    if os.name == "nt"
    else FileStorage(".lineapi/session.json")
)
client = LineClient(storage=storage)
client.on("operation", lambda op: print(op.revision, op.type_code, op.type_name))
client.on("op:RECEIVE_MESSAGE", lambda op: print(op.message.text if op.message else ""))
client.run_forever()

An empty HTTP 410 after a long /SYNC4 wait is treated as normal long-poll rotation: the saved revision is preserved and a fresh request is opened. Actual connection failures still use bounded exponential backoff and poll_error.

The supplied Android APK uses a full-duplex HTTP/2 stream at /PUSH/1/subs. Enable the independently extracted framing with push=True:

Without push=True, polling calls the dedicated /SYNC4 endpoint. General Talk RPCs continue to use /S4.

client.run_forever(push=True)

Or from the CLI:

python -m lineapi listen
python -m lineapi listen --push
python -m lineapi listen --push --self-dot-reply
python -m lineapi services

The CLI listener decrypts incoming and outgoing E2EE text before printing it. --self-dot-reply is an opt-in example automation: when the logged-in account sends exactly . in a private chat, room or group, it replies hello to the same exact message target. It never replays historical operations.

PublicBot and SelfBot

Both included bots continuously listen over the APK-compatible push stream and include hello -> Hello! and ping -> pong commands:

python -m lineapi public-bot
python -m lineapi self-bot
  • PublicBot handles op type 25 and 26 separately. For a direct incoming type-26 message it replies to the sender; for a group/room it replies to the original chat.
  • SelfBot accepts only type 25 messages sent by the logged-in account.

The service commands below are available in both bots. Targeted commands use LINE mentions, so users do not need to copy MIDs:

help | commands
hello | ping | ginfo
call info
call status
call members
call who
call host
call type
call media
call media route
call invite @user
call urls
call url create TITLE
call url info NUMBER
call url update NUMBER TITLE
call url delete NUMBER confirm
unsend NUMBER
contact @user
members
readers
message info
save
react 👍|❤️|😆|😮|😢|😡
unreact
ticket
e2ee status
health
services

save, message info, react, and unreact operate on the message replied to in LINE. Saved media is written under .lineapi/media/. Contact, member, reader, and active-call member output uses real LINE mention metadata when the Relation service can resolve the account names.

call urls prints a numbered list containing every title and real URL ID. The remaining URL commands accept the displayed number, so copying a raw ID is not required.

unsend 5 retracts up to five most recent messages sent by the logged-in account in the current chat. It excludes the command message itself, never targets another user's messages, and accepts values from 1 to 20.

PublicBot commands are public by default. Pass a control_mids set or change the example's CONTROL_MIDS value when only selected operators should be able to use call and unsend commands. SelfBot still accepts only the logged-in account's own op type 25 messages.

The editable examples keep operation routing and outgoing text in the application file:

from lineapi import LineClient

client = LineClient.from_config()

def bot(op, client):
    if op.type_code == 25:
        # command behavior and API calls are visible and editable here
        ...
    elif op.type_code == 26:
        # received-message behavior is visible and editable here
        ...

client.listen(bot, push=False)  # continuous /SYNC4 loop

See examples/public_bot.py and examples/self_bot.py for complete short scripts. Replies use resolve_message_text, so plain text and supported E2EE v1/v2 text messages share the same command path. Both examples print every outgoing target and text. Edit the explicit command, type-25 and type-26 branches to change behavior. The included ginfo command demonstrates calling get_chats inside a command before sending group details. Developer logs include compact self_bot.py:line locations so supported terminals can open the source line.

The examples can be launched directly without installing the package first; their small _bootstrap.py helper loads the repository source and root session:

cd examples
python self_bot.py

start_polling(push=True) runs the same listener in a managed daemon thread; call stop_polling() during shutdown. See docs/protocol-26.11.0.md for the APK-derived frame layout and service-type mapping.

Tests

python -m pip install -e ".[dev]"
python -m pytest -q

Security

Never commit .lineapi/, access tokens, QR URLs, PINs, certificates or E2EE private keys. See SECURITY.md for reporting instructions.

Protocol source

The schema generator reads JADX output from a locally supplied APK and records field IDs, types, method argument/result pairs and service paths. Decompiled APK sources and APK binaries are intentionally not included in this repository.

License

MIT. The license covers this repository's original source code only. LINE, related trademarks, application binaries and service contracts belong to their respective owners.

About

v0.1.0-alpha.1 - Phone registration with interactive human verification - Profile, chat, message and contact APIs - SYNC4 operation

Resources

Security policy

Stars

6 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages