Build your own message push service, support multiple message push methods, support Markdown, only a single executable file, ready to use out of the box
Program download·Deployment tutorial·Usage tutorial·Feedback·Online demonstration
Note : The official deployment site https://msgpusher.com is now online and registration is currently open. Welcome to use it. If you receive positive feedback, you may consider switching to a server with lower latency in the future.
Warning : Upgrading from
v0.3
tov0.4
requires manual migration of the database. For details, see Migrating the Database.
Deployment: 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
If it cannot be pulled, please replace justsong/message-pusher
with ghcr.io/songquanpeng/message-pusher
.
Update: docker run --rm -v /var/run/docker.sock:/var/run/docker.sock containrrr/watchtower -cR
The open port number is 3000. Then use Nginx to configure the domain name, reverse generation and SSL certificate. Please refer to the detailed deployment tutorial for details.
The data will be saved in the /home/ubuntu/data/message-pusher
directory of the host machine (there is only one SQLite database file). Please ensure that the directory exists and has write permission, or change it to a suitable directory.
Reference configuration of 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;
}
}
Then use Let's Encrypt's certbot to configure 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
and the password is 123456
.If the service needs to run for a long time, simply starting it is not enough. Detailed deployment tutorial.
If you need to use the WebSocket client push function, proxy_read_timeout
and proxy_send_timeout
in the Nginx configuration file must be set to more than 1 minute.
Recommended settings:
proxy_read_timeout 300s;
proxy_send_timeout 300s;
The system itself only needs to download an executable file to start using it, with no other dependencies.
You can configure it by setting environment variables or command line parameters.
After the system starts, use the root
user to log in to the system and make further configurations. The default password is 123456
.
REDIS_CONN_STRING
: After setting, Redis will be used as the storage for request frequency limit instead of using memory storage.REDIS_CONN_STRING=redis://default:redispw@localhost:49153
SESSION_SECRET
: After setting, a fixed session key will be used, so that the logged-in user's cookie will still be valid after the system is restarted.SESSION_SECRET=random_string
SQL_DSN
: After setting, the specified database will be used instead of SQLite.SQL_DSN=root:123456@tcp(localhost:3306)/message-pusher
Note: When deploying with Docker, please use -e key=value
to set environment variables.
Example: docker run -e SESSION_SECRET=random_string ...
--port <port_number>
: Specify the port number that the server listens to, the default is 3000
.--port 3000
--log-dir <log_dir>
: Specify the log folder. If not set, the log will not be saved.--log-dir ./logs
--version
: Print the system version number and exit.允许新用户注册
.更新用户信息
to change the default username and password.绑定邮箱地址
to bind email to enable email message push.默认推送方式
. The default is push via email.推送token
for push API call authentication. Leave it blank if not required.测试
button to test whether the configuration is successful.https://<domain>/push/<username>
<domain>
and <username>
with real values, for example: https://push.mydomain.cn/push/admin
GET
request method: https://<domain>/push/<username>?title=<标题>&description=<描述>&content=<Markdown 文本>&channel=<推送方式>&token=<推送token>
title
: Optional, limited by the specific message push method, it may be ignored.description
: required, can be replaced by desp
.content
: Optional, limited by the specific message push method, Markdown syntax support is different.channel
: Optional, if not filled in, the system will use the default push channel you set in the background. Note that the name of the message channel is filled in here, not the type. Optional push channel types are:email
: Push by sending an email (use the title
or description
field to set the email subject, use content
field to set the body, and support complete Markdown syntax).test
: Push through the WeChat test account (use the description
field to set the template message content, Markdown is not supported).corp_app
: Push through the enterprise WeChat application account (only when using the enterprise WeChat APP, if the content
field is set, title
and description
fields will be ignored; it is normal when using the enterprise WeChat plug-in in WeChat).lark_app
: Push through Feishu's self-built application.corp
: Pushed through the enterprise WeChat group robot (setting the content
field will render Markdown messages, supporting a subset of Markdown; setting description
field will render ordinary text messages).lark
: push via Feishuqun robot (notes are the same as above).ding
: push via DingTalk group robot (notes are the same as above).bark
: push through Bark (supports title
and description
fields).client
: push via WebSocket client (supports title
and description
fields).telegram
: push via Telegram robot (choose one of description
or content
fields, supports a subset of Markdown).discord
: Push through Discord group bot (notes are the same as above).one_api
: Push messages to QQ through the OneAPI protocol.group
: Push through a pre-configured message push channel group.custom
: Push through a pre-configured custom push channel.tencent_alarm
: Pushed through Tencent Cloud monitoring alarms, only the description
field is supported.none
: Only save to the database, do not push.token
: If you set a push token in the background, this item is required. This can also be set by setting the HTTP Authorization
header.url
: Optional, if not filled in, the system will automatically generate a URL for the message, and its content will be the message details.to
: Optional, push to the specified user. If not filled in, it will be pushed to yourself by default. It is limited by the specific message push method, and some push methods do not support this.@all
: Push to all users.user1|user2|user3
: Push to multiple users, separated by |
.async
: Optional, if set to true
, message push will be performed asynchronously in the background, and the return result contains the uuid
field, which can be used for subsequent [Get message sending status] (./docs/API.md#Get message sending status through message UUID) .render_mode
: optional,code
, the message body will be automatically nested in the code block for rendering;raw
, Markdown parsing will not be performed;markdown
, which means Markdown parsing.POST
request method: The fields are consistent with the GET
request method above.Content-Type
must be set to application/json
, otherwise it will be processed as Form.token
field in POST request mode can also be set through URL query parameters.Level of support for various channels:
Channel type | title | description | content | url | to | Markdown support |
---|---|---|---|---|---|---|
email | ✅ | ✅ | ✅ | ✅️ | ✅️ | |
test | ✅ | ✅ | ✅ | ✅️ | ✅️ | ✅ |
corp_app | ✅ | ✅ | ✅ | ✅️ | ✅ | ✅ |
corp | ✅ | ✅ | ✅️ | ✅️ | ✅ | |
lark | ✅ | ✅ | ✅ | ✅ | ||
lark_app | ✅ | ✅ | ️ | ✅ | ✅ | |
ding | ✅ | ✅ | ✅ | ✅️ | ✅ | ✅ |
bark | ✅ | ✅ | ✅ | ✅️ | ✅ | |
client | ✅ | ✅ | ||||
telegram | ✅ | ✅ | ✅ | |||
discord | ✅ | ✅ | ||||
tencent_alarm | ✅ |
Notice:
description
field and content
cannot exist at the same time. If you only need text messages, please use description
field. If you need to send Markdown messages, please use content
field.Example:
#! /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 '
Another version:
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 "
}
Then you can do something like this:
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 ) ;
} )
PRs are welcome to add examples in more languages.
SQLite is used as an example here. Please modify other databases by yourself. I have asked ChatGPT to translate it into the corresponding SQL version, see the bin
folder for reference.
v0.3
to v0.4
v0.4
version, start the program, and the program will automatically migrate the database table structure../bin/migrate_v3_to_v4.py
to migrate data. Note that before execution, please ensure that the order of the fields in the users
table in the database is consistent with that in the script, otherwise data confusion will occur.
v0.3
are based on Node.js. You can switch to nodejs
branch to view it. This version no longer has functional updates.v0.3
and subsequent versions are developed based on Gin Template v0.2.1
.pattern web/build: no matching files found
problem.