أنشئ خدمة دفع الرسائل الخاصة بك، ودعم طرق دفع الرسائل المتعددة، ودعم 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;
}
}
ثم استخدم certbot الخاص بـ Let's Encrypt لتكوين 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، فيجب ضبط proxy_read_timeout
و proxy_send_timeout
في ملف تكوين Nginx على أكثر من دقيقة واحدة.
الإعدادات الموصى بها:
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
لمصادقة استدعاء واجهة برمجة التطبيقات (Push 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
: قم بالتمرير عبر حساب اختبار WeChat (استخدم حقل description
لتعيين محتوى رسالة القالب، لا يتم دعم Markdown).corp_app
: قم بالدفع عبر حساب تطبيق WeChat للمؤسسة (فقط عند استخدام تطبيق WeChat للمؤسسة، إذا تم تعيين حقل 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 robot (اختر أحد حقول description
أو content
، ويدعم مجموعة فرعية من Markdown).discord
: قم بالدفع عبر روبوت مجموعة Discord (الملاحظات هي نفسها المذكورة أعلاه).one_api
: إرسال الرسائل إلى QQ من خلال بروتوكول OneAPI.group
: الدفع عبر مجموعة قنوات دفع الرسائل التي تم تكوينها مسبقًا.custom
: الدفع عبر قناة دفع مخصصة تم تكوينها مسبقًا.tencent_alarm
: يتم الضغط عليه من خلال إنذارات مراقبة Tencent Cloud، ويتم دعم حقل description
فقط.none
: احفظ فقط في قاعدة البيانات، ولا تضغط.token
: إذا قمت بتعيين رمز دفع في الخلفية، فهذا العنصر مطلوب. يمكن أيضًا تعيين ذلك عن طريق تعيين رأس Authorization
HTTP.url
: اختياري، إذا لم يتم ملؤه، سيقوم النظام تلقائيًا بإنشاء عنوان URL للرسالة، وسيكون محتواه عبارة عن تفاصيل الرسالة.to
: اختياري، ادفع إلى المستخدم المحدد إذا لم يتم ملؤه، فسيتم دفعه إليك بشكل افتراضي، وهو مقيد بطريقة دفع الرسالة المحددة، وبعض طرق الدفع لا تدعم ذلك.@all
: ادفع لجميع المستخدمين.user1|user2|user3
: أرسل إلى عدة مستخدمين، مفصولين بـ |
.async
: اختياري، إذا تم ضبطه على true
، فسيتم تنفيذ دفع الرسالة بشكل غير متزامن في الخلفية، وستحتوي النتيجة التي تم إرجاعها على حقل uuid
، والذي يمكن استخدامه لـ [الحصول على حالة إرسال الرسالة] (./docs/API.md#Get) حالة إرسال الرسالة من خلال رسالة UUID).render_mode
: اختياري،code
، فسيتم تضمين نص الرسالة تلقائيًا في كتلة التعليمات البرمجية للعرض؛raw
، فلن يتم تنفيذ تحليل Markdown؛markdown
، وهو ما يعني تحليل Markdown.POST
: تتوافق الحقول مع طريقة طلب GET
أعلاه.Content-Type
رأس HTTP على application/json
، وإلا ستتم معالجته كنموذج.token
في وضع طلب POST من خلال معلمات استعلام URL.مستوى الدعم للقنوات المختلفة:
نوع القناة | title | description | content | url | to | دعم تخفيض السعر |
---|---|---|---|---|---|---|
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 ) ;
} )
نرحب بممثلي العلاقات العامة لإضافة أمثلة بمزيد من اللغات.
يتم استخدام 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 v0.2.1
.pattern web/build: no matching files found
.