MoreRSS

site iconNoise修改

开发者,开发了Rss-Translation(众多RSS翻译项目之一)。
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

Noise的 RSS 预览

完全免费的AI文本转语音,一键部署

2025-05-26 10:00:00

1748146403780

LibreTTS 是一款免费的在线文本转语音工具,支持多种声音选择,可调节语速和语调,提供即时试听和下载功能,这个程序支持多国语言和ai配音,配音感情丰富,支持ai调整语气

项目地址:https://github.com/LibreSpark/LibreTTS

在线预览:https://libretts.is-an.org

1748103829101

功能特点

  • 🎯 支持超过300种不同语言和口音的声音
  • 🔊 实时预览和试听功能
  • ⚡ 支持长文本自动分段处理
  • 🎛️ 可调节语速和语调
  • 📱 响应式设计,支持移动端
  • 💾 支持音频下载
  • 📝 历史记录功能(最多保存50条)
  • 🔌 支持添加自定义OpenAI格式的TTS API

API 说明

本项目提供以下 API 端点:

Edge API 路径

  • /api/tts - 文本转语音 API
    • 支持 GET/POST 方法
    • GET 示例: /api/tts?t=你好世界&v=zh-CN-XiaoxiaoNeural&r=0&p=0
    • POST 示例: 请求体为JSON格式 {"text": "你好世界", "voice": "zh-CN-XiaoxiaoNeural", "rate": 0, "pitch": 0}
  • /api/voices - 获取可用语音列表 API
    • 仅支持 GET 方法
    • 示例: /api/voices?l=zh&f=1 (l参数用于筛选语言,f参数指定返回格式)

例如:https://libretts.is-an.org/api/tts

部署指南

Vercel 部署

  1. Fork 本仓库到你的 GitHub 账号
  2. 登录 Vercel,点击 “New Project”
  3. 导入你 fork 的仓库,并选择默认设置部署即可
  4. 部署完成后,你会获得一个 your-project.vercel.app 的域名

Cloudflare Pages 部署

  1. Fork 本仓库到你的 GitHub 账号
  2. 登录 Cloudflare Dashboard,进入 Pages 页面
  3. 创建新项目,选择从 Git 导入:
    • 选择你 fork 的仓库
    • 构建设置:
      • 构建命令:留空
      • 输出目录:/
      • 环境变量:无需设置
  4. 部署完成后,你会获得一个 xxx.pages.dev 的域名

环境变量

除了原有配置外,现在项目支持设置环境变量 PASSWORD 来开启访问密码验证。如果 PASSWORD 非空,则用户第一次访问页面时会显示密码输入界面,输入正确后在该设备上后续访问将不再需要验证。

popclip插件合集

2025-05-23 15:00:00

44r4

前言:给出代码的选中自动识别安装,需要下载安装的请前往相关地址安装

一、Blinko笔记

自制的,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// #popclip extension for Send to Blinko
// name: Blinko笔记
// icon: square filled BK
// language: javascript
// module: true
// entitlements: [network]
// options: [{
// identifier: "siteUrl",
// label: "服务端地址",
// type: "string",
// defaultValue: "https://note.noisework.cn",
// description: "请确保地址正确,不要带末尾斜杠"
// }, {
// identifier: "token",
// label: "API Token",
// type: "string",
// description: "从设置页面获取最新Token"
// }]

async function sendToShuo(input, options) {
try {
// 参数预处理
const siteUrl = (options.siteUrl || "").replace(/\/+$/g, "");
const token = (options.token || "").trim();
const content = (input.text || "").trim();

// 验证参数
if (!/^https:\/\/[\w.-]+(:\d+)?$/.test(siteUrl)) {
throw new Error("地址格式错误,示例: https://note.noisework.cn");
}
if (!token) throw new Error("Token不能为空");
if (!content) throw new Error("选中文本不能为空");

// 发送请求
await sendRequestWithXMLHttpRequest(siteUrl, token, content);
PopClip.showText("✓ 发送成功");
} catch (error) {
handleRequestError(error);
}
}

// 使用 XMLHttpRequest 实现网络请求
function sendRequestWithXMLHttpRequest(siteUrl, token, content) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
const url = `${siteUrl}/api/v1/note/upsert`;

xhr.open("POST", url, true);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.setRequestHeader("Authorization", `Bearer ${token}`);

xhr.timeout = 10000; // 设置超时时间(10秒)

// 设置回调函数
xhr.onreadystatechange = () => {
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.responseText);
} else {
let errorMsg = `请求失败 (${xhr.status})`;
try {
const data = JSON.parse(xhr.responseText);
errorMsg = data.message || errorMsg;
} catch {}
reject(new Error(errorMsg));
}
}
};

// 处理网络错误
xhr.onerror = () => reject(new Error("网络错误"));

// 处理超时错误
xhr.ontimeout = () => reject(new Error("请求超时"));

try {
// 发送请求
const payload = JSON.stringify({
content: `#Popclip\n${content}`, // 内容字段
type: 0, // 类型,固定为 0
attachments: [], // 附件,空数组
isArchived: null, // 是否归档
isTop: null, // 是否置顶
isShare: null, // 是否分享
isRecycle: null, // 是否放入回收站
references: [null] // 参考内容
});
xhr.send(payload);
} catch (error) {
reject(new Error("请求发送失败: " + error.message));
}
});
}

// 错误处理
function handleRequestError(error) {
console.error("请求错误:", error);

const errorMap = {
"Failed to fetch": "无法连接到服务器",
"aborted": "请求超时",
"网络错误": "网络错误",
"401": "认证失败,请检查Token",
"404": "API地址不存在"
};

const message = Object.entries(errorMap).find(([key]) =>
error.message.includes(key)
)?.[1] || `请求错误: ${error.message.split('\n')[0].slice(0, 50)}`;

PopClip.showText(`❌ ${message}`);
}

exports.actions = [{
title: "发送至Blinko",
code: sendToShuo,
icon: "square filled BK"
}];

二、多模式AI写手

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
// #popclip extension for ChatGPT
// name: smart writer
// icon: iconify:fluent:calligraphy-pen-24-regular
// language: javascript
// module: true
// entitlements: [network]
// options: [{
// identifier: apikey, label: API Key, type: string,
// description: 'Obtain API key from https://platform.openai.com/account/api-keys'
// }]

const prefixes = {
"polish": "你是我的写作助手,检查接收到的文字的拼写、语法错误,对其进行润色:\n",
"xiaohongshu": "扮演文本助理,使用小红书的 Emoji 风格润色我的内容,特点是每行开头都是一个相关表情符号,达到引人入胜的目的:\n",
"Summarize": "归纳总结这些文字,同时用列表列出要点:\n",
"translate": "将发送的文字内容都翻译成中文,如果内容是中文则翻译成标准的英文:\n",
"Official": "你将扮演专业的微信公众号运营者,优化润色我给的内容成为爆款标题:\n",
}
async function chat (input, options, lang, prefixName) {
const openai = require("axios").create({
baseURL: "https://www.noiseapi.top/v1",
headers: { Authorization: `Bearer ${options.apikey}` },
});

let messages
switch (lang) {
case "en":
messages = [
{"role": "system", "content": "I want you act as a proofreader. I will provide you texts and I would like you to review them for any spelling, grammar, or punctuation errors."},
{"role": "user", "content": `Proofread the following content and give me the result without extra delarations or comments:\n\n${input.text}`},
]
break;
case "zh":
messages = [
{"role": "system", "content": "你是我的写作助手,检查接收到的文字的拼写、语法错误,向我提供修改后的文字。"},
{"role": "user", "content": `修改下面的文字,直接输出修改后的结果,不需要额外的声明:\n${input.text}`}
]
break;
}

if (prefixName) {
messages = [{"role": "user", "content": `${prefixes[prefixName]}${input.text}`}]
}

const { data } = await openai.post("/chat/completions", {
model: "free-gpt-4o-2024-08-06",
messages,
});
const result = data.choices[0].message;
return input.text.trimEnd() + "\n\n" + result.content.trim();
};

exports.actions = [
{
title: "公众号爆款标题",
icon: "circle 标题",
after: "paste-result",
code: async (input, options) => chat(input, options, "", "Official"),
},
{
title: "小红书风格",
icon: "circle 红书",
after: "paste-result",
code: async (input, options) => chat(input, options, "", "xiaohongshu"),
},
{
title: "总结内容",
icon: "circle 总结",
after: "paste-result",
code: async (input, options) => chat(input, options, "", "Summarize"),
},
{
title: "中英互译",
icon: "square 翻译",
after: "paste-result",
code: async (input, options) => chat(input, options, "", "translate"),
},
{
title: "润色",
icon: "square 润色",
after: "paste-result",
code: async (input, options) => chat(input, options, "", "polish"),
},
];

