자신만의 메시지 푸시 서비스 구축, 다양한 메시지 푸시 방법 지원, Markdown 지원, 단일 실행 파일만 지원, 즉시 사용 가능
프로그램 다운로드·배포 튜토리얼·사용 튜토리얼·피드백·온라인 데모
참고 : 공식 배포 사이트인 https://msgpusher.com이 현재 온라인 상태이며 등록이 진행 중입니다. 긍정적인 피드백을 받으면 향후 대기 시간이 더 짧은 서버로 전환하는 것을 고려할 수 있습니다.
경고 :
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
: 설정 후에는 고정된 세션 키가 사용되므로 로그인한 사용자의 쿠키는 시스템을 다시 시작한 후에도 계속 유효합니다.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
설정합니다. 필요하지 않은 경우 공백으로 둡니다.测试
버튼을 클릭하여 구성이 성공했는지 테스트하세요.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
필드를 사용하여 본문을 설정하고, 전체 마크다운 구문을 지원합니다).test
: WeChat 테스트 계정을 통해 푸시합니다( description
필드를 사용하여 템플릿 메시지 내용을 설정합니다. Markdown은 지원되지 않습니다).corp_app
: 기업 WeChat 애플리케이션 계정을 통해 푸시합니다(기업 WeChat APP을 사용하는 경우에만 content
필드가 설정된 경우 title
및 description
필드가 무시됩니다. WeChat에서 기업 WeChat 플러그인을 사용할 때는 정상입니다).lark_app
: Feishu의 자체 구축 애플리케이션을 푸시합니다.corp
: 엔터프라이즈 WeChat 그룹 로봇을 통해 푸시됩니다( content
필드를 설정하면 Markdown 메시지가 렌더링되고 Markdown 하위 집합이 지원됩니다. description
필드를 설정하면 일반 텍스트 메시지가 렌더링됩니다).lark
: Feishuqun 로봇을 통해 푸시합니다(참고 사항은 위와 동일).ding
: DingTalk 그룹 로봇을 통해 푸시합니다. (참고사항은 위와 동일합니다.)bark
: Bark를 통해 푸시합니다( title
및 description
필드 지원).client
: WebSocket 클라이언트를 통해 푸시합니다( title
및 description
필드 지원).telegram
: Telegram 로봇을 통해 푸시합니다( description
또는 content
필드 중 하나 선택, Markdown 하위 집합 지원).discord
: Discord 그룹 봇을 통해 푸시합니다. (참고사항은 위와 동일합니다.)one_api
: OneAPI 프로토콜을 통해 QQ에 메시지를 푸시합니다.group
: 사전 구성된 메시지 푸시 채널 그룹을 통해 푸시합니다.custom
: 사전 구성된 사용자 정의 푸시 채널을 통해 푸시합니다.tencent_alarm
: Tencent Cloud 모니터링 알람을 통해 푸시되며 description
필드만 지원됩니다.none
: 데이터베이스에만 저장하고 푸시하지 않습니다.token
: 백그라운드에서 푸시 토큰을 설정하는 경우 필수 항목입니다. 이는 HTTP Authorization
헤더를 설정하여 설정할 수도 있습니다.url
: 선택사항입니다. 입력하지 않으면 시스템에서 자동으로 메시지에 대한 URL을 생성하며 해당 내용은 메시지 세부정보가 됩니다.to
: 선택 사항이며, 입력하지 않으면 기본적으로 자신에게 푸시됩니다. 특정 메시지 푸시 방법에 따라 제한되며 일부 푸시 방법은 이를 지원하지 않습니다.@all
: 모든 사용자에게 푸시합니다.user1|user2|user3
: |
로 구분하여 여러 사용자에게 푸시합니다.async
: 선택 사항, true
로 설정하면 메시지 푸시가 백그라운드에서 비동기식으로 수행되고 반환 결과에는 후속 [메시지 전송 상태 가져오기](./docs/API.md#Get)에 사용할 수 있는 uuid
필드가 포함됩니다. 메시지 UUID를 통한 메시지 전송 상태) .render_mode
: 선택사항,code
로 설정하면 메시지 본문이 렌더링을 위해 코드 블록에 자동으로 중첩됩니다.raw
로 설정하면 Markdown 구문 분석이 수행되지 않습니다.markdown
이며 이는 Markdown 구문 분석을 의미합니다.POST
요청 방법: 필드는 위의 GET
요청 방법과 일치합니다.Content-Type
application/json
으로 설정해야 하며, 그렇지 않으면 Form으로 처리됩니다.token
필드는 URL 쿼리 매개변수를 통해 설정할 수도 있습니다.다양한 채널에 대한 지원 수준:
채널 유형 | title | description | content | url | to | 마크다운 지원 |
---|---|---|---|---|---|---|
email | ✅ | ✅ | ✅ | ✅️ | ✅️ | |
test | ✅ | ✅ | ✅ | ✅️ | ✅️ | ✅ |
corp_app | ✅ | ✅ | ✅ | ✅️ | ✅ | ✅ |
corp | ✅ | ✅ | ✅️ | ✅️ | ✅ | |
lark | ✅ | ✅ | ✅ | ✅ | ||
lark_app | ✅ | ✅ | ️ | ✅ | ✅ | |
ding | ✅ | ✅ | ✅ | ✅️ | ✅ | ✅ |
bark | ✅ | ✅ | ✅ | ✅️ | ✅ | |
client | ✅ | ✅ | ||||
telegram | ✅ | ✅ | ✅ | |||
discord | ✅ | ✅ | ||||
tencent_alarm | ✅ |
알아채다:
description
필드와 content
동시에 존재할 수 없습니다. 문자 메시지만 필요한 경우에는 description
필드를 사용하세요. 마크다운 메시지를 보내려면 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
migration_v3_to_v4.py 스크립트를 실행하여 데이터를 마이그레이션합니다. 실행하기 전에 데이터베이스의 users
테이블에 있는 필드 순서가 스크립트의 필드 순서와 일치하는지 확인하십시오. 그렇지 않으면 데이터 혼란이 발생합니다.
v0.3
이전 버전은 nodejs
를 기반으로 합니다. 이 버전에는 더 이상 기능 업데이트가 없습니다.v0.3
이후 버전은 Gin Template v0.2.1
기반으로 개발되었습니다.pattern web/build: no matching files found
.