最近这个ChatGPT很火啊,看了B站上很多视频,自己非常手痒,高低自己得整一个啊,但是让我很难受的是,翻遍了github前十页,竟然没有一个C#的ChatGPT项目,我好难受啊!那能怎么办?自己搞一个吧。

但是,等等,现在的ChatGPT项目基本都是网页逆向获取几个token,我不会啊,我都不知道哪些cookie是重要的,那我只能找一个其它语言的ChatGPT API项目,自己造(翻译)一个c#的ChatGPT API库了。

ChatGPT是啥?

先了解GTP,摘自官网的一段话“A set of models that can understand and generate natural language”,其实,就是一个自然语言处理的模型。所以ChatGPT顾名思义就是基于GPT3的一个聊天AI。

但是想让他做苦力帮忙写代码的话所需要的其实不是这个模型,而是Codex这个模型,按照官网所说“Most capable Codex model. Particularly good at translating natural language to code. In addition to completing code, also supports inserting completions within code.”。真的是碉堡了!

准备

第一步:注册OpenAI账号

因为对大陆以及中国香港地区不开放,所以我们需要小小的科学一下。

来一个我就是参照着申请账号的园子的文章:

https://www.cnblogs.com/chatgpt/p/how-to-register-chatgpt-in-china.html

当然手机那里可以淘宝找,几块钱就能帮忙注册手机,账号里面默认还有18美元余额。

注意点:如果搭建了科学还是提示不对你的国家提供服务的话,尝试清空浏览器缓存或者打开浏览器的无痕窗口。Chrome默认在右上角三个点打开就能找到“打开新的无痕式窗口”。

万事具备,直接撸代码

1.网页获取所需token和cookie

我们需要三个东西:UserAgent,CfClearance,Session_token

我们需要先打开ChatGPT官方网站:https://chat.openai.com/chat 然后按下F12打开浏览器的开发者模式

UserAgent在网络里(只需要复制UserAgent:后面的值):



CfClearance和Session_token在应用程序->cookie里面

2.创建一个session用来表示一个会话

public OpenAISession(string session_token,string cfClearance,string userAgent)
{
Session_token = session_token;
CfClearance = cfClearance;
UserAgent = userAgent;
Headers = new Dictionary<string, string>();
Cookies = new Dictionary<string, string>();
Proxies = new Dictionary<string, string>();
}

Session可以刷新自己的AccessToken和Session_token

public async Task RefreshSessionAsync()
{
if (string.IsNullOrEmpty(Session_token))
{
throw new Exception("No tokens provided");
} // Set cookies
Cookies.Put("__Secure-next-auth.session-token", Session_token);
Cookies.Put("cf_clearance", CfClearance);
string cookiesString = GetCookiesString();
Dictionary<string, string> map = new Dictionary<string, string>();
Headers.Put("User-Agent", UserAgent);
Headers.Put("cookie", cookiesString);
Headers.Put("Cookie", cookiesString); var response = await GetAsync("https://chat.openai.com/api/auth/session");
if (!response.IsSuccessStatusCode)
{
Console.WriteLine("err code: " + response.StatusCode);
Console.WriteLine("cf_clearance: " + CfClearance);
Console.WriteLine("token: " + Session_token);
Console.WriteLine("userAgent: " + UserAgent);
Console.WriteLine("请检查以上参数是否正确,是否过期。"); throw new Exception("无法获取token!请重试");
} try
{
string name = "__Secure-next-auth.session-token=";
var cookies = response.Headers.GetValues("Set-Cookie");
var stoken = cookies.FirstOrDefault(x => x.StartsWith(name));
Session_token = stoken == null ? Session_token : stoken.Substring(name.Length, stoken.IndexOf(";") - name.Length);
Cookies.Put("__Secure-next-auth.session-token", Session_token);
var result = await response.Content.ReadAsStringAsync();
AccessToken = JsonSerializer.Deserialize<Profile>(result, _jsonSerializerOptions)?.AccessToken;
RefreshHeaders();
}
catch (Exception ex)
{
Console.WriteLine($"Error {nameof(RefreshSessionAsync)}:{ex}");
throw new Exception($"Error {nameof(RefreshSessionAsync)}", ex);
}
}

获取到的最新的AccessToken更新到header里,Session_token更新到cookie里

private void RefreshHeaders()
{
Headers.Put("Host", "chat.openai.com");
Headers.Put("Accept", "text/event-stream");
Headers.Put("Authorization", $"Bearer {AccessToken}");
Headers.Put("User-Agent", UserAgent);
Headers.Put("X-Openai-Assistant-App-Id", string.Empty);
Headers.Put("Connection", "close");
Headers.Put("Accept-Language", "en-US,en;q=0.9");
Headers.Put("Referer", "https://chat.openai.com/chat");
} string name = "__Secure-next-auth.session-token=";
var cookies = response.Headers.GetValues("Set-Cookie");
var stoken = cookies.FirstOrDefault(x => x.StartsWith(name));
Session_token = stoken == null ? Session_token : stoken.Substring(name.Length, stoken.IndexOf(";") - name.Length);
Cookies.Put("__Secure-next-auth.session-token", Session_token);