三、Github搜索

https://github.com/Wooden-Robot/Search-Github-PopClip

这是一个简单的 PopClip 插件,可以让您在 Mac 上直接搜索 Github 项目。只需选中要搜索的文本并触发插件,您无需复制粘贴即可快速查找结果。

功能

选中文本后,单击 Github 图标即可在 Github 上搜索。

在默认浏览器中打开搜索结果。

快速高效的搜索体验。

安装

下载 Search_Github_Extension.popclipextz 文件。

双击文件,安装该插件到 PopClip。

安装完成后,您可以在任何应用中选中文本,点击 PopClip 菜单中的 Github 按钮进行搜索。

四、文本发送到 flomo

https://github.com/extrastu/popclip-flomoplus

主作 SendToFlomo 将所选文本发送到 flomo。

配置

API 密钥

要使用此扩展,您需要为其提供 flomo 的 API 密钥 帐户。要获取 API 密钥:

在此处注册 flomo 帐户: https://flomoapp.com/

在此处生成 API 密钥:https://v.flomoapp.com/mine?source=incoming_webhook

将 API 密钥复制并粘贴到 API 密钥字段中 扩展的设置。

五、本地GPT-SoVITS

GPT-SoVITS接入苹果MacOs效率工具PopClip

安装PopClip软件

启动GPT-SoVITS接口服务

选中shell脚本的代码,安装插件即可

https://github.com/v3ucn/GPT-SoVITS_FOR_PopClip

六、说说笔记扩展

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// #popclip extension for Send to Shuo
// name: 说说笔记
// icon: square filled 说
// language: javascript
// module: true
// entitlements: [network]
// options: [{
// identifier: "siteUrl",
// label: "服务端地址",
// type: "string",
// defaultValue: "https://note.noisework.cn",
// description: "请确保地址正确,不要带末尾斜杠"
// }, {
// identifier: "token",
// label: "API Token",
// type: "string",
// description: "从设置页面获取最新Token"
// }]

async function sendToShuo(input, options) {
try {
// 参数预处理
const siteUrl = (options.siteUrl || "").replace(/\/+$/g, "");
const token = (options.token || "").trim();
const content = (input.text || "").trim();

// 验证参数
if (!/^https:\/\/[\w.-]+(:\d+)?$/.test(siteUrl)) {
throw new Error("地址格式错误,示例: https://note.noisework.cn");
}
if (!token) throw new Error("Token不能为空");
if (!content) throw new Error("选中文本不能为空");

// 发送请求
await sendRequestWithXMLHttpRequest(siteUrl, token, content);
PopClip.showText("✓ 发送成功");
} catch (error) {
handleRequestError(error);
}
}

// 使用 XMLHttpRequest 实现网络请求
function sendRequestWithXMLHttpRequest(siteUrl, token, content) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
const url = `${siteUrl}/api/token/messages`;

xhr.open("POST", url, true);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.setRequestHeader("Authorization", `Bearer ${token}`);

xhr.timeout = 10000; // 设置超时时间(10秒)

// 设置回调函数
xhr.onreadystatechange = () => {
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.responseText);
} else {
let errorMsg = `请求失败 (${xhr.status})`;
try {
const data = JSON.parse(xhr.responseText);
errorMsg = data.message || errorMsg;
} catch {}
reject(new Error(errorMsg));
}
}
};

// 处理网络错误
xhr.onerror = () => reject(new Error("网络错误"));

// 处理超时错误
xhr.ontimeout = () => reject(new Error("请求超时"));

try {
// 发送请求
const payload = JSON.stringify({
content: `#Popclip\n${content}`,
type: "text"
});
xhr.send(payload);
} catch (error) {
reject(new Error("请求发送失败: " + error.message));
}
});
}

// 错误处理
function handleRequestError(error) {
console.error("请求错误:", error);

const errorMap = {
"Failed to fetch": "无法连接到服务器",
"aborted": "请求超时",
"网络错误": "网络错误",
"401": "认证失败,请检查Token",
"404": "API地址不存在"
};

const message = Object.entries(errorMap).find(([key]) =>
error.message.includes(key)
)?.[1] || `请求错误: ${error.message.split('\n')[0].slice(0, 50)}`;

PopClip.showText(`❌ ${message}`);
}

exports.actions = [{
title: "发送至说说笔记",
code: sendToShuo,
icon: "square filled 说"
}];

七、快速生成ray.so截图

介绍:将所选文本发送到 ray.so 以获得美丽的图像 您的代码。

https://github.com/dofy/PopClip-Extensions

八、Obsidian 插件

https://github.com/canburaks/obsidian-popclip

它是如何工作的?

工作流有两个部分。

安装 Popclip 扩展。

安装 Obsidian 插件。

第一部分:Popclip 扩展

您可以选择以下文本来安装扩展。填写 作为您的 Obsidian Vault 名称,并作为目标目录,该目录必须相对于 Vault 的根目录。vaultlocation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# PopClip - Obsidian extension, markdown variant 
name: ObsidianClipper
icon: O
capture html: true
options:
- identifier: "vault"
label: "Vault name"
type: string
- identifier: "path"
label: "Location"
type: string
javascript: |
const vaultName = encodeURIComponent(popclip.options.vault)
const fileLocation = encodeURIComponent(popclip.options.path)
const data = {
clipping: popclip.input.markdown,
path: fileLocation || undefined,
}
let clipping = popclip.input.markdown
if (popclip.context.browserUrl.length > 0) { // append markdown source link if available
data["title"] = popclip.context.browserTitle
data["source"] = popclip.context.browserUrl
}
clipping = encodeURIComponent(JSON.stringify(data))
popclip.openUrl(`obsidian://advanced-uri?vault=${vaultName}&daily=true&heading=popclip&data=%0A${clipping}&mode=append`)
#end

安装 Obsidian 插件。

我还没有提交插件。我打算尽快提交。

因此,您需要手动安装它:

下载 Obsidian Popclip 插件的 GitHub 存储库。

复制 directory 下的文件夹。popclipdist

将复制的文件夹粘贴到 Obsidian 插件文件夹 下。popclip.obsidian/plugins

重新启动 Obsidian。

九、Bilibili搜索

一个简单的 PopClip 扩展,根据 Bilibili.com 中的选定视频直接将您带到 Bilibili.com。

https://github.com/brucemomo/PopClip-Extension-Bilibili

十、memos创建新备忘录

https://github.com/beffiy/popclip-usememos-extension

一个简单的 popclip 扩展,用于 usememos 创建新备忘录。

十一、发送到Cursor Editor

https://github.com/rizumita/composerize

一个 PopClip 扩展,用于将所选文本发送到光标编辑器的编辑器功能。它通过保留以前的剪贴板数据来安全地处理剪贴板内容。

特征

  • 自动将所选文本发送到 Cursor 的书写器
  • 保留剪贴板内容
  • 使用错误消息优雅地处理错误
  • 无需配置

用法

  1. 选择要发送到 Composer 的任何文本
  2. 单击 PopClip 菜单中的 Composerize 按钮
  3. 该扩展将:
    • 临时存储当前的剪贴板内容
    • 将所选文本发送到 Cursor 的 Composer
    • 恢复以前的剪贴板内容

要求

  • PopClip(推荐最新版本)
  • 光标编辑器
  • PopClip 的 macOS 辅助功能权限

安装

  1. 双击 composerize.popclipext 文件夹
  2. 在 PopClip 的对话框中确认安装
  3. 出现提示时授予必要的权限

十二、小红书搜索

划词点击图标跳转至小红书网页版搜索

下载:https://pan.quark.cn/s/6e7361338d14

解压后双击安装

十三、搜索 Z-lib 书籍

https://github.com/Wooden-Robot/Search_Z-lib_PopClip

这是一个简单的 PopClip 插件,可以让您在 Mac 上直接搜索 Z-lib 书籍。只需选中要搜索的文本并触发插件,您无需复制粘贴即可快速查找结果。

功能

  • 选中文本后,单击 Z 即可在 Z-lib 上搜索。
  • 在默认浏览器中打开搜索结果。
  • 快速高效的搜索体验。

安装

  1. 下载 Search_Z-lib_Extension.popclipextz 文件。
  2. 双击文件,安装该插件到 PopClip。
  3. 安装完成后,您可以在任何应用中选中文本,点击 PopClip 菜单中的 Z 按钮进行搜索。

