建立專屬於你的訊息推播服務,支援多種訊息推播方式,支援Markdown,僅單執行文件,開箱即用
程式下載· 部署教學· 使用教學· 意見回饋· 線上演示
Note :官方部署站https://msgpusher.com 現已上線,目前開放註冊,歡迎使用。如果收到正向回饋未來可以考慮換用延遲較低的伺服器。
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 msgpusher.com; # 请根据实际情况修改你的域名
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;
系統本身只需要下載一個可執行檔即可開始使用,無其他依賴。
你可以透過設定環境變數或命令列參數進行設定。
等到系統啟動後,使用root
使用者登入系統並做進一步的配置,預設密碼為123456
。
REDIS_CONN_STRING
:設定之後將使用Redis 作為請求頻率限制的存儲,而不是使用內存存儲。REDIS_CONN_STRING=redis://default:redispw@localhost:49153
SESSION_SECRET
:設定之後將使用固定的會話金鑰,這樣系統重新啟動後已登入使用者的cookie 將依舊有效。SESSION_SECRET=random_string
SQL_DSN
:設定之後將使用指定資料庫而非SQLite。SQL_DSN=root:123456@tcp(localhost:3306)/message-pusher
注意:使用Docker 部署時,請使用-e key=value
設定環境變數。
範例: docker run -e SESSION_SECRET=random_string ...
--port <port_number>
: 指定伺服器監聽的連接埠號,預設為3000
。--port 3000
--log-dir <log_dir>
: 指定日誌資料夾,如果沒有設置,日誌將不會被儲存。--log-dir ./logs
--version
: 列印系統版本號並退出。允许新用户注册
。更新用户信息
更改預設用戶名和密碼。绑定邮箱地址
綁定郵箱以啟用郵件訊息推送方式。默认推送方式
,預設為透過郵件進行推送。推送token
,以推送API 呼叫鑑權,如果不需要留空即可。测试
按鈕即可測試配置是否成功。https://<domain>/push/<username>
<domain>
以及<username>
替換為真實值,例如: https://push.mydomain.cn/push/admin
GET
請求方式: https://<domain>/push/<username>?title=<标题>&description=<描述>&content=<Markdown 文本>&channel=<推送方式>&token=<推送token>
title
:選填,受限於具體的訊息推播方式,其可能被忽略。description
:必填,可替換為desp
。content
:選填,受限於具體的訊息推播方式,Markdown 語法的支援有所區別。channel
:選填,如果不填則系統使用你在背景設定的預設推播通道。注意,此處填的是訊息頻道的名稱,而非類型。可選的推送通道類型有:email
:透過傳送郵件的方式進行推播(使用title
或description
欄位設定郵件主題,使用content
欄位設定正文,支援完整的Markdown 語法)。test
:透過微信測試號碼進行推送(使用description
欄位設定範本訊息內容,不支援Markdown)。corp_app
:透過企業微信應用程式號進行推播(只有在使用企業微信APP 時,如果設定了content
字段, title
和description
字段會被忽略;使用微信中的企業微信外掛程式時正常)。lark_app
:透過飛書自建應用程式進行推送。corp
:透過企業微信群組機器人推送(設定content
欄位則會渲染Markdown 訊息,支援Markdown 的子集;設定description
欄位則為普通文字訊息)。lark
:透過飛書群機器人進行推播(注意事項同上)。ding
:透過釘釘群機器人進行推播(注意事項同上)。bark
:透過Bark 進行推送(支援title
和description
欄位)。client
:透過WebSocket 客戶端進行推送(支援title
和description
欄位)。telegram
:透過Telegram 機器人進行推送( description
或content
字段二選一,支援Markdown 的子集)。discord
:透過Discord 群機器人進行推送(注意事項同上)。one_api
:透過OneAPI 協定推送訊息到QQ。group
:透過預先設定的訊息推播頻道群組進行推播。custom
:透過預先配置好的自訂推送通道進行推送。tencent_alarm
:透過騰訊雲監控警告進行推送,僅支援description
欄位。none
:只儲存到資料庫,不做推送。token
:如果你在背景設定了推送token,則此項必填。另外可以透過設定HTTP Authorization
頭部來設定此項目。url
:選填,如果不填則系統自動為訊息產生URL,其內容為訊息詳情。to
:選填,推送給指定用戶,如果不填則預設推送給自己,受限於具體的訊息推送方式,有些推送方式不支援此項目。@all
:推送給所有用戶。user1|user2|user3
:推送給多個用戶,用戶之間使用|
分隔。async
:選填,如果設定為true
則訊息推播將在後台非同步進行,傳回結果包含uuid
字段,可用於後續[取得訊息傳送狀態](./docs/API.md#透過訊息UUID 取得訊息傳送狀態) 。render_mode
:選填,code
,則訊息體會被自動嵌套在程式碼區塊中進行渲染;raw
,則不進行Markdown 解析;markdown
,即進行Markdown 解析。POST
請求方式:欄位與上面GET
請求方式保持一致。Content-Type
請務必設定為application/json
,否則一律按Form 處理。token
欄位也可以透過URL 查詢參數設定。各種通道的支援程度:
通道類型 | title | description | content | url | to | Markdown 支持 |
---|---|---|---|---|---|---|
email | ✅ | ✅ | ✅ | ✅️ | ✅️ | |
test | ✅ | ✅ | ✅ | ✅️ | ✅️ | ✅ |
corp_app | ✅ | ✅ | ✅ | ✅️ | ✅ | ✅ |
corp | ✅ | ✅ | ✅️ | ✅️ | ✅ | |
lark | ✅ | ✅ | ✅ | ✅ | ||
lark_app | ✅ | ✅ | ️ | ✅ | ✅ | |
ding | ✅ | ✅ | ✅ | ✅️ | ✅ | ✅ |
bark | ✅ | ✅ | ✅ | ✅️ | ✅ | |
client | ✅ | ✅ | ||||
telegram | ✅ | ✅ | ✅ | |||
discord | ✅ | ✅ | ||||
tencent_alarm | ✅ |
注意:
description
字段和content
是不能同時存在的,如果你只需要文字訊息,請使用description
字段,如果你需要發送Markdown 訊息,請使用content
字段。範例:
#! /bin/bash
MESSAGE_PUSHER_SERVER= " https://msgpusher.com "
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://msgpusher.com "
MESSAGE_PUSHER_USERNAME= " test "
MESSAGE_PUSHER_TOKEN= " 666 "
MESSAGE_PUSHER_CHANNEL= " lark "
sendmsg () {
if [ -t 0 ] ; then
local param= " $* "
else
local param= $( < /dev/stdin )
fi
curl -s -o /dev/null --get --data-urlencode " content= ${param} " " $MESSAGE_PUSHER_SERVER /push/ $MESSAGE_PUSHER_USERNAME ?channel= $MESSAGE_PUSHER_CHANNEL &token= $MESSAGE_PUSHER_TOKEN "
}
之後便可以進行這樣的操作:
uname -ra | sendmsg
import requests
SERVER = "https://msgpusher.com"
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 )
package main
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
)
var serverAddress = "https://msgpusher.com"
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 ( "推送成功!" )
}
}
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>
/// 消息推送工具
///
/// <para>开源地址:https://github.com/songquanpeng/message-pusher</para>
/// <para>支持:Framework、Net3.1、Net5、Net6</para>
/// <para>引用包:</para>
/// <para>dotnet add package Newtonsoft.Json -v 13.0.2</para>
/// <para>dotnet add package RestSharp -v 108.0.3</para>
/// </summary>
public class MessagePusherTool
{
/// <summary>
/// ServerAddress
/// </summary>
public const string ServerAddress = "https://msgpusher.com" ;
/// <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>
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 ; }
}
}
}
const axios = require ( 'axios' ) ;
const querystring = require ( 'querystring' ) ;
const MESSAGE_PUSHER_SERVER = 'https://msgpusher.com'
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
v0.4
版本,啟動程序,程式會自動進行資料庫表結構的遷移。./bin/migrate_v3_to_v4.py
,進行資料的遷移。請注意,執行前請確保資料庫中users
表中欄位的順序和腳本中的一致,否則會出現資料錯亂的情況。
v0.3
之前的版本是基於Node.js,你可以切換到nodejs
分支查看,不再有功能性更新。v0.3
以及後續版本是基於Gin Template v0.2.1
版本開發。pattern web/build: no matching files found
問題。