3.创建机器人绑定一个会话

public Chatbot(OpenAISession openAISession)
{
OpenAISession = openAISession;
ResetConversation();
} /// <summary>
/// 重置Conversation,开启一个新的会话
/// </summary>
public void ResetConversation()
{
_conversationId = null;
_parentMessageId = Guid.NewGuid().ToString();
}

如何保持上下文聊天以及多用户隔离?

上下文聊天已经内置,会自动与机器人的回复进行关联,当需要重新开启一个对话的时候,可以调用ResetConversation,这样对应的机器人对象发过去就是开启了一个新的对话。

如何多用户隔离?比如联合微信机器人使用,可以将一个人或者一个群对应一个Chatbot对象,并且利用id与机器人做关联。

当然这个_clientID需要使用者做唯一性管理

//可以作为隔离不同客户端聊天上下文的凭据,即一个机器人绑定一个客户端
//客户端编号可以是,微信id,qq id,飞书id,亦或者自己开发的软件的用户id
private readonly string _clientID; public Chatbot(OpenAISession openAISession, string clientID)
{
OpenAISession = openAISession;
_clientID = clientID == null ? Guid.NewGuid().ToString() : clientID;
ResetConversation();
}

最后构造对话对象,发送到对应api

public async Task<Reply> GetChatReplyAsync(string prompt)
{
var conversation = new Conversation();
conversation.Conversation_id = _conversationId;
conversation.Parent_message_id = _parentMessageId;
conversation.Messages = new Message[]
{
new Message()
{
Content = new Content
{
Parts = new string []{ prompt }
}
}
}; return await GetChatResponseAsync(conversation);
} /// <summary>
/// 获取响应
/// </summary>
/// <param name="conversation"></param>
/// <returns></returns>
/// <exception cref="Exception">服务器返回非200</exception>
private async Task<Reply> GetChatResponseAsync(Conversation conversation)
{
using (var client = new HttpClient())
{
var response = await OpenAISession.PostAsync(_conversation, JsonSerializer.Serialize(conversation, _jsonSerializerOptions));
if (!response.IsSuccessStatusCode)
{
throw new Exception($"Faild to request.StatusCode:{response.StatusCode}");
}
var msg = await response.Content.ReadAsStringAsync();
var data = msg.Split("\n")?.ToList().Where(x => !string.IsNullOrEmpty(x) && !x.Contains("data: [DONE]")).LastOrDefault()?.Substring(5);
var reply = JsonSerializer.Deserialize<Reply>(data, _jsonSerializerOptions);
_conversationId = reply.Conversation_id; return reply;
}
}

效果截图(仅测试)

OpenAISession openAIOptions = new OpenAISession(Session_token, CfClearance, UserAgent);
await openAIOptions.RefreshSessionAsync();
Chatbot chatbot = new Chatbot(openAIOptions);
Console.WriteLine("用c++写个冒泡查询");
var reply = await chatbot.GetChatReplyAsync("用c++写个冒泡查询");
Console.WriteLine(reply.Message.Content.Parts.FirstOrDefault());

源码(欢迎star)

https://github.com/BruceQiu1996/NChatGPTRev

