refactor: prompts sync

This commit is contained in:
lencx
2022-12-31 17:59:55 +08:00
parent 9a392a71f6
commit e1f8030009
11 changed files with 185 additions and 140 deletions

View File

@@ -25,7 +25,7 @@ thiserror = "1.0.38"
walkdir = "2.3.2" walkdir = "2.3.2"
regex = "1.7.0" regex = "1.7.0"
tokio = { version = "1.23.0", features = ["macros"] } tokio = { version = "1.23.0", features = ["macros"] }
# reqwest = "0.11.13" reqwest = "0.11.13"
[dependencies.tauri-plugin-log] [dependencies.tauri-plugin-log]
git = "https://github.com/tauri-apps/tauri-plugin-log" git = "https://github.com/tauri-apps/tauri-plugin-log"

View File

@@ -1,7 +1,8 @@
use crate::{ use crate::{
conf::ChatConfJson, conf::{ChatConfJson, GITHUB_PROMPTS_CSV_URL},
utils::{self, exists}, utils::{self, exists},
}; };
use log::info;
use std::{collections::HashMap, fs, path::PathBuf}; use std::{collections::HashMap, fs, path::PathBuf};
use tauri::{api, command, AppHandle, Manager}; use tauri::{api, command, AppHandle, Manager};
@@ -134,75 +135,130 @@ pub fn cmd_list() -> Vec<ModelRecord> {
} }
#[command] #[command]
pub fn sync_prompts(app: AppHandle, data: String, time: u64) { pub async fn sync_prompts(app: AppHandle, time: u64) -> Option<Vec<ModelRecord>> {
let data = parse_prompt(data) let res = utils::get_data(GITHUB_PROMPTS_CSV_URL, Some(&app))
.iter() .await
.map(move |i| ModelRecord { .unwrap();
cmd: if i.cmd.is_some() {
i.cmd.clone().unwrap()
} else {
utils::gen_cmd(i.act.clone())
},
act: i.act.clone(),
prompt: i.prompt.clone(),
tags: vec!["chatgpt-prompts".to_string()],
enable: true,
})
.collect::<Vec<ModelRecord>>();
let model = chat_root().join("chat.model.json"); if let Some(v) = res {
let model_cmd = chat_root().join("chat.model.cmd.json"); let data = parse_prompt(v)
let chatgpt_prompts = chat_root().join("cache_model").join("chatgpt_prompts.json"); .iter()
.map(move |i| ModelRecord {
if !exists(&model) { cmd: if i.cmd.is_some() {
fs::write( i.cmd.clone().unwrap()
&model, } else {
serde_json::json!({ utils::gen_cmd(i.act.clone())
"name": "ChatGPT Model", },
"link": "https://github.com/lencx/ChatGPT" act: i.act.clone(),
prompt: i.prompt.clone(),
tags: vec!["chatgpt-prompts".to_string()],
enable: true,
}) })
.to_string(), .collect::<Vec<ModelRecord>>();
let data2 = data.clone();
let model = chat_root().join("chat.model.json");
let model_cmd = chat_root().join("chat.model.cmd.json");
let chatgpt_prompts = chat_root().join("cache_model").join("chatgpt_prompts.json");
if !exists(&model) {
fs::write(
&model,
serde_json::json!({
"name": "ChatGPT Model",
"link": "https://github.com/lencx/ChatGPT"
})
.to_string(),
)
.unwrap();
}
// chatgpt_prompts.json
fs::write(
chatgpt_prompts,
serde_json::to_string_pretty(&data).unwrap(),
) )
.unwrap(); .unwrap();
let cmd_data = cmd_list();
// chat.model.cmd.json
fs::write(
model_cmd,
serde_json::to_string_pretty(&serde_json::json!({
"name": "ChatGPT CMD",
"last_updated": time,
"data": cmd_data,
}))
.unwrap(),
)
.unwrap();
let mut kv = HashMap::new();
kv.insert(
"sync_prompts".to_string(),
serde_json::json!({ "id": "chatgpt_prompts", "last_updated": time }),
);
let model_data = utils::merge(
&serde_json::from_str(&fs::read_to_string(&model).unwrap()).unwrap(),
&kv,
);
// chat.model.json
fs::write(model, serde_json::to_string_pretty(&model_data).unwrap()).unwrap();
// refresh window
api::dialog::message(
app.get_window("core").as_ref(),
"Sync Prompts",
"ChatGPT Prompts data has been synchronized!",
);
window_reload(app, "core");
return Some(data2);
} }
// chatgpt_prompts.json None
fs::write( }
chatgpt_prompts,
serde_json::to_string_pretty(&data).unwrap(), #[command]
) pub async fn sync_user_prompts(url: String, data_type: String) -> Option<Vec<ModelRecord>> {
.unwrap(); let res = utils::get_data(&url, None).await.unwrap();
let cmd_data = cmd_list();
info!("chatgpt_http_url: {}", url);
// chat.model.cmd.json
fs::write( if let Some(v) = res {
model_cmd, let data;
serde_json::to_string_pretty(&serde_json::json!({ if data_type == "csv" {
"name": "ChatGPT CMD", info!("chatgpt_http_csv_parser");
"last_updated": time, data = parse_prompt(v);
"data": cmd_data, } else if data_type == "json" {
})) info!("chatgpt_http_json_parser");
.unwrap(), data = serde_json::from_str(&v).unwrap_or_else(|err| {
) info!("chatgpt_http_json_parser_error: {}", err);
.unwrap(); vec![]
let mut kv = HashMap::new(); });
kv.insert( } else {
"sync_prompts".to_string(), info!("chatgpt_http_unknown_type");
serde_json::json!({ "id": "chatgpt_prompts", "last_updated": time }), data = vec![];
); }
let model_data = utils::merge(
&serde_json::from_str(&fs::read_to_string(&model).unwrap()).unwrap(), let data = data
&kv, .iter()
); .map(move |i| ModelRecord {
cmd: if i.cmd.is_some() {
// chat.model.json i.cmd.clone().unwrap()
fs::write(model, serde_json::to_string_pretty(&model_data).unwrap()).unwrap(); } else {
utils::gen_cmd(i.act.clone())
// refresh window },
api::dialog::message( act: i.act.clone(),
app.get_window("core").as_ref(), prompt: i.prompt.clone(),
"Sync Prompts", tags: vec!["user-sync".to_string()],
"ChatGPT Prompts data has been synchronized!", enable: true,
); })
window_reload(app, "core"); .collect::<Vec<ModelRecord>>();
return Some(data);
}
None
} }

View File

@@ -17,18 +17,20 @@ pub fn init(app: &mut App) -> std::result::Result<(), Box<dyn std::error::Error>
info!("global_shortcut"); info!("global_shortcut");
let handle = app.app_handle(); let handle = app.app_handle();
let mut shortcut = app.global_shortcut_manager(); let mut shortcut = app.global_shortcut_manager();
shortcut.register(&v, move|| { shortcut
if let Some(w) = handle.get_window("core") { .register(&v, move || {
if w.is_visible().unwrap() { if let Some(w) = handle.get_window("core") {
w.hide().unwrap(); if w.is_visible().unwrap() {
} else { w.hide().unwrap();
w.show().unwrap(); } else {
w.set_focus().unwrap(); w.show().unwrap();
w.set_focus().unwrap();
}
} }
} })
}).unwrap_or_else(|err| { .unwrap_or_else(|err| {
info!("global_shortcut_register_error: {}", err); info!("global_shortcut_register_error: {}", err);
}); });
} else { } else {
info!("global_shortcut_unregister"); info!("global_shortcut_unregister");
}; };

