✨ 搭建专属于你的消息推送服务,支持多种消息推送方式,支持 Markdown,仅单可执行文件,开箱即用✨
程序下载 · 部署教程 · 使用教程 · 意见反馈 · 在线演示
[!WARNING] 原域名(msgpusher.com)将于 2026-11-24 下线,请尽快迁移至 [***]
[!WARNING] 从
v0.3版本升级到v0.4版本需要手动迁移数据库,具体方法见迁移数据库。
部署:docker run -d --restart always --name message-pusher -p 3000:3000 -e TZ=Asia/Shanghai -v /home/ubuntu/data/message-pusher:/data justsong/message-pusher
如果无法拉取,请将 justsong/message-pusher 替换为 ghcr.io/songquanpeng/message-pusher。
更新:docker run --rm -v /var/run/docker.sock:/var/run/docker.sock containrrr/watchtower -cR
开放的端口号为 3000,之后用 Nginx 配置域名,反代以及 SSL 证书即可,具体参考详细部署教程。
数据将会保存在宿主机的 /home/ubuntu/data/message-pusher 目录(只有一个 SQLite 数据库文件),请确保该目录存在且具有写入权限,或者更改为合适的目录。
Nginx 的参考配置:
server{
server_name push.justsong.cn; # 请根据实际情况修改你的域名
location / {
client_max_body_size 64m;
proxy_http_version 1.1;
proxy_pass http://localhost:3000; # 请根据实际情况修改你的端口
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_cache_bypass $http_upgrade;
proxy_set_header Accept-Encoding gzip;
}
}
之后使用 Let's Encrypt 的 certbot 配置 HTTPS:
# Ubuntu 安装 certbot:
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
# 生成证书 & 修改 Nginx 配置
sudo certbot --nginx
# 根据指示进行操作
# 重启 Nginx
sudo service nginx restart
git clone https://github.com/songquanpeng/message-pusher.git
cd message-pusher/web
npm install
npm run build
cd ..
go mod download
go build -ldflags "-s -w" -o message-pusher
chmod u+x message-pusher
./message-pusher --port 3000 --log-dir ./logs
root,密码为 123456。如果服务需要长久运行,只是单纯地启动是不够的,详细部署教程。
如果需要使用 WebSocket 客户端推送功能,则 Nginx 的配置文件中 proxy_read_timeout 和 proxy_send_timeout 务必设置超过 1 分钟。
推荐设置:
proxy_read_timeout 300s;
proxy_send_timeout 300s;
| 通道类型 | title | description | content | url | to | Markdown 支持 |
|---|---|---|---|---|---|---|
email | ✅ | ✅ | ✅ | ❌ | ✅️ | ✅️ |
test | ✅ | ✅ | ✅ | ✅️ | ✅️ | ✅ |
corp_app | ✅ | ✅ | ✅ | ✅️ | ✅ | ✅ |
corp | ❌ | ✅ | ✅ | ✅️ | ✅️ | ✅ |
lark | ❌ | ✅ | ✅ | ❌ | ✅ | ✅ |
lark_app | ❌ | ✅ | ✅ | ❌️ | ✅ | ✅ |
ding | ✅ | ✅ | ✅ | ✅️ | ✅ | ✅ |
bark | ✅ | ✅ | ✅ | ✅️ | ❌ | ✅ |
client | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
telegram | ❌ | ❌ | ✅ | ❌ | ✅ | ✅ |
discord | ❌ | ❌ | ✅ | ❌ | ✅ | ❌ |
tencent_alarm | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
[!NOTE]
- 对于大部分通道,
description字段和content是不能同时存在的,如果你只需要文字消息,请使用description字段,如果你需要发送 Markdown 消息,请使用content字段。- 部分通道的 Markdown 支持实际上是通过 URL 跳转到本系统所渲染的消息详情实现的,其他通道的 Markdown 支持受限于具体的通道,支持的语法并不统一。
示例:
Bash 示例
#!/bin/bash
MESSAGE_PUSHER_SERVER="https://push.justsong.cn"
MESSAGE_PUSHER_USERNAME="test"
MESSAGE_PUSHER_TOKEN="666"
function send_message {
# POST Form
curl -s -X POST "$MESSAGE_PUSHER_SERVER/push/$MESSAGE_PUSHER_USERNAME" \
-d "title=$1&description=$2&content=$3&token=$MESSAGE_PUSHER_TOKEN" \
>/dev/null
}
function send_message_with_json {
# POST JSON
curl -s -X POST "$MESSAGE_PUSHER_SERVER/push/$MESSAGE_PUSHER_USERNAME" \
-H 'Content-Type: application/json' \
-d '{"title":"'"$1"'","desp":"'"$2"'", "content":"'"$3"'", "token":"'"$MESSAGE_PUSHER_TOKEN"'"}' \
>/dev/null
}
send_message 'title' 'description' 'content'
另一个版本:
MESSAGE_PUSHER_SERVER="https://push.justsong.cn"
MESSAGE_PUSHER_USERNAME="test"
MESSAGE_PUSHER_TOKEN="666"
MESSAGE_PUSHER_CHANNEL="lark"
sendmsg() {
if [ -t 0 ]; then
local param="$*"
else
local param=$(
Python 示例
import requests
SERVER = "https://push.justsong.cn"
USERNAME = "test"
TOKEN = "666"
def send_message(title, description, content):
# GET 方式
# res = requests.get(f"{SERVER}/push/{USERNAME}?title={title}"
# f"&description={description}&content={content}&token={TOKEN}")
# POST 方式
res = requests.post(f"{SERVER}/push/{USERNAME}", json={
"title": title,
"description": description,
"content": content,
"token": TOKEN
})
res = res.json()
if res["success"]:
return None
else:
return res["message"]
error = send_message("标题", "描述", "**Markdown 内容**")
if error:
print(error)
Go 示例
package main
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
)
var serverAddress = "https://push.justsong.cn"
var username = "test"
var token = "666"
type request struct {
Title string `json:"title"`
Description string `json:"description"`
Content string `json:"content"`
URL string `json:"url"`
Channel string `json:"channel"`
Token string `json:"token"`
}
type response struct {
Success bool `json:"success"`
Message string `json:"message"`
}
func SendMessage(title string, description string, content string) error {
req := request{
Title: title,
Description: description,
Content: content,
Token: token,
}
data, err := json.Marshal(req)
if err != nil {
return err
}
resp, err := http.Post(fmt.Sprintf("%s/push/%s", serverAddress, username),
"application/json", bytes.NewBuffer(data))
if err != nil {
return err
}
var res response
err = json.NewDecoder(resp.Body).Decode(&res)
if err != nil {
return err
}
if !res.Success {
return errors.New(res.Message)
}
return nil
}
func SendMessageWithForm(title string, description string, content string) error {
resp, err := http.PostForm(fmt.Sprintf("%s/push/%s", serverAddress, username),
url.Values{"title": {title}, "description": {description}, "content": {content}, "token": {token}})
if err != nil {
return err
}
var res response
err = json.NewDecoder(resp.Body).Decode(&res)
if err != nil {
return err
}
if !res.Success {
return errors.New(res.Message)
}
return nil
}
func main() {
//err := SendMessage("标题", "描述", "**Markdown 内容**")
err := SendMessageWithForm("标题", "描述", "**Markdown 内容**")
if err != nil {
fmt.Println("推送失败:" + err.Error())
} else {
fmt.Println("推送成功!")
}
}
C# 示例
using Newtonsoft.Json;
using RestSharp;
namespace Demo
{
public class Program
{
public static void Main(string[] args)
{
//推送消息
var sendMsg = MessagePusherTool.SendMessage("标题", "描述", "**Markdown 内容**");
if(sendMsg.Success)
{
Console.WriteLine($"推送成功!");
}
else
{
Console.WriteLine($"推送失败:{sendMsg.Message}");
}
}
}
/// <summary>
/// 消息推送工具
/// </summary>
/// <remarks>
/// 开源地址:https://github.com/songquanpeng/message-pusher
/// 支持:Framework、Net3.1、Net5、Net6
/// 引用包:
/// dotnet add package Newtonsoft.Json -v 13.0.2
/// dotnet add package RestSharp -v 108.0.3
/// </remarks>
public class MessagePusherTool
{
/// <summary>
/// ServerAddress
/// </summary>
public const string ServerAddress = "https://push.justsong.cn";
/// <summary>
/// UserName
/// </summary>
public const string UserName = "test";
/// <summary>
/// Token
/// </summary>
public const string Token = "666";
/// <summary>
/// SendMessage
/// </summary>
/// <param name="title">title</param>
/// <param name="description">description</param>
/// <param name="content">content</param>
/// <returns></returns>
public static Response SendMessage(string title, string description, string content)
{
var requestData = new Request()
{
Title = title,
Description = description,
Content = content,
Token = Token,
};
var url = $"{ServerAddress}";
var client = new RestClient(url);
var request = new RestRequest($"push/{UserName}", Method.Post);
request.AddJsonBody(requestData);
var response = client.Execute(request);
var responseData = response.Content;
var responseJson = JsonConvert.DeserializeObject<Response>(responseData);
return responseJson;
}
/// <summary>
/// Request
/// </summary>
public class Request
{
/// <summary>
/// Title
/// </summary>
[JsonProperty(PropertyName = "title")]
public string Title { get; set; }
/// <summary>
/// Description
/// </summary>
[JsonProperty(PropertyName = "description")]
public string Description { get; set; }
/// <summary>
/// Content
/// </summary>
[JsonProperty(PropertyName = "content")]
public string Content { get; set; }
/// <summary>
/// URL
/// </summary>
[JsonProperty(PropertyName = "url")]
public string URL { get; set; }
/// <summary>
/// Channel
/// </summary>
[JsonProperty(PropertyName = "channel")]
public string Channel { get; set; }
/// <summary>
/// Token
/// </summary>
[JsonProperty(PropertyName = "token")]
public string Token { get; set; }
}
/// <summary>
/// Response
/// </summary>
public class Response
{
/// <summary>
/// Success
/// </summary>
[JsonProperty(PropertyName = "success")]
public bool Success { get; set; }
/// <summary>
/// Message
/// </summary>
[JsonProperty(PropertyName = "message")]
public string Message { get; set; }
}
}
}
Node.js 示例
const axios = require('axios');
const querystring = require('querystring');
const MESSAGE_PUSHER_SERVER = 'https://push.justsong.cn'
const MESSAGE_PUSHER_USERNAME = 'test'
const MESSAGE_PUSHER_TOKEN = '666'
async function send_message(title, description, content) {
try {
const postData = querystring.stringify({
title: title,
desp: description,
content: content,
token: MESSAGE_PUSHER_TOKEN,
})
const response = await axios.post(`${MESSAGE_PUSHER_SERVER}/push/${MESSAGE_PUSHER_USERNAME}`, postData, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
})
if (response.data.success) {
return response.data
}
} catch (error) {
if (error.response) {
return error.response.data
} else {
throw error
}
}
}
send_message('标题', '描述', 'Markdown 内容') .then((response) => { if (response.success) { console.log('推送成功:', response) } else { console.log('推送失败:', response) } }, (error) => { console.log(error.message); })
欢迎 PR 添加更多语言的示例。
## 迁移数据库
此处均以 SQLite 为例,其他数据库请自行修改。我已通过 ChatGPT 将其翻译成对应的 SQL 版本,可参考 `bin` 文件夹中的内容。
### 从 `v0.3` 迁移到 `v0.4`
1. 首先备份数据库文件。
2. 下载最新的 `v0.4` 版本并启动程序,程序会自动迁移数据库表结构。
3. 终止程序。
4. 执行脚本:`./bin/migrate_v3_to_v4.py` 进行数据迁移。
5. 重新启动程序即可。
> [!NOTE] 执行前请确保数据库中 `users` 表的字段顺序与脚本一致,否则会出现数据错乱。
## 其他
1. `v0.3` 之前的版本基于 Node.js,可切换到 [`nodejs`](https://github.com/songquanpeng/message-pusher/tree/nodejs) 分支查看,该版本不再提供功能性更新。
2. `v0.3` 及后续版本基于 Gin Template [`v0.2.1`](https://github.com/songquanpeng/gin-template) 版本开发。
3. 如需自行编译,请先[编译前端](./web/README.md),再编译后端,否则会遇到 `pattern web/build: no matching files found` 问题。
探索更多轩辕镜像的使用方法,找到最适合您系统的配置方式
通过 Docker 登录认证访问私有仓库
无需登录使用专属域名
Kubernetes 集群配置 Containerd
K3s 轻量级 Kubernetes 镜像加速
VS Code Dev Containers 配置
Podman 容器引擎配置
HPC 科学计算容器配置
ghcr、Quay、nvcr 等镜像仓库
Harbor Proxy Repository 对接专属域名
Portainer Registries 加速拉取
Nexus3 Docker Proxy 内网缓存
需要其他帮助?请查看我们的 常见问题Docker 镜像访问常见问题解答 或 提交工单
docker search 限制
站内搜不到镜像
离线 save/load
插件要用 plugin install
WSL 拉取慢
安全与 digest
新手拉取配置
镜像合规机制
不支持 push
manifest unknown
no matching manifest(架构)
invalid tar header(解压)
TLS 证书失败
DNS 超时
域名连通性排查
410 Gone 排查
402 与流量用尽
401 认证失败
429 限流
D-Bus 凭证提示
413 与超大单层
来自真实用户的反馈,见证轩辕镜像的优质服务