标签 GitHub 下的文章 - 🥝 E 家 分 享 🥝
首页
📋 留言板
🔗 友情链接
🛠️ E家百宝箱
❤️ 关于
推荐
🔍 VPS监控
🐉 青龙面板
💽 E家网盘
----------
🔗 CloudFlare
🔗 甲骨文云
🔗 RackNerd
搜 索
1
自动提取 ChromeGo 一键翻墙包内的免费节点
145 阅读
2
【汇总:免费节点 - 每周更新】
114 阅读
3
GigaFile - 日本免费大文件加密分享服务,最长保留文档100天
71 阅读
4
【E家分享月刊系列】2024-12
56 阅读
5
CloudFlare WARP 免费 VPN 搭建教程
55 阅读
精选网站
网站搭建
科学上网搭建
有感而发
软件技巧
Excel技巧
WordPress技巧
登录
搜 索
标签搜索
WordPress
脚本
GitHub
科学上网
哈佛管理导师
E家分享月刊系列
V2ray
Mac软件
AI
Cloudflare
Docker
免费节点
建站在线工具
Excel技巧
Notion
Nginx
ChatGPT
图像编辑
免费图床
网盘资源
E家之长
累计撰写
195
篇文章
累计收到
245
条评论
首页
栏目
精选网站
网站搭建
科学上网搭建
有感而发
软件技巧
Excel技巧
WordPress技巧
页面
📋 留言板
🔗 友情链接
🛠️ E家百宝箱
❤️ 关于
推荐
🔍 VPS监控
🐉 青龙面板
💽 E家网盘
----------
🔗 CloudFlare
🔗 甲骨文云
🔗 RackNerd
用户登录
登录
找到
15
篇与
GitHub
相关的结果
2023-08-06
CloudFlare Workers VLESS 永久免费节点搭建
前言近期很火的利用 CloudFlare Workers 搭建 VLESS 节点,支持自定义伪装网页,可以 CDN 自选域名,13个端口随便换。注册 CloudFlare相信很多人都有 CloudFlare 帐户,这里额外介绍一个临时邮箱地址,用于注册 CloudFlare。 临时邮箱:https://www.linshiyouxiang.net/CloudFlare Workers vless 代码部署登录 CloudFlare:https://dash.cloudflare.com/点击 Workers 和 Pages,点击 创建 Workers。 创建一个脚本名称,比如 vless,点击 部署。 点击 配置 Worker,点击 快速编辑。清除默认代码。将下面章节提到的代码,粘贴到代码框中。找到 let userID = 和 let proxyIP,更改 UUID 和推荐的 CDN 加速 IP。点击 保存并部署。以下介绍两组代码供选择使用,强烈建议更换默认的 userID,如果不更换,容易被扫描盗用。生成 UUID在线生成地址:https://1024tools.com/uuid打开 V2ray 客户端,选择 添加 VLESS 服务器,在弹出的对话框中,点击 生成 按钮,即可生成 UUID。CDN 加速 IPcdn-all.xn--b6gac.eu.org cdn.xn--b6gac.eu.org cdn-b100.xn--b6gac.eu.org edgetunnel.anycast.eu.org cdn.anycast.eu.org(亚洲地区) jp.cloudflarest.link achk.cloudflarest.link(阿里香港优选)vless 部署代码1代码引用地址:https://github.com/zizifn/edgetunnel/blob/main/src/worker-vless.js 注意:需要更改 UUID, 添加 proxyIP 地址。// <!--GAMFC-->version base on commit 43fad05dcdae3b723c53c226f8181fc5bd47223e, time is 2023-06-22 15:20:02 UTC<!--GAMFC-END-->. // @ts-ignore import from 'cloudflare:sockets'; // How to generate your own UUID: // [Windows] Press "Win + R", input cmd and run: Powershell -NoExit -Command "[guid]::NewGuid()" let userID = 'd342d11e-d424-4583-b36e-524ab1f0afa4'; let proxyIP = ''; if (!isValidUUID(userID)) { throw new Error('uuid is not valid'); } export default { /** * @param request * @param } env * @param ctx * @returns */ async fetch(request, env, ctx) { try { userID = env.UUID || userID; proxyIP = env.PROXYIP || proxyIP; const upgradeHeader = request.headers.get('Upgrade'); if (!upgradeHeader || upgradeHeader !== 'websocket') { const url = new URL(request.url); switch (url.pathname) { case '/': return new Response(JSON.stringify(request.cf), ); case `/$`: { const vlessConfig = getVLESSConfig(userID, request.headers.get('Host')); return new Response(`$`, { status: 200, headers: { "Content-Type": "text/plain;charset=utf-8", } }); } default: return new Response('Not found', ); } } else { return await vlessOverWSHandler(request); } } catch (err) { /** @type */ let e = err; return new Response(e.toString()); } }, }; /** * * @param request */ async function vlessOverWSHandler(request) { /** @type */ // @ts-ignore const webSocketPair = new WebSocketPair(); const [client, webSocket] = Object.values(webSocketPair); webSocket.accept(); let address = ''; let portWithRandomLog = ''; const log = (/** @type */ info, /** @type */ event) => { console.log(`[$:$] $`, event || ''); }; const earlyDataHeader = request.headers.get('sec-websocket-protocol') || ''; const readableWebSocketStream = makeReadableWebSocketStream(webSocket, earlyDataHeader, log); /** @type }*/ let remoteSocketWapper = { value: null, }; let udpStreamWrite = null; let isDns = false; // ws --> remote readableWebSocketStream.pipeTo(new WritableStream({ async write(chunk, controller) { if (isDns && udpStreamWrite) { return udpStreamWrite(chunk); } if (remoteSocketWapper.value) { const writer = remoteSocketWapper.value.writable.getWriter() await writer.write(chunk); writer.releaseLock(); return; } const { hasError, message, portRemote = 443, addressRemote = '', rawDataIndex, vlessVersion = new Uint8Array([0, 0]), isUDP, } = processVlessHeader(chunk, userID); address = addressRemote; portWithRandomLog = `$--$ ${isUDP ? 'udp ' : 'tcp ' } `; if (hasError) { // controller.error(message); throw new Error(message); // cf seems has bug, controller.error will not end stream // webSocket.close(1000, message); return; } // if UDP but port not DNS port, close it if (isUDP) { if (portRemote === 53) { isDns = true; } else { // controller.error('UDP proxy only enable for DNS which is port 53'); throw new Error('UDP proxy only enable for DNS which is port 53'); // cf seems has bug, controller.error will not end stream return; } } // ["version", "附加信息长度 N"] const vlessResponseHeader = new Uint8Array([vlessVersion[0], 0]); const rawClientData = chunk.slice(rawDataIndex); // TODO: support udp here when cf runtime has udp support if (isDns) { const = await handleUDPOutBound(webSocket, vlessResponseHeader, log); udpStreamWrite = write; udpStreamWrite(rawClientData); return; } handleTCPOutBound(remoteSocketWapper, addressRemote, portRemote, rawClientData, webSocket, vlessResponseHeader, log); }, close() { log(`readableWebSocketStream is close`); }, abort(reason) { log(`readableWebSocketStream is abort`, JSON.stringify(reason)); }, })).catch((err) => { log('readableWebSocketStream pipeTo error', err); }); return new Response(null, { status: 101, // @ts-ignore webSocket: client, }); } /** * Handles outbound TCP connections. * * @param remoteSocket * @param addressRemote The remote address to connect to. * @param portRemote The remote port to connect to. * @param rawClientData The raw client data to write. * @param webSocket The WebSocket to pass the remote socket to. * @param vlessResponseHeader The VLESS response header. * @param log The logging function. * @returns The remote socket. */ async function handleTCPOutBound(remoteSocket, addressRemote, portRemote, rawClientData, webSocket, vlessResponseHeader, log,) { async function connectAndWrite(address, port) { /** @type */ const tcpSocket = connect({ hostname: address, port: port, }); remoteSocket.value = tcpSocket; log(`connected to $:$`); const writer = tcpSocket.writable.getWriter(); await writer.write(rawClientData); // first write, nomal is tls client hello writer.releaseLock(); return tcpSocket; } // if the cf connect tcp socket have no incoming data, we retry to redirect ip async function retry() { const tcpSocket = await connectAndWrite(proxyIP || addressRemote, portRemote) // no matter retry success or not, close websocket tcpSocket.closed.catch(error => { console.log('retry tcpSocket closed error', error); }).finally(() => { safeCloseWebSocket(webSocket); }) remoteSocketToWS(tcpSocket, webSocket, vlessResponseHeader, null, log); } const tcpSocket = await connectAndWrite(addressRemote, portRemote); // when remoteSocket is ready, pass to websocket // remote--> ws remoteSocketToWS(tcpSocket, webSocket, vlessResponseHeader, retry, log); } /** * * @param webSocketServer * @param earlyDataHeader for ws 0rtt * @param log for ws 0rtt */ function makeReadableWebSocketStream(webSocketServer, earlyDataHeader, log) { let readableStreamCancel = false; const stream = new ReadableStream({ start(controller) { webSocketServer.addEventListener('message', (event) => { if (readableStreamCancel) { return; } const message = event.data; controller.enqueue(message); }); // The event means that the client closed the client -> server stream. // However, the server -> client stream is still open until you call close() on the server side. // The WebSocket protocol says that a separate close message must be sent in each direction to fully close the socket. webSocketServer.addEventListener('close', () => { // client send close, need close server // if stream is cancel, skip controller.close safeCloseWebSocket(webSocketServer); if (readableStreamCancel) { return; } controller.close(); } ); webSocketServer.addEventListener('error', (err) => { log('webSocketServer has error'); controller.error(err); } ); // for ws 0rtt const = base64ToArrayBuffer(earlyDataHeader); if (error) { controller.error(error); } else if (earlyData) { controller.enqueue(earlyData); } }, pull(controller) { // if ws can stop read if stream is full, we can implement backpressure // https://streams.spec.whatwg.org/#example-rs-push-backpressure }, cancel(reason) { // 1. pipe WritableStream has error, this cancel will called, so ws handle server close into here // 2. if readableStream is cancel, all controller.close/enqueue need skip, // 3. but from testing controller.error still work even if readableStream is cancel if (readableStreamCancel) { return; } log(`ReadableStream was canceled, due to $`) readableStreamCancel = true; safeCloseWebSocket(webSocketServer); } }); return stream; } // https://xtls.github.io/development/protocols/vless.html // https://github.com/zizifn/excalidraw-backup/blob/main/v2ray-protocol.excalidraw /** * * @param vlessBuffer * @param userID * @returns */ function processVlessHeader( vlessBuffer, userID ) { if (vlessBuffer.byteLength < 24) { return { hasError: true, message: 'invalid data', }; } const version = new Uint8Array(vlessBuffer.slice(0, 1)); let isValidUser = false; let isUDP = false; if (stringify(new Uint8Array(vlessBuffer.slice(1, 17))) === userID) { isValidUser = true; } if (!isValidUser) { return { hasError: true, message: 'invalid user', }; } const optLength = new Uint8Array(vlessBuffer.slice(17, 18))[0]; //skip opt for now const command = new Uint8Array( vlessBuffer.slice(18 + optLength, 18 + optLength + 1) )[0]; // 0x01 TCP // 0x02 UDP // 0x03 MUX if (command === 1) { } else if (command === 2) { isUDP = true; } else { return { hasError: true, message: `command $ is not support, command 01-tcp,02-udp,03-mux`, }; } const portIndex = 18 + optLength + 1; const portBuffer = vlessBuffer.slice(portIndex, portIndex + 2); // port is big-Endian in raw data etc 80 == 0x005d const portRemote = new DataView(portBuffer).getUint16(0); let addressIndex = portIndex + 2; const addressBuffer = new Uint8Array( vlessBuffer.slice(addressIndex, addressIndex + 1) ); // 1--> ipv4 addressLength =4 // 2--> domain name addressLength=addressBuffer[1] // 3--> ipv6 addressLength =16 const addressType = addressBuffer[0]; let addressLength = 0; let addressValueIndex = addressIndex + 1; let addressValue = ''; switch (addressType) { case 1: addressLength = 4; addressValue = new Uint8Array( vlessBuffer.slice(addressValueIndex, addressValueIndex + addressLength) ).join('.'); break; case 2: addressLength = new Uint8Array( vlessBuffer.slice(addressValueIndex, addressValueIndex + 1) )[0]; addressValueIndex += 1; addressValue = new TextDecoder().decode( vlessBuffer.slice(addressValueIndex, addressValueIndex + addressLength) ); break; case 3: addressLength = 16; const dataView = new DataView( vlessBuffer.slice(addressValueIndex, addressValueIndex + addressLength) ); // 2001:0db8:85a3:0000:0000:8a2e:0370:7334 const ipv6 = []; for (let i = 0; i < 8; i++) { ipv6.push(dataView.getUint16(i * 2).toString(16)); } addressValue = ipv6.join(':'); // seems no need add [] for ipv6 break; default: return { hasError: true, message: `invild addressType is $`, }; } if (!addressValue) { return { hasError: true, message: `addressValue is empty, addressType is $`, }; } return { hasError: false, addressRemote: addressValue, addressType, portRemote, rawDataIndex: addressValueIndex + addressLength, vlessVersion: version, isUDP, }; } /** * * @param remoteSocket * @param webSocket * @param vlessResponseHeader * @param retry * @param log */ async function remoteSocketToWS(remoteSocket, webSocket, vlessResponseHeader, retry, log) { // remote--> ws let remoteChunkCount = 0; let chunks = []; /** @type */ let vlessHeader = vlessResponseHeader; let hasIncomingData = false; // check if remoteSocket has incoming data await remoteSocket.readable .pipeTo( new WritableStream({ start() { }, /** * * @param chunk * @param controller */ async write(chunk, controller) { hasIncomingData = true; // remoteChunkCount++; if (webSocket.readyState !== WS_READY_STATE_OPEN) { controller.error( 'webSocket.readyState is not open, maybe close' ); } if (vlessHeader) { webSocket.send(await new Blob([vlessHeader, chunk]).arrayBuffer()); vlessHeader = null; } else { // seems no need rate limit this, CF seems fix this??.. // if (remoteChunkCount > 20000) { // // cf one package is 4096 byte(4kb), 4096 * 20000 = 80M // await delay(1); // } webSocket.send(chunk); } }, close() { log(`remoteConnection!.readable is close with hasIncomingData is $`); // safeCloseWebSocket(webSocket); // no need server close websocket frist for some case will casue HTTP ERR_CONTENT_LENGTH_MISMATCH issue, client will send close event anyway. }, abort(reason) { console.error(`remoteConnection!.readable abort`, reason); }, }) ) .catch((error) => { console.error( `remoteSocketToWS has exception `, error.stack || error ); safeCloseWebSocket(webSocket); }); // seems is cf connect socket have error, // 1. Socket.closed will have error // 2. Socket.readable will be close without any data coming if (hasIncomingData === false && retry) { log(`retry`) retry(); } } /** * * @param base64Str * @returns */ function base64ToArrayBuffer(base64Str) { if (!base64Str) { return ; } try { // go use modified Base64 for URL rfc4648 which js atob not support base64Str = base64Str.replace(/-/g, '+').replace(/_/g, '/'); const decode = atob(base64Str); const arryBuffer = Uint8Array.from(decode, (c) => c.charCodeAt(0)); return ; } catch (error) { return ; } } /** * This is not real UUID validation * @param uuid */ function isValidUUID(uuid) { const uuidRegex = /^[0-9a-f]-[0-9a-f]-[4][0-9a-f]-[89ab][0-9a-f]-[0-9a-f]$/i; return uuidRegex.test(uuid); } const WS_READY_STATE_OPEN = 1; const WS_READY_STATE_CLOSING = 2; /** * Normally, WebSocket will not has exceptions when close. * @param socket */ function safeCloseWebSocket(socket) { try { if (socket.readyState === WS_READY_STATE_OPEN || socket.readyState === WS_READY_STATE_CLOSING) { socket.close(); } } catch (error) { console.error('safeCloseWebSocket error', error); } } const byteToHex = []; for (let i = 0; i < 256; ++i) { byteToHex.push((i + 256).toString(16).slice(1)); } function unsafeStringify(arr, offset = 0) { return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase(); } function stringify(arr, offset = 0) { const uuid = unsafeStringify(arr, offset); if (!isValidUUID(uuid)) { throw TypeError("Stringified UUID is invalid"); } return uuid; } /** * * @param webSocket * @param vlessResponseHeader * @param log */ async function handleUDPOutBound(webSocket, vlessResponseHeader, log) { let isVlessHeaderSent = false; const transformStream = new TransformStream({ start(controller) { }, transform(chunk, controller) { // udp message 2 byte is the the length of udp data // TODO: this should have bug, beacsue maybe udp chunk can be in two websocket message for (let index = 0; index < chunk.byteLength;) { const lengthBuffer = chunk.slice(index, index + 2); const udpPakcetLength = new DataView(lengthBuffer).getUint16(0); const udpData = new Uint8Array( chunk.slice(index + 2, index + 2 + udpPakcetLength) ); index = index + 2 + udpPakcetLength; controller.enqueue(udpData); } }, flush(controller) { } }); // only handle dns udp for now transformStream.readable.pipeTo(new WritableStream({ async write(chunk) { const resp = await fetch('https://1.1.1.1/dns-query', { method: 'POST', headers: { 'content-type': 'application/dns-message', }, body: chunk, }) const dnsQueryResult = await resp.arrayBuffer(); const udpSize = dnsQueryResult.byteLength; // console.log([...new Uint8Array(dnsQueryResult)].map((x) => x.toString(16))); const udpSizeBuffer = new Uint8Array([(udpSize >> 8) & 0xff, udpSize & 0xff]); if (webSocket.readyState === WS_READY_STATE_OPEN) { log(`doh success and dns message length is $`); if (isVlessHeaderSent) { webSocket.send(await new Blob([udpSizeBuffer, dnsQueryResult]).arrayBuffer()); } else { webSocket.send(await new Blob([vlessResponseHeader, udpSizeBuffer, dnsQueryResult]).arrayBuffer()); isVlessHeaderSent = true; } } } })).catch((error) => { log('dns udp has error' + error) }); const writer = transformStream.writable.getWriter(); return { /** * * @param chunk */ write(chunk) { writer.write(chunk); } }; } /** * * @param userID * @param hostName * @returns */ function getVLESSConfig(userID, hostName) { const vlessMain = `vless://$@$:443?encryption=none&security=tls&sni=$&fp=randomized&type=ws&host=$&path=%2F%3Fed%3D2048#$` return ` ################################################################ v2ray --------------------------------------------------------------- $ --------------------------------------------------------------- ################################################################ clash-meta --------------------------------------------------------------- - type: vless name: $ server: $ port: 443 uuid: $ network: ws tls: true udp: false sni: $ client-fingerprint: chrome ws-opts: path: "/?ed=2048" headers: host: $ --------------------------------------------------------------- ################################################################ `; }vless 部署代码2代码引用地址:https://github.com/yonggekkk/CF-workers-pages-vless/blob/main/\_worker.js需要更改 UUID自带 CloudFlare 证书的 proxyIP 地址代码104行,支持自定义伪装网页,修改对应的网页地址即可。代码中地址为 CCTV 官网// <!--GAMFC-->version base on commit 43fad05dcdae3b723c53c226f8181fc5bd47223e, time is 2023-06-22 15:20:02 UTC<!--GAMFC-END-->. // @ts-ignore import from 'cloudflare:sockets'; // How to generate your own UUID: // [Windows] Press "Win + R", input cmd and run: Powershell -NoExit -Command "[guid]::NewGuid()" let userID = 'd342d11e-d424-4583-b36e-524ab1f0afa4'; const proxyIPs = ['cdn-all.xn--b6gac.eu.org', 'cdn-all.xijingping.link', 'cdn.xn--b6gac.eu.org', 'cdn-b100.xn--b6gac.eu.org', 'edgetunnel.anycast.eu.org', 'cdn.anycast.eu.org']; let proxyIP = proxyIPs[Math.floor(Math.random() * proxyIPs.length)]; let dohURL = 'https://sky.rethinkdns.com/1:-Pf_____9_8A_AMAIgE8kMABVDDmKOHTAKg='; // https://cloudflare-dns.com/dns-query or https://dns.google/dns-query // v2board api environment variables let nodeId = ''; // 1 let apiToken = ''; //abcdefghijklmnopqrstuvwxyz123456 let apiHost = ''; // api.v2board.com if (!isValidUUID(userID)) { throw new Error('uuid is not valid'); } export default { /** * @param request * @param } env * @param ctx * @returns */ async fetch(request, env, ctx) { try { userID = env.UUID || userID; proxyIP = env.PROXYIP || proxyIP; dohURL = env.DNS_RESOLVER_URL || dohURL; nodeId = env.NODE_ID || nodeId; apiToken = env.API_TOKEN || apiToken; apiHost = env.API_HOST || apiHost; const upgradeHeader = request.headers.get('Upgrade'); if (!upgradeHeader || upgradeHeader !== 'websocket') { const url = new URL(request.url); switch (url.pathname) { case '/cf': return new Response(JSON.stringify(request.cf, null, 4), { status: 200, headers: { "Content-Type": "application/json;charset=utf-8", }, }); case '/connect': // for test connect to cf socket const [hostname, port] = ['cloudflare.com', '80']; console.log(`Connecting to $:$...`); try { const socket = await connect({ hostname: hostname, port: parseInt(port, 10), }); const writer = socket.writable.getWriter(); try { await writer.write(new TextEncoder().encode('GET / HTTP/1.1\r\nHost: ' + hostname + '\r\n\r\n')); } catch (writeError) { writer.releaseLock(); await socket.close(); return new Response(writeError.message, ); } writer.releaseLock(); const reader = socket.readable.getReader(); let value; try { const result = await reader.read(); value = result.value; } catch (readError) { await reader.releaseLock(); await socket.close(); return new Response(readError.message, ); } await reader.releaseLock(); await socket.close(); return new Response(new TextDecoder().decode(value), ); } catch (connectError) { return new Response(connectError.message, ); } case `/$`: { const vlessConfig = getVLESSConfig(userID, request.headers.get('Host')); return new Response(`$`, { status: 200, headers: { "Content-Type": "text/plain;charset=utf-8", } }); } default: // return new Response('Not found', ); // For any other path, reverse proxy to 'www.fmprc.gov.cn' and return the original response url.hostname = 'tv.cctv.com'; url.protocol = 'https:'; request = new Request(url, request); return await fetch(request); } } else { return await vlessOverWSHandler(request); } } catch (err) { /** @type */ let e = err; return new Response(e.toString()); } }, }; /** * * @param request */ async function vlessOverWSHandler(request) { /** @type */ // @ts-ignore const webSocketPair = new WebSocketPair(); const [client, webSocket] = Object.values(webSocketPair); webSocket.accept(); let address = ''; let portWithRandomLog = ''; const log = (/** @type */ info, /** @type */ event) => { console.log(`[$:$] $`, event || ''); }; const earlyDataHeader = request.headers.get('sec-websocket-protocol') || ''; const readableWebSocketStream = makeReadableWebSocketStream(webSocket, earlyDataHeader, log); /** @type }*/ let remoteSocketWapper = { value: null, }; let udpStreamWrite = null; let isDns = false; // ws --> remote readableWebSocketStream.pipeTo(new WritableStream({ async write(chunk, controller) { if (isDns && udpStreamWrite) { return udpStreamWrite(chunk); } if (remoteSocketWapper.value) { const writer = remoteSocketWapper.value.writable.getWriter() await writer.write(chunk); writer.releaseLock(); return; } const { hasError, message, portRemote = 443, addressRemote = '', rawDataIndex, vlessVersion = new Uint8Array([0, 0]), isUDP, } = await processVlessHeader(chunk, userID); address = addressRemote; portWithRandomLog = `$--$ ${isUDP ? 'udp ' : 'tcp ' } `; if (hasError) { // controller.error(message); throw new Error(message); // cf seems has bug, controller.error will not end stream // webSocket.close(1000, message); return; } // if UDP but port not DNS port, close it if (isUDP) { if (portRemote === 53) { isDns = true; } else { // controller.error('UDP proxy only enable for DNS which is port 53'); throw new Error('UDP proxy only enable for DNS which is port 53'); // cf seems has bug, controller.error will not end stream return; } } // ["version", "附加信息长度 N"] const vlessResponseHeader = new Uint8Array([vlessVersion[0], 0]); const rawClientData = chunk.slice(rawDataIndex); // TODO: support udp here when cf runtime has udp support if (isDns) { const = await handleUDPOutBound(webSocket, vlessResponseHeader, log); udpStreamWrite = write; udpStreamWrite(rawClientData); return; } handleTCPOutBound(remoteSocketWapper, addressRemote, portRemote, rawClientData, webSocket, vlessResponseHeader, log); }, close() { log(`readableWebSocketStream is close`); }, abort(reason) { log(`readableWebSocketStream is abort`, JSON.stringify(reason)); }, })).catch((err) => { log('readableWebSocketStream pipeTo error', err); }); return new Response(null, { status: 101, // @ts-ignore webSocket: client, }); } let apiResponseCache = null; let cacheTimeout = null; /** * Fetches the API response from the server and caches it for future use. * @returns A Promise that resolves to the API response object or null if there was an error. */ async function fetchApiResponse() { const requestOptions = { method: 'GET', redirect: 'follow' }; try { const response = await fetch(`https://$/api/v1/server/UniProxy/user?node_id=$&node_type=v2ray&token=$`, requestOptions); if (!response.ok) { console.error('Error: Network response was not ok'); return null; } const apiResponse = await response.json(); apiResponseCache = apiResponse; // Refresh the cache every 5 minutes (300000 milliseconds) if (cacheTimeout) { clearTimeout(cacheTimeout); } cacheTimeout = setTimeout(() => fetchApiResponse(), 300000); return apiResponse; } catch (error) { console.error('Error:', error); return null; } } /** * Returns the cached API response if it exists, otherwise fetches the API response from the server and caches it for future use. * @returns A Promise that resolves to the cached API response object or the fetched API response object, or null if there was an error. */ async function getApiResponse() { if (!apiResponseCache) { return await fetchApiResponse(); } return apiResponseCache; } /** * Checks if a given UUID is present in the API response. * @param targetUuid The UUID to search for. * @returns A Promise that resolves to true if the UUID is present in the API response, false otherwise. */ async function checkUuidInApiResponse(targetUuid) { // Check if any of the environment variables are empty if (!nodeId || !apiToken || !apiHost) { return false; } try { const apiResponse = await getApiResponse(); if (!apiResponse) { return false; } const isUuidInResponse = apiResponse.users.some(user => user.uuid === targetUuid); return isUuidInResponse; } catch (error) { console.error('Error:', error); return false; } } // Usage example: // const targetUuid = "65590e04-a94c-4c59-a1f2-571bce925aad"; // checkUuidInApiResponse(targetUuid).then(result => console.log(result)); /** * Handles outbound TCP connections. * * @param remoteSocket * @param addressRemote The remote address to connect to. * @param portRemote The remote port to connect to. * @param rawClientData The raw client data to write. * @param webSocket The WebSocket to pass the remote socket to. * @param vlessResponseHeader The VLESS response header. * @param log The logging function. * @returns The remote socket. */ async function handleTCPOutBound(remoteSocket, addressRemote, portRemote, rawClientData, webSocket, vlessResponseHeader, log,) { async function connectAndWrite(address, port) { /** @type */ const tcpSocket = connect({ hostname: address, port: port, }); remoteSocket.value = tcpSocket; log(`connected to $:$`); const writer = tcpSocket.writable.getWriter(); await writer.write(rawClientData); // first write, nomal is tls client hello writer.releaseLock(); return tcpSocket; } // if the cf connect tcp socket have no incoming data, we retry to redirect ip async function retry() { const tcpSocket = await connectAndWrite(proxyIP || addressRemote, portRemote) // no matter retry success or not, close websocket tcpSocket.closed.catch(error => { console.log('retry tcpSocket closed error', error); }).finally(() => { safeCloseWebSocket(webSocket); }) remoteSocketToWS(tcpSocket, webSocket, vlessResponseHeader, null, log); } const tcpSocket = await connectAndWrite(addressRemote, portRemote); // when remoteSocket is ready, pass to websocket // remote--> ws remoteSocketToWS(tcpSocket, webSocket, vlessResponseHeader, retry, log); } /** * * @param webSocketServer * @param earlyDataHeader for ws 0rtt * @param log for ws 0rtt */ function makeReadableWebSocketStream(webSocketServer, earlyDataHeader, log) { let readableStreamCancel = false; const stream = new ReadableStream({ start(controller) { webSocketServer.addEventListener('message', (event) => { if (readableStreamCancel) { return; } const message = event.data; controller.enqueue(message); }); // The event means that the client closed the client -> server stream. // However, the server -> client stream is still open until you call close() on the server side. // The WebSocket protocol says that a separate close message must be sent in each direction to fully close the socket. webSocketServer.addEventListener('close', () => { // client send close, need close server // if stream is cancel, skip controller.close safeCloseWebSocket(webSocketServer); if (readableStreamCancel) { return; } controller.close(); } ); webSocketServer.addEventListener('error', (err) => { log('webSocketServer has error'); controller.error(err); } ); // for ws 0rtt const = base64ToArrayBuffer(earlyDataHeader); if (error) { controller.error(error); } else if (earlyData) { controller.enqueue(earlyData); } }, pull(controller) { // if ws can stop read if stream is full, we can implement backpressure // https://streams.spec.whatwg.org/#example-rs-push-backpressure }, cancel(reason) { // 1. pipe WritableStream has error, this cancel will called, so ws handle server close into here // 2. if readableStream is cancel, all controller.close/enqueue need skip, // 3. but from testing controller.error still work even if readableStream is cancel if (readableStreamCancel) { return; } log(`ReadableStream was canceled, due to $`) readableStreamCancel = true; safeCloseWebSocket(webSocketServer); } }); return stream; } // https://xtls.github.io/development/protocols/vless.html // https://github.com/zizifn/excalidraw-backup/blob/main/v2ray-protocol.excalidraw /** * * @param vlessBuffer * @param userID * @returns */ async function processVlessHeader( vlessBuffer, userID ) { if (vlessBuffer.byteLength < 24) { return { hasError: true, message: 'invalid data', }; } const version = new Uint8Array(vlessBuffer.slice(0, 1)); let isValidUser = false; let isUDP = false; const slicedBuffer = new Uint8Array(vlessBuffer.slice(1, 17)); const slicedBufferString = stringify(slicedBuffer); const uuids = userID.includes(',') ? userID.split(",") : [userID]; const checkUuidInApi = await checkUuidInApiResponse(slicedBufferString); isValidUser = uuids.some(userUuid => checkUuidInApi || slicedBufferString === userUuid.trim()); console.log(`checkUuidInApi: $, userID: $`); if (!isValidUser) { return { hasError: true, message: 'invalid user', }; } const optLength = new Uint8Array(vlessBuffer.slice(17, 18))[0]; //skip opt for now const command = new Uint8Array( vlessBuffer.slice(18 + optLength, 18 + optLength + 1) )[0]; // 0x01 TCP // 0x02 UDP // 0x03 MUX if (command === 1) { } else if (command === 2) { isUDP = true; } else { return { hasError: true, message: `command $ is not support, command 01-tcp,02-udp,03-mux`, }; } const portIndex = 18 + optLength + 1; const portBuffer = vlessBuffer.slice(portIndex, portIndex + 2); // port is big-Endian in raw data etc 80 == 0x005d const portRemote = new DataView(portBuffer).getUint16(0); let addressIndex = portIndex + 2; const addressBuffer = new Uint8Array( vlessBuffer.slice(addressIndex, addressIndex + 1) ); // 1--> ipv4 addressLength =4 // 2--> domain name addressLength=addressBuffer[1] // 3--> ipv6 addressLength =16 const addressType = addressBuffer[0]; let addressLength = 0; let addressValueIndex = addressIndex + 1; let addressValue = ''; switch (addressType) { case 1: addressLength = 4; addressValue = new Uint8Array( vlessBuffer.slice(addressValueIndex, addressValueIndex + addressLength) ).join('.'); break; case 2: addressLength = new Uint8Array( vlessBuffer.slice(addressValueIndex, addressValueIndex + 1) )[0]; addressValueIndex += 1; addressValue = new TextDecoder().decode( vlessBuffer.slice(addressValueIndex, addressValueIndex + addressLength) ); break; case 3: addressLength = 16; const dataView = new DataView( vlessBuffer.slice(addressValueIndex, addressValueIndex + addressLength) ); // 2001:0db8:85a3:0000:0000:8a2e:0370:7334 const ipv6 = []; for (let i = 0; i < 8; i++) { ipv6.push(dataView.getUint16(i * 2).toString(16)); } addressValue = ipv6.join(':'); // seems no need add [] for ipv6 break; default: return { hasError: true, message: `invild addressType is $`, }; } if (!addressValue) { return { hasError: true, message: `addressValue is empty, addressType is $`, }; } return { hasError: false, addressRemote: addressValue, addressType, portRemote, rawDataIndex: addressValueIndex + addressLength, vlessVersion: version, isUDP, }; } /** * * @param remoteSocket * @param webSocket * @param vlessResponseHeader * @param retry * @param log */ async function remoteSocketToWS(remoteSocket, webSocket, vlessResponseHeader, retry, log) { // remote--> ws let remoteChunkCount = 0; let chunks = []; /** @type */ let vlessHeader = vlessResponseHeader; let hasIncomingData = false; // check if remoteSocket has incoming data await remoteSocket.readable .pipeTo( new WritableStream({ start() { }, /** * * @param chunk * @param controller */ async write(chunk, controller) { hasIncomingData = true; // remoteChunkCount++; if (webSocket.readyState !== WS_READY_STATE_OPEN) { controller.error( 'webSocket.readyState is not open, maybe close' ); } if (vlessHeader) { webSocket.send(await new Blob([vlessHeader, chunk]).arrayBuffer()); vlessHeader = null; } else { // seems no need rate limit this, CF seems fix this??.. // if (remoteChunkCount > 20000) { // // cf one package is 4096 byte(4kb), 4096 * 20000 = 80M // await delay(1); // } webSocket.send(chunk); } }, close() { log(`remoteConnection!.readable is close with hasIncomingData is $`); // safeCloseWebSocket(webSocket); // no need server close websocket frist for some case will casue HTTP ERR_CONTENT_LENGTH_MISMATCH issue, client will send close event anyway. }, abort(reason) { console.error(`remoteConnection!.readable abort`, reason); }, }) ) .catch((error) => { console.error( `remoteSocketToWS has exception `, error.stack || error ); safeCloseWebSocket(webSocket); }); // seems is cf connect socket have error, // 1. Socket.closed will have error // 2. Socket.readable will be close without any data coming if (hasIncomingData === false && retry) { log(`retry`) retry(); } } /** * * @param base64Str * @returns */ function base64ToArrayBuffer(base64Str) { if (!base64Str) { return ; } try { // go use modified Base64 for URL rfc4648 which js atob not support base64Str = base64Str.replace(/-/g, '+').replace(/_/g, '/'); const decode = atob(base64Str); const arryBuffer = Uint8Array.from(decode, (c) => c.charCodeAt(0)); return ; } catch (error) { return ; } } /** * This is not real UUID validation * @param uuid */ function isValidUUID(uuid) { const uuidRegex = /^[0-9a-f]-[0-9a-f]-[4][0-9a-f]-[89ab][0-9a-f]-[0-9a-f]$/i; return uuidRegex.test(uuid); } const WS_READY_STATE_OPEN = 1; const WS_READY_STATE_CLOSING = 2; /** * Normally, WebSocket will not has exceptions when close. * @param socket */ function safeCloseWebSocket(socket) { try { if (socket.readyState === WS_READY_STATE_OPEN || socket.readyState === WS_READY_STATE_CLOSING) { socket.close(); } } catch (error) { console.error('safeCloseWebSocket error', error); } } const byteToHex = []; for (let i = 0; i < 256; ++i) { byteToHex.push((i + 256).toString(16).slice(1)); } function unsafeStringify(arr, offset = 0) { return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase(); } function stringify(arr, offset = 0) { const uuid = unsafeStringify(arr, offset); if (!isValidUUID(uuid)) { throw TypeError("Stringified UUID is invalid"); } return uuid; } /** * * @param webSocket * @param vlessResponseHeader * @param log */ async function handleUDPOutBound(webSocket, vlessResponseHeader, log) { let isVlessHeaderSent = false; const transformStream = new TransformStream({ start(controller) { }, transform(chunk, controller) { // udp message 2 byte is the the length of udp data // TODO: this should have bug, beacsue maybe udp chunk can be in two websocket message for (let index = 0; index < chunk.byteLength;) { const lengthBuffer = chunk.slice(index, index + 2); const udpPakcetLength = new DataView(lengthBuffer).getUint16(0); const udpData = new Uint8Array( chunk.slice(index + 2, index + 2 + udpPakcetLength) ); index = index + 2 + udpPakcetLength; controller.enqueue(udpData); } }, flush(controller) { } }); // only handle dns udp for now transformStream.readable.pipeTo(new WritableStream({ async write(chunk) { const resp = await fetch(dohURL, // dns server url { method: 'POST', headers: { 'content-type': 'application/dns-message', }, body: chunk, }) const dnsQueryResult = await resp.arrayBuffer(); const udpSize = dnsQueryResult.byteLength; // console.log([...new Uint8Array(dnsQueryResult)].map((x) => x.toString(16))); const udpSizeBuffer = new Uint8Array([(udpSize >> 8) & 0xff, udpSize & 0xff]); if (webSocket.readyState === WS_READY_STATE_OPEN) { log(`doh success and dns message length is $`); if (isVlessHeaderSent) { webSocket.send(await new Blob([udpSizeBuffer, dnsQueryResult]).arrayBuffer()); } else { webSocket.send(await new Blob([vlessResponseHeader, udpSizeBuffer, dnsQueryResult]).arrayBuffer()); isVlessHeaderSent = true; } } } })).catch((error) => { log('dns udp has error' + error) }); const writer = transformStream.writable.getWriter(); return { /** * * @param chunk */ write(chunk) { writer.write(chunk); } }; } /** * * @param userID * @param hostName * @returns */ function getVLESSConfig(userID, hostName) { const vlessws = `vless://$@time.cloudflare.com:8880?encryption=none&type=ws&host=$&path=%2F%3Fed%3D2048#$` const vlesswstls = `vless://$@time.cloudflare.com:8443?encryption=none&security=tls&type=ws&host=兄弟,你的自定义域名呢?&path=%2F%3Fed%3D2048#$` return ` ==========================配置详解============================== ################################################################ 一、CF-workers-vless+ws节点,分享链接如下: $ --------------------------------------------------------------- 注意:当前节点无需域名,TLS选项关闭 --------------------------------------------------------------- 客户端必要文明参数如下: 客户端地址(address):自选域名 或者 自选IP 端口(port):7个http端口可任意选择(80、8080、8880、2052、2082、2086、2095) 用户ID(uuid):$ 传输协议(network):ws/websocket 伪装域名(host):$ 路径(path):/?ed=2048 ################################################################ ################################################################ 二、CF-workers-vless+ws+tls节点,分享链接如下: $ --------------------------------------------------------------- 注意:客户端ws选项后的伪装域名host必须改为你自定义的域名 --------------------------------------------------------------- 客户端必要文明参数如下: 客户端地址(address):自选域名 或者 自选IP 端口(port):6个https端口可任意选择(443、8443、2053、2083、2087、2096) 用户ID(uuid):$ 传输协议(network):ws/websocket 伪装域名(host):兄弟,你的自定义域名呢? 路径(path):/?ed=2048 传输安全(TLS):开启 跳过证书验证(allowlnsecure):false ################################################################ ################################################################ clash-meta --------------------------------------------------------------- - type: vless name: $ server: $ port: 443 uuid: $ network: ws tls: true udp: false sni: $ client-fingerprint: chrome ws-opts: path: "/?ed=2048" headers: host: $ --------------------------------------------------------------- ################################################################ `; }V2ray 使用 CloudFlare Workers vless 节点(无域名)复制刚刚创建的代码地址,格式为:https://vless.xxx.workers.dev/,其中 vless 为创建的脚本名称,xxx 为 CloudFlare 用户名。在浏览器地址栏中粘贴代码地址后,如果出现一段字符,即表明创建成功。接下来在该地址后输入 /UUID字符串,回车即可看到 vless 地址。 将页面中的 V2ray 地址复制,打开 V2ray 软件,点击 服务器 选项,选择 从剪贴板导入批量URL,即可看到创建的节点。双击刚刚创建的节点,将端口(port)改为 2052,传输层安全(TLS) 设置为空,传输协议(network) 为 ws,地址(address) 改为优选 IP。 注:7个 http 端口可任意选择:80、8080、8880、2052、2082、2086、2095 Workers 专用 IP 优选工具CloudFlare 优质 IP 自动切换:https://stock.hostmonit.com/CloudFlareYes172.64.32.1/24 (推荐移动,走香港) 104.28.14.0/24 (推荐移动,走新加坡) 104.23.240.0 ~ 104.23.243.254 (推荐联通、移动,线路未知) 108.162.236.1/24 (推荐联通,走美国) 104.20.157.0/24 (推荐联通,走日本) 104.16.160.1/24 (推荐电信,走洛杉矶) 172.64.0.0/24 (推荐电信,走旧金山) 172.64.32.* (走欧洲)优选工具 Windows 版本下载地址,断开所有 VPN 和科学上网方式,选择运行任意批处理文件,在运行后的结果中,选择 PING 值低的 IP 地址,复制到 V2ray 的 地址(address) 中。 CloudFlare 自选域名自选域名下载地址,断开所有 VPN 和科学上网方式,选择自选域名程序单文件,运行后在相同文件夹下中打开 txt 文件,选择 PING 值低的域名,复制到 V2ray 的 地址(address) 中。V2ray 使用 CloudFlare Workers vless 节点(有域名)在 CloudFlare Workers 中,打开刚才创建的脚本,点击 查看,点击 添加自定义域。 在自定义域对话框中输入已经在 CloudFlare 注册域名的二级域名,点击 添加自定义域。比如:vless.5iehome.cf。接下来只需在浏览器地址栏中粘贴输入 https://vless.5iehome.cf/UUID字符串,回车即可看到 vless 地址。将页面中的 V2ray 地址复制,打开 V2ray 软件,点击 服务器 选项,选择 从剪贴板导入批量URL,即可看到创建的节点。双击刚刚创建的节点,将端口(port)改为 443,传输层安全(TLS) 设置为 tls,传输协议(network) 为 ws,地址(address) 改为优选 IP。 注:6个 http 端口可任意选择:443、8443、2053、2083、2087、2096 节点测速V2ray 下测试节点速度,要选择 测试服务器真连接延迟(多选)(Ctrl+R),延迟数值为大于 0 即为导通,数值为 -1 即为不导通。IP 地址查看https://whatismyipaddress.com/https://ip.gs文中程序下载CloudFlare Workers VLESS 永久免费节点搭建https://www.aliyundrive.com/s/mXFeMtdBF2T 提取码: c1m7https://www.123pan.com/s/Oy5RVv-swXB.html 提取码: V3iX参考文章https://jdssl.top/index.php/2023/07/21/2023vpn/https://www.youtube.com/watch?v=5fvhws6ZXrMhttps://ygkkk.blogspot.com/2023/07/cfworkers-vless.htmlhttps://www.youtube.com/watch?v=9V9CQxmfwoA【END】
2023年08月06日
27 阅读
3 评论
0 点赞
2023-07-22
Badge制作指北 - 手把手教你制作Badge
本文作者:NowScott 转载地址:https://sspai.com/post/81310前前言本文并非正规指南,看完本文只能保证你可以像我一样做出各种各样的Badge,但深层原理并不会讲解,也不会教你如何在自己的服务器上渲染自己的Badge。前言标题中的Badge,是什么意思呢?你或许也见过类似这种的小图标:点击的时候还会跳转到其他的链接。不知道你是否像我第一次见到这类图标时一样,觉得这小东西蛮有意思,利用很小的一部分空间就简洁地说明了很多事。介绍 Badge最开始我以为这只是一张带着链接的图片,但是深挖之后发现,这确实是一张带着链接的图片,只不过这张图片是实时渲染出来的。事实上你所看到的大部分Badge都来自badge/shields这个项目,这是一个开源项目,提供了一个用于生成Badge的工具和服务,旨在帮助开发人员和项目所有者在GitHub项目中创建各种样式和内容的自定义徽章。(后文我就用徽章来代替Badge)这个项目的核心功能是通过生成svg格式的徽章,来展示各种项目指标和状态,这些指标可以包括构建状态、代码覆盖率、下载数量、版本号等等。同时因为这是一张图片,所以也可以用来画点其他的东西,例如:不开玩笑,这个小徽章能做的事还是非常多的,接下来正式进入教学。组成看起来徽标只是分成了左右两个部分,但实际上还有更多的内容。首先是左侧的label,一般会在label上表明这个徽章是做什么的,接着把具体要展示的数值放在右侧的message上。在Label中可以显示一个logo,并且可以调节logo的颜色,然后徽章两侧的颜色也都可以通过labelColor和color来调节,整体的风格style也可以选择一个预设。还有通过cacheSeconds参数来确定保存在缓存中的时间,可以通过延长在缓存中的时间来减小服务器的压力,也可以通过缩减在缓存中的时间来快速响应。最后在这个徽章上附上要跳转的link,整个徽章的组成就是这些了。注意:就我目前所学来看,左侧的label是可以没有的,而右侧的message是一定存在的。简单制作了解了组成之后,那么就可以开始制作了。制作徽章是通过shields.io这个网站进入的,点击Get started,就可以开始制作了。进入之后默认是到静态徽章(没有数据需要更新的徽章)的页面,入门我们先尝试做一个静态徽章。现在看到左侧这部分(今晚测试用Google Chrome不知道为啥出不来右边这部分,换到Safari就没问题了):这部分要填写的就是徽章内容,按照label、message、color这样的顺序进行填写。其中用-分隔不同的内容,用_或%20来代替空格,如果你想要输入一个真的-,那么用两个--来表示一个真的-例如:当我输入Now--Scott-Happy_birthday-red,点击Execute,就生成了如下图标:此时我们就学会的最基础的静态徽章的制作。在上一部分我说过,label是可以省略的,怎么体现呢?那就是当我只输入两个内容,例如:Happy_birthday-red,这个时候并不会因为缺少一个内容报错,而是生成如下图标:但是message和color是不可缺少的,不然就会404报错。学会了基础的制作以后,让我们来试试在基础上增加一些内容。点击这里,让我们看看其他参数的设置会带来怎样的效果。例如我做出如下的设置:得到的结果就是这样:简单制作部分到这里就结束了,其余的参数可以自行设置练习。高阶制作说成高阶制作,其实也没高阶到哪里去(自嘲)。这部分主要讲的是这个项目支持配置的内容,非常的丰富,可以通过预设,加上修改几个简单的参数,就做好一个徽章。还是回到这个网站刚进来的界面,这次看左边这一列:这其中包含了非常多的预设,我拿Analysis/GitHub top language来举例:我是需要输入两个信息:就可以得到这样一个徽标:这个页面中的预设非常多,涵盖GitHub上的很多内容,还有一些其他的内容,剩余的部分就留给大家自行探索吧,临时需要哪个找不到了可以在右上角进行检索。拓展像徽章这种类似的实时变化的图片链接其实还有很多,我也只是接触了一小部分,接下来我就给大家介绍一下这两个:第一个是这个Star History网址是:star-history通过这个网站你可以做出这样的手绘风格的图片,用来记录获得星星的历史。第二个是github-profile-summary-cards网址是:github-profile-summary-cards通过这个网站,我们可以做出这样的效果图,放在个人主页也是蛮好的。相关Markdown语法在Markdown中,直接插入图片是:而插入链接的方法是:[描述文字](网页链接)那么将两者结合起来,做为一个图片链接,就是这样:[](网页链接)没错,就是将两者结合。而且在Markdown中实际上是支持链接引用的,这可以让你的链接更加简洁易读,同时也可以在一处一起修改和设置所有被引用的链接。具体的语法如下:[引用名]:链接 "注释" [baidu]:https://www.baidu.com "baidu-url"因此,当你声明完图片链接和网页链接,就可以使用这样的形式来设置一个徽章[![描述文字][image-url]][web-url](后面两个换成对应的引用名)这样可以使你的README.md文件在代码模式下更易读,也更容易修改。同时,Markdown也是支持HTML的代码的,可以通过一些HTML代码来让你的徽章更加美观,在此就不多说了。结尾如果你通过我的文章有多学到一点知识,那我的目的就达成了。如果发现本文有什么错误可以在评论区发出来,我会虚心接受并及时改正。【END】
2023年07月22日
3 阅读
0 评论
0 点赞
2023-04-09
AI 提示词玩家
本文作者:NOISE 转载地址:https://sspai.com/post/79190导语众所周知,在AI的世界里,提示词就是和AI沟通语言的桥梁,提示关键词常用于AI对话及AI绘画等相关场景,通过准确的使用关键词,你就能更好的让AI辅助自己的工作,其中的成分重要性不言而喻,今天我们来汇总和探索一些提示词相关的网站注意:以下不含任何排名,可根据自己需要使用一、AIGC 提示词可视化编辑器这是一个旨在把 AIGC 提示词(现在支持 Midjourney)可视化并提供编辑功能的工具,主要用于AI绘画领域,有以下特性显示英文提示词的中文翻译翻译输入的中文提示词到英文(因为 Midjourney 仅支持英文提示词)为提示词进行分类(普通、样式、质量、命令)轻松的排序、隐藏提示词把提示词可视化结果导出为图片常用提示词词典通过 Notion 管理提示词词典使用地址:https://moonvy.com/apps/ops/开源:https://github.com/Moonvy/OpenPromptStudio二、ChatGPT 提示语汇集了丰富的 ChatGPT 提示语、创意写作灵感与实用技巧,包含了学术论文、创意写作、语言和翻译、技术文档等待类别使用地址:https://prompts.fresns.cn开源:https://github.com/jevantang/chatgpt-prompts三、发现、学习和测试不同的聊天 GPT 提示主界面为英文,可使用网页翻译进行查看其中包含角色扮演、ChatGpt聊天、代码编程、设计、游戏等类别提示,支持订阅查看更新使用地址:https://www.promptvibes.com四、PromptPerfect提示优化器通过可自定义的设置和直观的界面,PromptPerfect 简化了提示词优化并为我们节省了宝贵的时间。使用地址:https://promptperfect.jina.ai特征第一个自动提示词优化器第一个也是唯一一个可以自动优化任何 AI 模型提示词的提示词优化器。SOTA 人工智能模型优化 ChatGPT、GPT-3/3.5/4、DALL-E 2、Stable Diffusion 和 MidJourney 的提示。多目标优化根据您的需要自定义您的提示词优化,例如更快的优化、更短的提示词等等。10 秒内出结果在 10 秒或更短时间内获得优化的提示词。多语言提示词您的提示词不必是英文。您可以优化任何语言的提示词。API 和数据访问API 访问 AI 引擎,将您的数据导出为通用格式,允许您将 AI 集成到您自己的应用程序中五、ChatGPT ShortcutChatGPT 快捷指令,按照领域和功能分区,可对提示词进行标签筛选、关键词搜索和一键复制。特征? 简化流程:ChatGPT Shortcut 提供了快捷指令表,可以快速筛选和搜索适用于不同场景的提示词,帮助用户简化使用流程。? 提高生产力:通过使用优化过的提示词,用户可以获得更加准确、有用的回复,从而提高生产力。? 适合初学者:即使是初学者,只需复制提示词,稍加修改后发送给 ChatGPT,就能获得指定输出。? 定期更新:ChatGPT Shortcut 的提示词来自网络精选、投稿和 Awesome ChatGPT Prompts,定期进行更新,为用户提供新的提示词和思路。?? 方便中文用户:虽然提示词使用英文,但提供了中文翻译,方便中文用户理解和使用。? 开箱即用:https://ai.newzone.to开源:https://github.com/rockbenben/ChatGPT-Shortcut六、promptbase这是一个综合的AI提示市场,可以登录注册售卖自己的提示词或购买他人的包含GPT、DALL·E、Midjourney、Stable Diffusion等市场你也可以在直接在提示库中生成图像使用地址:https://promptbase.com七、提示工程指南提示工程(Prompt Engineering)是一门较新的学科,关注提示词开发和优化,帮助用户将大语言模型(Large Language Model, LLM)用于各场景和研究领域。 掌握了提示工程相关技能将有助于用户更好地了解大型语言模型的能力和局限性。研究人员可利用提示工程来提升大语言模型处理复杂任务场景的能力,如问答和算术推理能力。开发人员可通过提示工程设计、研发强大的工程技术,实现和大语言模型或其他生态工具的高效接轨。提示工程不仅仅是关于设计和研发提示词。它包含了与大语言模型交互和研发的各种技能和技术。提示工程在实现和大语言模型交互、对接,以及理解大语言模型能力方面都起着重要作用。用户可以通过提示工程来提高大语言模型的安全性,也可以赋能大语言模型,比如借助专业领域知识和外部工具来增强大语言模型能力。Web版本指南:https://www.promptingguide.ai 开源:https://github.com/yunwei37/Prompt-Engineering-Guide-zh-CN八、飞书文档chatgpt中文提示词1、由K-Render整理,主要记录chatgpt场景下的一些提示词飞书文档地址:https://qddmercny4.feishu.cn/sheets/shtcnMklYu0WsXEDUXXanrSEB2m 开源地址:https://github.com/K-Render/best-chinese-prompt2、Awesome Chinese Prompt访问地址:https://k7mi5ivd8c.feishu.cn/sheets/shtcnz8rSveP6WAMd6eDVr7e49d九、MidJourney 提示助手主界面为英文,可以使用网页翻译查看提示词工具,使人们可以轻松地直观地探索样式和复杂的 MidJourney 提示。官网https://prompt.noonshot.com十、发现DALL·E 生成的 AI 艺术和提示包含 、Midjourney、Stable Diffusion来自Openart,官网:https://openart.ai可以通过搜索或点击图片发现图片的相关提示词查看地址:https://openart.ai/discovery十一、Krea探索数百万 AI 生成的图像并创建提示集合跟上一个介绍相同,通过查看AI图片来找到提示关键词官网:https://www.krea.ai十二、DallelistDallelist 允许您使用图像和样式作为参考轻松生成提示(关键字)他们也提供与 DallE 网站集成的 chrome 扩展。官网https://www.dallelist.com/十三、ECommerce Prompt Generator用于ChatGPT 提示可帮助您快速设置商店和营销活动!可免费设置电子商务提示词主界面为英文,可使用网页翻译查看官网https://www.ecommerceprompts.com/十四、IMI Prompt 提示生成器IMI Prompt 的工作原理 IMI Prompt 使用深度学习算法分析大量文本数据,并创建一个语言模型,该模型可以根据文本上下文预测句子的下一个单词。 IMI Prompt 还可以通过使用语言模型生成连贯且适合文本上下文的单词和短语,从头开始生成文本。官网https://imiprompt.com十五、AiTuts 提示AiTuts Prompts 是 100% 免费、高质量的 Midjourney 提示库界面为英文,你可以使用网页翻译查看使用地址:https://prompts.aituts.com十六、promptoMANIA 提示生成器具有在线提示生成器的 AI 艺术社区,您也可以尝试使用其他扩散模型,例如 DALL-E 2、Disco Diffusion、WOMBO Dream 或任何扩散模型官网:https://promptomania.com提示生成:https://promptomania.com/prompt-builder十七、MidJourney AI使用的样式和关键字的参考包含页面显示比例比较、图像权重开源:https://github.com/willwulfken/MidJourney-Styles-and-Keywords-Reference十八、魔咒百科词典,AI绘画tag生成器简单易用的AI绘画tag生成器,可根据预设生成关键词使用地址:https://aitag.top十九、AI DawnmarkAI绘画词汇生成,包含中英文,涵盖Novel、Nijijourney、Midjourney、Dreamstudio、Stable Diffusion使用场景使用地址:https://ai.dawnmark.cn二十、NovelAiTag生成器主要围绕NovelAi场景使用,可以根据预设生成Tags使用地址:https://thereisnospon.github.io/NovelAiTag/二十一、NovelAI tag生成器 V2.1主要围绕NovelAi场景使用,可以根据预设生成Tags使用地址:https://wolfchen.top/tag/其它不再过多统计,如leonardo.ai本身就带有提示词及绘画生成.以上就是目前多次受用的一些网站。如果你有其它更好的提示词站点分享,欢迎随时联系我,我的主页:www.noisework.cn扩展阅读Midjourney 教程Midjourney 的教程链接:https://learningprompt.wiki/Github链接:https://github.com/thinkingjimmy/Learning-PromptMidjourney 教程的特点:不仅仅只是告诉各位 How,而是告诉各位 Why。现在 Midjourney 的很多教程都只会直接给你所谓的 prompt,就像应试教育那样,将所有东西都填给你。但你却不会教你如何创造。举个例子,各位应该看过不少 Midjourney 的 prompt 都有 HD、4K 这些词,各位不好奇,这些词是否有效的吗?如果你去翻官方社群 FAQ,你会发现,官方并不推荐使用,这些词只是安慰词。通过 11 个场景教你所需要的 17 个技巧,而且技巧不仅仅只是教你用,还教你如何学。包含 5 个常用风格词 List。【END】
2023年04月09日
2 阅读
0 评论
0 点赞
2023-04-05
Gopeed - 一款支持全平台的高速下载器
介绍Gopeed(全称 Go Speed),是一款由Golang+Flutter开发的高速下载器,开源、轻量、原生,支持 HTTP、BitTorrent、Magnet 等 协议下载,并且支持全平台使用。官方网站:https://gopeed.com/zh-CNGithub 地址:https://github.com/GopeedLab/gopeed已支持平台[x] windows[x] macos[x] linux[x] android[ ] ios[x] web[x] docker安装桌面版下载下载地址:https://github.com/GopeedLab/gopeed/releases/latest注:macos 版本运行如果提示损坏,请在终端执行 xattr -d com.apple.quarantine /Applications/Gopeed.app 命令Docker 安装直接运行docker run -d -p 9999:9999 -v /path/to/download:/download liwei2633/gopeed使用 Docker Composedocker-compose up -d访问服务当 docker 容器运行时,可以通过 http://localhost:9999 访问 web 页面。提示:在设置页面把下载路径修改为 /download 以便在宿主机访问下载完的文件。使用介绍以桌面版为例,打开软件后,点击“+”图标。 键入下载链接,支持 HTTP/HTTPS/MAGNET,也可以直接拖拽种子文件至下载链接框中 选择下载文件,选择“下载目录”,点击“下载”。 目前感觉下载速度尚可。 可以自定义 HTTP 连接数量,支持添加订阅 Tracker,支持每天自动更新。 【END】
2023年04月05日
7 阅读
0 评论
0 点赞
2023-04-01
自动切换 cdn.jsdelivr.net 域名的脚本
前言cdn.jsdelivr.net 有时候会出现国内无法访问的情况,以至于造成网站 js, css, image,字体等文件无法正常显示。 因此 BestTools 大神开发出自动检查 cdn.jsdelivr.net 是否可用的脚本, 如果不可用时,会自动把所有资源地址切换到其他可用的域名。比如:gcore.jsdelivr.net,fastly.jsdelivr.net等其他 CDN。项目地址Github 地址:https://github.com/PipecraftNet/jsdelivr-auto-fallback使用方法直接复制 index.js 或 index.min.js 里的内容,加到网站里。强烈建议添加到 head 标签最上面。所有 script 标签加上 defer 属性。如果原来有 async 属性,可以跳过。这个可以避免 pending 状态带来的等待时间,大大提升性能。如果是 hexo 生成的网站,可以安装 hexo-filter-jsdelivr-auto-fallback 插件,自动添加。示例:# 可以把 js 文件放到其他目录下进行引用 <script defer src="index.js"></script> <script defer src="index.min.js"></script>index.js 代码:((document) => { 'use strict'; let fastNode; let failed; let isRunning; const DEST_LIST = [ 'cdn.jsdelivr.net', 'fastly.jsdelivr.net', 'gcore.jsdelivr.net', 'cdn.zenless.top', 'testingcf.jsdelivr.net', 'test1.jsdelivr.net' ]; const PREFIX = '//'; const SOURCE = DEST_LIST[0]; const starTime = Date.now(); const TIMEOUT = 2000; const STORE_KEY = 'jsdelivr-auto-fallback'; const TEST_PATH = '/gh/PipecraftNet/jsdelivr-auto-fallback@main/empty.css?'; const shouldReplace = (text) => text && text.includes(PREFIX + SOURCE); const replace = (text) => text.replace(PREFIX + SOURCE, PREFIX + fastNode); const setTimeout = window.setTimeout; const $ = document.querySelectorAll.bind(document); const replaceElementSrc = () => { let element; let value; for (element of $('link[rel="stylesheet"]')) { value = element.href; if (shouldReplace(value) && !value.includes(TEST_PATH)) { element.href = replace(value); } } for (element of $('script')) { value = element.src; if (shouldReplace(value)) { const newNode = document.createElement('script'); newNode.src = replace(value); element.defer = true; element.src = ''; element.before(newNode); element.remove(); } } for (element of $('img')) { value = element.src; if (shouldReplace(value)) { // Used to cancel loading. Without this line it will remain pending status. element.src = ''; element.src = replace(value); } } // All elements that have a style attribute for (element of $('*[style]')) { value = element.getAttribute('style'); if (shouldReplace(value)) { element.setAttribute('style', replace(value)); } } for (element of $('style')) { value = element.innerHTML; if (shouldReplace(value)) { element.innerHTML = replace(value); } } }; const tryReplace = () => { if (!isRunning && failed && fastNode) { console.warn(SOURCE + ' is not available. Use ' + fastNode); isRunning = true; setTimeout(replaceElementSrc, 0); // Some need to wait for a while setTimeout(replaceElementSrc, 20); // Replace dynamically added elements setInterval(replaceElementSrc, 500); } }; const checkAvailable = (url, callback) => { let timeoutId; const newNode = document.createElement('link'); const handleResult = (isSuccess) => { if (!timeoutId) { return; } clearTimeout(timeoutId); timeoutId = 0; // Used to cancel loading. Without this line it will remain pending status. if (!isSuccess) newNode.href = 'data:text/plain;base64,'; newNode.remove(); callback(isSuccess); }; timeoutId = setTimeout(handleResult, TIMEOUT); newNode.addEventListener('error', () => handleResult(false)); newNode.addEventListener('load', () => handleResult(true)); newNode.rel = 'stylesheet'; newNode.text = 'text/css'; newNode.href = url + TEST_PATH + starTime; document.head.insertAdjacentElement('afterbegin', newNode); }; const cached = (() => { try { return Object.assign( , JSON.parse(localStorage.getItem(STORE_KEY) || '') ); } catch { return ; } })(); const main = () => { cached.time = starTime; cached.failed = false; cached.fastNode = null; for (const url of DEST_LIST) { checkAvailable('https://' + url, (isAvailable) => { // console.log(url, Date.now() - starTime, Boolean(isAvailable)); if (!isAvailable && url === SOURCE) { failed = true; cached.failed = true; } if (isAvailable && !fastNode) { fastNode = url; } if (isAvailable && !cached.fastNode) { cached.fastNode = url; } tryReplace(); }); } setTimeout(() => { // If all domains are timeout if (failed && !fastNode) { fastNode = DEST_LIST[1]; tryReplace(); } localStorage.setItem(STORE_KEY, JSON.stringify(cached)); }, TIMEOUT + 100); }; if ( cached.time && starTime - cached.time < 60 * 60 * 1000 && cached.failed && cached.fastNode ) { failed = true; fastNode = cached.fastNode; tryReplace(); setTimeout(main, 1000); } else { main(); } })(document);index.min.js 代码:(n=>for(e of v("img"))t=e.src,d(t)&&(e.src="",e.src=m(t));for(e of v("*[style]"))t=e.getAttribute("style"),d(t)&&e.setAttribute("style",m(t));for(e of v("style"))t=e.innerHTML,d(t)&&(e.innerHTML=m(t))},y=()=>,b=(()=>,JSON.parse(localStorage.getItem(c)||""))}catch}})();var h=()=>;r=u(l,o),s.addEventListener("error",()=>l(!1)),s.addEventListener("load",()=>l(!0)),s.rel="stylesheet",s.text="text/css",s.href=e+f+i,n.head.insertAdjacentElement("afterbegin",s)})("https://"+t,e=>);u(()=>,o+100)};b.time&&i-b.time<36e5&&b.failed&&b.fastNode?(s=!0,r=b.fastNode,y(),u(h,1e3)):h()})(document);用户脚本作为用户,你也可以使用油猴脚本将网站中的 cdn.jsdelivr.net 替换为可以访问的域名。浏览器安装 Tampermonkey。安装脚本: https://greasyfork.org/zh-CN/scripts/445701-jsdelivr-auto-fallbackjsdelivr 可用节点比较gcore.jsdelivr.netGcore 节点可用性高testingcf.jsdelivr.netCloudflare 节点可用性高quantil.jsdelivr.netQuantil 节点可用性尚可fastly.jsdelivr.netFastly 节点可用性尚可originfastly.jsdelivr.netFastly 节点可用性低cdn.jsdelivr.net通用节点可用性低参考文章https://www.neosey.com/archives/55.htmlhttps://iui.su/167/
2023年04月01日
5 阅读
0 评论
0 点赞
1
2
3