View File

@@ -88,21 +88,7 @@ async function init() {
}); });
window.__sync_prompts = async function() { window.__sync_prompts = async function() {
const res = await fetch('https://raw.githubusercontent.com/f/awesome-chatgpt-prompts/main/prompts.csv'); await invoke('sync_prompts', { time: Date.now() });
if (res.ok) {
const data = await res.text();
await invoke('sync_prompts', { data, time: Date.now() });
} else {
invoke('messageDialog', {
__tauriModule: 'Dialog',
message: {
cmd: 'messageDialog',
message: 'ChatGPT Prompts data sync failed, please try again!'.toString(),
title: 'Sync Prompts'.toString(),
type: 'error'
}
})
}
} }
} }

View File

@@ -14,6 +14,8 @@ use tauri::TitleBarStyle;
pub const ISSUES_URL: &str = "https://github.com/lencx/ChatGPT/issues"; pub const ISSUES_URL: &str = "https://github.com/lencx/ChatGPT/issues";
pub const UPDATE_LOG_URL: &str = "https://github.com/lencx/ChatGPT/blob/main/UPDATE_LOG.md"; pub const UPDATE_LOG_URL: &str = "https://github.com/lencx/ChatGPT/blob/main/UPDATE_LOG.md";
pub const AWESOME_URL: &str = "https://github.com/lencx/ChatGPT/blob/main/AWESOME.md"; pub const AWESOME_URL: &str = "https://github.com/lencx/ChatGPT/blob/main/AWESOME.md";
pub const GITHUB_PROMPTS_CSV_URL: &str =
"https://raw.githubusercontent.com/f/awesome-chatgpt-prompts/main/prompts.csv";
pub const DEFAULT_CHAT_CONF: &str = r#"{ pub const DEFAULT_CHAT_CONF: &str = r#"{
"stay_on_top": false, "stay_on_top": false,
"theme": "Light", "theme": "Light",

