File size: 14,791 Bytes
6a37520
1
{"version":3,"sources":["../src/bing-chat.ts","../src/fetch.ts"],"sourcesContent":["import crypto from 'node:crypto'\n\nimport WebSocket from 'ws'\n\nimport * as types from './types'\nimport { fetch } from './fetch'\n\nconst terminalChar = '\u001e'\n\nexport class BingChat {\n  protected _cookie: string\n  protected _debug: boolean\n\n  constructor(opts: {\n    cookie: string\n\n    /** @defaultValue `false` **/\n    debug?: boolean\n  }) {\n    const { cookie, debug = false } = opts\n\n    this._cookie = cookie\n    this._debug = !!debug\n\n    if (!this._cookie) {\n      throw new Error('Bing cookie is required')\n    }\n  }\n\n  /**\n   * Sends a message to Bing Chat, waits for the response to resolve, and returns\n   * the response.\n   *\n   * If you want to receive a stream of partial responses, use `opts.onProgress`.\n   *\n   * @param message - The prompt message to send\n   * @param opts.conversationId - Optional ID of a conversation to continue (defaults to a random UUID)\n   * @param opts.onProgress - Optional callback which will be invoked every time the partial response is updated\n   *\n   * @returns The response from Bing Chat\n   */\n  async sendMessage(\n    text: string,\n    opts: types.SendMessageOptions = {}\n  ): Promise<types.ChatMessage> {\n    const {\n      invocationId = '1',\n      onProgress,\n      locale = 'en-US',\n      market = 'en-US',\n      region = 'US',\n      location,\n      messageType = 'Chat',\n      variant = 'Balanced'\n    } = opts\n\n    let { conversationId, clientId, conversationSignature } = opts\n    const isStartOfSession = !(\n      conversationId &&\n      clientId &&\n      conversationSignature\n    )\n\n    if (isStartOfSession) {\n      const conversation = await this.createConversation()\n      conversationId = conversation.conversationId\n      clientId = conversation.clientId\n      conversationSignature = conversation.conversationSignature\n    }\n\n    const result: types.ChatMessage = {\n      author: 'bot',\n      id: crypto.randomUUID(),\n      conversationId,\n      clientId,\n      conversationSignature,\n      invocationId: `${parseInt(invocationId, 10) + 1}`,\n      text: ''\n    }\n\n    const responseP = new Promise<types.ChatMessage>(\n      async (resolve, reject) => {\n        const chatWebsocketUrl = 'wss://sydney.bing.com/sydney/ChatHub'\n        const ws = new WebSocket(chatWebsocketUrl, {\n          perMessageDeflate: false,\n          headers: {\n            'accept-language': 'en-US,en;q=0.9',\n            'cache-control': 'no-cache',\n            pragma: 'no-cache'\n          }\n        })\n\n        let isFulfilled = false\n\n        function cleanup() {\n          ws.close()\n          ws.removeAllListeners()\n        }\n\n        ws.on('error', (error) => {\n          console.warn('WebSocket error:', error)\n          cleanup()\n          if (!isFulfilled) {\n            isFulfilled = true\n            reject(new Error(`WebSocket error: ${error.toString()}`))\n          }\n        })\n        ws.on('close', () => {\n          // TODO\n        })\n\n        ws.on('open', () => {\n          ws.send(`{\"protocol\":\"json\",\"version\":1}${terminalChar}`)\n        })\n        let stage = 0\n\n        ws.on('message', (data) => {\n          const objects = data.toString().split(terminalChar)\n\n          const messages = objects\n            .map((object) => {\n              try {\n                return JSON.parse(object)\n              } catch (error) {\n                return object\n              }\n            })\n            .filter(Boolean)\n\n          if (!messages.length) {\n            return\n          }\n\n          if (stage === 0) {\n            ws.send(`{\"type\":6}${terminalChar}`)\n\n            const traceId = crypto.randomBytes(16).toString('hex')\n\n            // example location: 'lat:47.639557;long:-122.128159;re=1000m;'\n            const locationStr = location\n              ? `lat:${location.lat};long:${location.lng};re=${\n                  location.re || '1000m'\n                };`\n              : undefined\n\n            // Sets the correct options for the variant of the model\n            const optionsSets = [\n              'nlu_direct_response_filter',\n              'deepleo',\n              'enable_debug_commands',\n              'disable_emoji_spoken_text',\n              'responsible_ai_policy_235',\n              'enablemm'\n            ]\n            if (variant == 'Balanced') {\n              optionsSets.push('galileo')\n            } else {\n              optionsSets.push('clgalileo')\n              if (variant == 'Creative') {\n                optionsSets.push('h3imaginative')\n              } else if (variant == 'Precise') {\n                optionsSets.push('h3precise')\n              }\n            }\n            const params = {\n              arguments: [\n                {\n                  source: 'cib',\n                  optionsSets,\n                  allowedMessageTypes: [\n                    'Chat',\n                    'InternalSearchQuery',\n                    'InternalSearchResult',\n                    'InternalLoaderMessage',\n                    'RenderCardRequest',\n                    'AdsQuery',\n                    'SemanticSerp'\n                  ],\n                  sliceIds: [],\n                  traceId,\n                  isStartOfSession,\n                  message: {\n                    locale,\n                    market,\n                    region,\n                    location: locationStr,\n                    author: 'user',\n                    inputMethod: 'Keyboard',\n                    messageType,\n                    text\n                  },\n                  conversationSignature,\n                  participant: { id: clientId },\n                  conversationId\n                }\n              ],\n              invocationId,\n              target: 'chat',\n              type: 4\n            }\n\n            if (this._debug) {\n              console.log(chatWebsocketUrl, JSON.stringify(params, null, 2))\n            }\n\n            ws.send(`${JSON.stringify(params)}${terminalChar}`)\n\n            ++stage\n            return\n          }\n\n          for (const message of messages) {\n            // console.log(JSON.stringify(message, null, 2))\n\n            if (message.type === 1) {\n              const update = message as types.ChatUpdate\n              const msg = update.arguments[0].messages?.[0]\n\n              if (!msg) continue\n\n              // console.log('RESPONSE0', JSON.stringify(update, null, 2))\n\n              if (!msg.messageType) {\n                result.author = msg.author\n                result.text = msg.text\n                result.detail = msg\n\n                onProgress?.(result)\n              }\n            } else if (message.type === 2) {\n              const response = message as types.ChatUpdateCompleteResponse\n              if (this._debug) {\n                console.log('RESPONSE', JSON.stringify(response, null, 2))\n              }\n              const validMessages = response.item.messages?.filter(\n                (m) => !m.messageType\n              )\n              const lastMessage = validMessages?.[validMessages?.length - 1]\n\n              if (lastMessage) {\n                result.conversationId = response.item.conversationId\n                result.conversationExpiryTime =\n                  response.item.conversationExpiryTime\n\n                result.author = lastMessage.author\n                result.text = lastMessage.text\n                result.detail = lastMessage\n\n                if (!isFulfilled) {\n                  isFulfilled = true\n                  resolve(result)\n                }\n              }\n            } else if (message.type === 3) {\n              if (!isFulfilled) {\n                isFulfilled = true\n                resolve(result)\n              }\n\n              cleanup()\n              return\n            } else {\n              // TODO: handle other message types\n              // these may be for displaying \"adaptive cards\"\n              // console.warn('unexpected message type', message.type, message)\n            }\n          }\n        })\n      }\n    )\n\n    return responseP\n  }\n\n  async createConversation(): Promise<types.ConversationResult> {\n    const requestId = crypto.randomUUID()\n\n    const cookie = this._cookie.includes(';')\n      ? this._cookie\n      : `_U=${this._cookie}`\n\n    return fetch('https://www.bing.com/turing/conversation/create', {\n      headers: {\n        accept: 'application/json',\n        'accept-language': 'en-US,en;q=0.9',\n        'content-type': 'application/json',\n        'sec-ch-ua':\n          '\"Not_A Brand\";v=\"99\", \"Microsoft Edge\";v=\"109\", \"Chromium\";v=\"109\"',\n        'sec-ch-ua-arch': '\"x86\"',\n        'sec-ch-ua-bitness': '\"64\"',\n        'sec-ch-ua-full-version': '\"109.0.1518.78\"',\n        'sec-ch-ua-full-version-list':\n          '\"Not_A Brand\";v=\"99.0.0.0\", \"Microsoft Edge\";v=\"109.0.1518.78\", \"Chromium\";v=\"109.0.5414.120\"',\n        'sec-ch-ua-mobile': '?0',\n        'sec-ch-ua-model': '',\n        'sec-ch-ua-platform': '\"macOS\"',\n        'sec-ch-ua-platform-version': '\"12.6.0\"',\n        'sec-fetch-dest': 'empty',\n        'sec-fetch-mode': 'cors',\n        'sec-fetch-site': 'same-origin',\n        'x-edge-shopping-flag': '1',\n        'x-ms-client-request-id': requestId,\n        'x-ms-useragent':\n          'azsdk-js-api-client-factory/1.0.0-beta.1 core-rest-pipeline/1.10.0 OS/MacIntel',\n        cookie\n      },\n      referrer: 'https://www.bing.com/search',\n      referrerPolicy: 'origin-when-cross-origin',\n      body: null,\n      method: 'GET',\n      mode: 'cors',\n      credentials: 'include'\n    }).then((res) => {\n      if (res.ok) {\n        return res.json()\n      } else {\n        throw new Error(\n          `unexpected HTTP error createConversation ${res.status}: ${res.statusText}`\n        )\n      }\n    })\n  }\n}\n","/// <reference lib=\"dom\" />\n\nconst fetch = globalThis.fetch\n\nif (typeof fetch !== 'function') {\n  throw new Error('Invalid environment: global fetch not defined')\n}\n\nexport { fetch }\n"],"mappings":"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","names":[]}