十四、深度求索翻译

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
 #popclip
{"name": "深度求索翻译", "icon": "square filled 翻", "interpreter": "python3", "options": [{"identifier": "apikey", "label": "DeepSeek API Key", "type": "string"}], "after": "paste-result"}

import os
import json
import urllib.request
import urllib.error

def translate():
# 获取环境变量
text = os.environ['POPCLIP_TEXT']
api_key = os.environ['POPCLIP_OPTION_APIKEY']

# 准备请求数据
data = {
"model": "deepseek-chat",
"messages": [
{
"role": "system",
"content": "你现在是一位英文翻译专家,专注于翻译用户提供的内容。如果用户提交的内容是中文,请将其翻译成英文;如果是英文,则翻译成简体中文。请注意,你无需对内容提出任何评论或解答,仅需提供准确的翻译。"
},
{
"role": "user",
"content": text
}
],
"temperature": 0.3
}

# 准备请求
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {api_key}"
}

try:
# 创建请求
req = urllib.request.Request(
"https://api.deepseek.com/v1/chat/completions",
data=json.dumps(data).encode('utf-8'),
headers=headers,
method="POST"
)

# 发送请求
with urllib.request.urlopen(req) as response:
result = json.loads(response.read().decode('utf-8'))
translation = result['choices'][0]['message']['content'].strip()
print(translation, end='')

except urllib.error.URLError as e:
print(f"请求错误: {str(e)}", end='')
except json.JSONDecodeError:
print("JSON解析错误", end='')
except KeyError:
print("API响应格式错误", end='')
except Exception as e:
print(f"未知错误: {str(e)}", end='')

translate()

自媒体运营助手自动化工具,一键发布到小红书、快手、抖音、油管、B站等平台

2025-05-23 10:00:00

coeever

介绍

一个基于浏览器自动化的自媒体运营工具,可实现一键视频搬家、一键发布视频到多平台,抖音视频下载,一键搬家到其他平台(快手、YouTube、小红书、美拍、哔哩哔哩等)

项目地址:https://github.com/spider-ios/autox-release

使用前请确保系统已安装 Chrome 浏览器。软件依赖 Chrome 实现自动操作。

预览

主页

主页

使用(来自仓库说明)

首次使用时操作步骤:

如果要发布的平台(如 YouTube、小红书等)尚未登录账号,程序将提示您进行登录。

您可以点击程序页面中的 “打开浏览器” 按钮,系统会自动为您打开一个新的浏览器窗口。如下图所示: 打开浏览器

打开浏览器

在这个新打开的浏览器中,请访问对应平台(如 youtube.com、xiaohongshu.com)并登录您的账号。

注意: 程序打开的浏览器是一个与您平时使用的浏览器隔离的进程(例如另一个独立的 Chrome 实例),它不会共享历史登录状态或插件。

登录成功后,浏览器会自动保存您的登录状态。只要不长时间退出或清除缓存,程序在后续发布时无需重复登录。

📌 说明:

每个平台都需要单独登录一次;

浏览器保持登录状态是确保发布成功的前提;

如果发现登录失效,可重新点击“打开浏览器”按钮进行登录。

📥 抖音搬家功能

抖音搬家

🔐 发布平台登录说明

使用前需确保浏览器已登录对应平台账号:

  • 首次使用或登录状态失效时,请点击界面中的“打开浏览器”按钮
  • 在新打开的浏览器中登录对应平台(如 YouTube、小红书等)
  • 软件启动的浏览器进程独立于系统浏览器,您可能会看到一个新的 Chrome 窗口
  • 登录成功后,登录状态会保持有效,除非长时间未登录或平台手动登出

打开浏览器1打开浏览器2

📌 操作步骤

功能使用

1.下载链接

输入要搬家的抖音主页链接 ,【目前只支持输入用户主页链接,可输入多个地址,地址直接回车换行】,如“https://www.douyin.com/user/MS4wLjABAAAAgfJrGAfliIV-_qU4wUWAMmyeTjhyjUqqESXwxrBQBYJosRs9O7-_2I_lWOvu_qrF?from_tab_name=main&vid=7484152559486160188”)

  1. 选择作品发布的时间段
  2. 设置 cookie (如何获取 cookie ?)

也可以参考以下步骤: 获取 cookie获取 cookie获取 cookie获取 cookie

  1. 选择要发布到的平台和账号,开始搬家操作 获取 cookie

🌐 多平台视频发布

多平台发布

🔐 登录状态说明(同抖音搬家一致)

  • 每个平台发布前,请确保浏览器中已登录账号
  • 可点击“打开浏览器”进入登录界面,平台如:YouTube、小红书、哔哩哔哩等
  • 程序会打开一个新的独立 Chrome 窗口用于自动化登录与发布

网盘下载

https://pan.quark.cn/s/41e27b02000b

oba live tool-抖音小店、巨量百应、抖音团购、小红书直播小工具

2025-05-23 10:00:00

cov99er

一个用于直播带货工具,支持抖音小店、巨量百应、抖音团购、小红书千帆平台,能自动弹窗,自动发言,AI助力回复,支持win、mac

项目地址:https://github.com/qiutongxue/oba-live-tool

预览

主界面预览

ai

自动回复

✨ 功能

🍟 多账号管理:支持多组账号配置,针对不同直播间使用不同的配置

🎯 智能消息助手:自动发送消息,告别重复机械喊话

📦 商品自动讲解:自动商品弹窗,随心所欲弹讲解

💃 AI 自动回复:实时监听直播互动评论(包括进入直播间、点赞、关注、加入品牌会员、加入粉丝团、下单信息等)、自动生成回复内容

🤖 AI 智能助理:接入 DeepSeek,支持官方、OpenRouter、硅基流动、火山引擎等所有支持 openai chat接口的提供商

🚀 快速开始

系统要求

  • 操作系统:Windows 10 及以上 | MacOS 也能试试
  • 浏览器:电脑上需要安装 Chrome 或 Edge 浏览器
  • 抖音小店/巨量百应/抖音团购/小红书千帆:账号需要能正常进入中控台

下载安装

访问 Releases 页面下载最新版本安装包

https://pan.quark.cn/s/c09f681c5cfc

源码构建

1
2
3
4
git clone https://github.com/qiutongxue/oba-live-tool.git
cd oba-live-tool
pnpm install
pnpm build

📖 使用方法

第一步:连接到中控台

自动发言、自动弹窗、自动回复功能都需要先连接到中控台才能使用。

  1. 点击功能列表的「打开中控台」进入直播控制台页面,点击「连接直播控制台」按钮

    如果软件显示找不到浏览器,或者想要自己指定浏览器位置,请前往 应用设置 页面的 浏览器设置 中进行相关设置。

  2. 如果是第一次连接,请在弹出的页面中登录账号
  3. 等待控制台状态显示绿色圆点和「已连接」,即连接成功

抖音小店和巨量百应理论上支持所有功能,抖音团购和小红书暂时只支持弹窗和自动发言

自动弹窗和自动发言

自动弹窗

  • 自动弹窗的商品 ID 为直播间上架商品的序号(从 1 开始),并非商品的真实 ID!

自动发言

  • 抖音团购和小红书没有提供置顶功能,所以勾选了置顶也没用

目前暂时还没提供运行时更新设置的功能,所以如果需要让新的任务配置生效,需要重启任务

自动回复

自动回复目前只支持抖音小店/巨量百应两个平台

自动回复目前可以通过两种方式监听直播互动信息:

  1. 中控台的直播互动窗口:只有评论互动内容
  2. 直播大屏主播版:拥有评论互动、进入直播间、点赞、加入品牌会员、关注直播间、加入粉丝团、下单信息

注意事项

  • 请预先在自动回复的设置里设置好要回复的内容
  • 关键词回复AI回复可以同时设置,在同时设置的情况下,若命中了关键词则优先使用关键词回复,未命中情况下才会执行AI回复
    • 若重复的关键词出现在多个规则中,优先使用序号靠前的规则
  • 入场、点赞等额外互动消息的自动回复等功能只有把入口设置为罗盘大屏时才生效
  • 程序会自动将回复内容里的所有 {用户名}替换为实际的用户昵称,你可以灵活调整{用户名}出现的位置
    • 比如此时进入直播间的用户叫张三,且设置了自动回复的内容为 @{用户名} 欢迎来到直播间,实际发送的内容为 @张三 欢迎来到直播间
      • 如果设置了隐藏用户名,实际发送的内容为 @张*** 欢迎来到直播间
  • 当配置了多条回复内容时,程序会随机选择一条发送
  • 先点击开始监听按钮准备开始监听评论消息,监听成功后开始任务才能正常执行自动回复。