View File

@@ -63,6 +63,7 @@ async fn main() {
cmd::get_chat_model_cmd, cmd::get_chat_model_cmd,
cmd::parse_prompt, cmd::parse_prompt,
cmd::sync_prompts, cmd::sync_prompts,
cmd::sync_user_prompts,
cmd::window_reload, cmd::window_reload,
cmd::cmd_list, cmd::cmd_list,
fs_extra::metadata, fs_extra::metadata,

View File

@@ -109,3 +109,22 @@ pub fn gen_cmd(name: String) -> String {
let re = Regex::new(r"[^a-zA-Z0-9]").unwrap(); let re = Regex::new(r"[^a-zA-Z0-9]").unwrap();
re.replace_all(&name, "_").to_lowercase() re.replace_all(&name, "_").to_lowercase()
} }
pub async fn get_data(
url: &str,
app: Option<&tauri::AppHandle>,
) -> Result<Option<String>, reqwest::Error> {
let res = reqwest::get(url).await?;
let is_ok = res.status() == 200;
let body = res.text().await?;
if is_ok {
Ok(Some(body))
} else {
info!("chatgpt_http_error: {}", body);
if let Some(v) = app {
tauri::api::dialog::message(v.get_window("core").as_ref(), "ChatGPT HTTP", body);
}
Ok(None)
}
}

View File

