-
Siri ChatGPT 使用教程
将 Siri 接入 ChatGPT,直接语音唤醒,并且支持连续对话。
第一步:拷贝项目
点击右上角「Get a copy」,这会打开 AirCode ,并基于此模板创建一个你自己的项目。如果没登录的话,可能会先跳转到登录页面,推荐使用 GitHub 登录,会快一些。
在弹出的创建对话框中,输入你的项目名称,并点击「Create」完成创建。
至此,你完成了所有配置过程,直接在手机中通过「嘿 Siri,打开机器人」就可以唤醒 ChatGPT,然后问问题了。
另外,你也可以在快捷指令的编辑页面中,点击下方的「分享」按钮,在弹出的菜单中选择「添加到主屏幕」,这样就可以在桌面通过点击打开对话框。
Enjoy your life with ChatGPT!
- 可以加入我们的 飞书用户群
- 我们的 GitHub 仓库 ,可以提 issue 或者直接贡献代码
- 欢迎加入我们的 Discord 讨论区
- 也可以直接发邮件给我们 [email protected]
- 用 JavaScript 开发钉钉 ChatGPT 机器人
- 用 JavaScript 开发企业微信 ChatGPT 机器人
- 用 JavaScript 开发飞书 ChatGPT 机器人
1// @see https://docs.aircode.io/guide/functions/
2const aircode = require('aircode');
3const { v4: uuidv4 } = require('uuid');
4const Api2d = require('./api2d.js');
6const { db } = aircode;
7const ChatTable = db.table('chat');
9// Setup OpenAI configurations
10const OPENAI_KEY = process.env.OPENAI_KEY || '';
11const OPENAI_MODEL = process.env.MODEL || 'gpt-3.5-turbo';
12const MAX_MESSAGES_PER_CHAT = process.env.MESSAGE_COUNT || 5;
14const API_BASE_URL = 'https://openai.api2d.net';
16const systemContent = 'You are a helpful assistant.';
18module.exports = async function (params, context) {
19 console.log('Received params:', params);
20 const { question, cid } = params;
22 // Create a chat ID if not provided
23 const chatId = cid ? cid : uuidv4();
25 // Save user's question to the ChatTable
26 await ChatTable.save({ chatId, role: 'user', content: question });
28 // Retrieve chat history
29 const chats = await ChatTable.where({ chatId }).sort({ createdAt: -1 }).limit(MAX_MESSAGES_PER_CHAT).find();
31 // Construct message array for GPT-3.5 Turbo
32 const messages = [{ role: 'system', content: 'You are a helpful assistant.' }, ...chats.reverse().map((one) => ({ role: one.role, content: one.content }))];
34 const api = new Api2d(OPENAI_KEY, API_BASE_URL);
36 try {
37 // Request completion from GPT-3.5 Turbo
38 const completion = await api.completion({
39 model: OPENAI_MODEL,
40 messages,
41 temperature: 1,
42 n: 1,
43 stream: false
44 });
46 // console.log('completion', completion);
48 const responseMessage = completion.choices[0].message;
50 // Save generated response to ChatTable
51 await ChatTable.save({ chatId, ...responseMessage });
53 // Return response message and chat ID
54 return { reply: responseMessage.content, cid: chatId };
55 } catch (error) {
56 // Set the response status to 500 (Internal Server Error)
57 context.status(500);
58 // Log the error
59 console.log('error', error.response || error);
61 // Initialize an error message variable
62 let errorMessage;
64 // If there is a response object in the error,
65 // it means the request was made and the server responded with an error status
66 if (error.response) {
67 const { status, statusText, data } = error.response;
69 if (status === 401) {
70 // If the status code is 401, set a specific error message related to the OpenAI API key
71 errorMessage = 'Unauthorized: Invalid OpenAI API key, please check your API key in the AirCode Environments tab.';
72 } else if (data.error && data.error.message) {
73 // If there is an error message in the data, use it as the error message
74 errorMessage = data.error.message;
75 } else {
76 // Otherwise, use the status code and status text as the error message
77 errorMessage = `Request failed with status code ${status}: ${statusText}`;
79 } else if (error.request) {
80 // If there is a request object in the error,
81 // it means the request was made but no response was received
82 errorMessage = 'No response received from the server';
83 } else if (error.code === 'ENOTFOUND' || error.code === 'ECONNREFUSED') {
84 // If there is a network error, such as DNS resolution or connection refused
85 errorMessage = `Network error: ${error.message}`;
86 } else {
87 // If none of the above conditions are met,
88 // it means there was an error setting up the request
89 errorMessage = `Request setup error: ${error.message}`;
92 // Return an object containing the error message
93 return { error: errorMessage };
95};
96
1const fetchEventSource = require('@microsoft/fetch-event-source');
2const fetch = require('node-fetch');
3module.exports = class Api2d {
4 // 设置key和apiBaseUrl
5 constructor(key = null, apiBaseUrl = null, timeout = 60000) {
6 this.key = key;
7 this.apiBaseUrl = apiBaseUrl || (key && key.startsWith('fk') ? 'https://stream.api2d.net' : 'https://api.openai.com');
8 this.timeout = timeout;
9 this.controller = new AbortController();
12 // set key
13 setKey(key) {
14 this.key = key;
17 // set apiBaseUrl
18 setApiBaseUrl(apiBaseUrl) {
19 this.apiBaseUrl = apiBaseUrl;
22 setTimeout(timeout) {
23 this.timeout = parseInt(timeout) || 60 * 1000;
26 abort() {
27 this.controller.abort();
30 // Completion
31 async completion(options) {
32 // 拼接目标URL
33 const url = this.apiBaseUrl + '/v1/chat/completions';
34 // 拼接headers
35 const headers = {
36 'Content-Type': 'application/json',
37 Authorization: 'Bearer ' + this.key
38 };
40 const { onMessage, onEnd, model, ...restOptions } = options;
42 // 如果是流式返回,且有回调函数
43 if (restOptions.stream && onMessage) {
44 // 返回一个 Promise
45 return new Promise(async (resolve, reject) => {
46 try {
47 let chars = '';
48 console.log('in stream');
49 // 使用 fetchEventSource 发送请求
50 const timeout_handle = setTimeout(() => {
51 this.controller.abort();
52 // throw new Error( "Timeout "+ this.timeout );
53 reject(new Error(`[408]:Timeout by ${this.timeout} ms`));
54 }, this.timeout);
55 const response = await fetchEventSource(url, {
56 signal: this.controller.signal,
57 method: 'POST',
58 headers: { ...headers, Accept: 'text/event-stream' },
59 body: JSON.stringify({ ...restOptions, model: model || 'gpt-3.5-turbo' }),
60 async onopen(response) {
61 if (response.status != 200) {
62 throw new Error(`[${response.status}]:${response.statusText}`);
64 },
65 onmessage: (e) => {
66 if (timeout_handle) {
67 clearTimeout(timeout_handle);
69 if (e.data == '[DONE]') {
70 // console.log( 'DONE' );
71 if (onEnd) onEnd(chars);
72 resolve(chars);
73 } else {
74 // console.log( e.data );
75 const event = JSON.parse(e.data);
76 if (event.choices[0].delta.content) chars += event.choices[0].delta.content;
77 if (onMessage) onMessage(chars);
79 },
80 onerror: (error) => {
81 console.log(error);
82 throw new Error(String(error)?.match(/\[(\d+)\]/)?.[1] ? error : `[500]:${error}`);
84 });
86 // const ret = await response.json();
87 } catch (error) {
88 console.log(error);
89 reject(error);
91 });
92 } else {
93 // 使用 fetch 发送请求
94 const response = await fetch(url, {
95 signal: this.controller.signal,
96 method: 'POST',
97 headers: headers,
98 body: JSON.stringify({ ...restOptions, model: model || 'gpt-3.5-turbo' })
99 });
100 const timeout_handle = setTimeout(() => {
101 this.controller.abort();
102 }, this.timeout);
103 const ret = await response.json();
104 clearTimeout(timeout_handle);
105 return ret;
106 }
107 }
109 async embeddings(options) {
110 // 拼接目标URL
111 const url = this.apiBaseUrl + '/v1/embeddings';
112 // 拼接headers
113 const headers = {
114 'Content-Type': 'application/json',
115 Authorization: 'Bearer ' + this.key
116 };
117 const { model, ...restOptions } = options;
118 // 使用 fetch 发送请求
119 const response = await fetch(url, {
120 signal: this.controller.signal,
121 method: 'POST',
122 headers: headers,
123 body: JSON.stringify({ ...restOptions, model: model || 'text-embedding-ada-002' })
124 });
125 const timeout_handle = setTimeout(() => {
126 this.controller.abort();
127 }, this.timeout);
128 const ret = await response.json();
129 clearTimeout(timeout_handle);
130 return ret;
131 }
133 async billing() {
134 const url = this.apiBaseUrl + '/dashboard/billing/credit_grants';
135 const headers = {
136 'Content-Type': 'application/json',
137 Authorization: 'Bearer ' + this.key
138 };
139 const response = await fetch(url, {
140 signal: this.controller.signal,
141 method: 'GET',
142 headers: headers
143 });
144 const timeout_handle = setTimeout(() => {
145 this.controller.abort();
146 }, this.timeout);