{"id":20282556,"url":"https://github.com/veops/messenger","last_synced_at":"2025-04-15T22:53:54.868Z","repository":{"id":209812770,"uuid":"724899144","full_name":"veops/messenger","owner":"veops","description":"一个简单轻量的消息发送服务","archived":false,"fork":false,"pushed_at":"2024-07-11T10:10:44.000Z","size":1200,"stargazers_count":72,"open_issues_count":1,"forks_count":21,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-04-15T22:53:44.931Z","etag":null,"topics":["gin","golang","messenger"],"latest_commit_sha":null,"homepage":"https://veops.cn","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/veops.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"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}},"created_at":"2023-11-29T02:35:08.000Z","updated_at":"2025-03-14T13:59:08.000Z","dependencies_parsed_at":"2023-11-29T09:45:42.643Z","dependency_job_id":"ca8af59e-1723-4814-a74a-cfd38f7f1025","html_url":"https://github.com/veops/messenger","commit_stats":null,"previous_names":["veops/messenger"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/veops%2Fmessenger","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/veops%2Fmessenger/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/veops%2Fmessenger/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/veops%2Fmessenger/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/veops","download_url":"https://codeload.github.com/veops/messenger/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249167439,"owners_count":21223505,"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":["gin","golang","messenger"],"created_at":"2024-11-14T14:10:17.914Z","updated_at":"2025-04-15T22:53:54.832Z","avatar_url":"https://github.com/veops.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch3 align=\"center\"\u003eMessenger\u003c/h3\u003e\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/veops/messenger/blob/main/LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/github/license/veops/messenger\" alt=\"Apache License 2.0\"\u003e\u003c/a\u003e\n  \u003ca href=\"\"\u003e\u003cimg src=\"https://img.shields.io/badge/Go-%3E%3D%201.18-%23007d9c\" alt=\"go\u003e=1.18\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://goreportcard.com/report/github.com/veops/messenger\"\u003e\u003cimg src=\"https://goreportcard.com/badge/github.com/veops/messenger\" alt=\"API\"\u003e\u003c/a\u003e\n\u003c/p\u003e\nmessenger是一个简单轻量的消息发送服务，支持邮件、微信、飞书、钉钉，同时支持配置动态修改，使用简单灵活\n\n---\n\n## 安装\n\n### 源码\n```bash\ncd messenger\nsh build.sh\ncp conf/confTemplate.yaml conf/conf.yaml # edit your config\n./messenger\n```\n\n### docker\n```bash\n# 源码构建\ncd messenger\ncp conf/confTemplate.yaml conf/conf.yaml # edit your config\ndocker build --tag messenger .\ndocker run -d --name messenger -p 8888:8888 -v $(pwd)/conf:/messenger/conf --restart=always messenger\n\n# 镜像\ndocker pull registry.cn-hangzhou.aliyuncs.com/veops/messenger:latest\n```\n\n## API\n\n\u003e 您也可以通过访问 http://127.0.0.1:8888/swagger/index.html 查看swagger api文档 \n\n### 发送消息\n\n请求方式：POST\n\n请求地址：http://127.0.0.1:8888/v1/message\n\n参数说明：\n\n| 参数       | 是否必须 | 类型     | 说明                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |\n| :--------- | :------- | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| sender     | 是       | string   | sender名称：发送消息具体sender的名称，对应conf中的name                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |\n| msgtype    | 是       | string   | 消息内容类型：每种消息发送方式支持多种消息内容类型，各发送方式支持的消息内容类型参考如下 \u003cbr\u003e wechatBot: [微信机器人](https://developer.work.weixin.qq.com/document/path/99110#%E6%B6%88%E6%81%AF%E7%B1%BB%E5%9E%8B%E5%8F%8A%E6%95%B0%E6%8D%AE%E6%A0%BC%E5%BC%8F)  \u0026emsp;\u0026emsp;\u0026emsp;\u0026nbsp; wechatApp：[微信应用](https://developer.work.weixin.qq.com/document/path/90236#%E6%B6%88%E6%81%AF%E7%B1%BB%E5%9E%8B) \u003cbr\u003e  feishuBot：[飞书机器人](https://open.feishu.cn/document/client-docs/bot-v3/add-custom-bot#5a997364) \u0026emsp;\u0026emsp;\u0026emsp;\u0026nbsp; feishuApp：[飞书应用](https://open.feishu.cn/document/server-docs/im-v1/message-content-description/create_json#3c92befd) \u003cbr\u003e dingdingBot：[钉钉机器人](https://open.dingtalk.com/document/orgapp/custom-robot-access#title-72m-8ag-pqw) \u0026emsp;\u0026emsp; dingdingApp：[钉钉应用](https://open.dingtalk.com/document/orgapp/types-of-messages-sent-by-robots?spm) \u003cbr\u003eemail (邮件): text/plain text/html \u003cbr\u003e aliSms (阿里云短信): sms \u003cbr\u003e |\n| content    | 是       | string   | 消息内容：IM（微信、飞书、钉钉）消息内容本身具有结构，传入其JSON序列化之后的字符串，如微信应用的文本消息填写`{\"content\":\"my content\"}`序列化后字符；邮件可直接填写内容字符串；阿里云短信填写模板变量JSON序列化后字符串如：{\"name\":\"张三\",\"number\":\"1390000****\"} 序列化后字符串                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |\n| title      | 否       | string   | 消息标题：仅用于 email 类型                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |\n| tos        | 否       | []string | 接收人列表：发送邮件、应用消息时需要填写                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |\n| ccs        | 否       | []string | 抄送人列表：仅用于 email 类型                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |\n| extra      | 否       | string   | 额外参数：通常情况下您只需要关注消息内容类型和其内容发送人，但是当您需要传递一些额外参数时，比如微信应用开启重复检查和检查时间间隔，可以将extra设置为`{\"enable_duplicate_check\":1, \"duplicate_check_interval\": 1800}`序列化后字符串                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |\n| sync       | 否       | bool     | 同步发送：默认情况下，发送请求接受成功即返回200，消息会异步发送，若sync为true则会同步等待消息发送结果并返回                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |\n| simple     | 否       | bool     | 简单内容：默认情况下，消息内容是json字符串（参考content参数），对于简单的消息类型text和markdown可设置simple=true，此时content仅填写内容字符串本身即可，如`my content`。\u003cbr\u003e支持的消息类型（msgtype）\u003cbr\u003etext: wechatBot wechatApp feishuBot feishuApp dingdingBot dingdingApp\u003cbr\u003emarkdown: wechatBot wechatApp dingdingBot dingdingApp                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |\n| ats        | 否       | []string | @列表： 使用@all代表@所有人; 支持wechatBot(text, markdown(无法@all)), feishuBot(text), dingdingBot (text,  markdown)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |\n| at_mobiles | 否       | []string | @列表： 同ats参数，但使用手机号而非user id; 支持wechatBot(text), dingdingBot(text, markdown)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |\n\n返回结果：\n```json\n// 正常 httpStatusCode==200\n{\n  \"msg\": \"ok\"\n}\n\n// 异常 httpStatusCode!=200\n{\n  \"msg\": \"xxxx\"\n}\n```\n\n请求示例：\n\n### curl\n```bash\ncurl  -X POST \\\n  'http://localhost:8888/v1/message' \\\n  --header 'Content-Type: application/json' \\\n  --data-raw '{\n  \"sender\": \"yourSenderName\",\n  \"msgtype\": \"text\",\n  \"content\": \"{\\\"content\\\":\\\"一行文本内容\\\"}\"\n}'\n```\n\n### python\n```python\nimport json\nimport requests\n\nreqUrl = \"http://localhost:8888/v1/message\"\n\nresponse = requests.post(reqUrl, json={\n    \"sender\": \"yourSenderName\",\n    \"msgtype\": \"text\",\n    \"content\": json.dumps({\n        \"content\": \"一行文本内容1\"\n    })\n})\n\nprint(response.status_code)\n```\n\n### golang\n```golang\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/go-resty/resty/v2\"\n)\n\nfunc main() {\n\treqUrl := \"http://localhost:8888/v1/message\"\n\n\tcontent, _ := json.Marshal(map[string]any{\n\t\t\"content\": \"一行文本内容2\",\n\t})\n\tresp, err := resty.New().R().\n\t\tSetBody(map[string]any{\n\t\t\t\"sender\":  \"yourSenderName\",\n\t\t\t\"msgtype\": \"text\",\n\t\t\t\"content\": string(content),\n\t\t}).Post(reqUrl)\n\n\tfmt.Println(err, resp.StatusCode())\n}\n```\n\n### 更新配置\n\n请求方式：POST PUT DELETE\n\n请求地址：http://127.0.0.1:8888/v1/senders\n\n参数说明：\n\n| 参数（请求体） | 是否必须 | 类型 | 说明                                                                                                                                                                                                                   |\n| :------------- | :------- | :--- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| body           | 是       | json | 请求body为您的sender配置，如`{\"wechatBot\": [{\"name\": \"yourSenderName\", \"url\": \"https://xxx\"}]}`\u003cbr\u003ePOST：同类型配置会被全部覆盖\u003cbr\u003ePUT：同类型同名称的配置会被更新，新配置将被添加\u003cbr\u003eDELETE：同类型同名称配置将被删除 |\n\n返回结果：\n```json\n// 正常 httpStatusCode==200\n{\n  \"msg\": \"ok\"\n}\n\n// 异常 httpStatusCode!=200\n{\n  \"msg\": \"xxxx\"\n}\n```\n\n### 查询用户ID\n\n请求方式：POST\n\n请求地址：http://127.0.0.1:8888/v1/uid/getbyphone\n\n参数说明：\n\n| 参数   | 是否必须 | 类型   | 说明                         |\n| :----- | :------- | :----- | :--------------------------- |\n| sender | 是       | string | 查询用户id时使用的sender名称 |\n| phone  | 是       | string | 手机号                       |\n\n返回结果：\n```json\n// 正常 httpStatusCode==200\n{\n  \"uid\": \"xxxxxxxxxxxxx\",\n  \"msg\": \"ok\"\n}\n\n// 异常 httpStatusCode!=200\n{\n  \"msg\": \"xxxx\"\n}\n```\n\n### 查询消息历史\n\n请求方式：GET\n\n请求地址：http://127.0.0.1:8888/v1/histories\n\n参数说明：\n\n| 参数       | 是否必须 | 类型   | 说明               |\n| :--------- | :------- | :----- | :----------------- |\n| page_index | 是       | int    | 分页序号，从1开始  |\n| page_size  | 是       | int    | 分页每页数量       |\n| start      | 否       | int64  | 起始时间unix时间戳 |\n| end        | 否       | int64  | 结束时间unix时间戳 |\n| sender     | 否       | string | 发送方式名称       |\n| content    | 否       | string | 消息内容           |\n\n返回结果：\n```json\n// 正常 httpStatusCode==200\n{\n  \"count\": 1,\n  \"list\": [\n    {\n      \"CreatedAt\": 1705911411,\n      \"Err\": \"\",\n      \"Id\": 1,\n      \"Message\": \"json string of message\",\n      \"ReceivedAt\": 1705911410,\n      \"Req\": \"curl command of request\",\n      \"Resp\": \"json string of response body and http code\",\n      \"Status\": true\n    }\n  ],\n  \"msg\": \"ok\"\n}\n\n// 异常 httpStatusCode!=200\n{\n  \"msg\": \"xxxx\"\n}\n```\n\n### 鉴权\n\n当配置文件中开启auths鉴权配置后，请求需要加入鉴权信息，目前支持三种鉴权方式.\n#### IP\n发送请求的客户端ip需匹配pattern\n\n#### token\n发送请求中需要添加请求头 X-Token = token in your yaml config\n\n#### sign\n签名鉴权需要添加请求头 X-TS = 当前unix秒数时间戳 X-Nonce = 随机内容 X-Sign = 根据签名算法生成的签名\n\n签名算法步骤为\n1. 将ts和nonce信息加入body中 body[\"ts\"] = X-TS, body[\"nonce\"] = X-Nonce\n2. 将body中的键值对按键排序后拼接\n3. 使用配置文件中的secret计算sha256的值，并将结果进行base64编码，如 secret=666时，步骤二中结果为 \n4. 设置请求头中的 X-Sign = 步骤三结果\n```golang\n// golang 签名示例\nsecret := \"666\"\nbody := map[string]any{\n\"sender\":  \"wechatBot\",\n\"msgtype\": \"text\",\n\"content\": \"{\\\"content\\\":\\\"一行文本内容\\\"}\",\n}\nbody[\"ts\"] = \"1695005697\" // cast.ToString(time.Now().Unix())\nbody[\"nonce\"] = \"123\"\nkeys := lo.Keys(body)\nsort.Strings(keys)\n\n// content{\"content\":\"一行文本内容\"}msgtypetextnonce123senderwechatBotts1695005697\nkvStr := strings.Join(lo.Map(keys, func(k string, _ int) string { return fmt.Sprintf(\"%s%s\", k, body[k]) }), \"\")\n\nmac := hmac.New(sha256.New, []byte(secret))\n_, _ = mac.Write([]byte(kvStr))\n\n// nAW4/1vz8EjdJEVXqTevmX7yBOzQtUti1Z2TIgAxogc=\nsign := base64.StdEncoding.EncodeToString(mac.Sum(nil))\n```\n\n## 配置说明\n\nyaml配置文件定义了\n1. app 服务配置ip的、端口\n2. auths 鉴权方式。多种鉴权方式同时配置时，按配置先后进行检查，满足任意一种方式即通过鉴权。支持的鉴权方式为\n   - ip\n   - token\n   - sign签名\n3. senders 具体发送方式。senders支持动态增删，即在服务已经启动的情况下可以直接修改senders列表，服务会持续读取最新的改动。支持的发送方式类型\n   - email\n   - wechatBot\n   - wechatApp\n   - feishuBot\n   - feishuApp\n   - dingdingBot\n   - dingdingApp\n   - aliSms\n\n```yaml\napp:\n  ip:\n  port: 8888\n\nauths:\n  # - type: ip\n  #   pattern: 192.168.*.*\n\n  # - type: token\n  #   token: your token\n\n  # - type: sign\n  #   secret: your secret\n\nsenders:\n  email:\n    # - name: yourSenderName1\n    #   host: mail.xxx.com\n    #   port: 25\n    #   account: test@xxx.com\n    #   password: #无密码时留空即可\n    #   tls: \"false\"\n  wechatBot:\n    # - name: yourSenderName2\n    #   url: https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxxxx\n  wechatApp:\n    # - name: yourSenderName3\n    #   corpid: xxxx\n    #   agentid: xxxx\n    #   corpsecret: xxxx\n  feishuBot:\n    # - name: yourSenderName4\n    #   url: https://open.feishu.cn/open-apis/bot/v2/hook/xxxxxx\n  feishuApp:\n    # - name: yourSenderName5\n    #   app_id: cli_xxxx\n    #   app_secret: xxxx\n  dingdingBot:\n    # - name: yourSenderName6\n    #   url: https://oapi.dingding.com/robot/send?access_token=xxxx\n    #   token: xxxx #仅加密方式为加签时填写\n  dingdingApp:\n    # - name: yourSenderName7\n    #   appKey: xxxx\n    #   appSecret: xxxx\n    #   robotCode: xxxx\n  aliSms:\n    # - name: yourSenderName8\n    #   accessKey: xxxx\n    #   accessSecret: xxxx\n    #   templateCode: SMS_123456789\n    #   signName: xxxx\n```\n\n## 自定义发送\n\n通常情况下，以上7中方式能满足大部分需求，但是如果你想要定制自己的sender，可以按如下步骤进行开发\n\n1. 在send目录下创建你的sender文件，如mysender.go\n2. 定义mysender结构体并实现sender接口\n   ```golang\n   type sender interface {\n\t   send(*message) error\n\t   getConf() map[string]string\n   }\n   ```\n3. 新增init方法将你的sender注册到后台goroutine中，registered的key mysender即为你的sender的类型，可以在配置文件中使用。通常建议将文件名、结构体名、类型名保持一直\n   ```golang\n   func init() {\n\t   registered[\"mysender\"] = func(conf config) sender {\n\t       return \u0026wechatBot{conf: conf}\n\t   }\n   }\n   ```\n\n## Web\n\nhttp://127.0.0.1:8888/web\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fveops%2Fmessenger","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fveops%2Fmessenger","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fveops%2Fmessenger/lists"}