알림과 웹훅
폰의 알림을 PC에서 받고, 특정 알림을 기다리고, 모든 알림에 반응하고, 알림에 답장하거나 열거나 닫고, 웹훅으로 전달합니다.
번역된 페이지입니다. 영어 문서와 내용이 다르면 영어 문서가 기준입니다. English
Droidline은 폰에 도착한 알림을 PC로 넘길 수 있습니다. 안드로이드의 알림 접근 권한을 쓰며, 이 권한으로는 제목과 본문 전체를 볼 수 있습니다(접근성 서비스만으로는 줄인 텍스트만 보입니다).
켜기
- 폰에서 Droidline의 설정 탭을 열고 알림 접근을 허용합니다.
- 어떤 앱의 알림을 PC로 보낼지 고릅니다. 고르기 전에는 아무것도 보내지 않으므로 개인 메시지가 폰 밖으로 나가지 않습니다. 앱의 상태 탭에 있는 PC로 보내는 알림에서 앱을 고르거나, 코드에서 지정합니다.
d.notify_filter(["com.google.android.gm", "com.google.android.apps.messaging"])
print(d.notify_filter()) # 현재 목록 읽기await d.notifyFilter(["com.google.android.gm", "com.google.android.apps.messaging"]);
console.log(await d.notifyFilter()); // 현재 목록 읽기droidline notify_filter com.google.android.gm,com.google.android.apps.messaging
droidline notify_filter알림 하나 기다리기
wait_notification은 조건에 맞는 알림이 도착할 때까지 기다렸다가 그 알림을 돌려줍니다.
n = d.wait_notification("textContains", "verification code", 60)
print(n["package"], n["title"], n["text"])const n = await d.waitNotification("textContains", "verification code", 60);
console.log(n.package, n.title, n.text);droidline wait_notification textContains "verification code" 60첫 번째 인자는 무엇을 비교할지 정합니다. text와 textContains는 제목과 본문을 모두 보고, title은 제목만 보며, package는 앱을 비교합니다. 한 앱의 알림만 받으려면 package=도 함께 넘기세요.
기다리는 일은 서버가 하므로, 그동안 같은 폰에 보낸 다른 명령도 계속 실행됩니다. 이 명령은 호출한 뒤에 도착하는 알림을 기다립니다. 이미 떠 있는 알림을 보려면 notifications()나 has_notification(by, value)를 쓰세요.
모든 알림에 반응하기
stop = d.on_notification(package="com.google.android.gm",
callback=lambda n: print(n["title"], n["text"]))
# ... 프로그램은 계속 실행됩니다. 끝내려면 stop()을 호출하세요const stop = await d.onNotification({ package: "com.google.android.gm" },
(n) => console.log(n.title, n.text));
// ... 끝내려면 stop()을 호출하세요. 그 전까지 프로세스는 계속 실행됩니다droidline on_notification --package com.google.android.gmCLI는 Ctrl+C를 누를 때까지 알림마다 JSON 한 줄을 출력합니다. 그래서 다른 도구로 파이프하기 쉽습니다. 알림 하나는 다음과 같습니다.
{"event":"notification","device":"k7d2q9xa","key":"0|com.google.android.gm|1|null|10123","package":"com.google.android.gm","title":"New message","text":"Lunch at 12?","time":1791360000000,"actions":["reply","mark_read"]}
알림 다루기
알림마다 key가 있습니다. 이 값으로 그 알림을 열거나, 답장하거나, 닫습니다.
for n in d.notifications():
if "Bank" in n["title"]:
d.notification_click(n["key"]) # 알림이 가리키는 곳을 엽니다
d.notification_reply(key, "On my way") # 답장 동작이 있는 알림에만 해당
d.notification_dismiss(key)for (const n of await d.notifications()) {
if (n.title.includes("Bank")) await d.notificationClick(n.key);
}
await d.notificationReply(key, "On my way");
await d.notificationDismiss(key);KEY=$(droidline --json notifications | jq -r '.value[0].key')
droidline notification_click "$KEY"actions에는 알림이 제공하는 동작이 나옵니다. notification_reply는 여기에 reply가 있을 때만 동작합니다.
폰이 오프라인일 때
폰은 PC에 접속할 수 없는 동안 최근 알림을 최대 500개까지 보관했다가, 다시 접속하면 순서대로 보냅니다.
웹훅
서버는 조건에 맞는 알림을 원하는 URL로 POST할 수 있습니다. 그래서 다른 시스템이 SDK 없이도 알림에 반응할 수 있습니다. config.toml에 웹훅마다 섹션을 하나씩 추가하고 droidline serve를 다시 시작하세요.
[[webhooks]]
url = "https://example.com/hooks/droidline"
package = "com.google.android.gm" # 선택
text_contains = "order" # 선택, 제목이나 본문에서 찾음
device = "shelf-01" # 선택
본문은 알림을 JSON으로 담고 device와 device_name을 더한 것입니다. 모든 요청에는 서명이 붙어 있어서 내 서버가 보낸 요청인지 확인할 수 있습니다.
X-Droidline-Timestamp: 1791360000
X-Droidline-Signature: sha256=<hex HMAC-SHA256 of timestamp + "." + body>
서명용 시크릿은 droidline webhook secret으로 확인합니다. 수신 측에서 서명을 검사하는 코드입니다.
import hashlib, hmac
def valid(secret: str, timestamp: str, body: bytes, signature: str) -> bool:
mac = hmac.new(secret.encode(), timestamp.encode() + b"." + body, hashlib.sha256).hexdigest()
return hmac.compare_digest("sha256=" + mac, signature)import { createHmac, timingSafeEqual } from "node:crypto";
function valid(secret, timestamp, body, signature) {
const mac = "sha256=" + createHmac("sha256", secret).update(`${timestamp}.`).update(body).digest("hex");
return mac.length === signature.length && timingSafeEqual(Buffer.from(mac), Buffer.from(signature));
}전송에 실패하면 1초 뒤와 4초 뒤, 두 번 다시 시도합니다.
Android 15 이상의 인증번호
Android 15는 시스템 자체 앱이 아닌 앱에게 알림 속 인증번호를 숨깁니다. 알림은 그대로 받지만 번호는 가려져 있습니다. 알림은 신호로만 쓰고, 번호는 앱 안에서 읽으세요.
d.wait_notification("package", "com.example.bank", 120)
d.launch("com.example.bank")
code = d.get_text("id", "otp_value")await d.waitNotification("package", "com.example.bank", 120);
await d.launch("com.example.bank");
const code = await d.getText("id", "otp_value");droidline wait_notification package com.example.bank 120
droidline launch com.example.bank
droidline get_text id otp_value