@@ -15,13 +15,6 @@
"globalShortcut": { "globalShortcut": {
"all": true "all": true
}, },
"http": {
"all": true,
"scope": [
"https://**",
"http://**"
]
},
"fs": { "fs": {
"all": true, "all": true,
"scope": [ "scope": [

View File

@@ -1,6 +1,6 @@
import { useState, useRef, useEffect } from 'react'; import { useState, useRef, useEffect } from 'react';
import { Table, Modal, Button, message } from 'antd'; import { Table, Modal, Button, message } from 'antd';
import { invoke, http, path, fs } from '@tauri-apps/api'; import { invoke, path, fs } from '@tauri-apps/api';
import useData from '@/hooks/useData'; import useData from '@/hooks/useData';
import useChatModel, { useCacheModel } from '@/hooks/useChatModel'; import useChatModel, { useCacheModel } from '@/hooks/useChatModel';
@@ -10,7 +10,7 @@ import { CHAT_MODEL_JSON, chatRoot, readJSON, genCmd } from '@/utils';
import { syncColumns, getPath } from './config'; import { syncColumns, getPath } from './config';
import SyncForm from './Form'; import SyncForm from './Form';
const setTag = (data: Record<string, any>[]) => data.map((i) => ({ ...i, tags: ['user-sync'], enable: true })) const fmtData = (data: Record<string, any>[] = []) => (Array.isArray(data) ? data : []).map((i) => ({ ...i, cmd: i.cmd ? i.cmd : genCmd(i.act), tags: ['user-sync'], enable: true }));
export default function SyncCustom() { export default function SyncCustom() {
const [isVisible, setVisible] = useState(false); const [isVisible, setVisible] = useState(false);
@@ -44,9 +44,16 @@ export default function SyncCustom() {
setVisible(true); setVisible(true);
} }
if (['delete'].includes(opInfo.opType)) { if (['delete'].includes(opInfo.opType)) {
const data = opRemove(opInfo?.opRecord?.[opSafeKey]); (async () => {
modelSet(data); try {
opInfo.resetRecord(); const file = await path.join(await chatRoot(), 'cache_model', `${opInfo?.opRecord?.id}.json`);
await fs.removeFile(file);
} catch(e) {}
const data = opRemove(opInfo?.opRecord?.[opSafeKey]);
modelSet(data);
opInfo.resetRecord();
modelCacheCmd();
})();
} }
}, [opInfo.opType, formRef]); }, [opInfo.opType, formRef]);
@@ -58,20 +65,9 @@ export default function SyncCustom() {
// https or http // https or http
if (/^http/.test(record?.protocol)) { if (/^http/.test(record?.protocol)) {
const res = await http.fetch(filePath, { const data = await invoke('sync_user_prompts', { url: filePath, dataType: record?.ext });
method: 'GET', if (data) {
responseType: isJson ? 1 : 2, await modelCacheSet(data as [], file);
});
if (res.ok) {
if (isJson) {
// parse json
await modelCacheSet(setTag(Array.isArray(res?.data) ? res?.data : []), file);
} else {
// parse csv
const list: Record<string, string>[] = await invoke('parse_prompt', { data: res?.data });
const fmtList = list.map(i => ({ ...i, cmd: i.cmd ? i.cmd : genCmd(i.act), enable: true, tags: ['user-sync'] }));
await modelCacheSet(fmtList, file);
}
await modelCacheCmd(); await modelCacheCmd();
message.success('ChatGPT Prompts data has been synchronized!'); message.success('ChatGPT Prompts data has been synchronized!');
} else { } else {
@@ -83,13 +79,12 @@ export default function SyncCustom() {
if (isJson) { if (isJson) {
// parse json // parse json
const data = await readJSON(filePath, { isRoot: true }); const data = await readJSON(filePath, { isRoot: true });
await modelCacheSet(setTag(Array.isArray(data) ? data : []), file); await modelCacheSet(fmtData(data), file);
} else { } else {
// parse csv // parse csv
const data = await fs.readTextFile(filePath); const data = await fs.readTextFile(filePath);
const list: Record<string, string>[] = await invoke('parse_prompt', { data }); const list: Record<string, string>[] = await invoke('parse_prompt', { data });
const fmtList = list.map(i => ({ ...i, cmd: i.cmd ? i.cmd : genCmd(i.act), enable: true, tags: ['user-sync'] })); await modelCacheSet(fmtData(list), file);
await modelCacheSet(fmtList, file);
} }
await modelCacheCmd(); await modelCacheCmd();
}; };

View File

@@ -1,13 +1,13 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { Table, Button, message, Popconfirm } from 'antd'; import { Table, Button, Popconfirm } from 'antd';
import { invoke, http, path, shell } from '@tauri-apps/api'; import { invoke, path, shell } from '@tauri-apps/api';
import useInit from '@/hooks/useInit'; import useInit from '@/hooks/useInit';
import useData from '@/hooks/useData'; import useData from '@/hooks/useData';
import useColumns from '@/hooks/useColumns'; import useColumns from '@/hooks/useColumns';
import useChatModel, { useCacheModel } from '@/hooks/useChatModel'; import useChatModel, { useCacheModel } from '@/hooks/useChatModel';
import useTable, { TABLE_PAGINATION } from '@/hooks/useTable'; import useTable, { TABLE_PAGINATION } from '@/hooks/useTable';
import { fmtDate, chatRoot, GITHUB_PROMPTS_CSV_URL, genCmd } from '@/utils'; import { fmtDate, chatRoot } from '@/utils';
import { syncColumns } from './config'; import { syncColumns } from './config';
import './index.scss'; import './index.scss';
@@ -33,24 +33,13 @@ export default function SyncPrompts() {
}, [modelCacheJson.length]); }, [modelCacheJson.length]);
const handleSync = async () => { const handleSync = async () => {
const res = await http.fetch(GITHUB_PROMPTS_CSV_URL, { const data = await invoke('sync_prompts', { time: Date.now() });
method: 'GET', if (data) {
responseType: http.ResponseType.Text, opInit(data as any[]);
});
const data = (res.data || '') as string;
if (res.ok) {
// const content = data.replace(/"(\s+)?,(\s+)?"/g, '","');
const list: Record<string, string>[] = await invoke('parse_prompt', { data });
const fmtList = list.map(i => ({ ...i, cmd: i.cmd ? i.cmd : genCmd(i.act), enable: true, tags: ['chatgpt-prompts'] }));
await modelCacheSet(fmtList);
opInit(fmtList);
modelSet({ modelSet({
id: 'chatgpt_prompts', id: 'chatgpt_prompts',
last_updated: Date.now(), last_updated: Date.now(),
}); });
message.success('ChatGPT Prompts data has been synchronized!');
} else {
message.error('ChatGPT Prompts data sync failed, please try again!');
} }
}; };

View File

@@ -24,7 +24,9 @@ export const syncColumns = () => [
dataIndex: 'tags', dataIndex: 'tags',
key: 'tags', key: 'tags',
// width: 150, // width: 150,
render: () => <Tag>chatgpt-prompts</Tag>, render: (v: string[]) => (
<span className="chat-prompts-tags">{v?.map(i => <Tag key={i}>{i}</Tag>)}</span>
),
}, },
{ {
title: 'Enable', title: 'Enable',