AI回复
  1. 设置好你的 API KEY 及模型,确保可用。
  2. 在「提示词配置」中设置好相关的提示词。

    提示词决定了 AI 会扮演什么样的角色,以及 AI 会如何回答用户的问题,会计入 token 消耗。

开启 AI 回复时,程序会将「开始任务」之后的新的用户评论交给 AI 处理,用户评论会以 JSON 格式原封不动地作为对话的内容交给 AI:

1
2
3
4
{
"nickname": "用户昵称",
"content": "用户评论内容",
}

所以可以把 nicknamecommentTag 等插入到提示词中,你的提示词可以是:

1
2
3
4
5
6
你是一个直播间的助手,负责回复观众的评论。请参考下面的要求、产品介绍和直播商品,用简短友好的语气回复,一定一定不要超过45个字。

## 要求

- 回复格式为:@<nickname第一个字符>*** <你的回复> (注意!:三个星号是必须的)
...

AI 助手

AI 助手只支持文本对话,在使用 AI 助手功能前,请先设置好你的 API KEY 及模型,确保可用。

API KEY 设置

想要使用 AI 功能,需要先设置 API KEY。

软件提供了四种 DeepSeek 模型的预设:

除此之外,「自定义」还支持几乎任何兼容 openai 对话模型接口的服务。

在 「AI 助手」或「自动回复」的页面,点击「配置 API Key」按钮,就能选择自己需要的提供商和模型了。

注意: 有的(大多数)模型是收费的,使用 AI 功能前请一定要先了解清楚,使用收费模型时请确保自己在提供商的账户有能够消耗的额度。

火山引擎

火山引擎的设置方式和其它提供商有些微区别,除了需要 API KEY 之外,还需要 创建接入点。创建成功后,将接入点的 id 复制到原先选择模型的位置中即可使用。

火山引擎接入点

其它

软件更新

你可以选择更新源,但是目前最稳定的还是 Github。

亲测:Github 绝对可用。gh-proxy.com 偶尔可用。其余的github代理基本都不可用。

开发者模式

启用开发者模式后,可以使用鼠标右键菜单,在菜单中可打开开发者工具。

启用开发者模式后,连接到中控台时会关闭浏览器的无头模式。

高度完善的轻量说说笔记系统及memos的开源替代品-你的私人朋友圈

2025-04-12 10:00:00

T1HaBIgPGZEXlfc

前言:近期碰到一个喜欢的一个项目,看到可以类似memos的轻松发布自己的内容…本着喜欢就改造的原则,这个版本的说说笔记就诞生了,使用它,你的数据将由你自由掌控,且可完全免费一键部署,重要的是碎片化的信息记录加自动化就会变的非常高自由度,感兴趣的话不妨来试试

介绍

这是基于Ech0基本框架的二次开发、魔改及完善,类似朋友圈样式风格,支持后台配置修改如背景图、个性签名等,支持api 获取内容、更新操作等,支持对b站视频、网易云音乐、youtube等的解析添加、支持一键复制,一键生成内容图片、支持http post发送内容到平台,支持对接webhook、telegram、企业微信、飞书的一键推送,支持内容热力图组件等个性化组件,它完全属于个人的自定化使用,会加入定制化的一些功能,由于代码已重构,不同步于原版

预览:https://note.noisework.cn

源码:https://github.com/rcy1314/echo-noise

原版介绍

Ech0 是一款专为轻量级分享而设计的开源自托管平台,支持快速发布与分享你的想法、文字与链接。简单直观的操作界面,轻松管理你的内容,让分享变得更加自由,确保数据完全掌控,随时随地与世界连接。

原版地址:https://github.com/lin-snow/Ech0


268shot99

整体改版

编辑器部分:

  1. 自适应高度和拖拽调整功能
  2. 扩展的工具栏功能
  3. 完整的响应式支持
  4. 平滑的过渡动画效果
  5. 优化的间距和字体设置
  6. md格式图片即时预览
  7. 添加定制化的组件

主页部分:

  1. 调整页面内容自适应高度和宽度
  2. 添加随机背景图的展示并带有模糊效果
  3. 增加md 格式下对网易云音乐、哔哩哔哩视频、youtube、qq 音乐的解析渲染
  4. 调整信息条目的背景ui 及显示尺寸的优化
  5. 调整ui及加载响应页面的整体显示效果
  6. 添加朋友圈样式主图banner,并和背景图使用相同
  7. 所有链接都可通过新标签页打开
  8. 长内容的折叠展开处理
  9. 完善的二次编辑及预览保存
  10. 一键复制及生成内容图片的功能化组件
  11. 增加标签系统路由及组件

代码部分

  1. 调整jwk验证为session方式,同时调整token的验证机制
  2. 调整优化数据库的迁移及连接处理
  3. 增加不同的路由及调整控制器
  4. 增加额外的外挂插件文件
  5. 增加定期清理缓存

特征

  • 一键部署无服务器平台-fly.io、zeabur、railway、vercel

  • 外部扩展-支持快捷指令及popclip一键发布内容到站点

  • 支持推送渠道(webhook、tg、企业微信、飞书)

  • 标签系统和图片api 路由

  • 支持链接远程数据库PostgreSQL、MySQL的连接支持,默认SQLite

  • 个性化前端组件如发布日历-热力图组件,默认不显示,点击日历图标后显示

    1743765992985_副本

    01.45.31

  • 内容二次编辑及一键复制一键生成内容图片

  • 数据库文件的一键备份、上传

    ehS1BxwbUKyD2Vm


安装部署

💡 部署完成后访问 ip:1314 即可使用

docker部署

一键部署

1
2
3
4
5
6
docker run -d \
--name Ech0-Noise \
--platform linux/amd64 \
-p 1314:1314 \
-v /opt/data/noise.db:/app/data/noise.db \
noise233/echo-noise

/opt/data/noise.db是你本地的原有数据库文件,如果没有,可以去掉这个挂载命令,它也会自动创建

说明:如果你是经常使用附件图片发布内容的则可以这样:-v /opt/data:/app/data \

默认用户名:admin

默认用户密码:admin

docker-componse构建部署

在该目录下执行以下命令启动服务(不修改环境变量时默认使用本地数据库.db 文件):

1
docker-compose up -d

无服务器平台+postgres免费数据库部署

数据库使用 Neon PostgreSQL 云数据库服务,其它也支持

请先前往官网https://console.neon.tech部署好你的基础数据库

以下部署文件已放入根目录下的noise文件夹内

部署成功示例:

SDOAt8BsdIiCzXF

Fly.io部署

fly.toml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
app = 'ech0-noise'    # 修改为你的自定义容器名
primary_region = 'hkg'

[experimental]
auto_rollback = true

[build]
image = 'noise233/echo-noise'
dockerfile = 'Dockerfile'

[env]
CGO_ENABLED = '1'
DB_HOST = 'example.aws.neon.tech' # 修改为数据库的HOST地址
DB_NAME = 'noise' # 修改为数据库的名称
DB_PASSWORD = 'example' # 修改为数据库的密码
DB_PORT = '5432'
DB_SSL_MODE = 'require'
DB_TYPE = 'postgres'
DB_USER = 'noise_owner' # 修改为数据库的用户名
TZ = 'Asia/Shanghai'

[http_service]
internal_port = 1314
force_https = true
auto_stop_machines = 'stop'
auto_start_machines = true
min_machines_running = 0

[[services]]
protocol = 'tcp'
internal_port = 1314

[[services.ports]]
port = 1314

[[vm]]
memory = '512mb'
cpu_kind = 'shared'
cpus = 1

部署命令
在准备好 fly.toml 文件后,你可以使用以下命令来部署你的应用到 Fly.io:

初始化 Fly.io 应用(如果尚未初始化)

fly launch

部署应用

fly deploy

确保你已经安装并配置好了 Fly.io 的 CLI 工具,并且已经登录到你的 Fly.io 账号。如果你还没有安装 Fly.io CLI,可以通过以下命令安装:

1
curl -L https://fly.io/install.sh | sh

安装完成后,使用 fly auth login 登录到你的 Fly.io 账号。

zeabur部署

zeabur.toml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
app = "ech0-noise"

[build]
dockerfile = "Dockerfile"
image = "noise233/echo-noise"

