Một thư viện chat
cho Facebook, hiện đại hơn.
A chat library
for Facebook, reimagined.
fbchat-v2 là thư viện mã nguồn mở để tự động hóa tin nhắn, quản lý nhóm và lắng nghe sự kiện Messenger thời gian thực — không cần Graph API. fbchat-v2 is an open-source library for automating Messenger conversations, managing group threads, and listening to real-time events — no Graph API required.
_listening_e2ee (bridge Go dựa trên Signal Protocol). Đọc/gửi được cả tin nhắn 1-1 lẫn nhóm ở chế độ E2EE.
Since November 2024, Facebook has enabled end-to-end encryption by default on Messenger. The library has supported Secret Conversation decryption since May 12, 2026 through the _listening_e2ee module (a Go bridge based on the Signal Protocol). It can read and send both one-to-one and group E2EE messages.
Vì sao chọn fbchat-v2Why Choose fbchat-v2
Không như Graph API chỉ hoạt động với Fanpage,Unlike the Graph API, which is limited to Pages, fbchat-v2 tích hợp sâu qua tài khoản cá nhân.integrates directly with personal accounts.
Kiến trúc ba tầngThree-Layer Architecture
Mã nguồn được phân chia thành 3 lớp rõ ràng để dễ bảo trì.The codebase is split into three clear layers for maintainability.
Cài đặt và lựa chọn namespaceInstallation & Namespace Selection
Chọn một trong hai phương thức cài đặt dưới đây. Gói PyPI sử dụng namespace fbchat_v2; bản chạy trực tiếp từ mã nguồn sử dụng các package cấp cao nhất trong src/.Choose one of the two installation methods below. The PyPI package uses the fbchat_v2 namespace; running directly from source uses the top-level packages in src/.
PyPI: from fbchat_v2._core...
Mã nguồn:Source: from _core... sau khi thêm src vào đường dẫn import. Một ứng dụng nên sử dụng thống nhất một kiểu import.from _core... after adding src to the import path. An application should consistently use one import style.
python -m venv .venv
# Windows PowerShell
.venv\Scripts\Activate.ps1
# Linux / macOS
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install "fbchat-v2=="
from fbchat_v2._core._session import dataGetHome
from fbchat_v2._messaging._send import api as SendAPI
git clone --recurse-submodules https://github.com/m008v/fbchat-v2.git
cd fbchat-v2
python -m venv .venv
python -m pip install -e .
# Windows PowerShell
$env:PYTHONPATH = "src"
python src/main.py
# Linux / macOS
export PYTHONPATH=src
python src/main.py
from _core._session import dataGetHome
from _messaging._send import api as SendAPI
Yêu cầu hệ thốngSystem Requirements
| Thành phầnComponent | Tối thiểuMinimum | Khi nào cầnWhen needed | Ghi chúNotes |
|---|---|---|---|
| Python | 3.10 | Luôn luônAlways | Khuyến nghị 3.11 or 3.12Recommended 3.11 or 3.12 |
| httpx | 0.27 | Luôn luônAlways | Transport async chínhMain async transport |
| paho-mqtt | 1.6.1 | Listener / LS task | Vòng lặp blocking được đặt sau boundary asyncBlocking loop placed behind the async boundary |
| Go | 1.26.5 theoper go.mod | Chỉ khi tự build bridgeOnly when building the bridge manually | Không cần nếu dùng binary release phù hợpNot needed when using a compatible release binary |
| Git | Bản hiện hànhCurrent version | Clone source / submodule | Phụ thuộc bridge:Bridge dependency: bridge-e2ee/meta |
Kiểm tra khả năng importCheck Import Capability
import asyncio
from fbchat_v2._core._session import dataGetHome
from fbchat_v2._messaging._send import api as SendAPI
async def main() -> None:
assert callable(dataGetHome)
assert callable(SendAPI().send)
print("fbchat-v2 2.3.0 import OK")
asyncio.run(main())
Mô hình thực thi bất đồng bộAsynchronous Execution Model
API v2.3.0 được thiết kế theo hướng async-first. Khi một coroutine được gọi mà không có await, thao tác tương ứng chưa được thực thi và giá trị trả về chỉ là một đối tượng coroutine.The v2.3.0 API follows an async-first design. When a coroutine is called without await, the operation has not run; the returned value is only a coroutine object.
data_fb = dataGetHome(cookie)
result = SendAPI().send(data_fb, "hello", user_id)
listener.connect_mqtt()Kết quả là một đối tượng coroutine và có thể phát sinh cảnh báo was never awaited hoặc lỗi object has no attribute 'get'.The result is a coroutine object and may trigger a was never awaited warning or an object has no attribute 'get' error.
data_fb = await dataGetHome(cookie)
result = await SendAPI().send(data_fb, "hello", user_id)
task = asyncio.create_task(listener.connect_mqtt())Các thao tác ngắn được await trực tiếp; listener chạy dài hạn được quản lý bằng một task.Short operations are awaited directly; long-running listeners are managed by a task.
Điểm vào của ứng dụng CLICLI Application Entry Point
import asyncio
async def main() -> None:
# Entire async workflow goes here.
...
if __name__ == "__main__":
asyncio.run(main())
Khi ứng dụng đã có event loopWhen App Already Has an Event Loop
FastAPI, Jupyter, Discord bot hoặc framework async đã sở hữu event loop. Trong coroutine của framework, gọi await trực tiếp. Không lồng asyncio.run().FastAPI, Jupyter, Discord bots, and other async frameworks already own an event loop. Inside a framework coroutine, use await directly. Do not nest asyncio.run().
Chạy đồng thời có kiểm soátControlled Concurrency
import asyncio
import httpx
from fbchat_v2._features._facebook import _notification, _search
async with httpx.AsyncClient(timeout=30) as client:
async with asyncio.TaskGroup() as group:
notification_task = group.create_task(
_notification.func(data_fb, client=client)
)
search_task = group.create_task(
_search.func(data_fb, "m008v", client=client)
)
notifications = notification_task.result()
users = search_task.result()
Python 3.10 chưa hỗ trợ TaskGroup; hãy sử dụng asyncio.gather(). Luôn thiết lập timeout tại ranh giới tác vụ và không bỏ qua CancelledError.Python 3.10 does not support TaskGroup; use asyncio.gather(). Always configure timeouts at task boundaries and do not ignore CancelledError.
Từ cookie đến dataFBFrom Cookies to dataFB
dataGetHome() truy cập trang chủ Facebook, trích xuất các token phiên và trả về cấu trúc dữ liệu dùng chung cho các module còn lại.dataGetHome() accesses the Facebook home page, extracts session tokens, and returns the shared data structure used by the other modules.
import asyncio
from fbchat_v2._core._session import dataGetHome
async def main() -> None:
cookie = "c_user=...; xs=...; fr=...; datr=...;"
data_fb = await dataGetHome(cookie)
if data_fb is None:
raise RuntimeError("Cookie expired or Facebook changed homepage token.")
required = {
"FacebookID",
"fb_dtsg",
"jazoest",
"sessionID",
"clientRevision",
"cookieFacebook",
}
missing = [key for key in required if not data_fb.get(key)]
if missing:
raise RuntimeError(f"dataFB missing field: {', '.join(missing)}")
asyncio.run(main())
Hợp đồng dataFBdataFB Contract
| Field | Vai tròRole | Có nhạy cảm?Is sensitive? |
|---|---|---|
FacebookID | ID tài khoản hiện hànhCurrent account ID | CóYes |
fb_dtsg, jazoest | Token chống giả mạo request nội bộInternal request anti-forgery token | Rất nhạy cảmVery sensitive |
sessionID | ID sinh cho vòng đời requestID generated for the request lifecycle | CóYes |
clientRevision | Revision Facebook client | Ít hơn, nhưng vẫn không nên log toàn objectLess, but still shouldn't log entire objects |
cookieFacebook | Cookie gốc dùng cho HTTP và bridgeBase cookie used for HTTP and bridge | Tuyệt đối bí mậtStrictly confidential |
Lưu trữ phiên cục bộLocal session storage
from pathlib import Path
from fbchat_v2._core._storage import FileSessionStorage
storage = FileSessionStorage(Path(".secrets/facebook-session.json"))
storage.save(cookie)
cookie = storage.load()
storage.clear()
from fbchat_v2._core._storage import EnvSessionStorage
storage = EnvSessionStorage("FBCHAT_COOKIES")
cookie = storage.load()
if not cookie:
raise RuntimeError("Thiếu biến FBCHAT_COOKIES")from fbchat_v2._core._storage import EnvSessionStorage
storage = EnvSessionStorage("FBCHAT_COOKIES")
cookie = storage.load()
if not cookie:
raise RuntimeError("Missing FBCHAT_COOKIES variable")
Không lưu cookie thật trongDo not store real cookies in config.example.json, mã nguồn, ảnh chụp màn hình hoặc nhật ký CI. Tệp lưu trữ cục bộ chỉ tách thông tin bí mật khỏi mã nguồn và không tự động mã hóa nội dung., source code, screenshots, or CI logs. Local storage files only separate secrets from source code; they do not encrypt their contents automatically.
Tái sử dụng httpx.AsyncClientReusing httpx.AsyncClient
Một HTTP client được duy trì trong suốt vòng đời ứng dụng giúp tái sử dụng kết nối TCP/TLS, thống nhất cấu hình timeout và hỗ trợ kiểm thử bằng transport giả lập.A single HTTP client maintained throughout the app lifecycle helps reuse TCP/TLS connections, unifies timeout configurations, and supports testing with mock transports.
import asyncio
import httpx
from fbchat_v2._features._facebook import _notification, _search
timeout = httpx.Timeout(30.0, connect=10.0)
limits = httpx.Limits(max_connections=20, max_keepalive_connections=10)
async with httpx.AsyncClient(
timeout=timeout,
limits=limits,
follow_redirects=True,
) as client:
notifications, users = await asyncio.gather(
_notification.func(data_fb, client=client),
_search.func(data_fb, "Minh", client=client),
)
Bên gọi truyền client=Caller Passes client=
Bên gọi sở hữu client và chịu trách nhiệm đóng tài nguyên. Phương thức này phù hợp với bot, worker và web service hoạt động dài hạn.The caller owns the client and is responsible for closing it. This approach suits long-running bots, workers, and web services.
Không truyền client=Without Passing client=
Helper tự tạo và đóng client cho từng lần gọi. Phương thức này phù hợp với script ngắn nhưng làm tăng chi phí thiết lập kết nối khi gọi nhiều API liên tiếp.The helper creates and closes the client for each call. This method is suitable for short scripts but increases connection setup costs when calling multiple APIs consecutively.
Timeout tại ranh giới tác vụTimeout at Task Boundaries
try:
result = await asyncio.wait_for(
_search.func(data_fb, "query", client=client),
timeout=20,
)
except asyncio.TimeoutError:
result = {"error": 1, "messages": "Search timed out."}
- Không chia sẻ một client giữa nhiều event loop ở các thread khác nhau.Do not share one client across multiple event loops in different threads.
- Không sử dụng client sau khi thoátDo not use the client after leaving the
async withkhối.block. - Không tắt xác minh TLS để chữa lỗi chứng chỉ. Sửa CA/proxy của môi trường.Do not disable TLS verification to fix certificate errors. Fix the environment's CA/proxy.
- Không thử lại vô hạn đối với các request làm thay đổi trạng thái, chẳng hạn đăng bài hoặc gửi tin nhắn.Do not retry indefinitely for state-changing requests, such as posting or sending messages.
Gửi tin nhắn thườngSending Normal Messages
SendAPI().send() hỗ trợ gửi tới người dùng, nhóm, nhiều người nhận, trả lời tin nhắn và tệp đính kèm. Mỗi lần gọi tạo form riêng nên có thể thực thi đồng thời.supports users, groups, multiple recipients, replies, and attachments. Each call creates its own form, so calls can run concurrently.
await SendAPI().send(
dataFB,
contentSend,
threadID,
typeAttachment=None,
attachmentID=None,
typeChat=None,
replyMessage=None,
messageID=None,
client=None,
)
| Tham sốParameters | KiểuType | Quy tắcRule |
|---|---|---|
contentSend | str | int | Use chuỗi rỗng khi chỉ gửi attachmentUse empty string when only sending attachments |
threadID | ID hoặc danh sách IDID or list of IDs | Danh sách chỉ hợp lệ khi typeChat="user"A list is valid only when typeChat="user" |
typeChat | "user" | None | User direct or group threadUser direct or group thread |
typeAttachment | gif, image, video, file, audio | Phải đi cùng attachmentIDMust be provided with attachmentID |
replyMessage | bool | Khi true phải có messageIDWhen true, messageID is required |
client | httpx.AsyncClient | Optional, truyền bằng keywordOptional, passed by keyword |
result = await SendAPI().send(
data_fb,
"Xin chào",
threadID="100012345678",
typeChat="user",
client=client,
)result = await SendAPI().send(
data_fb,
"Hello",
threadID="100012345678",
typeChat="user",
client=client,
)result = await SendAPI().send(
data_fb,
"Thông báo nhóm",
threadID="987654321",
typeChat=None,
client=client,
)result = await SendAPI().send(
data_fb,
"Group notification",
threadID="987654321",
typeChat=None,
client=client,
)result = await SendAPI().send(
data_fb,
"Thông báo riêng",
threadID=["10001", "10002"],
typeChat="user",
client=client,
)result = await SendAPI().send(
data_fb,
"Private notification",
threadID=["10001", "10002"],
typeChat="user",
client=client,
)result = await SendAPI().send(
data_fb,
"Nội dung trả lời",
threadID="100012345678",
typeChat="user",
replyMessage=True,
messageID="mid.$original",
client=client,
)result = await SendAPI().send(
data_fb,
"Reply content",
threadID="100012345678",
typeChat="user",
replyMessage=True,
messageID="mid.$original",
client=client,
)Xử lý kết quả trả vềHandling Responses
if not isinstance(result, dict):
raise TypeError("Send API did not return dict.")
if result.get("error"):
payload = result.get("payload") or {}
raise RuntimeError(
payload.get("error-description") or "Facebook rejected message sending."
)
message_id = (result.get("payload") or {}).get("messageID")
Tải lên và gửi tệp đính kèmUploading & Sending Attachments
Tải tệp và gửi tin nhắn là hai bước độc lập. ChỉUploading a file and sending a message are separate steps. Only a valid attachmentID trong metadata hợp lệ mới được chuyển sang sender;from the returned metadata may be passed to the sender; uploadID không thể thay thế giá trị này.is not a substitute.
Module từ chối danh sách rỗng và đường dẫn không tồn tại trước khi gửi request.The module rejects empty lists and non-existent paths before sending the request.
Truyền httpx.AsyncClient dùng chung để request được thực thi bất đồng bộ.Pass a shared httpx.AsyncClient so the request runs asynchronously.
Kết quả hợp lệ phải có attachmentID và typeAttachment.A valid result must contain attachmentID and typeAttachment.
Truyền đúng loại tệp và ID đính kèm sangPass the correct file type and attachment ID to SendAPI.
from fbchat_v2._messaging import _attachments
from fbchat_v2._messaging._send import api as SendAPI
uploaded = await _attachments.func(
"photo.jpg",
data_fb,
client=client,
include_error=True,
)
if not uploaded or not uploaded.get("attachmentID"):
raise RuntimeError(f"Upload failed: {uploaded}")
result = await SendAPI().send(
data_fb,
"Attached image",
threadID="100012345678",
typeChat="user",
typeAttachment=uploaded["typeAttachment"],
attachmentID=uploaded["attachmentID"],
client=client,
)
Cấu trúc kết quả thành côngSuccess Result Structure
{
"attachmentID": "123...",
"attachmentUrl": "https://...",
"videoDuration": None,
"attachmentType": "image/jpeg",
"typeAttachment": "image",
}
Tải lên nhiều tệpUploading Multiple Files
Parser hiện trả về phần tử metadata đầu tiên và không cung cấp danh sách tương ứng với toàn bộ đường dẫn. Quy trình đáng tin cậy là tải từng tệp, kiểm tra từng kết quả rồi truyền danh sách ID:The parser currently returns the first metadata element and does not provide a list corresponding to the entire path. A reliable process is to upload each file, check each result, and then pass the ID list:
attachment_ids = []
for path in ["a.jpg", "b.jpg"]:
item = await _attachments.func(path, data_fb, client=client, include_error=True)
if not item or not item.get("attachmentID"):
raise RuntimeError(f"Could not upload {path}: {item}")
attachment_ids.append(item["attachmentID"])
await SendAPI().send(
data_fb,
"Album",
threadID=user_id,
typeChat="user",
typeAttachment="image",
attachmentID=attachment_ids,
client=client,
)
metadata: {"0": null} hoặc mã lỗi 1357054 cho biết máy chủ không trả về metadata của tệp đính kèm. Hãy kiểm tra phiên đăng nhập, MIME, tệp và endpoint. Bật include_error=True để nhận phần tóm tắt lỗi và trích đoạn phản hồi đã được giới hạn độ dài.metadata: {"0": null} or error code 1357054 means the server did not return attachment metadata. Check the login session, MIME type, file, and endpoint. Enable include_error=True to receive an error summary and a length-limited response excerpt.
Listener MQTT thườngNormal MQTT Listener
Listener này xử lý sự kiện thường và một số hội thoại nhóm. Đối với hội thoại cá nhân E2EE, hãy sử dụng listener dựa trên bridge.This listener handles normal events and some group threads. For 1-on-1 E2EE threads, use the bridge-based listener.
import asyncio
from fbchat_v2._messaging._listening import listeningEvent
listener = listeningEvent(data_fb, message_queue_maxsize=1000)
listener_task = asyncio.create_task(
listener.connect_mqtt(),
name="fbchat-mqtt-listener",
)
try:
while True:
if listener_task.done():
listener_task.result()
event = await listener.get_message(timeout=30)
if event is None:
continue
print(event["body"], event["messageID"])
finally:
await listener.disconnect()
await listener_task
| Method | Mục đíchPurpose | Lưu ýNote |
|---|---|---|
get_last_seq_id() | Lấy sequence ID ban đầuGet initial sequence ID | Async |
connect_mqtt() | Chạy reconnect loopRun reconnect loop | Tạo task vì chạy dài hạnCreate a task for long-running operation |
get_message(timeout) | Lấy sự kiện từ hàng đợiConsume an event from the queue | TrảReturns None khi timeouton timeout |
disconnect() | Dừng clientStop the client | Gọi trongCall inside finally |
Cấu trúc sự kiện đã chuẩn hóaStandardized Event Structure
{
"body": "ping",
"timestamp": 1710000000000,
"userID": "1000...",
"messageID": "mid.$...",
"replyToID": "thread-id",
"type": "thread",
"attachments": {"id": 0, "url": None},
"mentions": [],
}
Hàng đợi có giới hạn và loại bỏ sự kiện cũ nhất khi đầy. Theo dõiThe queue is bounded and drops the oldest event when full. Monitor listener.droppedMessages để phát hiện consumer xử lý chậm. Không nên thăm dòto detect a slow consumer. Do not poll bodyResults trong ứng dụng mới vì đây chỉ là ảnh chụp trạng thái cuối cùng.in new applications because it is only a snapshot of the latest state.
Cài bridge E2EESetup E2EE Bridge
Python wrapper khởi chạy tệp thực thi fbchat-bridge-e2ee. Có thể sử dụng tệp dựng sẵn từ GitHub Release hoặc tự biên dịch từ mã nguồn.The Python wrapper launches the fbchat-bridge-e2ee executable. Use a prebuilt binary from GitHub Releases or compile it from source.
Phương thức 1 - sử dụng binary phát hành sẵnMethod 1 - Using Pre-built Binaries
| Nền tảngPlatform | Asset |
|---|---|
| Windows x64 | fbchat-bridge-e2ee-windows-amd64.exe |
| Linux x64 | fbchat-bridge-e2ee-linux-amd64 |
| Linux ARM64 | fbchat-bridge-e2ee-linux-arm64 |
| macOS Intel | fbchat-bridge-e2ee-darwin-amd64 |
| macOS Apple Silicon | fbchat-bridge-e2ee-darwin-arm64 |
Tải tệp phù hợp từ GitHub Releases, đặt vào thư mục build/ hoặc khai báo đường dẫn tuyệt đối:Download the matching binary from GitHub Releases, place it in build/, or provide an absolute path:
# Windows PowerShell
$env:FBCHAT_E2EE_BIN = "C:\path\to\fbchat-bridge-e2ee-windows-amd64.exe"
# Linux / macOS
chmod +x ./fbchat-bridge-e2ee-linux-amd64
export FBCHAT_E2EE_BIN=/absolute/path/fbchat-bridge-e2ee-linux-amd64
Phương thức 2 - biên dịch từ mã nguồnMethod 2 - Compile from Source
git submodule update --init --recursive bridge-e2ee/meta
Set-Location bridge-e2ee
go mod download
New-Item -ItemType Directory -Force ..\build | Out-Null
go build -ldflags="-s -w" -o ..\build\fbchat-bridge-e2ee.exe .
Set-Location ..
.\build\fbchat-bridge-e2ee.exe --helpgit submodule update --init --recursive bridge-e2ee/meta
cd bridge-e2ee
go mod download
mkdir -p ../build
go build -ldflags="-s -w" -o ../build/fbchat-bridge-e2ee .
cd ..
./build/fbchat-bridge-e2ee --helpThứ tự xác định binaryBinary resolution order
binary_path=truyền vào constructor.passed to the constructor.- Biến môi trường
FBCHAT_E2EE_BIN.TheFBCHAT_E2EE_BINenvironment variable. - Đường dẫn mặc định trong
build/.The default path underbuild/. - Tự động tải tệp GitHub Release phù hợp nếu đường dẫn mặc định chưa tồn tại.Automatically download the suitable GitHub Release file if the default path does not exist.
e2ee_memory_only=True giữ trạng thái thiết bị trong RAM. Khi lưu trạng thái bằng device_path, cần bảo vệ tệp này như thông tin xác thực vì nó đại diện cho thiết bị E2EE đã được ghép nối.e2ee_memory_only=True keeps device state in memory. When persisting state through device_path, protect the file like a credential because it represents a paired E2EE device.
Vòng đời listener E2EEE2EE Listener Lifecycle
Đăng ký callback trước khi khởi động, chạy listener bằng một task, chờ hoàn tất handshake trong worker thread, xử lý sự kiện qua hàng đợi và dừng bridge trong khốiRegister the callback before startup, run the listener as a task, wait for the worker-thread handshake, process events through a queue, and stop the bridge in a finally. block.
import asyncio
import contextlib
from fbchat_v2._messaging._listening_e2ee import listeningE2EEEvent
async def run_e2ee(data_fb: dict) -> None:
listener = listeningE2EEEvent(
data_fb,
log_level="none",
e2ee_memory_only=True,
enable_e2ee=True,
)
loop = asyncio.get_running_loop()
events: asyncio.Queue[dict] = asyncio.Queue(maxsize=1000)
def enqueue(event: dict) -> None:
if events.full():
with contextlib.suppress(asyncio.QueueEmpty):
events.get_nowait()
events.put_nowait(event)
listener.on_message(
lambda event: loop.call_soon_threadsafe(enqueue, event)
)
task = asyncio.create_task(
listener.connect_mqtt(),
name="fbchat-e2ee-listener",
)
try:
ready = await asyncio.to_thread(
listener.wait_until_connected,
90,
require_e2ee=True,
)
if not ready:
raise TimeoutError("E2EE handshake has not completed.")
while True:
if task.done():
task.result()
event = await events.get()
if event.get("type") not in {"e2eeMessage", "message"}:
continue
await handle_message(listener, event)
finally:
listener.stop()
try:
await asyncio.wait_for(task, timeout=5)
except asyncio.TimeoutError:
task.cancel()
with contextlib.suppress(asyncio.CancelledError):
await task
Các loại sự kiện quan trọngImportant Event Types
| Type | Ý nghĩaMeaning | Ứng dụng nên làm gìWhat the app should do |
|---|---|---|
ready | Bridge has created clientBridge has created client | Status recorded; E2EE handshake may not be completeStatus recorded; E2EE handshake may not be complete |
e2eeConnected | E2EE đã sẵn sàngE2EE ready | Allow sending operationsAllow sending actions |
e2eeMessage | Tin đã giải mãDecrypted message | Use chatJid, senderJid, idUse chatJid, senderJid, id |
message | Normal event via bridgeNormal event via bridge | Use threadId, senderIdUse threadId, senderId |
error | Request or bridge errorRequest or bridge error | Log error type but not secretsLog error type but not secrets |
bridge_fatal | Watchdog has exhausted retriesWatchdog exhausted retries | Mark health check failed; do not initialize infinite listenerMark health check as failed; do not initialize infinite listener |
Cấu trúc e2eeMessagee2eeMessage Structure
{
"type": "e2eeMessage",
"data": {
"id": "message-id",
"text": "ping",
"timestampMs": 1710000000000,
"senderId": "1000...",
"threadId": "...",
"chatJid": "1000...@msgr",
"senderJid": "1000...@msgr",
"attachments": [],
"mentions": [],
},
}
Gửi và trả lời tin nhắn E2EESending & Replying E2EE Messages
data = event["data"]
result = await listener.send_e2ee_message(
data["chatJid"],
"pong",
reply_to_id=data["id"],
reply_to_sender_jid=data["senderJid"],
)
on_message() là callback đồng bộ được gọi từ vòng lặp thăm dò của bridge. Không gọi coroutine trực tiếp trong callback này. Hãy chuyển sự kiện về event loop bằng loop.call_soon_threadsafe().on_message() is a synchronous callback invoked from the bridge polling loop. Do not call a coroutine directly from this callback. Send the event back to the event loop with loop.call_soon_threadsafe().
Thao tác nâng cao qua bridgeAdvanced Operations via Bridge
Sau khi listener sẵn sàng, tạo BridgeActions từ bridge đang hoạt động để tái sử dụng cùng một tiến trình cho các thao tác tiếp theo.After the listener is ready, create BridgeActions from the active bridge to reuse the same process for subsequent operations.
from fbchat_v2._messaging._bridge_actions import BridgeActions
if listener._bridge is None:
raise RuntimeError("Bridge is not ready.")
actions = BridgeActions(listener._bridge)
| Method async | Mục đíchPurpose |
|---|---|
edit_message(message_id, new_text) | Edit normal messageEdit normal message |
unsend_message(message_id) | Unsend normal messageUnsend normal message |
edit_e2ee_message(chat_jid, message_id, new_text) | Edit E2EE messageEdit E2EE message |
unsend_e2ee_message(chat_jid, message_id) | Unsend E2EE messageUnsend E2EE message |
send_typing_indicator(thread_id, is_typing, ...) | Typing thườngNormal typing |
mark_read(thread_id, watermark_ts) | Đánh dấu đã đọcMark as read |
send_e2ee_typing(chat_jid, is_typing) | Typing E2EE |
send_e2ee_audio(chat_jid, data, ...) | Send audio bytesSend audio bytes |
send_e2ee_image(chat_jid, data, ...) | Gửi image bytesSend image bytes |
download_media(url) | Tải media thường thành bytesDownload normal media as bytes |
download_e2ee_media(...) | Tải và giải mã media E2EEDownload and decrypt E2EE media |
Gửi ảnh E2EE mà không chặn event loopSend E2EE Images Without Blocking Event Loop
from pathlib import Path
import asyncio
image_bytes = await asyncio.to_thread(Path("photo.jpg").read_bytes)
result = await actions.send_e2ee_image(
chat_jid,
image_bytes,
mime_type="image/jpeg",
caption="Test image",
)
Quản lý và dọn trạng thái typingManage & Cleanup Typing State
await actions.send_e2ee_typing(chat_jid, True)
try:
response = await build_response(message)
await listener.send_e2ee_message(chat_jid, response)
finally:
await actions.send_e2ee_typing(chat_jid, False)
_send_e2ee.api là sender tương thích dành cho tích hợp blocking. Ứng dụng asyncio mới nên tái sử dụng listener và gọi các method async nêu trên._send_e2ee.api is a compatibility sender for blocking integrations. New asyncio applications should reuse the listener and call the async methods above.
API nghiệp vụ FacebookFacebook Feature APIs
Phần lớn module tính năng sử dụng hàm công khai tênMost feature modules expose a public function named func và phải được gọi bằngthat must be called with await. Các tên. The func_sync và aliasnames and func_async không còn được cung cấp.aliases are no longer available.
_search.func(dataFB, keywordSearch, *, client=None)Tìm người dùng, loại kết quả trùng theo ID và trả tối đa 5 kết quả. Từ khóa rỗng bị từ chối.Search for users, filter duplicates by ID and return a maximum of 5 results. Empty keywords are rejected.
_notification.func(dataFB, *, client=None)Trả về dict có key NotificationResults. Cần lấy danh sách từ key này trước khi thực hiện slice.Returns a dict with the NotificationResults key. Extract the list from this key before slicing.
_get_user_info.func(dataFB, userID, *, client=None)Lấy thông tin hồ sơ và trả về dict lỗi khi payload không chứa người dùng yêu cầu.Fetch profile info and return error dict when payload does not contain the requested user.
_changeBio.func(dataFB, newContents, uploadPost=False, *, client=None)Đổi bio và tùy chọn chia sẻ thay đổi.Updates the bio and optionally shares the change.
_createPost.func(dataFB, newContents, attachmentID=None, *, client=None)Đăng bài viết văn bản. Attachment hiện raiseCreates a text post. Attachments currently raise NotImplementedError để tránh báo thành công không chính xác.to prevent false success reports.
_professional.func(dataFB, statusBusiness=None, *, client=None)Bật hoặc tắt Professional mode; chấp nhận giá trị bool và chuỗi on/off đã chuẩn hóa.Enable or disable Professional mode; accepts bool values and normalized on/off strings.
_blocking.func(dataFB, idUser, choiceInteract, *, client=None)choiceInteract chỉ nhậnaccepts only block hoặcor unblock.
_registerOnProfile.func(dataFB, newName, newUsername, *, client=None)Tạo hồ sơ bổ sung; khả năng sử dụng phụ thuộc phạm vi triển khai của Facebook.Create additional profiles; availability depends on Facebook's rollout scope.
_marketplace.createItem(...)Kiểm tra tên, giá, ảnh, danh mục và tọa độ trước khi gửi request.Check name, price, image, category and coordinates before sending request.
_marketplace.getInformationProductItemMarketPlace(...)Lấy chi tiết item Marketplace theo product ID.Get Marketplace item details by product ID.
Quy trình sử dụng HTTP client dùng chungWorkflow Using Shared HTTP Client
import httpx
from fbchat_v2._features._facebook import (
_get_user_info,
_notification,
_search,
)
async with httpx.AsyncClient(timeout=30) as client:
search = await _search.func(data_fb, "m008v", client=client)
profile = await _get_user_info.func(
data_fb,
"100012345678",
client=client,
)
notifications = await _notification.func(data_fb, client=client)
latest = notifications.get("NotificationResults", [])[:5]
Facebook có thể trả HTTP 200 trong khi payload vẫn chứaFacebook may return HTTP 200 while the payload still contains error, errors hoặc đối tượng null. Luôn kiểm tra kiểu dữ liệu trả về vàor a null object. Always validate the returned data type and result.get("error"), không chỉ dựa vào HTTP status code.instead of relying only on the HTTP status code.
Quản trị thread và groupThread & Group Management
Các API thay đổi tên, emoji, biệt danh, quyền quản trị và đọc metadata hội thoại đều được triển khai dưới dạng coroutine.APIs for changing names, emojis, nicknames, administrative privileges, and reading conversation metadata are all implemented as coroutines.
| Module | Public API | Ghi chúNotes |
|---|---|---|
_all_thread_data | await func(dataFB, client=...) | Trả dict, không phải listReturns dict, not list |
_all_thread_data | await features(dataGet, threadID, commandUse) | Parse payload đã tải trong RAMParse payload loaded in RAM |
_changeNameThread | await func(dataFB, threadID, newNameThread, client=...) | Tên rỗng bị rejectEmpty name rejected |
_changeEmoji | await func(dataFB, threadID, newEmoji, client=...) | Emoji rỗng bị rejectEmpty emojis are rejected |
_changeNickname | await func(dataFB, threadID, idUser, NewNickname, client=...) | User phải thuộc threadUser must be in thread |
_addAdmin | await func(dataFB, threadID, idUser, statusChoice=True, client=...) | False là gỡ adminFalse is to remove admin |
from fbchat_v2._features._thread import (
_addAdmin,
_all_thread_data,
_changeEmoji,
_changeNameThread,
_changeNickname,
)
threads = await _all_thread_data.func(data_fb, client=client)
if threads.get("error") or not threads.get("dataGet"):
raise RuntimeError(f"Could not fetch thread: {threads}")
info = await _all_thread_data.features(
threads["dataGet"],
"thread-id",
"threadInfomation",
)
await _changeNameThread.func(data_fb, "thread-id", "New name", client=client)
await _changeEmoji.func(data_fb, "thread-id", "🔥", client=client)
await _changeNickname.func(
data_fb, "thread-id", "user-id", "Nickname", client=client
)
await _addAdmin.func(
data_fb, "thread-id", "user-id", statusChoice=True, client=client
)
Tên commandThe command name threadInfomation giữ nguyên cách viết legacy để bảo đảm tương thích. Call site phải sử dụng chính xác giá trị này; một giá trị khác sẽ trả về kết quả lỗi.retains its legacy spelling for compatibility. Call sites must use this exact value; any other value returns an error.
Bot mẫu src/main.pyExample Bot src/main.py
Bot mẫu minh họa vòng đời ứng dụng và không được thiết kế như một framework hoàn chỉnh cho môi trường production. Ví dụ thể hiện cách kết hợp storage, session, HTTP client, hàng đợi E2EE, bộ định tuyến lệnh và quy trình kết thúc ứng dụng.The example bot illustrates the application lifecycle and is not designed as a complete framework for production environments. The example shows how to combine storage, session, HTTP client, E2EE queue, command router and the application teardown process.
Cấu hình cục bộLocal configuration
{
"botName": "fbchat-v2 demo bot",
"prefix": "/",
"cookies": "c_user=...; xs=...; fr=...; datr=...;",
"admins": ["100012345678"],
"version": "0.0.1"
}
Kiểm tra prefix, danh sách quản trị viên và cookie cục bộ.Check prefix, admin list and local cookies.
Sử dụngUse FileSessionStorage vàand await dataGetHome().
Tạo một httpx.AsyncClient dùng chung cho các lệnh HTTP.Create a shared httpx.AsyncClient for HTTP commands.
Callback chuyển sự kiện vào hàng đợi theo cách an toàn giữa các luồng.Callback pushes events into the queue in a thread-safe manner.
Loại message ID trùng lặp, bỏ qua tin nhắn do bot gửi và kiểm tra prefix.Filter duplicate message IDs, ignore messages sent by bot and check prefix.
Khi có chatJid, bot gửi qua E2EE; khi chỉ có threadId, bot sử dụng luồng gửi thường.When chatJid is present, the bot sends over E2EE; when only threadId is present, it uses the regular sending flow.
Dừng bridge, chờ listener task hoàn tất và đóng HTTP client.Stop the bridge, wait for the listener task to complete, and close the HTTP client.
Lệnh có sẵnAvailable Commands
/pingĐo latency từ timestamp eventMeasure latency from event timestamp
/helpLiệt kê commandList commands
/idIn chatJid, threadID, senderJid và messageIDIn chatJid, threadID, senderJid and messageID
/echo <text>Reply lại nội dungReply to content
/search <query>Tìm user qua HTTP client dùng chungFind user via shared HTTP client
/unsendAdmin thu hồi tin E2EE gần nhất của botAdmin unsends bot's latest E2EE message
Thêm lệnh mớiAdding New Commands
# In __init__
self._handlers["hello"] = self._cmd_hello
async def _cmd_hello(self, message: dict, argument: str) -> None:
sender_id = str(message.get("userID") or "")
if self.admins and sender_id not in self.admins:
await self._reply(message, "You do not have permission to use this command.")
return
name = argument.strip() or "you"
await self._reply(message, f"Hello {name}!")
Không thực thi tác vụ CPU-bound hoặc I/O blocking trực tiếp trong handler. Hãy chuyển tác vụ blocking sangDo not run CPU-bound or blocking I/O work directly in a handler. Move blocking work to asyncio.to_thread(), process pool hoặc hàng đợi worker phù hợp., a process pool, or an appropriate worker queue.
Bảng tra API asyncAsync API Reference
Bảng dưới đây liệt kê các lời gọi công khai. Helper có hậu tố _blocking là ranh giới tương thích nội bộ và không phải API mặc định cho ứng dụng asyncio.The table below lists public calls. Helpers with the _blocking suffix are internal compatibility boundaries, not the default API for asyncio applications.
CoreCore
| API | ResultResult |
|---|---|
await dataGetHome(cookie=None, storage=None) | dataFB | None |
await post_async(request_kwargs, client=None) | HTTP response wrapper |
await get_async(request_kwargs, client=None) | HTTP response wrapper |
await loginFacebook(...).main() | Login result dict |
await GetToken2FA(key2Fa) | OTP string or lỗiOTP string or error |
Messaging
| Module | API chínhMain APIs |
|---|---|
_send | await api().send(...) |
_attachments | await func(filenames, dataFB, client=None, include_error=False) |
_reactions | await func(dataFB, typeAdded, messageID, emojiChoice, client=None) |
_unsend | await func(messageID, dataFB, client=None) |
_editMessage | await func(dataFB, messageID, newText) |
_message_requests | await func(dataFB, client=None) |
_changeTheme | await listThemes(), findTheme(), changeTheme(), func() |
_createNotes | await checkNote(), createNote(), deleteNote(), recreateNote(), func() |
_listening | connect_mqtt(), get_message(), disconnect() |
_listening_e2ee | connect_mqtt(), send_message(), send_e2ee_message() |
Reaction, chỉnh sửa và thu hồiReaction, Edit & Unsend
from fbchat_v2._messaging import _editMessage, _reactions, _unsend
edited = await _editMessage.func(
data_fb,
"mid.$message",
"New content",
)
reacted = await _reactions.func(
data_fb,
"ADD_REACTION",
"mid.$message",
"🔥",
client=client,
)
removed = await _unsend.func(
"mid.$message",
data_fb,
client=client,
)
Theme và Messenger NotesTheme & Messenger Notes
from fbchat_v2._messaging import _changeTheme, _createNotes
themes = await _changeTheme.listThemes(data_fb)
theme = await _changeTheme.findTheme(data_fb, "Love")
if theme.get("error"):
raise RuntimeError(theme)
changed = await _changeTheme.changeTheme(
data_fb,
threadID="thread-id",
themeName="Love",
)
current = await _createNotes.checkNote(data_fb)
created = await _createNotes.createNote(
data_fb,
text="Running async",
privacy="FRIENDS",
)
deleted = await _createNotes.deleteNote(data_fb, noteID="note-id")
Nâng cấp từ v2.1.4 đến v2.3.0Upgrading from v2.1.4 to v2.3.0
Tách quá trình nâng cấp thành hai chặng: v2.2 thay đổi mô hình API sang async-first, còn v2.3 siết hợp đồng bridge, vòng đời tài nguyên, xử lý lỗi và chuỗi cung ứng bản phát hành.Treat the upgrade as two stages: v2.2 moves the API to an async-first model, while v2.3 tightens the bridge contract, resource lifecycle, error handling, and release supply chain.
Chặng 1 · v2.1.4 → v2.2.0: chuyển đổi asyncStage 1 · v2.1.4 → v2.2.0: async migration
Đây là chặng có thay đổi không tương thích ngược. Cần cập nhật call site, vòng đời listener, transport HTTP và đường import khi chuyển sang package.This stage contains backward-incompatible changes. Update call sites, listener lifecycle, HTTP transport, and import paths when moving to the package.
data_fb = dataGetHome(cookie)data_fb = await dataGetHome(cookie)result = _search.func(data_fb, "Minh")result = await _search.func(data_fb, "Minh")result = sender.send(data_fb, "hi", uid)result = await sender.send(data_fb, "hi", uid)threading.Thread(target=listener.connect_mqtt)task = asyncio.create_task(listener.connect_mqtt())func_async(...)
func_sync(...)await func(...)requests.Session()httpx.AsyncClient()Checklist chuyển API v2.1 → v2.2v2.1 → v2.2 API Checklist
- Cố định phiên bản
fbchat-v2==2.2.0trong môi trường staging khi chuyển đổi call site.Pinfbchat-v2==2.2.0in staging while migrating call sites. - Cập nhật mọi lời gọi API mạng để sử dụng
await.Update all network API calls to useawait. - Đổi import
func_async/func_syncvềfunc.Change importfunc_async/func_synctofunc. - Chỉ gọiCall
asyncio.run(main())tại điểm vào CLI duy nhất.only at the single CLI entry point. - Tạo một
httpx.AsyncClienttheo lifecycle ứng dụng.Create ahttpx.AsyncClientbound to app lifecycle. - Chuyển listener nền từ thread target sang asyncio task được quản lý.Move background listener from thread target to managed asyncio task.
- Đối với E2EE, chờFor E2EE, wait for
require_e2ee=Truetrước khi gửi tin nhắn.before sending messages. - Bổ sung quy trình dọn tài nguyên trongAdd resource cleanup in a
finallyvà kiểm thử cancellation.block and test cancellation. - Kiểm tra dict kết quả thay vì giả định HTTP 200 luôn biểu thị thành công.Check result dict instead of assuming HTTP 200 means success.
- Chạy toàn bộ bộ kiểm thử và smoke test bằng cookie thử nghiệm riêng trước khi triển khai với tài khoản production.Run full test suite and smoke test with separate test cookies before deploying with production accounts.
Chặng 2 · v2.2.x → v2.3.0: siết vận hànhStage 2 · v2.2.x → v2.3.0: operational hardening
v2.3.0 không chủ động loại bỏ API Python công khai nào của dòng v2.2.x. Phần cần sửa nằm ở cách ứng dụng quản lý lỗi, client HTTP, listener E2EE và binary bridge; bỏ qua các ranh giới này thì code vẫn chạy được cho tới lúc production nổi drama.v2.3.0 does not intentionally remove any public Python API from the v2.2.x line. Applications must instead update how they manage errors, HTTP clients, E2EE listeners, and bridge binaries; ignoring those boundaries can produce failures that only surface in production.
Custom bridge phải cùng phiên bản 2.3.0 với package và hỗ trợ protocol 1. Binary cũ, sai capability hoặc không khớp checksum phải bị từ chối thay vì âm thầm hạ cấp sang transport thường.A custom bridge must match package version 2.3.0 and support protocol 1. Old binaries, missing capabilities, or checksum mismatches must be rejected instead of silently falling back to the regular transport.
- Cố định source hoặc package ở
2.3.0trong staging và xác minh version runtime trước khi rollout.Pin the source or package to2.3.0in staging and verify the runtime version before rollout. - Tiếp tục
awaitmọi API async; code đồng bộ chỉ dùng wrapper*_blockingtương ứng.Continue toawaitevery async API; synchronous code must use the matching*_blockingwrapper. - Với mutation archive, move-to-trash và unfriend, kiểm tra cả payload
{"error": 1, ...}; HTTP 200 không đồng nghĩa thao tác thành công.For archive, move-to-trash, and unfriend mutations, also check for a{"error": 1, ...}payload; HTTP 200 does not mean the operation succeeded. - Ứng dụng tự tạo
httpx.AsyncClientthì cũng phải đóng client trong lifecycle shutdown.An application that creates its ownhttpx.AsyncClientmust also close it during shutdown. - Listener E2EE là single-use: sau khi dừng, tạo instance mới để reconnect và chờ
require_e2ee=Truetrước khi gửi.An E2EE listener is single-use: after stopping it, create a new instance to reconnect and wait forrequire_e2ee=Truebefore sending. - Giữ
log_message_contentvàdebug_errorsởfalsetrừ khi đang debug trong môi trường cô lập không chứa dữ liệu thật.Keeplog_message_contentanddebug_errorsset tofalseunless debugging in an isolated environment without real data. - Windows ARM64 chưa có binary dựng sẵn; tự build bằng Go version trong
bridge-e2ee/go.modvà xác minh SHA-256 trước khi chạy.Windows ARM64 has no prebuilt binary; build it with the Go version frombridge-e2ee/go.modand verify its SHA-256 before use.
Hai cờ log mới của v2.3 mặc định tắt. Không bật chúng trong production chỉ để đổi lấy một log dài hơn nhưng chứa luôn nội dung tin nhắn và traceback.The two v2.3 logging flags are disabled by default. Do not enable them in production merely to get longer logs that also expose message content and tracebacks.
{
"log_message_content": false,
"debug_errors": false
}Lỗi thường gặp và hướng xử lýCommon Errors & Solutions
Xác định chính xác triệu chứng và kiểm tra tại đúng ranh giới phát sinh lỗi. Cơ chế thử lại phải có giới hạn để tránh gây checkpoint hoặc hạn chế tài khoản.Accurately identify symptoms and check at the exact boundary where the error occurs. The retry mechanism must have limits to avoid triggering a checkpoint or account restriction.
RuntimeWarning: coroutine was never awaited
Hàm async đã được gọi như hàm đồng bộ hoặc được truyền trực tiếp làm target của thread.Async function called as a synchronous function or passed directly as a thread target.
# Wrong
listener.connect_mqtt()
# Right
task = asyncio.create_task(listener.connect_mqtt())'coroutine' object has no attribute 'get'
Kết quả coroutine chưa được await.Coroutine result not awaited.
# Wrong
info = bridge.call("connect")
user = info.get("user")
# Right
info = await bridge.call("connect")
user = info.get("user", {})dataGetHome() trảreturns None
- Kiểm tra cookie có
c_user,xs,fr,datr.Check if cookie hasc_user,xs,fr,datr. - Kiểm tra checkpoint và thời hạn phiên.Check for checkpoints and session expiration.
- Kiểm tra proxy và kết nối mạng tới Facebook.Check proxy and network connection to Facebook.
- Xác minh Facebook còn trả các token cần thiết trong HTML trang chủ.Verify Facebook still returns required tokens in homepage HTML.
Chỉ ghi tên field còn thiếu và không ghi giá trị cookie.Only log missing field names and do not log cookie values.
Upload trảUpload returns metadata: {"0": null} hoặcor 1357054
Máy chủ không trả metadata của tệp đính kèm. BậtThe server did not return attachment metadata. Enable include_error=True, sau đó kiểm tra MIME, tệp, phiên đăng nhập và endpoint. Không sử dụng, then check the MIME type, file, login session, and endpoint. Do not use uploadID thay cho attachment ID.as an attachment ID.
Listener E2EE không nhận lệnhE2EE listener does not receive commands
- Đăng ký callback trước khi khởi động listener.Register the callback before starting the listener.
- ChờWait for
wait_until_connected(..., require_e2ee=True)hoàn tất.to complete. - Xử lý cả hai loại sự kiện
e2eeMessagevàmessage.Handle bothe2eeMessageandmessageevents. - Chuyển callback về event loop bằng
call_soon_threadsafe.Send the callback back to the event loop withcall_soon_threadsafe. - Kiểm tra ID của bot, prefix và cơ chế loại bỏ sự kiện trùng lặp.Check the bot ID, prefix, and duplicate-event filter.
- Kiểm tra listener task có phát sinh exception hay không.Check whether the listener task raised an exception.
Bridge binary không tìm thấy hoặc sai kiến trúcBridge binary not found or wrong architecture
Xác nhận tệp phát hành phù hợp với hệ điều hành và kiến trúc, có quyền thực thi trên Unix và sử dụng đường dẫn tuyệt đối hợp lệ. Nếu tự biên dịch, cần khởi tạo bridge-e2ee/meta trước. Khi binary_path= trỏ tới tệp không tồn tại, wrapper sẽ phát sinh lỗi và không tải bản thay thế.Verify that the release binary matches the OS and architecture, is executable on Unix, and is referenced by a valid absolute path. If compiling manually, initialize bridge-e2ee/meta first. When binary_path= points to a missing file, the wrapper raises an error and does not download a replacement.
Bridge liên tục khởi động lạiBridge continuously restarts
Kiểm tra stderr của bridge, trạng thái cookie, sự đồng bộ phiên bản giữa binary và mã nguồn, cùng quyền ghi tại đường dẫn lưu state. Không khởi động thêm listener để che lấp lỗi vì mỗi listener sẽ tạo thêm một tiến trình.Check bridge's stderr, cookie status, version synchronization between binary and source code, and write permission at the state storage path. Do not start extra listeners to cover up errors because each listener will spawn an additional process.
Get Notifications: slice(None, 2, None)
API notification trả về dict. Cần lấy danh sách từ NotificationResults trước khi thực hiện slice:The notification API returns a dict. Extract the list from NotificationResults before slicing:
result = await _notification.func(data_fb)
items = result.get("NotificationResults", [])
print(items[:2])All Thread Data: 0 hoặc lỗi indexor an index error
Không truy cập response như một list. Hãy kiểm traDo not access the response as a list. Check result.get("error"), dataGet và đối tượng batchand the batch object o0. Facebook có thể trả về đối tượng lỗi thay vì danh sách hội thoại.. Facebook may return an error object instead of a thread list.
Đăng nhập bằng thông tin xác thực trả mãLogging in with credentials returns code -1
Nguyên nhân thường là transport hoặc schema không khớp, hoặc phản hồi không có mã lỗi chuẩn. Hãy đọc mô tả đã được lọc, kiểm tra biến môi trường override nếu có và ưu tiên khởi tạo bằng cookie phiên. Chỉ kết luận mật khẩu không hợp lệ khi phản hồi cung cấp đầy đủ bằng chứng.Usually caused by transport/schema mismatch, or response lacks standard error code. Read the filtered description, check override env vars if any, and prioritize session cookie initialization. Only conclude invalid password when the response provides sufficient evidence.
Bảo mật và giới hạnSecurity & Limits
Thư viện thao tác trên tài khoản Facebook thật thông qua các endpoint nội bộ. Quyền của bot gần tương đương quyền của phiên cookie, do đó phạm vi ảnh hưởng khi thông tin phiên bị lộ là đáng kể.The library operates on a real Facebook account through internal endpoints. The bot's privileges are almost equivalent to the cookie session's privileges, therefore the impact is significant if session information is leaked.
Quản lý thông tin bí mậtManage Secrets
Lưu cookie trong secret manager hoặc tệp cục bộ bị Git bỏ qua. Làm mới phiên ngay khi có dấu hiệu bị lộ.Store cookies in a secret manager or local files ignored by Git. Refresh the session immediately upon signs of exposure.
Đặc quyền tối thiểuLeast Privilege
Sử dụng tài khoản riêng cho tự động hóa, giới hạn lệnh quản trị và áp dụng danh sách cho phép đối với người gửi hoặc hội thoại.Use a separate account for automation, limit administrative commands, and apply a whitelist for senders or conversations.
Nhật ký an toànSafe Logging
Chỉ ghi loại sự kiện, message ID rút gọn và lớp lỗi. Không ghi nội dung request, cookie, token, mật khẩu hoặc TOTP.Log only the event type, truncated message ID, and error class. Do not log request contents, cookies, tokens, passwords, or TOTP.
Xác minh TLSTLS Verification
Luôn giữ xác minh chứng chỉ. Không sử dụng verify=False hoặc CERT_NONE để xử lý lỗi proxy.Keep certificate verification enabled. Do not use verify=False or CERT_NONE to work around proxy errors.
Kiểm soát tần suấtRate Limiting
Sử dụng timeout, hàng đợi có giới hạn, backoff hữu hạn và cơ chế idempotency tại tầng ứng dụng.Use timeouts, bounded queues, finite backoff, and application-level idempotency.
Độ tin cậy của phụ thuộcDependency Reliability
Cố định phiên bản, kiểm tra tệp phát hành và cập nhật đồng bộ bridge với Python wrapper.Pin versions, verify release files, and keep the bridge synchronized with the Python wrapper.
Không ghi dataFB vào nhật kýDo Not Log dataFB
def safe_session_status(data_fb: dict) -> dict:
required = (
"FacebookID",
"fb_dtsg",
"jazoest",
"sessionID",
"clientRevision",
"cookieFacebook",
)
return {
"facebook_id_present": bool(data_fb.get("FacebookID")),
"missing_fields": [key for key in required if not data_fb.get(key)],
}
fbchat-v2 không phải SDK chính thức của Meta. Endpoint, HTML, token, subcode và chính sách có thể thay đổi bất kỳ lúc nào. Việc sử dụng có thể dẫn tới checkpoint hoặc hạn chế tài khoản. Người triển khai chịu trách nhiệm bảo vệ tài khoản và đánh giá rủi ro sử dụng.fbchat-v2 is not an official Meta SDK. Endpoints, HTML, tokens, subcodes, and policies can change at any time. Usage may lead to checkpoints or account restrictions. Implementers are responsible for securing accounts and assessing usage risks.
Kiểm thử và phát hànhTesting & Release
Workflow release hiện tự build năm binary bridge, smoke test JSON-RPC, tạo SHA256SUMS, attest provenance và tải asset đã xác minh lên GitHub Release. Wheel và sdist được giữ dưới dạng workflow artifact để tải thủ công; CI không tự publish PyPI.The release workflow now builds five bridge binaries, smoke-tests JSON-RPC, creates SHA256SUMS, attests provenance, and uploads verified assets to the GitHub Release. The wheel and sdist are retained as workflow artifacts for manual download; CI does not automatically publish to PyPI.
python -m compileall -q src tests scripts
ruff check src tests scripts
black --check src tests scripts
mypy
python -m pytest tests/ -v --tb=short
git diff --checkcd bridge-e2ee
go test ./...
go vet ./...
go build ./...python scripts/release_artifacts.py validate \
--release-tag v2.3.0
python scripts/release_artifacts.py verify \
--release-tag v2.3.0 \
--binaries-dir all-binaries \
--dist-dir dist \
--manifest all-binaries/SHA256SUMS
python scripts/verify_distribution.py \
dist/fbchat_v2-2.3.0-py3-none-any.whlDanh sách kiểm tra phát hànhRelease Checklist
- Phiên bản trong package, tag, changelog và ghi chú phát hành phải khớp nhau.Versions in package, tag, changelog, and release notes must match.
- Bộ kiểm thử chạy trên Python 3.10, 3.11, 3.12, 3.13 và 3.14 đúng với ma trận CI của v2.3.The test suite runs on Python 3.10, 3.11, 3.12, 3.13, and 3.14, matching the v2.3 CI matrix.
- Workflow tạo đúng năm bridge binary cho Windows amd64, Linux và macOS amd64/arm64; Windows ARM64 phải tự build.The workflow produces exactly five bridge binaries for Windows amd64 plus Linux and macOS amd64/arm64; Windows ARM64 must be built manually.
- Đối chiếu
SHA256SUMS, checksum nhúng trong package và provenance trước khi dùng artifact.VerifySHA256SUMS, checksums embedded in the package, and provenance before using an artifact. - Không đưa thông tin bí mật, cấu hình cục bộ, cơ sở dữ liệu, báo cáo nội bộ hoặc binary ngoài phạm vi vào staging area.Do not include secrets, local configs, databases, internal reports, or out-of-scope binaries in the staging area.
- Kiểm tra UTF-8 không hợp lệ, U+FFFD, NUL và dấu hiệu mojibake thực tế trước khi commit.Check for invalid UTF-8, U+FFFD, NUL, and actual mojibake signs before committing.
- Commit dùng conventional format có scope, ví dụ
docs(website): document v2.3 operations.Commit using conventional format with scope, e.g.docs(website): document v2.3 operations.
Cộng đồng & Ghi công Community & Credits
Quy tắc Ứng xử Code of Conduct
Vui lòng tuân thủ quy tắc ứng xử của cộng đồng khi tham gia đóng góp. Please follow the community code of conduct when participating.
Chúng tôi cam kết tạo ra một môi trường thân thiện, an toàn và hòa nhập cho tất cả mọi người. Mọi hành vi quấy rối hoặc không tôn trọng sẽ không được dung thứ. Mọi thành viên đóng góp đều phải sử dụng ngôn từ lịch sự và tôn trọng. We are committed to providing a friendly, safe and welcoming environment for all. Harassment or disrespect of any kind will not be tolerated. All contributors must use polite and respectful language.