我做了第一个ChatGPT .net api聊天库的更多相关文章

  1. 我用开天平台做了一个字符串检查API,hin 简单~~

    摘要:本文使用了工作台的API全生命周期管理和函数管理功能,编写字符串检查的函数,实现了API的快速创建和发布. 本文分享自华为云社区<[我用开天平台做了一个字符串检查API>,作者:人类 ...

  2. 如何设计一个优秀的API(转载)

    最近在整理框架的一些 API,觉得很有必要总结一下 API 兼容性的设计.下图是我自己当下的一些总结,慢慢维护: 网上搜索了一下,一个多月前,“标点符”已经发布了下面这篇文章,觉得写得非常不错,转载于 ...

  3. 如何设计一个优秀的API(转)

    到目前为止,已经负责API接近两年了,这两年中发现现有的API存在的问题越来越多,但很多API一旦发布后就不再能修改了,即时升级和维护是必须的.一旦API发生变化,就可能对相关的调用者带来巨大的代价, ...

  4. 并发编程 —— 自己写一个异步回调 API

    1. 前言 在并发编程中,异步回调的效率不言而喻,在业务开发中,如果由阻塞的任务需要执行,必然要使用异步线程.并且,如果我们想在异步执行之后,根据他的结果执行一些动作. JDK 8 之前的 Futur ...

  5. 如何设计一个优秀的API

    如何设计一个优秀的API - 文章 - 伯乐在线 http://blog.jobbole.com/42317/ 如何设计一个优秀的API - 标点符 https://www.biaodianfu.co ...

  6. 我的第一个netcore2.2 api项目搭建(三)续

    上一章快速陈述了自定义验证功能添加的过程,我的第一个netcore2.2 api项目搭建(三) 但是并没有真正的去实现,这一章将要实现验证功能的添加. 这一章实现目标三:jwt认证授权添加 在netc ...

  7. 我的第一个netcore2.2 api项目搭建(三)

    上一章快速添加了swagger文档管理功能,我的第一个netcore2.2 api项目搭建(二) 这一章实现目标三:api添加身份验证功能 在实现该目标之前,先得理解netcore运行机制. 这是微软 ...

  8. 一个扩展搜索API的优化过程

    概述 API 是一个服务的门面,就像衣装是人的形象一样. 优雅的 API 设计,能让业务方使用起来倍儿爽,提升开发效率,降低维护成本:糟糕的 API 设计,则让业务方遭心,陷入混沌. 本文将展示一个扩 ...

  9. 如何使用 Gin 和 Gorm 搭建一个简单的 API 服务 (一)

    介绍   Go 语言最近十分火热,但对于新手来说,想立马上手全新的语法和各种各样的框架还是有点难度的.即使是基础学习也很有挺有挑战性.   在这篇文章中,我想用最少的代码写出一个可用的 API 服务. ...

  10. 通过beego快速创建一个Restful风格API项目及API文档自动化

    通过beego快速创建一个Restful风格API项目及API文档自动化 本文演示如何快速(一分钟内,不写一行代码)的根据数据库及表创建一个Restful风格的API项目,及提供便于在线测试API的界 ...

随机推荐

  1. H5与APP的交互框架(WebViewJavascriptBridge)

    基本原理是: 把 OC 的方法注册到桥梁中,让 JS 去调用. 把 JS 的方法注册在桥梁中,让 OC 去调用.(注册自己,调用它人.) WebViewJavaScriptBridge 使用的基本步骤 ...

  2. 洛谷P2367 语文成绩(差分)

    标准的差分应用题,不要想的太复杂,写成了线段树. 1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=5e6+10 ...

  3. Docker | 镜像浅析,以及制作自己的镜像

    分层下载,docker image 的核心: 联合文件系统 镜像是什么 镜像就是一个轻量的.独立的软件包.用来打包运行环境和基于运行环境开发的软件.它包含软件运行所需的所有内容(包括代码.运行时.库. ...

  4. JavaFx 使用字体图标记录

    原文:JavaFx 使用字体图标记录 - Stars-One的杂货小窝 之前其实也是研究过关于字体图标的使用,还整了个库Tornadofx学习笔记(4)--IconTextFx开源库,整合5000+个 ...

  5. C语言实现扫雷游戏(完整版)

    头文件定义.函数声明 下面就是扫雷中使用到的所有函数,为了省事我把所有的代码都放在一个C文件中实现 宏定义中设置了游戏的界面布局,以及设置地雷的个数(这里默认的是10个地雷),界面是一个9*9的方格布 ...

  6. [Err] 1052 - Column ‘roleId‘ in where clause is ambiguous

    1.先看错误的sql语句: select a.authName from roles as r,authority as a,role_ah as ra where ra.roleId=r.roleI ...

  7. HTML基础知识(1)常用标签的使用 h、p、img、meta、a、iframe...

    文章目录 1.html简介 2.html注释 3.标签的属性 3.1 代码 3.2 测试结果 4.常用的标签 4.1 代码 4.2 测试结果 5.实体 5.1 代码 5.2 测试结果 6.图片引入 6 ...

  8. Tesla-E380,4K eDP一键点屏神器问世

    eDP屏快速点亮,EDID回读, eDP屏调试 是否为点屏的准备工作感到烦躁: 1)查找LCD模组的数据手册(常常还未必能找着) 2)在上位机软件或者单片机程序里设置一大堆的LCD屏参,这个频率,那个 ...

  9. 27.路由器Routers

    像一些reils这样的web框架提供自动生成urls的功能,但是Django没有 rest framework为django添加了这一功能,以一种简单.快速.一致的方式 routers必须配合view ...

  10. Optimize(优化实验)

    十大优化法则 1.更快(本课程重点!) 2.更省(存储空间.运行空间) 3.更美(UI 交互) 4.更正确(本课程重点!各种条件下) 5.更可靠 6.可移植 7.更强大(功能) 8.更方便(使用) 9 ...