[env]
DB_TYPE = "postgres"
DB_HOST = 'example.aws.neon.tech' # 修改为数据库的HOST地址
DB_PORT = "5432"
DB_USER = 'noise_owner' # 修改为数据库的用户名
DB_PASSWORD = 'example' # 修改为数据库的密码
DB_NAME = 'noise' # 修改为数据库的名称
DB_SSL_MODE = "require"
CGO_ENABLED = "1"
TZ = "Asia/Shanghai"

[http_service]
internal_port = 1314
force_https = true

[[services]]
protocol = "tcp"
internal_port = 1314

[[services.ports]]
port = 1314

[[vm]]
memory = "512mb"
cpu_kind = "shared"
cpus = 1

部署命令:

1
zeabur deploy

Railway部署

railway.toml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
app = "ech0-noise"

[build]
dockerfile = "Dockerfile"
image = "noise233/echo-noise"

[env]
DB_TYPE = "postgres"
DB_HOST = 'example.aws.neon.tech' # 修改为数据库的HOST地址
DB_PORT = "5432"
DB_USER = 'noise_owner' # 修改为数据库的用户名
DB_PASSWORD = 'example' # 修改为数据库的密码
DB_NAME = 'noise' # 修改为数据库的名称
DB_SSL_MODE = "require"
CGO_ENABLED = "1"
TZ = "Asia/Shanghai"

[service]
internal_port = 1314
protocol = "tcp"

[service.ports]
port = 1314

[vm]
memory = "512mb"
cpu_kind = "shared"
cpus = 1

部署命令:

1
railway up

预览

注意⚠️

如果你是直接在平台拉取项目部署而不是通过命令部署,你需要拷贝fork本项目并将fly.toml、railway.toml、zeabur.toml文件放入根目录下才能一键部署

数据库连接部分

本地数据库直接docker部署即可

远程数据库服务则可以通过环境变量连接

连接远程 PostgreSQL:

1
2
3
4
5
6
7
8
9
10
11
12
docker run -d \
--name Ech0-Noise \
--platform linux/amd64 \
-p 1314:1314 \
-e DB_TYPE=postgres \
-e DB_HOST=your.postgres.host \
-e DB_PORT=5432 \
-e DB_USER=your_username \
-e DB_PASSWORD=your_password \
-e DB_NAME=noise \
-v /opt/data/images:/app/data/images \
noise233/echo-noise

连接远程 MySQL:

1
2
3
4
5
6
7
8
9
10
11
12
docker run -d \
--name Ech0-Noise \
--platform linux/amd64 \
-p 1314:1314 \
-e DB_TYPE=mysql \
-e DB_HOST=your.mysql.host \
-e DB_PORT=3306 \
-e DB_USER=your_username \
-e DB_PASSWORD=your_password \
-e DB_NAME=noise \
-v /opt/data/images:/app/data/images \
noise233/echo-noise

注意事项:

  1. 确保远程数据库允许外部连接
  2. 检查防火墙设置
  3. 使用正确的数据库连接信息
  4. 建议使用加密连接
  5. 注意数据库的字符集设置

对于 Neon PostgreSQL (地址https://console.neon.tech )这样的云数据库服务,需要使用特定的连接参数。以下是连接命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
docker run -d \
--name Ech0-Noise \
--platform linux/amd64 \
-p 1314:1314 \
-e DB_TYPE=postgres \
-e DB_HOST=your.host \
-e DB_PORT=5432 \
-e DB_USER=user_owner \
-e DB_PASSWORD=password \
-e DB_NAME=yourname \
-e DB_SSL_MODE=require \
-v /opt/data/images:/app/data/images \
noise233/echo-noise

注意事项:

  1. 添加了 DB_SSL_MODE=require 环境变量,因为 Neon 要求 SSL 连接
  2. 使用了连接 URL 中提供的主机名、用户名、密码和数据库名
  3. 保持图片目录的挂载

数据的备份恢复

对于所有数据库类型(SQLite/PostgreSQL/MySQL),点击后台数据库下载按钮后,都会先备份数据库文件

  • 然后会将包含数据库备份和图片打包成 zip 文件
  • zip 文件中会包含:
    • 数据库备份文件(.db/.sql)
    • images 目录下的所有图片
1
2
3
4
5
备份过程:
本地 -> 执行备份命令 -> 生成备份文件 -> 打包下载

恢复过程:
上传备份文件 -> 解压缩 -> 执行恢复命令 -> 导入到云数据库

恢复要求:

  • SQLite本地数据库备份和上传时默认使用的文件名是一致为noise.db
  • 非本地数据库PostgreSQL/MySQL请命名为database.sql并放入database.zip来恢复
  • 如果备份时zip中有图片文件夹则同时会恢复 images 目录下的所有图片

⚠️ :因PostgreSQL/MySQL云服务会有SSL连接、兼容版本号、数据表格式等要求,后台一键备份恢复不一定能满足你需要连接的远程数据库,请尽量前往服务商处下载备份

API指南🧭

先到后台获取api token,然后可以参考下面的命令运行或使用其它服务(记得将https://your.localhost.com 更改为你自己的服务地址)

1743847126537

1
2
3
4
5
6
7
8
# 发送纯文本信息
curl -X POST 'https://your.localhost.com/api/token/messages' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer c721249bd66e1133fba430ea9e3c32f1' \
-d '{
"content": "测试信息",
"type": "text"
}'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 方式1:使用 Markdown 语法发送文本
curl -X POST 'https://your.localhost.com/api/token/messages' \
-H 'Content-Type: application/json' \
-H 'Authorization: c721249bd66e1133fba430ea9e3c32f1' \
-d '{
"content": "# 标题\n这是一段文字\n![图片描述](https://example.com/image.jpg)",
"type": "text"
}'

# 方式2:使用 type: image 发送图片消息
curl -X POST 'https://your.localhost.com/api/token/messages' \
-H 'Content-Type: application/json' \
-H 'Authorization: c721249bd66e1133fba430ea9e3c32f1' \
-d '{
"content": "图片描述文字",
"type": "image",
"image": "https://example.com/image.jpg"
}'

如果你想使用session 认证方式

1
2
3
4
5
6
7
curl -v -X POST 'https://your.localhost.com/api/messages' \
-H 'Content-Type: application/json' \
--cookie "your_session_cookie" \
-d '{
"content": "测试信息",
"type": "text"
}'

对于图文混合消息,可以这样发送:

1
2
3
4
5
6
7
curl -X POST 'https://your.localhost.com/api/token/messages' \
-H 'Content-Type: application/json' \
-H 'Authorization: c721249bd66e1133fba430ea9e3c32f1' \
-d '{
"content": "# 这是标题\n\n这是一段文字说明\n\n![图片描述](https://example.com/image.jpg)\n\n继续写文字内容",
"type": "text"
}'
1
2
3
4
5
6
7
8
9
10
或者使用 multipart 类型:

curl -X POST 'https://your.localhost.com/api/token/messages' \
-H 'Content-Type: application/json' \
-H 'Authorization: c721249bd66e1133fba430ea9e3c32f1' \
-d '{
"content": "# 这是标题\n\n这是一段文字说明",
"type": "multipart",
"image": "https://example.com/image.jpg"
}

API 文档(待增加)

公共接口

1. 获取前端配置

  • 路径: /api/frontend/config
  • 方法: GET
  • 描述: 获取前端配置信息
  • 示例请求:
    1
    curl http://localhost:8080/api/frontend/config

2. 用户登录

  • 路径: /api/login
  • 方法: POST
  • 描述: 用户登录接口
  • 请求体:
    1
    2
    3
    4
    {
    "username": "admin",
    "password": "password"
    }
  • 示例请求:
    1
    2
    3
    curl -X POST http://localhost:8080/api/login \
    -H "Content-Type: application/json" \
    -d '{"username":"admin","password":"password"}'

3. 用户注册

  • 路径: /api/register
  • 方法: POST
  • 描述: 用户注册接口
  • 请求体:
    1
    2
    3
    4
    5
    {
    "username": "newuser",
    "password": "password",
    "email": "[email protected]"
    }
  • 示例请求:
    1
    2
    3
    curl -X POST http://localhost:8080/api/register \
    -H "Content-Type: application/json" \
    -d '{"username":"newuser","password":"password","email":"[email protected]"}'

4. 获取系统状态

  • 路径: /api/status
  • 方法: GET
  • 描述: 获取系统运行状态
  • 示例请求:
    1
    curl http://localhost:8080/api/status

5. 消息相关公共接口

5.1 获取所有消息

  • 路径: /api/messages
  • 方法: GET
  • 描述: 获取所有公开消息
  • 示例请求:
    1
    curl http://localhost:8080/api/messages

