{"id":13455878,"url":"https://github.com/songquanpeng/message-pusher","last_synced_at":"2025-04-10T12:13:22.945Z","repository":{"id":38326256,"uuid":"291926647","full_name":"songquanpeng/message-pusher","owner":"songquanpeng","description":"搭建专属于你的消息推送服务，支持多种消息推送方式，支持 Markdown，基于 Golang 仅单可执行文件，开箱即用","archived":false,"fork":false,"pushed_at":"2025-03-13T00:34:33.000Z","size":612,"stargazers_count":3051,"open_issues_count":44,"forks_count":434,"subscribers_count":19,"default_branch":"master","last_synced_at":"2025-04-03T07:55:16.405Z","etag":null,"topics":["bark","dingtalk","discord","email","feishu","lark","message-pusher","telegram","websocket","wechat"],"latest_commit_sha":null,"homepage":"https://msgpusher.com","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/songquanpeng.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"custom":["https://iamazing.cn/page/reward"]}},"created_at":"2020-09-01T07:22:28.000Z","updated_at":"2025-04-03T06:49:10.000Z","dependencies_parsed_at":"2024-01-13T16:24:59.129Z","dependency_job_id":"855ba761-0de6-4858-bbe0-356d4e551dff","html_url":"https://github.com/songquanpeng/message-pusher","commit_stats":{"total_commits":319,"total_committers":17,"mean_commits":"18.764705882352942","dds":0.09717868338557989,"last_synced_commit":"109756674f60b58013a23d3eed6b1fc60b8f5c9a"},"previous_names":[],"tags_count":61,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/songquanpeng%2Fmessage-pusher","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/songquanpeng%2Fmessage-pusher/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/songquanpeng%2Fmessage-pusher/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/songquanpeng%2Fmessage-pusher/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/songquanpeng","download_url":"https://codeload.github.com/songquanpeng/message-pusher/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248217081,"owners_count":21066633,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bark","dingtalk","discord","email","feishu","lark","message-pusher","telegram","websocket","wechat"],"created_at":"2024-07-31T08:01:12.744Z","updated_at":"2025-04-10T12:13:22.923Z","avatar_url":"https://github.com/songquanpeng.png","language":"JavaScript","funding_links":["https://iamazing.cn/page/reward"],"categories":["JavaScript","Notification"],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/songquanpeng/message-pusher\"\u003e\u003cimg src=\"https://raw.githubusercontent.com/songquanpeng/message-pusher/master/web/public/logo.png\" width=\"150\" height=\"150\" alt=\"message-pusher logo\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cdiv align=\"center\"\u003e\n\n# 消息推送服务\n\n_✨ 搭建专属于你的消息推送服务，支持多种消息推送方式，支持 Markdown，仅单可执行文件，开箱即用✨_\n\n\u003c/div\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://raw.githubusercontent.com/songquanpeng/message-pusher/main/LICENSE\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/license/songquanpeng/message-pusher?color=brightgreen\" alt=\"license\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/songquanpeng/message-pusher/releases/latest\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/v/release/songquanpeng/message-pusher?color=brightgreen\u0026include_prereleases\" alt=\"release\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://hub.docker.com/repository/docker/justsong/message-pusher\"\u003e\n    \u003cimg src=\"https://img.shields.io/docker/pulls/justsong/message-pusher?color=brightgreen\" alt=\"docker pull\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/songquanpeng/message-pusher/releases/latest\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/downloads/songquanpeng/message-pusher/total?color=brightgreen\u0026include_prereleases\" alt=\"release\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://goreportcard.com/report/github.com/songquanpeng/message-pusher\"\u003e\n    \u003cimg src=\"https://goreportcard.com/badge/github.com/songquanpeng/message-pusher\" alt=\"GoReportCard\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/songquanpeng/message-pusher/releases\"\u003e程序下载\u003c/a\u003e\n  ·\n  \u003ca href=\"#部署\"\u003e部署教程\u003c/a\u003e\n  ·\n  \u003ca href=\"#用法\"\u003e使用教程\u003c/a\u003e\n  ·\n  \u003ca href=\"https://github.com/songquanpeng/message-pusher/issues\"\u003e意见反馈\u003c/a\u003e\n  ·\n  \u003ca href=\"https://message-pusher.onrender.com/\"\u003e在线演示\u003c/a\u003e\n\u003c/p\u003e\n\n\u003e **Note**：官方部署站 https://msgpusher.com 现已上线，当前开放注册，欢迎使用。如果收到积极反馈未来可以考虑换用延迟更低的服务器。\n\n\u003e **Warning**：从 `v0.3` 版本升级到 `v0.4` 版本需要手动迁移数据库，具体方法见[迁移数据库](#迁移数据库)。\n\n## 描述\n1. **多种消息推送方式**：\n   + 邮件消息，\n   + 微信测试号，\n   + QQ，\n   + 企业微信应用号，\n   + 企业微信群机器人\n   + 飞书自建应用\n   + 飞书群机器人，\n   + 钉钉群机器人，\n   + Bark App,\n   + WebSocket 客户端（[官方客户端](https://github.com/songquanpeng/personal-assistant)，[接入文档](./docs/API.md#websocket-客户端)），\n   + Telegram 机器人，\n   + Discord 群机器人，\n   + 腾讯云自定义告警：免费的短信提醒，\n   + **群组消息**：可以将多个推送通道组合成一个群组，然后向群组发送消息，可以实现一次性推送到多个渠道的功能，\n   + **自定义消息**：可以自定义消息请求 URL 和请求体格式，实现与其他服务的对接，支持[众多第三方服务](https://iamazing.cn/page/message-pusher-common-custom-templates)。\n2. 支持**自定义 Webhook，反向适配各种调用平台**，你可以接入各种已有的系统，而无需修改其代码。\n3. 支持在 Web 端编辑 \u0026 管理发送的消息，新消息发送后 Web 端**即时刷新**。\n4. 支持**异步**消息发送。\n5. 支持用户管理，支持多种用户登录注册方式：\n   + 邮箱登录注册以及通过邮箱进行密码重置。\n   + [GitHub 开放授权](https://github.com/settings/applications/new)。\n   + 微信公众号授权（需要额外部署 [WeChat Server](https://github.com/songquanpeng/wechat-server)）。\n6. 支持 Markdown。\n7. 支持 Cloudflare Turnstile 用户校验。\n8. 支持在线发布公告，设置关于界面以及页脚。\n9. API **兼容**其他消息推送服务，例如 [Server 酱](https://sct.ftqq.com/)。\n\n## 用途\n1. [整合进自己的博客系统，每当有人登录时发微信消息提醒](https://github.com/songquanpeng/blog/blob/486d63e96ef7906a6c767653a20ec2d3278e9a4a/routes/user.js#L27)。\n2. 在进行深度学习模型训练时，在每个 epoch 结束后[将关键数据发送到微信](https://github.com/songquanpeng/pytorch-template/blob/b2ba113659056080d3009b3014a67e977e2851bf/solver/solver.py#L223)以方便及时监控。\n3. 在各种脚本运行结束后发消息提醒，例如[监控 GitHub Star 数量的脚本](https://github.com/songquanpeng/scripts/blob/main/star_watcher.py)，又例如[自动健康填报的脚本](https://github.com/songquanpeng/daily-report)，用来通知运行结果。\n4. 为[其他系统](https://github.com/songquanpeng/personal-assistant#个人助理应用)提供消息推送功能。\n\n## 部署\n### 通过 Docker 部署\n部署：`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`\n\n如果无法拉去，请将 `justsong/message-pusher` 替换为 `ghcr.io/songquanpeng/message-pusher`。\n\n更新：`docker run --rm -v /var/run/docker.sock:/var/run/docker.sock containrrr/watchtower -cR`\n\n开放的端口号为 3000，之后用 Nginx 配置域名，反代以及 SSL 证书即可，具体参考[详细部署教程](https://iamazing.cn/page/how-to-deploy-a-website)。\n\n数据将会保存在宿主机的 `/home/ubuntu/data/message-pusher` 目录（只有一个 SQLite 数据库文件），请确保该目录存在且具有写入权限，或者更改为合适的目录。\n\nNginx 的参考配置：\n```\nserver{\n   server_name msgpusher.com;  # 请根据实际情况修改你的域名\n   \n   location / {\n          client_max_body_size  64m;\n          proxy_http_version 1.1;\n          proxy_pass http://localhost:3000;  # 请根据实际情况修改你的端口\n          proxy_set_header Host $host;\n          proxy_set_header X-Forwarded-For $remote_addr;\n          proxy_cache_bypass $http_upgrade;\n          proxy_set_header Accept-Encoding gzip;\n   }\n}\n```\n\n之后使用 Let's Encrypt 的 certbot 配置 HTTPS：\n```bash\n# Ubuntu 安装 certbot：\nsudo snap install --classic certbot\nsudo ln -s /snap/bin/certbot /usr/bin/certbot\n# 生成证书 \u0026 修改 Nginx 配置\nsudo certbot --nginx\n# 根据指示进行操作\n# 重启 Nginx\nsudo service nginx restart\n```\n\n### 手动部署\n1. 从 [GitHub Releases](https://github.com/songquanpeng/message-pusher/releases/latest) 下载可执行文件或者从源码编译：\n   ```shell\n   git clone https://github.com/songquanpeng/message-pusher.git\n   cd message-pusher/web\n   npm install\n   npm run build\n   cd ..\n   go mod download\n   go build -ldflags \"-s -w\" -o message-pusher\n   ````\n2. 运行：\n   ```shell\n   chmod u+x message-pusher\n   ./message-pusher --port 3000 --log-dir ./logs\n   ```\n3. 访问 [http://localhost:3000/](http://localhost:3000/) 并登录。初始账号用户名为 `root`，密码为 `123456`。\n\n如果服务需要长久运行，只是单纯地启动是不够的，[详细部署教程](https://iamazing.cn/page/how-to-deploy-a-website)。\n\n### 注意\n如果需要使用 WebSocket 客户端推送功能，则 Nginx 的配置文件中 `proxy_read_timeout` 和 `proxy_send_timeout` 务必设置超过 1 分钟。\n\n推荐设置：\n```\nproxy_read_timeout 300s;\nproxy_send_timeout 300s;   \n```\n\n## 配置\n系统本身仅需要下载一个可执行文件即可开始使用，无其他依赖。\n\n你可以通过设置环境变量或者命令行参数进行配置。\n\n等到系统启动后，使用 `root` 用户登录系统并做进一步的配置，默认密码为 `123456`。\n\n### 环境变量\n1. `REDIS_CONN_STRING`：设置之后将使用 Redis 作为请求频率限制的存储，而非使用内存存储。\n    + 例子：`REDIS_CONN_STRING=redis://default:redispw@localhost:49153`\n2. `SESSION_SECRET`：设置之后将使用固定的会话密钥，这样系统重新启动后已登录用户的 cookie 将依旧有效。\n    + 例子：`SESSION_SECRET=random_string`\n3. `SQL_DSN`：设置之后将使用指定数据库而非 SQLite。\n    + 例子：`SQL_DSN=root:123456@tcp(localhost:3306)/message-pusher`\n\n注意：使用 Docker 部署时，请使用 `-e key=value` 设置环境变量。 \n\n例子：`docker run -e SESSION_SECRET=random_string ...`\n\n### 命令行参数\n1. `--port \u003cport_number\u003e`: 指定服务器监听的端口号，默认为 `3000`。\n    + 例子：`--port 3000`\n2. `--log-dir \u003clog_dir\u003e`: 指定日志文件夹，如果没有设置，日志将不会被保存。\n    + 例子：`--log-dir ./logs`\n3. `--version`: 打印系统版本号并退出。\n\n\n### 进一步的配置\n1. 系统设置：\n   1. 填写服务器地址。\n   2. 配置登录注册选项，如果系统不对外开放，请取消选择`允许新用户注册`。\n   3. 配置 SMTP 服务，可以使用 QQ 邮箱的 SMTP 服务。\n   4. 其他配置可选，请按照页面上的指示完成配置。\n2. 个人设置：\n   1. 点击`更新用户信息`更改默认用户名和密码。\n   2. 点击`绑定邮箱地址`绑定邮箱以启用邮件消息推送方式。\n3. 推送设置：\n   1. 设置`默认推送方式`，默认为通过邮件进行推送。\n   2. 设置`推送 token`，用以推送 API 调用鉴权，如果不需要留空即可。\n   3. 设置其他推送方式，按照页面上的指示即可，完成配置后点击对应的`测试`按钮即可测试配置是否成功。\n4. 其他设置：如果系统对外提供服务，本系统也提供了一定的个性化设置功能，你可以设置关于界面和页脚，以及发布公告。\n\n## 用法\n1. 消息推送 API URL：`https://\u003cdomain\u003e/push/\u003cusername\u003e`\n   + 将上面的 `\u003cdomain\u003e` 以及 `\u003cusername\u003e` 替换为真实值，例如：`https://push.mydomain.cn/push/admin`\n2. `GET` 请求方式：`https://\u003cdomain\u003e/push/\u003cusername\u003e?title=\u003c标题\u003e\u0026description=\u003c描述\u003e\u0026content=\u003cMarkdown 文本\u003e\u0026channel=\u003c推送方式\u003e\u0026token=\u003c推送 token\u003e`\n   1. `title`：选填，受限于具体的消息推送方式，其可能被忽略。\n   2. `description`：必填，可以替换为 `desp`。\n   3. `content`：选填，受限于具体的消息推送方式，Markdown 语法的支持有所区别。\n   4. `channel`：选填，如果不填则系统使用你在后台设置的默认推送通道。注意，此处填的是消息通道的名称，而非类型。可选的推送通道类型有：\n      1. `email`：通过发送邮件的方式进行推送（使用 `title` 或 `description` 字段设置邮件主题，使用 `content` 字段设置正文，支持完整的 Markdown 语法）。\n      2. `test`：通过微信测试号进行推送（使用 `description` 字段设置模板消息内容，不支持 Markdown）。\n      3. `corp_app`：通过企业微信应用号进行推送（仅当使用企业微信 APP 时，如果设置了 `content` 字段，`title` 和 `description` 字段会被忽略；使用微信中的企业微信插件时正常）。\n      4. `lark_app`：通过飞书自建应用进行推送。\n      5. `corp`：通过企业微信群机器人推送（设置 `content` 字段则将渲染 Markdown 消息，支持 Markdown 的子集；设置 `description` 字段则为普通文本消息）。\n      6. `lark`：通过飞书群机器人进行推送（注意事项同上）。\n      7. `ding`：通过钉钉群机器人进行推送（注意事项同上）。\n      8. `bark`：通过 Bark 进行推送（支持 `title` 和 `description` 字段）。\n      9. `client`：通过 WebSocket 客户端进行推送（支持 `title` 和 `description` 字段）。\n      10. `telegram`：通过 Telegram 机器人进行推送（`description` 或 `content` 字段二选一，支持 Markdown 的子集）。\n      11. `discord`：通过 Discord 群机器人进行推送（注意事项同上）。\n      12. `one_api`：通过 OneAPI 协议推送消息到 QQ。\n      13. `group`：通过预先配置的消息推送通道群组进行推送。\n      14. `custom`：通过预先配置好的自定义推送通道进行推送。\n      15. `tencent_alarm`：通过腾讯云监控告警进行推送，仅支持 `description` 字段。\n      16. `none`：仅保存到数据库，不做推送。\n   5. `token`：如果你在后台设置了推送 token，则此项必填。另外可以通过设置 HTTP `Authorization` 头部设置此项。\n      * 注意令牌有两种，一种是全局鉴权令牌，一种是通道维度的令牌，前者可以鉴权任何通道，后者只能鉴权指定通道。\n   6. `url`：选填，如果不填则系统自动为消息生成 URL，其内容为消息详情。\n   7. `to`：选填，推送给指定用户，如果不填则默认推送给自己，受限于具体的消息推送方式，有些推送方式不支持此项。\n      1. `@all`：推送给所有用户。\n      2. `user1|user2|user3`：推送给多个用户，用户之间使用 `|` 分隔。\n   8. `async`：选填，如果设置为 `true` 则消息推送将在后台异步进行，返回结果包含 `uuid` 字段，可用于后续[获取消息发送状态](./docs/API.md#通过消息 UUID 获取消息发送状态)。\n   9. `render_mode`：选填，\n      1. 如果设置为 `code`，则消息体会被自动嵌套在代码块中进行渲染；\n      2. 如果设置为 `raw`，则不进行 Markdown 解析；\n      3. 默认 `markdown`，即进行 Markdown 解析。\n3. `POST` 请求方式：字段与上面 `GET` 请求方式保持一致。\n   + 如果发送的是 JSON，HTTP Header `Content-Type` 请务必设置为 `application/json`，否则一律按 Form 处理。\n   + POST 请求方式下的 `token` 字段也可以通过 URL 查询参数进行设置。\n\n\n**各种通道的支持程度：**\n\n|      通道类型       | `title` | `description` | `content` | `url` | `to` | Markdown 支持 |\n|:---------------:|:-------:|:-------------:|:---------:|:-----:|:----:|:-----------:|\n|     `email`     |    ✅    |       ✅       |     ✅     |   ❌   |  ✅️  |     ✅️      |\n|     `test`      |    ✅    |       ✅       |     ✅     |  ✅️   |  ✅️  |      ✅      |\n|   `corp_app`    |    ✅    |       ✅       |     ✅     |  ✅️   |  ✅   |      ✅      |\n|     `corp`      |    ❌    |       ✅       |     ✅     |  ✅️   |  ✅️  |      ✅      |\n|     `lark`      |    ❌    |       ✅       |     ✅     |   ❌   |  ✅   |      ✅      |\n|   `lark_app`    |    ❌    |       ✅       |     ✅     |  ❌️   |  ✅   |      ✅      |\n|     `ding`      |    ✅    |       ✅       |     ✅     |  ✅️   |  ✅   |      ✅      |\n|     `bark`      |    ✅    |       ✅       |     ✅     |  ✅️   |  ❌   |      ✅      |\n|    `client`     |    ✅    |       ✅       |     ❌     |   ❌   |  ❌   |      ❌      |\n|   `telegram`    |    ❌    |       ❌       |     ✅     |   ❌   |  ✅   |      ✅      |\n|    `discord`    |    ❌    |       ❌       |     ✅     |   ❌   |  ✅   |      ❌      |\n| `tencent_alarm` |    ❌    |       ✅       |     ❌     |   ❌   |  ❌   |      ❌      |\n\n注意：\n1. 对于大部分通道，`description` 字段和 `content` 是不能同时存在的，如果你只需要文字消息，请使用 `description` 字段，如果你需要发送 Markdown 消息，请使用 `content` 字段。\n2. 部分通道的 Markdown 支持实际上是通过 URL 跳转到本系统所渲染的消息详情实现的，其他通道的 Markdown 支持受限于具体的通道，支持的语法并不统一。\n\n**示例：**\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eBash 示例 \u003c/strong\u003e\u003c/summary\u003e\n\u003cdiv\u003e\n\n```shell\n#!/bin/bash\n\nMESSAGE_PUSHER_SERVER=\"https://msgpusher.com\"\nMESSAGE_PUSHER_USERNAME=\"test\"\nMESSAGE_PUSHER_TOKEN=\"666\"\n\nfunction send_message {\n  # POST Form\n  curl -s -X POST \"$MESSAGE_PUSHER_SERVER/push/$MESSAGE_PUSHER_USERNAME\" \\\n    -d \"title=$1\u0026description=$2\u0026content=$3\u0026token=$MESSAGE_PUSHER_TOKEN\" \\\n    \u003e/dev/null\n}\n\nfunction send_message_with_json {\n  # POST JSON\n  curl -s -X POST \"$MESSAGE_PUSHER_SERVER/push/$MESSAGE_PUSHER_USERNAME\" \\\n    -H 'Content-Type: application/json' \\\n    -d '{\"title\":\"'\"$1\"'\",\"desp\":\"'\"$2\"'\", \"content\":\"'\"$3\"'\", \"token\":\"'\"$MESSAGE_PUSHER_TOKEN\"'\"}' \\\n    \u003e/dev/null\n}\n\nsend_message 'title' 'description' 'content'\n```\n\n另一个版本：\n```shell\nMESSAGE_PUSHER_SERVER=\"https://msgpusher.com\"\nMESSAGE_PUSHER_USERNAME=\"test\"\nMESSAGE_PUSHER_TOKEN=\"666\"\nMESSAGE_PUSHER_CHANNEL=\"lark\"\n\nsendmsg() {\n    if [ -t 0 ]; then\n        local param=\"$*\"\n    else\n        local param=$(\u003c/dev/stdin)\n    fi\n    curl -s -o /dev/null --get --data-urlencode \"content=${param}\" \"$MESSAGE_PUSHER_SERVER/push/$MESSAGE_PUSHER_USERNAME?channel=$MESSAGE_PUSHER_CHANNEL\u0026token=$MESSAGE_PUSHER_TOKEN\"\n}\n```\n\n之后便可以进行这样的操作：\n```shell\nuname -ra | sendmsg\n```\n\n\u003c/div\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003ePython 示例 \u003c/strong\u003e\u003c/summary\u003e\n\u003cdiv\u003e\n\n```python\nimport requests\n\nSERVER = \"https://msgpusher.com\"\nUSERNAME = \"test\"\nTOKEN = \"666\"\n\n\ndef send_message(title, description, content):\n    # GET 方式\n    # res = requests.get(f\"{SERVER}/push/{USERNAME}?title={title}\"\n    #                    f\"\u0026description={description}\u0026content={content}\u0026token={TOKEN}\")\n\n    # POST 方式\n    res = requests.post(f\"{SERVER}/push/{USERNAME}\", json={\n        \"title\": title,\n        \"description\": description,\n        \"content\": content,\n        \"token\": TOKEN\n    })\n    res = res.json()\n    if res[\"success\"]:\n        return None\n    else:\n        return res[\"message\"]\n\n\nerror = send_message(\"标题\", \"描述\", \"**Markdown 内容**\")\nif error:\n    print(error)\n```\n\n\u003c/div\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eGo 示例 \u003c/strong\u003e\u003c/summary\u003e\n\u003cdiv\u003e\n\n```go\npackage main\n\nimport (\n   \"bytes\"\n   \"encoding/json\"\n   \"errors\"\n   \"fmt\"\n   \"net/http\"\n   \"net/url\"\n)\n\nvar serverAddress = \"https://msgpusher.com\"\nvar username = \"test\"\nvar token = \"666\"\n\ntype request struct {\n   Title       string `json:\"title\"`\n   Description string `json:\"description\"`\n   Content     string `json:\"content\"`\n   URL         string `json:\"url\"`\n   Channel     string `json:\"channel\"`\n   Token       string `json:\"token\"`\n}\n\ntype response struct {\n   Success bool   `json:\"success\"`\n   Message string `json:\"message\"`\n}\n\nfunc SendMessage(title string, description string, content string) error {\n   req := request{\n      Title:       title,\n      Description: description,\n      Content:     content,\n      Token:       token,\n   }\n   data, err := json.Marshal(req)\n   if err != nil {\n      return err\n   }\n   resp, err := http.Post(fmt.Sprintf(\"%s/push/%s\", serverAddress, username),\n      \"application/json\", bytes.NewBuffer(data))\n   if err != nil {\n      return err\n   }\n   var res response\n   err = json.NewDecoder(resp.Body).Decode(\u0026res)\n   if err != nil {\n      return err\n   }\n   if !res.Success {\n      return errors.New(res.Message)\n   }\n   return nil\n}\n\nfunc SendMessageWithForm(title string, description string, content string) error {\n   resp, err := http.PostForm(fmt.Sprintf(\"%s/push/%s\", serverAddress, username),\n      url.Values{\"title\": {title}, \"description\": {description}, \"content\": {content}, \"token\": {token}})\n   if err != nil {\n      return err\n   }\n   var res response\n   err = json.NewDecoder(resp.Body).Decode(\u0026res)\n   if err != nil {\n      return err\n   }\n   if !res.Success {\n      return errors.New(res.Message)\n   }\n   return nil\n}\n\nfunc main() {\n   //err := SendMessage(\"标题\", \"描述\", \"**Markdown 内容**\")\n   err := SendMessageWithForm(\"标题\", \"描述\", \"**Markdown 内容**\")\n   if err != nil {\n      fmt.Println(\"推送失败：\" + err.Error())\n   } else {\n      fmt.Println(\"推送成功！\")\n   }\n}\n```\n\n\u003c/div\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eC# 示例 \u003c/strong\u003e\u003c/summary\u003e\n\u003cdiv\u003e\n\n```csharp\nusing Newtonsoft.Json;\nusing RestSharp;\n\nnamespace Demo\n{\n    public class Program\n    {\n        public static void Main(string[] args)\n        {\n            //推送消息\n            var sendMsg = MessagePusherTool.SendMessage(\"标题\", \"描述\", \"**Markdown 内容**\");\n            if(sendMsg.Success)\n            {\n                Console.WriteLine($\"推送成功！\");\n            }\n            else\n            {\n                Console.WriteLine($\"推送失败：{sendMsg.Message}\");\n            }\n        }\n    }\n\n    /// \u003csummary\u003e\n    /// 消息推送工具\n    /// \n    /// \u003cpara\u003e开源地址：https://github.com/songquanpeng/message-pusher\u003c/para\u003e\n    /// \u003cpara\u003e支持：Framework、Net3.1、Net5、Net6\u003c/para\u003e\n    /// \u003cpara\u003e引用包：\u003c/para\u003e\n    /// \u003cpara\u003edotnet add package Newtonsoft.Json -v 13.0.2\u003c/para\u003e\n    /// \u003cpara\u003edotnet add package RestSharp -v 108.0.3\u003c/para\u003e\n    /// \u003c/summary\u003e\n    public class MessagePusherTool\n    {\n        /// \u003csummary\u003e\n        /// ServerAddress\n        /// \u003c/summary\u003e\n        public const string ServerAddress = \"https://msgpusher.com\";\n\n        /// \u003csummary\u003e\n        /// UserName\n        /// \u003c/summary\u003e\n        public const string UserName = \"test\";\n\n        /// \u003csummary\u003e\n        /// Token\n        /// \u003c/summary\u003e\n        public const string Token = \"666\";\n\n        /// \u003csummary\u003e\n        /// SendMessage\n        /// \u003c/summary\u003e\n        /// \u003cparam name=\"title\"\u003etitle\u003c/param\u003e\n        /// \u003cparam name=\"description\"\u003edescription\u003c/param\u003e\n        /// \u003cparam name=\"content\"\u003econtent\u003c/param\u003e\n        public static Response SendMessage(string title, string description, string content)\n        {\n            var requestData = new Request()\n            {\n                Title = title,\n                Description = description,\n                Content = content,\n                Token = Token,\n            };\n            var url = $\"{ServerAddress}\";\n            var client = new RestClient(url);\n            var request = new RestRequest($\"push/{UserName}\", Method.Post);\n            request.AddJsonBody(requestData);\n            var response = client.Execute(request);\n            var responseData = response.Content;\n            var responseJson = JsonConvert.DeserializeObject\u003cResponse\u003e(responseData);\n            return responseJson;\n        }\n\n        /// \u003csummary\u003e\n        /// Request\n        /// \u003c/summary\u003e\n        public class Request\n        {\n            /// \u003csummary\u003e\n            /// Title\n            /// \u003c/summary\u003e\n            [JsonProperty(PropertyName = \"title\")]\n            public string Title { get; set; }\n\n            /// \u003csummary\u003e\n            /// Description\n            /// \u003c/summary\u003e\n            [JsonProperty(PropertyName = \"description\")]\n            public string Description { get; set; }\n\n            /// \u003csummary\u003e\n            /// Content\n            /// \u003c/summary\u003e\n            [JsonProperty(PropertyName = \"content\")]\n            public string Content { get; set; }\n\n            /// \u003csummary\u003e\n            /// URL\n            /// \u003c/summary\u003e\n            [JsonProperty(PropertyName = \"url\")]\n            public string URL { get; set; }\n\n            /// \u003csummary\u003e\n            /// Channel\n            /// \u003c/summary\u003e\n            [JsonProperty(PropertyName = \"channel\")]\n            public string Channel { get; set; }\n\n            /// \u003csummary\u003e\n            /// Token\n            /// \u003c/summary\u003e\n            [JsonProperty(PropertyName = \"token\")]\n            public string Token { get; set; }\n        }\n\n        /// \u003csummary\u003e\n        /// Response\n        /// \u003c/summary\u003e\n        public class Response\n        {\n            /// \u003csummary\u003e\n            /// Success\n            /// \u003c/summary\u003e\n            [JsonProperty(PropertyName = \"success\")]\n            public bool Success { get; set; }\n\n            /// \u003csummary\u003e\n            /// Message\n            /// \u003c/summary\u003e\n            [JsonProperty(PropertyName = \"message\")]\n            public string Message { get; set; }\n        }\n    }\n}\n```\n\n\u003c/div\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eNode.js 示例 \u003c/strong\u003e\u003c/summary\u003e\n\u003cdiv\u003e\n\n```javascript\nconst axios = require('axios');\nconst querystring = require('querystring');\n\nconst MESSAGE_PUSHER_SERVER = 'https://msgpusher.com'\nconst MESSAGE_PUSHER_USERNAME = 'test'\nconst MESSAGE_PUSHER_TOKEN = '666'\n\nasync function send_message(title, description, content) {\n  try {\n    const postData = querystring.stringify({\n      title: title,\n      desp: description,\n      content: content,\n      token: MESSAGE_PUSHER_TOKEN,\n    })\n\n    const response = await axios.post(`${MESSAGE_PUSHER_SERVER}/push/${MESSAGE_PUSHER_USERNAME}`, postData, {\n      headers: {\n        'Content-Type': 'application/x-www-form-urlencoded',\n      },\n    })\n    if (response.data.success) {\n      return response.data\n    }\n  } catch (error) {\n    if (error.response) {\n      return error.response.data\n    } else {\n      throw error\n    }\n\n  }\n}\n\nsend_message('标题', '描述', '**Markdown 内容**')\n  .then((response) =\u003e {\n    if (response.success) {\n      console.log('推送成功:', response)\n    } else {\n      console.log('推送失败:', response)\n    }\n  }, (error) =\u003e {\n    console.log(error.message);\n  })\n\n```\n\u003c/div\u003e\n\u003c/details\u003e\n\n欢迎 PR 添加更多语言的示例。\n\n\n## 迁移数据库\n此处均以 SQLite 为例，其他数据库请自行修改。我已经让 ChatGPT 翻译成对应的 SQL 版本，见 `bin` 文件夹，供参考。\n\n### 从 `v0.3` 迁移到 `v0.4`\n1. 首先备份你的数据库文件。\n2. 下载最新的 `v0.4` 版本，启动程序，程序会自动进行数据库表结构的迁移。\n3. 终止程序。\n4. 之后执行脚本：`./bin/migrate_v3_to_v4.py`，进行数据的迁移。\n5. 重新启动程序即可。\n\n注意，执行前请确保数据库中 `users` 表中字段的顺序和脚本中的一致，否则会出现数据错乱的情况。\n\n## 其他\n1. `v0.3` 之前的版本基于 Node.js，你可以切换到 [`nodejs`](https://github.com/songquanpeng/message-pusher/tree/nodejs) 分支查看，该版本不再有功能性更新。\n2. `v0.3` 以及后续版本基于 Gin Template [`v0.2.1`](https://github.com/songquanpeng/gin-template) 版本开发。\n3. 如果想要自行编译，请首先[编译前端](./web/README.md)，之后再编译后端，否则会遇到 `pattern web/build: no matching files found` 问题。\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsongquanpeng%2Fmessage-pusher","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsongquanpeng%2Fmessage-pusher","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsongquanpeng%2Fmessage-pusher/lists"}