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 Nov 2024, Facebook enables end-to-end encryption by default on Messenger. The library has supported Secret Conversation decryption since 24/03/2026 via the _listening_e2ee module (Go bridge on Signal Protocol). Both 1-on-1 and group E2EE messages can be read and sent.
Vì sao chọn fbchat-v2Why choose fbchat-v2
Why fbchat-v2
Không như Graph API chỉ hoạt động với Fanpage,Unlike Graph API which only works with Fanpages, fbchat-v2 tích hợp sâu qua tài khoản cá nhân.integrates deeply through personal accounts.
Unlike the Graph API limited to fanpages, fbchat-v2 integrates deeply via user accounts.
Kiến trúc ba tầngThree-Layer Architecture
Three-Layer Architecture
Mã nguồn được phân chia thành 3 lớp rõ ràng để dễ bảo trì.The source code is divided into 3 clear layers for easy maintenance.
The project 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 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 theo go.mod | Chỉ khi tự build bridgeOnly when building bridge manually | Không cần nếu dùng binary release phù hợpNot needed if using suitable binary release |
| Git | Bản hiện hànhCurrent version | Clone source / submodule | Bridge phụ thuộcDependent bridgebridge-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.2.0 import OK")
asyncio.run(main())
Mô hình thực thi bất đồng bộAsynchronous Execution Model
API v2.2.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.API v2.2.0 is designed with an async-first approach. When a coroutine is called without await, the corresponding operation has not been executed, and the returned value is just 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 raise a 'was never awaited' warning or '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 bot or async frameworks already have an event loop. Inside the framework's coroutine, call 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 cookie 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 homepage, extracts session tokens, and returns a shared data structure for the remaining 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 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 trong , 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.Do not store actual cookies in , source code, screenshots, or CI logs. Local storage files only separate secrets from source code and do not automatically encrypt contents.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 and do not automatically encrypt contents.
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 resources. This method is suitable for 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át .Do not use client after exiting .Không sử dụng client sau khi thoátDo not use client after exiting
async with. - 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
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 sending to users, groups, multiple recipients, replying to messages, and attachments. Each call creates a separate form so they can be executed concurrently.SendAPI().send()
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 or list IDID or list of IDs | List chỉ hợp lệ khi typeChat="user"List is only valid 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 accompany 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 returns 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ỉ trong metadata hợp lệ mới được chuyển sang sender; không thể thay thế giá trị này.Uploading files and sending messages are independent steps. Only a valid in metadata is passed to the sender; cannot replace this value.Tải tệp và gửi tin nhắn là hai bước độc lập. ChỉDownloading files and sending messages are two independent steps. Only attachmentID trong metadata hợp lệ mới được chuyển sang sender;in valid metadata will be passed to sender; uploadID không thể thay thế giá trị này.cannot replace this value.
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 executes asynchronously.
Kết quả hợp lệ phải có attachmentID và typeAttachment.Valid results must have attachmentID and typeAttachment.
Truyền đúng loại tệp và ID đính kèm sang .Pass the correct file type and attachment ID to .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 indicates the server did not return attachment metadata. Please check login session, MIME, file, and endpoint. Enable include_error=True to receive an error summary and 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) | Consume event từ queueConsume event from queue | Trả khi timeoutReturns on timeoutTrảReturns None khi timeout |
disconnect() | Dừng clientStop client | Gọi trongCall insidefinally |
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õi để phát hiện consumer xử lý chậm. Không nên thăm dò trong ứng dụng mới vì đây chỉ là ảnh chụp trạng thái cuối cùng.Bounded queue that drops the oldest events when full. Monitor to detect slow consumers. Do not poll in new applications as this is only a snapshot of the final state.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 slow-processing consumers. You should 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 apps because this is only a snapshot of the final 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. You can use pre-built files from GitHub Release 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 suitable files from GitHub Releases, put them in build/ directory or declare 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
- truyền vào constructor.pass to constructor.
binary_path= - Biến môi trường
FBCHAT_E2EE_BIN.Environment variable FBCHAT_E2EE_BIN. - Đường dẫn mặc định trong
build/.Default path in build/. - 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 the device state in RAM. When saving state using device_path, this file must be protected like credentials because it represents the 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ối .Register callbacks before starting, run listener as a task, wait for handshake completion in worker thread, handle events via queue, and stop bridge in block.Đă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 callback before starting, run listener as a task, wait for handshake completion in worker thread, process events via queue and stop bridge in block finally.
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's polling loop. Do not call coroutines directly in this callback. Push the event back to the event loop using 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 compatible sender designed for blocking integration. New asyncio applications should reuse the listener and call the async methods mentioned above.
API nghiệp vụ FacebookFacebook Business API
Phần lớn module tính năng sử dụng hàm công khai tên và phải được gọi bằng . Các tên và alias không còn được cung cấp.Most feature modules use public functions named and must be called with . The names and aliases are no longer provided.Phần lớn module tính năng sử dụng hàm công khai tênMost feature modules use a public function named func và phải được gọi bằngand must be called with await. Các tên. The names func_sync và aliasand aliases func_async không còn được cung cấp.are no longer provided.
_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 key NotificationResults. You need to 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.Bio changed and sharing options changed.
_createPost.func(dataFB, newContents, attachmentID=None, *, client=None)Đăng bài viết văn bản. Attachment hiện raise để tránh báo thành công không chính xác.Post text status. Attachments currently raise to prevent incorrect success reporting.Đăng bài viết văn bản. Attachment hiện raisePost a text post. Attachment currently raises NotImplementedError để tránh báo thành công không chính xác.to avoid inaccurate success reporting.
_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)chỉ nhận hoặc .only accepts or .choiceInteract chỉ nhậnonly accepts 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ứa , hoặc đối tượng null. Luôn kiểm tra kiểu dữ liệu trả về và , không chỉ dựa vào HTTP status code.Facebook may return HTTP 200 while payload contains , , or null objects. Always check the return data type and , not just the HTTP status code.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 check the returned data type and result.get("error"), không chỉ dựa vào HTTP status code., not just relying 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 command 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.The command name retains its legacy spelling to ensure compatibility. The call site must use this exact value; any other value will return an error.Tên commandCommand 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.keeps legacy casing to ensure compatibility. Call sites must use exactly this value; any other value will return 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ụng và .Use and .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 via E2EE; when only threadId is present, it uses the standard 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 sang , process pool hoặc hàng đợi worker phù hợp.Do not execute CPU-bound or I/O blocking tasks directly in the handler. Move blocking tasks to , process pool, or appropriate worker queues.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 execute CPU-bound or I/O blocking tasks directly in the handler. Move blocking tasks to asyncio.to_thread(), process pool hoặc hàng đợi worker phù hợp., process pool or a suitable 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 and are 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")
Chuyển từ v2.1.4 sang v2.2.0Migrating from v2.1.4 to v2.2.0
Đây là bản phát hành có thay đổi không tương thích ngược. Quá trình nâng cấp cần cập nhật call site, vòng đời listener, transport HTTP và đường import khi chuyển sang gói PyPI.This is a backward-incompatible release. The upgrade process requires updating call sites, listener lifecycle, HTTP transport, and import paths when switching to the PyPI 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()Danh sách kiểm tra nâng cấpMigration Checklist
- Nâng cấp package và cố định phiên bản
fbchat-v2==trong môi trường staging.Upgrade package and pin versionfbchat-v2==in staging environment. - 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ọi tại điểm vào CLI duy nhất.Only call at the single CLI entry point.Chỉ gọiOnly call
asyncio.run(main())tại điểm vào CLI duy nhất.at a 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ờ trước khi gửi tin nhắn.For E2EE, wait for before sending messages.Đố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 trong và kiểm thử cancellation.Add resource cleanup in and test cancellation.Bổ sung quy trình dọn tài nguyên trongAdd a resource cleanup process in
finallyvà kiểm thử cancellation.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.
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ật , sau đó kiểm tra MIME, tệp, phiên đăng nhập và endpoint. Không sử dụng thay cho attachment ID.Server does not return attachment metadata. Enable , then check MIME, file, login session, and endpoint. Do not use instead of attachment ID.Máy chủ không trả metadata của tệp đính kèm. BậtServer 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 MIME, file, login session and endpoint. Do not use uploadID thay cho 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 callback before starting listener.
- Chờ hoàn tất.Wait for to complete.ChờWait
wait_until_connected(..., require_e2ee=True)hoàn tất.to complete. - Xử lý cả hai loại sự kiện
e2eeMessagevàmessage.Handle bothe2eeMessageandmessage. - Chuyển callback về event loop bằng
call_soon_threadsafe.Switch callback to event loop using call_soon_threadsafe. - Kiểm tra ID của bot, prefix và cơ chế loại bỏ sự kiện trùng lặp.Check bot ID, prefix and duplicate event filtering mechanism.
- Kiểm tra listener task có phát sinh exception hay không.Check if 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 the release file matches the OS and architecture, is executable on Unix, and uses 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 load a fallback.
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:Notification API returns a dict. You must extract the list from NotificationResults before performing slice:
result = await _notification.func(data_fb)
items = result.get("NotificationResults", [])
print(items[:2])All Thread Data: 0 hoặc lỗi indexor index error
Không truy cập response như một list. Hãy kiểm tra , và đối tượng batch . Facebook có thể trả về đối tượng lỗi thay vì danh sách hội thoại.Do not access response as a list. Check , , and the batch object . Facebook may return an error object instead of a thread list.Không truy cập response như một list. Hãy kiểm traDo not access response as a list. Check result.get("error"), dataGet và đối tượng batchand 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.Always keep certificate verification. Do not use verify=False or CERT_NONE to handle 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
Thực hiện đầy đủ các bước kiểm tra Python, bridge Go, encoding và diff trước khi push. Tệp phát hành E2EE phải được biên dịch và tải lên riêng; GitHub chỉ tự động tạo gói mã nguồn ZIP và TAR.Perform full Python, Go bridge, encoding, and diff checks before pushing. E2EE release files must be compiled and uploaded separately; GitHub only automatically creates ZIP and TAR source packages.
pytest tests/ -v --tb=short
python -m compileall -q src tests
ruff check src tests
ruff format --check src tests
git diff --checkcd bridge-e2ee
go test ./...
go vet ./...
go build ./...# Example upload asset after workflow builds
gh release upload v2.2.0 \
fbchat-bridge-e2ee-windows-amd64.exe \
fbchat-bridge-e2ee-linux-amd64 \
fbchat-bridge-e2ee-linux-arm64 \
fbchat-bridge-e2ee-darwin-amd64 \
fbchat-bridge-e2ee-darwin-arm64 \
--clobber# Example upload asset after workflow builds
gh release upload v2.2.0 \
fbchat-bridge-e2ee-windows-amd64.exe \
fbchat-bridge-e2ee-linux-amd64 \
fbchat-bridge-e2ee-linux-arm64 \
fbchat-bridge-e2ee-darwin-amd64 \
fbchat-bridge-e2ee-darwin-arm64 \
--clobberDanh 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ử được chạy trên Python 3.10, 3.11, 3.12 hoặc ma trận phiên bản mà dự án công bố hỗ trợ.The test suite runs on Python 3.10, 3.11, 3.12, or the version matrix officially supported by the project.
- Bridge được biên dịch đầy đủ cho Windows, Linux và macOS với kiến trúc amd64 hoặc arm64 tương ứng.Bridge is fully compiled for Windows, Linux, and macOS with respective amd64 or arm64 architectures.
- 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): expand async v2.2 documentation.Commit using conventional format with scope, e.g.docs(website): expand async v2.2 documentation.
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.