5.2 获取单条消息

  • 路径: /api/messages/:id
  • 方法: GET
  • 描述: 获取指定ID的消息
  • 示例请求:
    1
    curl http://localhost:8080/api/messages/1

5.3 分页获取消息

  • 路径: /api/messages/page
  • 方法: POST或GET
  • 描述: 分页获取消息列表
  • 请求体:
    1
    2
    3
    4
    {
    "page": 1,
    "pageSize": 10
    }
  • 示例请求:
    1
    2
    3
    curl -X POST http://localhost:8080/api/messages/page \
    -H "Content-Type: application/json" \
    -d '{"page":1,"pageSize":10}'

5.4 获取消息日历数据

  • 路径: /api/messages/calendar
  • 方法: GET
  • 描述: 获取消息发布热力图数据
  • 示例请求:
    1
    curl http://localhost:8080/api/messages/calendar

5.5 搜索消息

  • 路径: /api/messages/search
  • 方法: GET
  • 参数:
    • keyword: 搜索关键词
    • page: 页码
    • pageSize: 每页数量
  • 示例请求:
    1
    curl "http://localhost:8080/api/messages/search?keyword=测试&page=1&pageSize=10"

6. RSS 相关接口

6.1 获取 RSS 订阅

  • 路径: /rss
  • 方法: GET
  • 描述: 获取 RSS 订阅内容
  • 示例请求:
    1
    curl http://localhost:1314/rss

需要认证的接口

1. 消息操作接口

1.1 发布消息

  • 路径: /api/messages
  • 方法: POST
  • 描述: 发布新消息
  • 请求体:
    1
    2
    3
    4
    5
    {
    "content": "消息内容",
    "private": false,
    "imageURL": ""
    }
  • 示例请求:
    1
    2
    3
    4
    curl -X POST http://localhost:8080/api/messages \
    -H "Content-Type: application/json" \
    -H "Cookie: session=xxx" \
    -d '{"content":"测试消息","private":false}'

1.2 更新消息

  • 路径: /api/messages/:id
  • 方法: PUT
  • 描述: 更新指定消息
  • 请求体:
    1
    2
    3
    {
    "content": "更新后的内容"
    }
  • 示例请求:
    1
    2
    3
    4
    curl -X PUT http://localhost:8080/api/messages/1 \
    -H "Content-Type: application/json" \
    -H "Cookie: session=xxx" \
    -d '{"content":"更新后的内容"}'

1.3 删除消息

  • 路径: /api/messages/:id
  • 方法: DELETE
  • 描述: 删除指定消息
  • 示例请求:
    1
    2
    curl -X DELETE http://localhost:8080/api/messages/1 \
    -H "Cookie: session=xxx"

2. 用户相关接口

2.1 获取用户信息

  • 路径: /api/user
  • 方法: GET
  • 描述: 获取当前登录用户信息
  • 示例请求:
    1
    2
    curl http://localhost:8080/api/user \
    -H "Cookie: session=xxx"

2.2 修改密码

  • 路径: /api/user/change_password
  • 方法: PUT
  • 请求体:
    1
    2
    3
    4
    {
    "oldPassword": "旧密码",
    "newPassword": "新密码"
    }
  • 示例请求:
    1
    2
    3
    4
    curl -X PUT http://localhost:8080/api/user/change_password \
    -H "Content-Type: application/json" \
    -H "Cookie: session=xxx" \
    -d '{"oldPassword":"old","newPassword":"new"}'

2.3 更新用户信息

  • 路径: /api/user/update
  • 方法: PUT
  • 示例请求:
    1
    2
    3
    4
    curl -X PUT http://localhost:8080/api/user/update \
    -H "Content-Type: application/json" \
    -H "Cookie: session=xxx" \
    -d '{"username":"newname"}'

2.4 退出登录

  • 路径: /api/user/logout
  • 方法: POST
  • 示例请求:
    1
    2
    curl -X POST http://localhost:8080/api/user/logout \
    -H "Cookie: session=xxx"

3. Token 相关接口

3.1 获取用户 Token

  • 路径: /api/user/token
  • 方法: GET
  • 示例请求:
    1
    2
    curl http://localhost:8080/api/user/token \
    -H "Cookie: session=xxx"

3.2 重新生成 Token

  • 路径: /api/user/token/regenerate
  • 方法: POST
  • 示例请求:
    1
    2
    curl -X POST http://localhost:8080/api/user/token/regenerate \
    -H "Cookie: session=xxx"

4. 系统设置接口

4.1 更新系统设置

  • 路径: /api/settings
  • 方法: PUT
  • 请求体:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    {
    "allowRegistration": true,
    "frontendSettings": {
    "siteTitle": "网站标题",
    "subtitleText": "副标题",
    "avatarURL": "头像URL",
    "username": "显示用户名",
    "description": "描述",
    "backgrounds": ["背景图URL"],
    "cardFooterTitle": "页脚标题",
    "cardFooterLink": "页脚链接",
    "pageFooterHTML": "页脚HTML",
    "rssTitle": "RSS标题",
    "rssDescription": "RSS描述",
    "rssAuthorName": "RSS作者",
    "rssFaviconURL": "RSS图标URL",
    "walineServerURL": "评论系统URL"
    }
    }
  • 示例请求:
    1
    2
    3
    4
    curl -X PUT http://localhost:8080/api/settings \
    -H "Content-Type: application/json" \
    -H "Cookie: session=xxx" \
    -d '{"allowRegistration":true,"frontendSettings":{"siteTitle":"我的网站"}}'

5. 备份相关接口

5.1 下载备份

  • 路径: /api/backup/download
  • 方法: GET
  • 示例请求:
    1
    2
    3
    curl http://localhost:8080/api/backup/download \
    -H "Cookie: session=xxx" \
    --output backup.sql

5.2 恢复备份

  • 路径: /api/backup/restore
  • 方法: POST
  • 描述: 从备份文件恢复数据
  • 示例请求:
    1
    2
    3
    curl -X POST http://localhost:8080/api/backup/restore \
    -H "Cookie: session=xxx" \
    -F "[email protected]"

6. 图片上传接口

6.1 上传图片

  • 路径: /api/images/upload
  • 方法: POST
  • 描述: 上传图片文件
  • 示例请求:
    1
    2
    3
    curl -X POST http://localhost:8080/api/images/upload \
    -H "Cookie: session=xxx" \
    -F "[email protected]"

7.推送配置路由使用说明

获取推送配置

  • 路径: /api/notify/config
  • 方法: GET
  • 描述: 获取当前推送渠道配置
  • 示例请求:
1
2
curl -X GET http://localhost:8080/api/notify/config \
-H "Cookie: session=xxx"

保存推送配置

  • 路径: /api/notify/config
  • 方法: PUT
  • 描述: 更新推送渠道配置
  • 请求体示例:
1
2
3
4
5
6
7
8
9
10
11
12
{
"webhookEnabled": true,
"webhookURL": "https://webhook.example.com",
"telegramEnabled": true,
"telegramToken": "bot123:ABC",
"telegramChatID": "-100123456",
"weworkEnabled": false,
"weworkKey": "",
"feishuEnabled": true,
"feishuWebhook": "https://open.feishu.cn/xxx",
"feishuSecret": "signature_key"
}
  • 示例请求:
1
2
3
4
5
6
7
curl -X PUT http://localhost:8080/api/notify/config \
-H "Cookie: session=xxx" \
-H "Content-Type: application/json" \
-d '{
"webhookEnabled": true,
"webhookURL": "https://webhook.example.com"
}'

测试推送

  • 路径: /api/notify/test
  • 方法: POST
  • 描述: 测试指定推送渠道
  • 请求体示例:
1
2
3
{
"type": "telegram"
}
  • 示例请求:
1
2
3
4
curl -X POST http://localhost:8080/api/notify/test \
-H "Cookie: session=xxx" \
-H "Content-Type: application/json" \
-d '{"type": "telegram"}'

发送推送

  • 路径: /api/notify/send
  • 方法: POST
  • 描述: 手动触发推送(需已配置推送渠道)
  • 请求体示例:
1
2
3
4
5
{
"content": "测试消息内容",
"images": ["https://example.com/image.jpg"],
"format": "markdown"
}
  • 示例请求:
1
2
3
4
curl -X POST http://localhost:8080/api/notify/send \
-H "Cookie: session=xxx" \
-H "Content-Type: application/json" \
-d '{"content": "紧急通知!"}'

