范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

代码Rust实现微信公众号OpenAI机器人

  Cargo.toml 配置:[package] name = "robot" version = "0.1.0" authors = ["xwb"] edition = "2021"  # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html  [dependencies] actix-web = "4.*" actix = "0.*" actix-rt = "2.*" awc = { version = "3.*", features = ["openssl"] } serde = {version = "1.*", features = ["derive"]} serde_json = "1.*" quick-xml = "0.*" hex = "0.*" sha1 = "0.*" redis = "0.*"   [profile.release] incremental = true
  main.rs 代码:use actix_web::{App, HttpMessage, HttpRequest, HttpResponse, HttpServer}; use actix_web::web::Query;  use awc::ClientBuilder; use serde::Deserialize; use serde_json; use serde_json::value::Value; use redis::{Commands, RedisResult}; use quick_xml::events::{Event, BytesCData, BytesEnd, BytesStart}; use quick_xml::reader::Reader; use quick_xml::writer::Writer; use sha1::{Sha1, Digest}; use hex;  use std::{env, str}; use std::time::SystemTime; use std::collections::HashMap; use std::io::Cursor; use std::time::Duration;   const OPENAI_COMPLETION_URL: &str = "https://api.openai.com/v1/completions"; const OPENAI_MODEL: &str = "text-davinci-003"; const OPENAI_MAX_TOKENS: u32 = 1024; const OPENAI_IMAGE_URL: &str = "https://api.openai.com/v1/images/generations"; const OPENAI_IMAGE_SIZE: &str = "512x512";  const RETRY_MSG: &str = "请给小木一点时间思考,然后再问小木一次";   fn get_env_var(k: &str, default: &str) -> String {     match env::var(k) {         Ok(v) => v,         Err(_) => default.to_owned()     } }   fn sha1(v: &str) -> String {     let mut sha = Sha1::new();     sha.update(v.as_bytes());     hex::encode(sha.finalize().as_slice()) }   fn parse_xml(body: &str) -> HashMap {     let mut reader = Reader::from_str(body);     let mut buf = Vec::new();     reader.trim_text(true);      let mut map: HashMap = HashMap::new();     let mut name = String::from("");     loop {         match reader.read_event_into(&mut buf) {             Ok(Event::Start(e)) => {                 name = str::from_utf8(e.name().as_ref()).unwrap().to_owned()             }             Ok(Event::Text(e)) => {                 let v = e.unescape().unwrap().into_owned();                 map.insert(name.clone(), v);             }             Ok(Event::CData(e)) => {                 let v = str::from_utf8(e.into_inner().into_owned().as_slice()).unwrap().to_owned();                 map.insert(name.clone(), v);             }             Ok(Event::Eof) => break,             _ => (),         }     }     map }   async fn request_openai(content: &str) -> String {     let openai_api_key = get_env_var("OPENAI_API_KEY", "");     let client = ClientBuilder::new()         .timeout(Duration::from_secs(100))         .bearer_auth(openai_api_key)         .finish();     if content.starts_with("#") {         let data = serde_json::json!({             "prompt": content,             "size": OPENAI_IMAGE_SIZE,         });         let mut rsp = client.post(OPENAI_IMAGE_URL)             .send_json(&data)             .await             .unwrap();         let body = rsp.body().await.unwrap();         let rst: HashMap = serde_json::from_str(             str::from_utf8(body.as_ref()).unwrap()         ).unwrap();         rst["data"][0]["url"].as_str().unwrap().to_owned()     } else {         let data = serde_json::json!({             "model": OPENAI_MODEL,             "prompt": content,             "max_tokens": OPENAI_MAX_TOKENS,         });         let mut rsp = client.post(OPENAI_COMPLETION_URL)             .send_json(&data)             .await             .unwrap();         let body = rsp.body().await.unwrap();         let rst: HashMap = serde_json::from_str(             str::from_utf8(body.as_ref()).unwrap()         ).unwrap();         rst["choices"][0]["text"].as_str().unwrap().to_owned()     } }    #[derive(Deserialize)] struct VerifyTokenInfo {     signature: String,     timestamp: String,     nonce: String,     echostr: String }   #[actix_web::get("/")] async fn verify_token(info: Query) -> HttpResponse {     let mut l = std::vec![         get_env_var("WX_TOKEN", ""),         info.timestamp.clone(),         info.nonce.clone()     ];     l.sort();      let msg = l.join("");     if info.signature.eq(sha1(msg.as_str()).as_str()) {         HttpResponse::Ok().body(info.echostr.clone())     } else {         HttpResponse::Forbidden().body("")     } }   #[actix_web::post("/")] async fn chat(req: HttpRequest, body: String) -> HttpResponse {     if !req.content_type().to_lowercase().ends_with("xml") {         return HttpResponse::Ok().body("");     }      let map = parse_xml(body.as_str());     if !map.get("MsgType").unwrap().eq("text") {         return HttpResponse::Ok().body("");     }      let content = map.get("Content").unwrap();     let mut cache = redis::Client::open("redis://127.0.0.1/0")         .unwrap()         .get_connection()         .unwrap();     let k = sha1(content);     let v: RedisResult = cache.get(&k);     let content_rsp: String;     match v {         Ok(d) => {             content_rsp = d;         }         Err(_) => {             let _: () = cache.set_ex(&k, RETRY_MSG, 30).unwrap();             content_rsp = request_openai(content).await;             let _: () = cache.set_ex(&k, content_rsp.as_str(), 600).unwrap();         }     }      let create_time = SystemTime::now()         .duration_since(SystemTime::UNIX_EPOCH)         .unwrap()         .as_secs()         .to_string();      let mut writer = Writer::new(Cursor::new(Vec::new()));     assert!(writer.write_event(Event::Start(BytesStart::new("xml"))).is_ok());     for (k, v) in [         ("FromUserName", map.get("ToUserName").unwrap().as_str()),         ("ToUserName", map.get("FromUserName").unwrap().as_str()),         ("CreateTime", create_time.as_str()),         ("MsgType", "text"),         ("Content", content_rsp.as_str())     ] {         assert!(writer.write_event(Event::Start(BytesStart::new(k))).is_ok());         assert!(writer.write_event(Event::CData(BytesCData::new(v))).is_ok());         assert!(writer.write_event(Event::End(BytesEnd::new(k))).is_ok());     }     assert!(writer.write_event(Event::End(BytesEnd::new("xml"))).is_ok());      let body_rsp = str::from_utf8(writer.into_inner().into_inner().as_slice()).unwrap().to_owned();     println!("{}", body_rsp);     return HttpResponse::Ok().body(body_rsp); }    #[actix_web::main] async fn main() -> std::io::Result<()> {     let workers = get_env_var("WEB_WORKERS", "4").parse::().unwrap();     let port = get_env_var("WEB_PORT", "8000").parse::().unwrap();     HttpServer::new(|| {         App::new()             .service(verify_token)             .service(chat)     })     .bind(("0.0.0.0", port))?     .workers(workers)     .run()     .await }
  start.sh 脚本:export WX_TOKEN= export WX_APP_ID= export WX_APP_SECRET=  export OPENAI_API_KEY=  export WEB_PORT=8000 export WEB_WORKERS=8   pkill -f robot nohup  ./target/release/robot &
  占用资源非常少,适合小配置机器。
  测试

