### Setup gomobile for Mobile Development Source: https://github.com/ltaoo/wx_channels_download/blob/main/pkg/gopeed/README_zh-TW.md Installs gomobile and initializes it for mobile development. Required for building Android and iOS versions. ```bash go install golang.org/x/mobile/cmd/gomobile@latest go get -u golang.org/x/mobile/bind gomobile init ``` -------------------------------- ### Setup gomobile for Mobile Builds Source: https://github.com/ltaoo/wx_channels_download/blob/main/pkg/gopeed/README.md Installs the gomobile tool and initializes it, which is necessary for building Gopeed for mobile platforms. Ensure cgo environment is prepared. ```bash go install golang.org/x/mobile/cmd/gomobile@latest go get golang.org/x/mobile/bind gomobile init ``` -------------------------------- ### Install Gopeed CLI Source: https://github.com/ltaoo/wx_channels_download/blob/main/pkg/gopeed/README_zh-TW.md Use 'go install' to install the Gopeed CLI tool. Ensure you have Go installed and configured. ```bash go install github.com/GopeedLab/gopeed/cmd/gopeed@latest ``` -------------------------------- ### Run Project Locally Source: https://github.com/ltaoo/wx_channels_download/blob/main/README.md To run the project locally, start a terminal as an administrator and execute the go run command. This is for development purposes. ```bash go run main.go ``` -------------------------------- ### Build Gopeed for Windows Desktop Source: https://github.com/ltaoo/wx_channels_download/blob/main/pkg/gopeed/README.md Builds the Gopeed shared library for Windows and then builds the Flutter Windows application. Requires cgo environment setup. ```bash go build -tags nosqlite -ldflags="-w -s" -buildmode=c-shared -o ui/flutter/windows/libgopeed.dll github.com/GopeedLab/gopeed/bind/desktop cd ui/flutter flutter build windows ``` -------------------------------- ### Build Gopeed for Web Source: https://github.com/ltaoo/wx_channels_download/blob/main/pkg/gopeed/README.md Builds the Flutter web application, copies the output to the web command directory, and then builds the Go web server. Requires cgo environment setup. ```bash cd ui/flutter flutter build web cd ../.. rm -rf cmd/web/dist cp -r ui/flutter/build/web cmd/web/dist go build -tags nosqlite,web -ldflags="-s -w" -o bin/ github.com/GopeedLab/gopeed/cmd/web ``` -------------------------------- ### Build Gopeed for iOS Source: https://github.com/ltaoo/wx_channels_download/blob/main/pkg/gopeed/README.md Binds the mobile package for iOS and then builds the Flutter iOS application. Requires gomobile and cgo setup. ```bash gomobile bind -tags nosqlite -ldflags="-w -s" -o ui/flutter/ios/Frameworks/Libgopeed.xcframework -target=ios github.com/GopeedLab/gopeed/bind/mobile cd ui/flutter flutter build ios --no-codesign ``` -------------------------------- ### Build Gopeed for Linux Desktop Source: https://github.com/ltaoo/wx_channels_download/blob/main/pkg/gopeed/README.md Builds the Gopeed shared library for Linux and then builds the Flutter Linux application. Requires cgo environment setup. ```bash go build -tags nosqlite -ldflags="-w -s" -buildmode=c-shared -o ui/flutter/linux/bundle/lib/libgopeed.so github.com/GopeedLab/gopeed/bind/desktop cd ui/flutter flutter build linux ``` -------------------------------- ### Build Gopeed for macOS Desktop Source: https://github.com/ltaoo/wx_channels_download/blob/main/pkg/gopeed/README.md Builds the Gopeed shared library for macOS and then builds the Flutter macOS application. Requires cgo environment setup. ```bash go build -tags nosqlite -ldflags="-w -s" -buildmode=c-shared -o ui/flutter/macos/Frameworks/libgopeed.dylib github.com/GopeedLab/gopeed/bind/desktop cd ui/flutter flutter build macos ``` -------------------------------- ### Install Root Certificate on Debian/Ubuntu Source: https://github.com/ltaoo/wx_channels_download/blob/main/docs/guide/certificate.md Installs the SunnyRoot.cer certificate into the system's trusted certificate store on Debian-based systems. Ensure the certificate file is downloaded before running these commands. ```bash sudo mkdir -p /usr/local/share/ca-certificates sudo install -m 0644 ~/Downloads/SunnyRoot.cer /usr/local/share/ca-certificates/WeChatAppEx_CA.crt sudo update-ca-certificates --fresh ``` -------------------------------- ### Install Root Certificate on Fedora/RHEL/CentOS Source: https://github.com/ltaoo/wx_channels_download/blob/main/docs/guide/certificate.md Installs the SunnyRoot.cer certificate into the system's trusted certificate store on Fedora, RHEL, or CentOS. The certificate file should be downloaded prior to execution. ```bash sudo mkdir -p /etc/pki/ca-trust/source/anchors sudo install -m 0644 ~/Downloads/SunnyRoot.cer /etc/pki/ca-trust/source/anchors/WeChatAppEx_CA.crt sudo update-ca-trust extract ``` -------------------------------- ### Install Root Certificate on Arch/Manjaro Source: https://github.com/ltaoo/wx_channels_download/blob/main/docs/guide/certificate.md Installs the SunnyRoot.cer certificate into the system's trusted certificate store on Arch Linux or Manjaro. Download the certificate file before running these commands. ```bash sudo mkdir -p /etc/ca-certificates/trust-source/anchors sudo install -m 0644 ~/Downloads/SunnyRoot.cer /etc/ca-certificates/trust-source/anchors/WeChatAppEx_CA.crt sudo trust extract-compat ``` -------------------------------- ### Build Gopeed for Android Source: https://github.com/ltaoo/wx_channels_download/blob/main/pkg/gopeed/README.md Binds the mobile package for Android and then builds the Flutter Android application. Requires gomobile and cgo setup, and targets Android API 21. ```bash gomobile bind -tags nosqlite -ldflags="-w -s -checklinkname=0" -o ui/flutter/android/app/libs/libgopeed.aar -target=android -androidapi 21 -javapkg="com.gopeed" github.com/GopeedLab/gopeed/bind/mobile cd ui/flutter flutter build apk ``` -------------------------------- ### Build Script Reference Source: https://github.com/ltaoo/wx_channels_download/blob/main/README.md Refer to the build script located in the build/ directory for instructions on how to package the application. ```bash build/build.sh ``` -------------------------------- ### Add 'Download Video and Cover' Menu Item Source: https://github.com/ltaoo/wx_channels_download/blob/main/docs/feature/custom-menu.md Add this JavaScript code to a `global.js` file in the same directory as `wx_video_download.exe` to include a new option in the floating menu that downloads both the video and its cover image. This snippet handles video decryption and saving both assets as a zip file. ```javascript // global.js WXU.unshiftMenuItems([ { label: "下载视频和封面", async onClick() { var [err, feed] = WXU.check_feed_existing({ silence: true, }); if (err) return; var filename = WXU.build_filename( feed, feed.spec[0], WXU.config.downloadFilenameTemplate ); if (WXU.config.downloadPauseWhenDownload) { WXU.pause_cur_video(); } var ins = WXU.loading(); var [err, response] = await WXU.fetch(feed.url); if (err) { WXU.error({ msg: err.message, }); return; } var media_blob = await WXU.download_with_progress(response, { onStart({ total_size }) { WXU.log({ msg: `总大小 ${WXU.bytes_to_size(total_size)}`, }); }, onProgress({ loaded_size, progress }) { WXU.log({ replace: 1, msg: progress === null ? `${WXU.bytes_to_size(loaded_size)}` : `${progress}%`, }); }, }); var media_buf = new Uint8Array(await media_blob.arrayBuffer()); if (feed.key) { WXU.log({ msg: "下载完成,开始解密", }); var [err, data] = await WXU.decrypt_video(media_buf, feed.key); if (err) { WXU.error({ msg: "解密失败," + err.message, alert: 0 }); WXU.error({ msg: "尝试使用 decrypt 命令解密", alert: 0 }); } else { WXU.log({ msg: "解密成功" }); media_buf = data; } } var decrypted_media_blob = new Blob([media_buf], { type: "video/mp4" }); var zip = await WXU.Zip(); zip.file(filename + ".mp4", decrypted_media_blob); var cover_url = feed.cover_url.replace(/^http/, "https"); var [err, cover_response] = await WXU.fetch(cover_url); if (err) { WXU.error({ msg: err.message, }); return; } var cover_blob = await cover_response.blob(); zip.file(filename + ".jpg", cover_blob); var zip_blob = await zip.generateAsync({ type: "blob" }); WXU.save(zip_blob, filename + ".zip"); ins.hide(); if (WXU.config.downloadPauseWhenDownload) { WXU.play_cur_video(); } }, }, ]); ``` -------------------------------- ### Initialize Management WebSocket Source: https://github.com/ltaoo/wx_channels_download/blob/main/internal/officialaccount/ui/manager.html Initializes a WebSocket connection for management tasks. Includes automatic reconnection and a ping interval to maintain the connection. ```javascript let manageWs = null; function initManageWebSocket() { if (manageWs) return; const proto = window.location.protocol === "https:" ? "wss:" : "ws:"; const url = proto + "//" + window.location.host + "/ws/manage"; manageWs = new WebSocket(url); manageWs.onopen = () => { console.log("Manage WS connected"); // Send a ping every 30s to keep alive setInterval(() => { if (manageWs.readyState === WebSocket.OPEN) { manageWs.send(JSON.stringify({ type: "ping", data: "manager" })); } }, 30000); }; manageWs.onmessage = (event) => { try { const msg = JSON.parse(event.data); if (msg.type === "refresh_progress") { updateProgressModal(msg.data); } } catch (e) { console.error("WS message error", e); } }; manageWs.onclose = () => { console.log("Manage WS closed, retrying in 5s..."); manageWs = null; setTimeout(initManageWebSocket, 5000); }; } ``` -------------------------------- ### Handle File Selection Source: https://github.com/ltaoo/wx_channels_download/blob/main/internal/api/ui/filehelper.html Placeholder function for handling file selection. Currently displays a 'feature under development' toast and resets the input. ```javascript function handleFileSelect(event) { const file = event.target.files[0]; if (!file) return; showToast('文件发送功能开发中...'); event.target.value = ''; } ``` -------------------------------- ### Load Accounts with Pagination Source: https://github.com/ltaoo/wx_channels_download/blob/main/internal/officialaccount/ui/manager.html Loads accounts with specified page and page size. Resets to page 1 if page size changes. ```javascript loadAccounts({ page: 1, keyword: "" }); } function changePageSize(v) { const next = parseInt(String(v || "10"), 10); loadAccounts({ page: 1, pageSize: Number.isFinite(next) && next > 0 ? next : 10 }); } ``` -------------------------------- ### Navigate Pagination Source: https://github.com/ltaoo/wx_channels_download/blob/main/internal/officialaccount/ui/manager.html Handles previous and next page navigation for account lists. ```javascript function prevPage() { loadAccounts({ page: Math.max(1, (LIST_STATE.page || 1) - 1) }); } function nextPage() { loadAccounts({ page: (LIST_STATE.page || 1) + 1 }); } ``` -------------------------------- ### 监听 feed 事件并发送到服务器 Source: https://github.com/ltaoo/wx_channels_download/blob/main/docs/feature/event.md 当加载 feed 时,将视频信息发送到指定的 API 端点。需要 WXU 库支持。错误会通过 WXU.error 记录,成功响应通过 WXU.log 显示。 ```javascript // global.js WXU.onFeed(async (feed) => { const [err, res] = await WXU.request({ method: "POST", url: "http://127.0.0.1:1234/api/feed", body: feed, }); if (err) { WXU.error({ msg: err.message }); return; } WXU.log({ msg: JSON.stringify(res) }); }); ``` -------------------------------- ### Clone Gopeed Repository Source: https://github.com/ltaoo/wx_channels_download/blob/main/pkg/gopeed/README.md Use this command to clone the Gopeed project repository from GitHub. ```bash git clone git@github.com:GopeedLab/gopeed.git ``` -------------------------------- ### Initial Status Check Source: https://github.com/ltaoo/wx_channels_download/blob/main/internal/api/ui/filehelper.html Performs an initial check of the user's login status when the page loads. ```javascript checkStatus(); ``` -------------------------------- ### Handle Image Selection Source: https://github.com/ltaoo/wx_channels_download/blob/main/internal/api/ui/filehelper.html Placeholder function for handling image file selection. Currently displays a 'feature under development' toast and resets the input. ```javascript function handleImageSelect(event) { const file = event.target.files[0]; if (!file) return; showToast('图片发送功能开发中...'); event.target.value = ''; } ``` -------------------------------- ### Go 服务端处理 feed 请求 Source: https://github.com/ltaoo/wx_channels_download/blob/main/docs/feature/event.md 一个简单的 Go HTTP 服务器,用于接收来自前端的 feed 数据。实现了 CORS 支持和 POST 请求处理,并将接收到的 body 打印到日志。适用于接收前端发送的视频信息。 ```go package main import ( "io" "log" "net/http" ) func setCORS(w http.ResponseWriter) { h := w.Header() h.Set("Access-Control-Allow-Origin", "*") h.Set("Access-Control-Allow-Methods", "POST, OPTIONS") h.Set("Access-Control-Allow-Headers", "Content-Type, Authorization") } func feedHandler(w http.ResponseWriter, r *http.Request) { setCORS(w) if r.Method == http.MethodOptions { w.WriteHeader(http.StatusNoContent) return } if r.Method != http.MethodPost { http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) return } body, err := io.ReadAll(r.Body) if err != nil { log.Printf("read body error: %v", err) http.Error(w, "Bad Request", http.StatusBadRequest) return } defer r.Body.Close() log.Printf("feed: %s", string(body)) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte(`{"ok":true}`)) } func main() { mux := http.NewServeMux() mux.HandleFunc("/api/feed", feedHandler) addr := "127.0.0.1:1234" log.Printf("HTTP server listening on %s", addr) if err := http.ListenAndServe(addr, mux); err != nil { log.Fatalf("server error: %v", err) } } ``` -------------------------------- ### Copy Refresh Links Source: https://github.com/ltaoo/wx_channels_download/blob/main/internal/officialaccount/ui/manager.html Copies up to 5 refresh URIs to the clipboard. Falls back to document.execCommand if navigator.clipboard is unavailable. ```javascript async function copyRefreshLinks(btn) { if (REMOTE_MODE) return; let links = []; try { const data = await fetchAccountPage({ page: 1, pageSize: 5, keyword: LIST_STATE.keyword || "" }); links = (data.list || []) .map((it) => (it && it.refresh_uri ? String(it.refresh_uri).trim() : "")) .filter(Boolean); } catch (e) { links = (Array.isArray(ACCOUNTS_CACHE) ? ACCOUNTS_CACHE : []) .map((it) => (it && it.refresh_uri ? String(it.refresh_uri).trim() : "")) .filter(Boolean) .slice(0, 5); } if (!links.length) { alert("列表中没有可复制的 refresh_uri"); return; } const text = links.join("\n"); const old = btn ? btn.textContent : ""; try { if (navigator.clipboard && navigator.clipboard.writeText) { await navigator.clipboard.writeText(text); } else { const ta = document.createElement("textarea"); ta.value = text; ta.style.position = "fixed"; ta.style.left = "-9999px"; ta.style.top = "-9999px"; document.body.appendChild(ta); ta.focus(); ta.select(); const ok = document.execCommand("copy"); document.body.removeChild(ta); if (!ok) throw new Error("复制失败"); } if (btn) btn.textContent = "已复制"; setTimeout(() => { if (btn) btn.textContent = old; }, 1200); } catch (e) { alert(String(e && e.message ? e.message : e)); } } ``` -------------------------------- ### Refresh All Accounts Source: https://github.com/ltaoo/wx_channels_download/blob/main/internal/officialaccount/ui/manager.html Initiates a refresh for all selected accounts via a POST request. Shows a progress modal and updates button state. ```javascript async function refreshAll(btn) { const checked = Array.from(document.querySelectorAll(".select-row:checked")); const bizList = checked.map(c => c.value); // Show modal const modal = document.getElementById("progress-modal"); if (modal) { modal.classList.add("visible"); document.getElementById("prog-success").textContent = "0"; document.getElementById("prog-failed").textContent = "0"; document.getElementById("prog-fill").style.width = "0%"; document.getElementById("prog-percent").textContent = "0%"; document.getElementById("prog-text").textContent = "正在请求任务..."; document.getElementById("modal-close-btn").style.visibility = "hidden"; } if (btn) { btn.disabled = true; var old = btn.textContent; btn.textContent = "刷新中..."; } try { const r = await fetch("/api/mp/refresh_with_frontend", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ biz_list: bizList }) }); const j = await r.json().catch(() => ({})); if (j && j.code !== undefined && j.code !== 0) { alert(j.msg || "接口错误"); closeProgressModal(); } } catch (e) { alert(String(e && e.message ? e.message : e)); closeProgressModal(); } finally { if (btn) { btn.disabled = false; btn.textContent = old; } } loadAccounts(); loadWsPool(); } ``` -------------------------------- ### Check Login Status Source: https://github.com/ltaoo/wx_channels_download/blob/main/internal/api/ui/filehelper.html Fetches the user's login status from the API. Shows the chat section if logged in, otherwise refreshes the QR code. ```javascript async function checkStatus() { try { const resp = await fetch(`${API_BASE}/api/filehelper/status`); const result = await resp.json(); if (result.code === 0 && result.data?.logged_in) { showChatSection(); } else { refreshQRCode(); } } catch (e) { refreshQRCode(); } } ``` -------------------------------- ### Add Certificate to User NSS Database Source: https://github.com/ltaoo/wx_channels_download/blob/main/docs/guide/certificate.md Adds the SunnyRoot.cer certificate to the current user's NSS database for browsers and Electron applications. This command requires `certutil` from `libnss3-tools` or `nss-tools`. It ensures the database exists and then imports the certificate with trust settings. ```bash mkdir -p ~/.pki/nssdb [ -f ~/.pki/nssdb/cert9.db ] || certutil -d sql:$HOME/.pki/nssdb -N --empty-password certutil -d sql:$HOME/.pki/nssdb -D -n WeChatAppEx_CA 2>/dev/null || true certutil -d sql:$HOME/.pki/nssdb -A -n WeChatAppEx_CA -t "CT,C,C" -i ~/Downloads/SunnyRoot.cer ``` -------------------------------- ### Trigger File Input Source: https://github.com/ltaoo/wx_channels_download/blob/main/internal/api/ui/filehelper.html Simulates a click on a hidden file input element to allow users to select a file. ```javascript function sendFile() { document.getElementById('fileInput').click(); } ``` -------------------------------- ### Format Video Duration Source: https://github.com/ltaoo/wx_channels_download/blob/main/internal/api/ui/filehelper.html Formats a given number of seconds into a 'minutes:seconds' string. Ensures seconds are padded with a leading zero if less than 10. ```javascript function formatDuration(seconds) { const s = parseInt(seconds) || 0; const m = Math.floor(s / 60); const sec = s % 60; return `${m}:${sec.toString().padStart(2, '0')}`; } ``` -------------------------------- ### Handle User Logout Source: https://github.com/ltaoo/wx_channels_download/blob/main/internal/api/ui/filehelper.html Logs the user out after confirmation. Resets polling flags, clears messages, and shows the login section. ```javascript async function handleLogout() { if (!confirm('确定要退出登录吗?')) return; try { await fetch(`${API_BASE}/api/filehelper/logout`, { method: 'POST' }); polling = false; syncCheckPolling = false; messages = []; showLoginSection(); refreshQRCode(); } catch (e) { showToast('退出失败: ' + e.message); } } ``` -------------------------------- ### Refresh Single Account Source: https://github.com/ltaoo/wx_channels_download/blob/main/internal/officialaccount/ui/manager.html Initiates a refresh for a single specified account via a POST request. Updates button state during the operation. ```javascript async function refreshOne(btn, biz) { if (!biz) return; btn.disabled = true; const old = btn.textContent; btn.textContent = "刷新中"; try { const r = await fetch("/api/mp/refresh_with_frontend", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ biz_list: [biz] }) }); const j = await r.json().catch(() => ({})); if ``` -------------------------------- ### Trigger Image Input Source: https://github.com/ltaoo/wx_channels_download/blob/main/internal/api/ui/filehelper.html Simulates a click on a hidden file input element to allow users to select an image. ```javascript function sendImage() { document.getElementById('imageInput').click(); } ``` -------------------------------- ### Send Message via API Source: https://github.com/ltaoo/wx_channels_download/blob/main/internal/api/ui/filehelper.html Sends a text message to the server using a POST request. Handles success by clearing the input and updating the message list, or shows an error toast on failure. ```javascript async function sendMessage() { const input = document.getElementById('messageInput'); const text = input.value.trim(); if (!text) return; try { const resp = await fetch(`${API_BASE}/api/filehelper/send`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text }) }); const result = await resp.json(); if (result.code === 0) { input.value = ''; messages.push({ MsgId: Date.now().toString(), type: 'text', text: text, isMine: true, CreateTime: Math.floor(Date.now() / 1000) }); renderMessages(); } else { showToast('发送失败: ' + result.msg); } } catch (e) { showToast('发送失败: ' + e.message); } } ``` -------------------------------- ### Disclaimer Text Source: https://github.com/ltaoo/wx_channels_download/blob/main/README.md This is a disclaimer text outlining the purpose and usage restrictions of the open-source project. It emphasizes legal compliance and prohibits illegal use. ```text 本项目为开源项目 仅用于技术交流学习和研究的目的 请遵守法律法规,请勿用作任何非法用途 否则造成一切后果自负 若您下载并使用即视为您知晓并同意 ``` -------------------------------- ### Update Progress Modal Source: https://github.com/ltaoo/wx_channels_download/blob/main/internal/officialaccount/ui/manager.html Updates the visibility and content of a progress modal based on received WebSocket data. ```javascript function updateProgressModal(data) { const modal = document.getElementById("progress-modal"); if (!modal || !modal.classList.contains("visible")) return; const total = data.total || 0; const current = data.current || 0; const success = data.success || 0; const failed = data.failed || 0; const percent = data.percent || 0; document.getElementById("prog-success").textContent = success; document.getElementById("prog-failed").textContent = failed; document.getElementById("prog-fill").style.width = percent + "%ிகளில்"; document.getElementById("prog-percent").textContent = percent + "%ிகளில்"; document.getElementById("prog-text").textContent = `正在处理: ${current} / ${total}`; if (current >= total && total > 0) { document.getElementById("prog-text").textContent = "处理完成"; document.getElementById("modal-close-btn").style.visibility = "visible"; } } ``` -------------------------------- ### Parse Video Channel XML Message Source: https://github.com/ltaoo/wx_channels_download/blob/main/internal/api/ui/filehelper.html Parses XML content from video channels to extract feed information. Handles potential XML parsing errors and returns a structured object or null. ```javascript function parseFinderFeed(content) { try { const xml = decodeAndCleanXml(content); const parser = new DOMParser(); const doc = parser.parseFromString(xml, 'text/xml'); if (doc.querySelector('parsererror')) { console.error('XML 解析失败:', xml.substring(0, 200)); return null; } const finderFeed = doc.querySelector('finderFeed'); if (!finderFeed) return null; const getTextContent = (selector) => { const el = finderFeed.querySelector(selector); return el ? el.textContent.trim() : ''; }; const media = finderFeed.querySelector('mediaList media'); return { nickname: getTextContent('nickname'), avatar: getTextContent('avatar'), desc: getTextContent('desc'), coverUrl: media ? (media.querySelector('coverUrl')?.textContent.trim() || '') : '', thumbUrl: media ? (media.querySelector('thumbUrl')?.textContent.trim() || '') : '', videoUrl: media ? (media.querySelector('url')?.textContent.trim() || '') : '', duration: media ? (media.querySelector('videoPlayDuration')?.textContent.trim() || '0') : '0', }; } catch (e) { console.error('解析视频号消息失败:', e); return null; } } ``` -------------------------------- ### Handle Enter Key for Sending Message Source: https://github.com/ltaoo/wx_channels_download/blob/main/internal/api/ui/filehelper.html Prevents default form submission when the Enter key is pressed without the Shift key, allowing the sendMessage function to be called. ```javascript function handleKeyDown(event) { if (event.key === 'Enter' && !event.shiftKey) { event.preventDefault(); sendMessage(); } } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.