注意事项:

  1. 所有需要认证的接口都需要在请求头中携带有效的 session cookie
  2. 部分接口可能需要管理员权限
  3. 所有请求示例中的域名和端口号需要根据实际部署情况调整
  4. 文件上传接口需要使用 multipart/form-data 格式
  5. Token 认证接口可以使用 Token 替代 session 进行认证

发布说明

目前会构建两个版本,

稳定版:latest镜像

实验版:last镜像

如果你需要构建自己的镜像发布-示例:

1
docker buildx build --platform linux/amd64,linux/arm64 -t noise233/echo-noise:latest --push --no-cache .

Memos数据库迁移示例

其中,你需要设置设置源数据库和目标数据库的路径,源数据库为memos_prod.db(memos数据)目标数据库为database.db(本站数据库),你还需要修改构建插入的数据中的用户名为你自己的用户名,分别迁移了原文本内容、发布时间,可以在noise/memos迁移文件夹中找到该脚本

,运行python3 main.py即可,

1744202949838

迁移结束后将你的数据库文件和原图片文件夹(有的话)打包为zip格式,进入站点后台选择恢复数据上传即可。

Popclip发送扩展

选中后自动识别安装,发送时会自动添加一个popclip开头的标签,token可在后台找到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// #popclip extension for Send to Shuo
// name: 说说笔记
// icon: square filled 说
// language: javascript
// module: true
// entitlements: [network]
// options: [{
// identifier: "siteUrl",
// label: "服务端地址",
// type: "string",
// defaultValue: "https://note.noisework.cn",
// description: "请确保地址正确,不要带末尾斜杠"
// }, {
// identifier: "token",
// label: "API Token",
// type: "string",
// description: "从设置页面获取最新Token"
// }]

async function sendToShuo(input, options) {
try {
// 参数预处理
const siteUrl = (options.siteUrl || "").replace(/\/+$/g, "");
const token = (options.token || "").trim();
const content = (input.text || "").trim();

// 验证参数
if (!/^https:\/\/[\w.-]+(:\d+)?$/.test(siteUrl)) {
throw new Error("地址格式错误,示例: https://note.noisework.cn");
}
if (!token) throw new Error("Token不能为空");
if (!content) throw new Error("选中文本不能为空");

// 发送请求
await sendRequestWithXMLHttpRequest(siteUrl, token, content);
PopClip.showText("✓ 发送成功");
} catch (error) {
handleRequestError(error);
}
}

// 使用 XMLHttpRequest 实现网络请求
function sendRequestWithXMLHttpRequest(siteUrl, token, content) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
const url = `${siteUrl}/api/token/messages`;

xhr.open("POST", url, true);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.setRequestHeader("Authorization", `Bearer ${token}`);

xhr.timeout = 10000; // 设置超时时间(10秒)

// 设置回调函数
xhr.onreadystatechange = () => {
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.responseText);
} else {
let errorMsg = `请求失败 (${xhr.status})`;
try {
const data = JSON.parse(xhr.responseText);
errorMsg = data.message || errorMsg;
} catch {}
reject(new Error(errorMsg));
}
}
};

// 处理网络错误
xhr.onerror = () => reject(new Error("网络错误"));

// 处理超时错误
xhr.ontimeout = () => reject(new Error("请求超时"));

try {
// 发送请求
const payload = JSON.stringify({
content: `#Popclip\n${content}`,
type: "text"
});
xhr.send(payload);
} catch (error) {
reject(new Error("请求发送失败: " + error.message));
}
});
}

// 错误处理
function handleRequestError(error) {
console.error("请求错误:", error);

const errorMap = {
"Failed to fetch": "无法连接到服务器",
"aborted": "请求超时",
"网络错误": "网络错误",
"401": "认证失败,请检查Token",
"404": "API地址不存在"
};

const message = Object.entries(errorMap).find(([key]) =>
error.message.includes(key)
)?.[1] || `请求错误: ${error.message.split('\n')[0].slice(0, 50)}`;

PopClip.showText(`❌ ${message}`);
}

exports.actions = [{
title: "发送至说说笔记",
code: sendToShuo,
icon: "square filled 说"
}];

Web组件示例

如果你想将内容作为说说嵌入或结合到你的网站、博客可以参考

说明:host为站点地址,limit为每页内容数量,domId为容器名,下面的代码展示了使用js来请求数据内容到前端并渲染处理的基本框架,其余需要你自己再丰富css样式和你自己的页面

html前端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<link rel="stylesheet" href="./assets/css/note.css">
<!-- note容器部分 -->
<div id="note" class="note page active">
<div class="note-container">
<div class="loading-wrapper" style="text-align: center; padding: 20px;">
加载中...
</div>
</div>
<script type="text/javascript">
var note = {
host: 'https://note.noisework.cn', //请修改为你自己的站点地址
limit: '10',
domId: '#note'
}
</script>
<!-- 添加note.js脚本 -->
<script type="text/javascript" src="./assets/js/note.js"></script>