体坛联播莱万哑火巴萨02拜仁,字母哥遭驱逐希腊队淘汰14日凌晨,在欧冠的赛场上,拜仁勒沃库森和法兰克福都赢球了,上一次有三支德甲球队在同一天赢下欧冠,还要追溯到25年前。在这三场比赛中,最受关注的自然是拜仁20巴萨的对决。卢卡斯和萨轿子雪山要建旅游大环线,上百公里国家步道串联10个乡镇过去,旅客到轿子雪山旅游更多的是乘坐观光缆车登顶的打卡式旅游,将来,国家步道项目将赋予雪山旅游更多的内涵。日前,环轿子山国家步道建设概念性规划项目计划在10月份招标,项目将串联山下中超山东泰山平武汉三镇9月14日,山东泰山队球员刘洋(右)在比赛中进球后。新华社记者朱峥摄当日,在山东济南举行的2022赛季中国足球协会超级联赛第18轮比赛中,山东泰山队主场以1比1战平武汉三镇队。9月欧洲杯最新夺冠赔率出炉!法国蹿升第1德国第2,戈贝尔拒绝爆冷?北京时间9月15日,2022年男篮欧洲杯结束四分之一决赛,随着法国通过加时赛93比85击败意大利,波兰爆冷90比87击败斯洛文尼亚,今年欧洲杯夺冠赔率也发生了变化。法国冲上第一,东期待!这天尤其适合拍照海报制作冯娟星空有约嫦娥与天王将在天宇邂逅天文预报显示,9月15日,月球将与天王星相遇并擦肩而过,上演月掩天王星的现象。天文科普专家表示,我国虽然看不到遮掩的过程,但感兴趣的公众1国庆节倒计时提醒怎么设置?当我们想要知道距离某一天或者某件事还有多久,一般会采用倒计时的方式。这不马上就要国庆了,想必许多小伙伴都在数着日子上班,今天分享给大家一个设置倒计时的方法,既能增加一些期待感,也方张翰,要不你还是去拍土味小视频吧一个男的,喝醉酒,进错房间,躺在了一个陌生女人的床上,当两人惊醒后,你猜他俩啥反应?男的,明明喝成了一个二百五,却一点不耽误他在第一时间审视陌生女人的三围,看对方长得挺漂亮,或许还风云战国之枭雄今晚开播,3大看点,7位实力派,有爆款潜质认真想一下,已经很久没有看过一部历史剧佳作了。前几天,和一位朋友一起看了几集大明风华,也是感慨良多,现在的历史剧大多处于一种尴尬的境地既不是历史正剧,但又比戏说多了那么一丝严谨。简相同预算,买笔记本还是台式机?光学习办公用,有必要挑台式机?如果你想买台笔记本,询问一下身边懂行的大佬来推荐推荐他多半会来一句但是笔记本真的不如台式机吗?相同预算下,买笔记本还是台式机?买台台式机,怎么挑才能不出错?光学习办公用,有必要挑台打针之后,为何感冒好的更慢了?感冒时应该如何用药?最近朋友跟小南聊起他家孩子,因为平时上班比较忙,孩子都是爷爷奶奶照顾,一有点头疼感冒爷爷奶奶就立马带着孩子去打针,结果几年下来,孩子变得弱不禁风,现在感冒了打针一般都得一个星期以上五岁时还不会说话的孩子后来怎么样了?如果有人问一个五岁时还不会说话的孩子后来怎么样了?也许人们说会成为哑巴,或者语言障碍而说话不清,或智力发育迟缓不够聪明。其实,他仍然和绝大多数普通的孩子一样,不是特别聪明,也不是很
北美世预赛美加墨前三出线哥斯达黎加附加赛PK新西兰32强产生29席北京时间3月31日上午,2022卡塔尔世界杯中北美及加勒比地区预选赛末轮的比赛结束,最终的世界杯参赛球队也全部产生,加拿大墨西哥和美国直接晋级卡塔尔世界杯,哥斯达黎加将参加附加赛,低血压只有生脉饮?清明时节还有一样补气血的好东西之前分享了治疗高血压的方子,有朋友看到后就私信我,问我低血压怎么办?那今天就和大家聊一聊低血压,也给低血压的朋友分享一些治疗经验。首先,低血压也是一种常见病,我在门诊中接触了不少低2022年苦日子要来?全球通胀下,这3件事不要做在当今社会之下,什么是苦日子?大概就是没收入有支出,还不能出去工作。简单来说,如今的疫情困扰在家待命导致的没有办法创收,其实就是苦日子。这种苦日子,可以说是在疫情时代下的产物。因为比香烟还危险!这种食物伤害全身,你竟然还天天吃大家的认知里普遍知道吸烟有害健康,要少吸烟,但还知道有一种东西比香烟还毒吗?更重要的是它是合法的!它就是糖,糖的成瘾性是可卡因的八倍,致死的可能性也有五倍之多,但糖是合法的。早在2普尔命中6个三分,拿到31分,勇士4分优势主场力克爵士北京时间2022年4月3日8时30分,NBA常规赛再战一场。勇士在主场大通中心迎接爵士的挑战。两队首发阵容勇士普尔,汤普森,格林,威金斯,卢尼爵士奥尼尔,戈贝尔,康利,波格丹诺维奇聚合支付背后的五大优势在移动支付与第三方支付业务的快速发展下,聚合支付逐渐进入人们的视野当中,可能会有人疑问,为什么要选择聚合支付呢?它能给人们带来什么?下面小白从多个角度为大家分析聚合支付的优势。1。ampampquot7连涨ampampquot来了!目前中国油价水平如何?比全球60个国家或地区还低3月31日,国内汽柴油价格每吨均提高110元,其中92号汽油95号汽油均上调0。09元。尽管涨幅并不大,但国内油价已是近期的连续第7次上调。国内油价的涨跌与国际油价密切相关。近一段中国名将打人事件持续发酵,英国法院发声零容忍!职业前景遭重创2022年斯诺克巡回锦标赛可谓是受到了极高的关注度,国内众多球迷也是熬夜在观看比赛,但是让人万万没想到的是,半决赛结束后一觉醒来赛场外的一件丑闻震惊了所有球迷。根据英媒发布的最新消耗时18个月,首个国产GPU芯片诞生,创始人曾是英伟达全球副总裁俗话说,万事开头难。只有成功实现从0到1的巨大突破,才能为以后的演进发展奠定基础,尤其是对于技术封锁和难度最高的国内半导体芯片行业而言,要想实现这一步更难。3月30日,摩尔线程正式母乳VS奶粉的差别,优势真的太明显嗨,我是小清,点击上方关注,定期为你分享育儿干货。近日,新浪微博上有一个关于婴儿喂养母乳与奶粉差别大吗的话题,引起热议。阅读量2813。9万,讨论人数1970。宝宝快要出生的时候,北京医院血管外科刁永鹏颈动脉狭窄3个高危要素,纠正不良习惯颈动脉是人体内颅内血供的一个十分关键的通道,所以颈动脉狭窄的出现对病患的健康所造成的损害相对于其他周围性动脉病变而言要更加严峻。颈动脉狭窄病患一定要争取对疾病做到早期诊断早期治疗,