note.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
// Note says content loading script
document.addEventListener('DOMContentLoaded', function() {
// get parameters from global configuration
const config = window.note || {
host: 'https://note.noisework.cn',
limit: '10',
domId: '#note'
};

// 修改容器选择器
const container = document.querySelector('#note .note-container');
let currentPage = 1;
let isLoading = false;
let hasMore = true;

// create load more button
const loadMoreBtn = document.createElement('button');
loadMoreBtn.id = 'load-more-note';
loadMoreBtn.className = 'load-more';
loadMoreBtn.textContent = '加载更多';
loadMoreBtn.style.display = 'none';
loadMoreBtn.addEventListener('click', loadMoreContent);

// create already loaded all prompt
const loadedAll = document.createElement('div');
loadedAll.id = 'loaded-all-note';
loadedAll.className = 'loaded-all';
loadedAll.textContent = '已加载全部';
loadedAll.style.display = 'none';

container.appendChild(loadMoreBtn);
container.appendChild(loadedAll);

// initial load
loadInitialContent();

async function loadInitialContent() {
try {
console.log(`请求URL: ${config.host}/api/messages/page?page=${currentPage}&pageSize=${config.limit}`);
const response = await fetch(`${config.host}/api/messages/page?page=${currentPage}&pageSize=${config.limit}`);

if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}

const result = await response.json();
console.log('API响应数据:', result);

// 修改为检查result.data.items
if (result && result.code === 1 && result.data && result.data.items && Array.isArray(result.data.items)) {
const sortedData = result.data.items.sort((a, b) =>
new Date(b.created_at) - new Date(a.created_at)
);
renderMessages(sortedData);

if (result.data.items.length >= config.limit) {
loadMoreBtn.style.display = 'block';
} else {
loadedAll.style.display = 'block';
hasMore = false;
}
} else {
container.querySelector('.loading-wrapper').textContent = '暂无内容';
hasMore = false;
}
} catch (error) {
console.error('加载内容失败:', error);
container.querySelector('.loading-wrapper').textContent = '加载失败,请刷新重试';
}
}

async function loadMoreContent() {
if (isLoading || !hasMore) return;

isLoading = true;
loadMoreBtn.textContent = '加载中...';
currentPage++;

try {
const response = await fetch(`${config.host}/api/messages/page?page=${currentPage}&pageSize=${config.limit}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();

// 同样修改为检查result.data.items
if (result && result.code === 1 && result.data && result.data.items && Array.isArray(result.data.items)) {
const sortedData = result.data.items.sort((a, b) =>
new Date(b.created_at) - new Date(a.created_at)
);
renderMessages(sortedData);

if (result.data.items.length < config.limit) {
loadMoreBtn.style.display = 'none';
loadedAll.style.display = 'block';
hasMore = false;
}
} else {
loadMoreBtn.style.display = 'none';
loadedAll.style.display = 'block';
hasMore = false;
}
} catch (error) {
console.error('加载更多内容失败:', error);
currentPage--;
} finally {
isLoading = false;
loadMoreBtn.textContent = '加载更多';
}
}

function renderMessages(messages) {
const loadingWrapper = container.querySelector('.loading-wrapper');
if (loadingWrapper) {
loadingWrapper.style.display = 'none';
}

messages.forEach(message => {
const messageElement = createMessageElement(message);
container.insertBefore(messageElement, loadMoreBtn);
});
}

function createMessageElement(message) {
const messageDiv = document.createElement('div');
messageDiv.className = 'rssmergecard';

const contentDiv = document.createElement('div');
contentDiv.className = 'rssmergecard-content';

const title = document.createElement('h3');
title.className = 'rssmergecard-title';
title.textContent = message.username || '匿名用户';

const description = document.createElement('div');
description.className = 'rssmergecard-description';

// 解析Markdown内容和特殊链接
let processedContent = message.content || '无内容';
processedContent = parseMarkdown(processedContent);
processedContent = parseSpecialLinks(processedContent);
description.innerHTML = processedContent;

// 如果有图片则添加图片
if (message.image_url) {
const img = document.createElement('img');
img.src = message.image_url.startsWith('http') ? message.image_url : config.host + message.image_url;
img.style.maxWidth = '100%';
img.style.borderRadius = '6px';
img.style.margin = '10px 0';
description.appendChild(img);
}

const metaDiv = document.createElement('div');
metaDiv.className = 'rssmergecard-meta';

const timeSpan = document.createElement('span');
timeSpan.className = 'rssmergecard-time';
timeSpan.textContent = formatDate(message.created_at);

metaDiv.appendChild(timeSpan);
contentDiv.appendChild(title);
contentDiv.appendChild(description);
contentDiv.appendChild(metaDiv);
messageDiv.appendChild(contentDiv);

return messageDiv;
}

function parseMarkdown(content) {
// 处理标题
content = content.replace(/^#\s(.+)$/gm, '<h1>$1</h1>');
content = content.replace(/^##\s(.+)$/gm, '<h2>$1</h2>');
content = content.replace(/^###\s(.+)$/gm, '<h3>$1</h3>');

// 处理图片 ![alt](url)
content = content.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, '<img src="$2" alt="$1" style="max-width:100%;border-radius:6px;margin:10px 0;">');

// 处理链接 [text](url)
content = content.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank">$1</a>');

// 处理粗体 **text**
content = content.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');

// 处理斜体 *text*
content = content.replace(/\*([^*]+)\*/g, '<em>$1</em>');

// 处理代码块 `code`
content = content.replace(/`([^`]+)`/g, '<code>$1</code>');

return content;
}

function parseSpecialLinks(content) {
// 定义各种平台的正则表达式
const BILIBILI_REG = /https:\/\/www\.bilibili\.com\/video\/((av[\d]{1,10})|(BV[\w]{10}))\/?/g;
const BILIBILI_A_TAG_REG = /<a\shref="https:\/\/www\.bilibili\.com\/video\/((av[\d]{1,10})|(BV[\w]{10}))\/?">.*<\/a>/g;
const QQMUSIC_REG = /<a\shref="https\:\/\/y\.qq\.com\/.*(\/[0-9a-zA-Z]+)(\.html)?".*?>.*?<\/a>/g;
const QQVIDEO_REG = /<a\shref="https:\/\/v\.qq\.com\/.*\/([a-zA-Z0-9]+)\.html".*?>.*?<\/a>/g;
const SPOTIFY_REG = /<a\shref="https:\/\/open\.spotify\.com\/(track|album)\/([\s\S]+)".*?>.*?<\/a>/g;
const YOUKU_REG = /<a\shref="https:\/\/v\.youku\.com\/.*\/id_([a-zA-Z0-9=]+)\.html".*?>.*<\/a>/g;
const YOUTUBE_REG = /<a\shref="https:\/\/(www\.youtube\.com\/watch\?v=|youtu\.be\/)([a-zA-Z0-9_-]{11})".*?>.*<\/a>/g;
const NETEASE_MUSIC_REG = /<a\shref="https?:\/\/music\.163\.com\/.*?id=(\d+)<\/a>/g;

// 解析各种链接
return content
.replace(BILIBILI_REG, "<div class='video-wrapper'><iframe src='https://www.bilibili.com/blackboard/html5mobileplayer.html?bvid=$1&as_wide=1&high_quality=1&danmaku=0' scrolling='no' border='0' frameborder='no' framespacing='0' allowfullscreen='true' style='position:absolute;height:100%;width:100%;'></iframe></div>")
.replace(YOUTUBE_REG, "<div class='video-wrapper'><iframe src='https://www.youtube.com/embed/$2' title='YouTube video player' frameborder='0' allow='accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture' allowfullscreen></iframe></div>")
.replace(NETEASE_MUSIC_REG, "<div class='music-wrapper'><meting-js auto='https://music.163.com/#/song?id=$1'></meting-js></div>")
.replace(QQMUSIC_REG, "<meting-js auto='https://y.qq.com/n/yqq/song$1.html'></meting-js>")
.replace(QQVIDEO_REG, "<div class='video-wrapper'><iframe src='//v.qq.com/iframe/player.html?vid=$1' allowFullScreen='true' frameborder='no'></iframe></div>")
.replace(SPOTIFY_REG, "<div class='spotify-wrapper'><iframe style='border-radius:12px' src='https://open.spotify.com/embed/$1/$2?utm_source=generator&theme=0' width='100%' frameBorder='0' allowfullscreen='' allow='autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture' loading='lazy'></iframe></div>")
.replace(YOUKU_REG, "<div class='video-wrapper'><iframe src='https://player.youku.com/embed/$1' frameborder=0 'allowfullscreen'></iframe></div>");
}

function formatDate(dateString) {
if (!dateString) return '未知时间';
return new Date(dateString).toLocaleString();
}
});

示例note.css

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
/* 基础卡片样式 */
.rssmergecard {
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
margin-bottom: 20px;
padding: 20px;
transition: all 0.3s ease;
}

.rssmergecard:hover {
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
}

/* 标题样式 */
.rssmergecard-title {
color: #333;
font-size: 18px;
margin: 0 0 10px 0;
}

/* 内容样式 - 支持Markdown渲染 */
.rssmergecard-description {
color: #555;
line-height: 1.6;
font-size: 15px;
}

.rssmergecard-description p {
margin: 10px 0;
}

.rssmergecard-description a {
color: #3498db;
text-decoration: none;
}

.rssmergecard-description a:hover {
text-decoration: underline;
}

.rssmergecard-description img {
max-width: 100%;
height: auto;
border-radius: 4px;
}

/* 元信息样式 */
.rssmergecard-meta {
margin-top: 15px;
font-size: 13px;
color: #999;
}

/* 加载更多按钮样式 */
.load-more {
background: #3498db;
color: white;
border: none;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
margin: 20px auto;
display: block;
}

.load-more:hover {
background: #2980b9;
}

.loaded-all {
text-align: center;
color: #999;
font-size: 14px;
margin: 20px 0;
}

/* 特殊链接卡片样式 */
.media-card {
background: #f8f9fa;
border-left: 4px solid #3498db;
padding: 15px;
margin: 15px 0;
border-radius: 0 4px 4px 0;
}

.media-card-title {
font-weight: bold;
margin-bottom: 5px;
}

.video-wrapper {
position: relative;
padding-bottom: 56.25%; /* 16:9 */
height: 0;
margin: 15px 0;
}

.video-wrapper iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: 8px;
}

.music-wrapper, .spotify-wrapper {
margin: 15px 0;
border-radius: 8px;
overflow: hidden;
min-height: 86px; /* 确保有足够高度显示播放器 */
}

.music-wrapper meting-js {
width: 100%;
height: 86px;
}

ios快捷指令

使用快捷指令发布内容到站内,获取:https://www.icloud.com/shortcuts/8ba1240ab39d4bf2b4a02b69a5cc12bf

idpz8Ea9DQMfyex

问题🙋

数据库可以直接迁移吗

1、直接上传至部署时挂载的路径中,重新启用,或者在容器文件夹/app/data/noise.db直接替换即可

2、使用后台数据库管理备份功能,支持一键下载、上传

​ 数据库文件下载为zip格式,上传也必须为zip,本地数据库恢复包中必须有noise.db文件

关于魔改指南🌈

👉如何自定义化前端数据后添加到数据库?

需要在setting.go、migrate.go、models.go、controllers.go同时写入前端参数的后端定义,并修改前端参数信息为后端可读取的参数,其中controllers.go为控制器

  • database.go 用于数据库连接管理
  • migrate.go 用于数据库迁移和数据初始化

👉前端基本在web目录下,目前模版文件为components目录文件,pages下index.vue为父级模版

👉建议:不要和我一样在同一个文件里修改添加,造成一个文件上千行代码…请尽量使用父子层级来添加代码

MAC摸鱼神器-半透明web浏览器

2025-04-12 10:00:00

cov111er

TransparentSafari是一个比较有意思的项目,没有过多介绍,一般人还真难发现它,发行包才136kb大小,用途嘛…可以适当摸鱼,透明度可以自由调节,网址书签可以在内部添加

项目地址:https://github.com/976431yang/TransparentSafari

3432332r

131321