{"id":13435855,"url":"https://github.com/node-webot/wechat","last_synced_at":"2025-05-14T02:07:17.329Z","repository":{"id":6342981,"uuid":"7579124","full_name":"node-webot/wechat","owner":"node-webot","description":"微信公共平台消息接口服务中间件","archived":false,"fork":false,"pushed_at":"2018-12-28T07:17:45.000Z","size":3475,"stargazers_count":5124,"open_issues_count":15,"forks_count":1179,"subscribers_count":466,"default_branch":"master","last_synced_at":"2025-05-13T21:24:31.116Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"saltstack/libnacl","license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/node-webot.png","metadata":{"files":{"readme":"README.en.md","changelog":null,"contributing":null,"funding":null,"license":"MIT-License","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-01-12T18:53:32.000Z","updated_at":"2025-05-13T07:41:10.000Z","dependencies_parsed_at":"2022-08-06T19:15:30.513Z","dependency_job_id":null,"html_url":"https://github.com/node-webot/wechat","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/node-webot%2Fwechat","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/node-webot%2Fwechat/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/node-webot%2Fwechat/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/node-webot%2Fwechat/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/node-webot","download_url":"https://codeload.github.com/node-webot/wechat/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254052798,"owners_count":22006716,"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":[],"created_at":"2024-07-31T03:00:39.937Z","updated_at":"2025-05-14T02:07:12.314Z","avatar_url":"https://github.com/node-webot.png","language":"JavaScript","funding_links":[],"categories":["JavaScript","Web 后端"],"sub_categories":[],"readme":"wechat [![NPM version](https://badge.fury.io/js/wechat.png)](http://badge.fury.io/js/wechat) [![Build Status](https://travis-ci.org/node-webot/wechat.png?branch=master)](https://travis-ci.org/node-webot/wechat) [![Dependencies Status](https://david-dm.org/node-webot/wechat.png)](https://david-dm.org/node-webot/wechat) [![Coverage Status](https://coveralls.io/repos/node-webot/wechat/badge.png)](https://coveralls.io/r/node-webot/wechat)\n======\n\nWechat is a middleware and SDK of Wechat Official Account Admin Platform (mp.weixin.qq.com).\n\nThis wechat document is translated by [Guo Yu](https://github.com/turingou/), if you have some understanding problems, please feel free open an issue [here](https://github.com/turingou/wechat/issues).\n\n## Features\n\n- Auto reply (text, image, videos, music, thumbnails posts are supported)\n- CRM message (text, image, videos, music, thumbnails posts are supported)\n- Menu settings (CRD are supported)\n- QR codes (CR are supported, both temporary and permanent)\n- Group settings (CRUD are supported)\n- Followers infomation (fetching user's info or followers list)\n- Media (upload or download)\n- Reply Waiter (good for surveys)\n- Sessions\n- OAuth API\n- Payment (deliver notify and order query)\n\nAPI details located [here](http://node-webot.github.io/wechat/api.html)\n\n## Installation\n\n```\nnpm install wechat\n```\n\n## Use with Connect/Express\n\n```\nvar wechat = require('wechat');\n\napp.use(connect.query()); // Or app.use(express.query());\napp.use('/wechat', wechat('some token', function (req, res, next) {\n  // message is located in req.weixin\n  var message = req.weixin;\n  if (message.FromUserName === 'diaosi') {\n    // reply with text\n    res.reply('hehe');\n  } else if (message.FromUserName === 'text') {\n    // another way to reply with text\n    res.reply({\n      content: 'text object',\n      type: 'text'\n    });\n  } else if (message.FromUserName === 'hehe') {\n    // reply with music\n    res.reply({\n      type: \"music\",\n      content: {\n        title: \"Just some music\",\n        description: \"I have nothing to lose\",\n        musicUrl: \"http://mp3.com/xx.mp3\",\n        hqMusicUrl: \"http://mp3.com/xx.mp3\"\n      }\n    });\n  } else {\n    // reply with thumbnails posts\n    res.reply([\n      {\n        title: 'Come to fetch me',\n        description: 'or you want to play in another way ?',\n        picurl: 'http://nodeapi.cloudfoundry.com/qrcode.jpg',\n        url: 'http://nodeapi.cloudfoundry.com/'\n      }\n    ]);\n  }\n}));\n```\n\n*Tips*: you'll have to apply `token` at [Wechat platform (this page is in Chinese)](http://mp.weixin.qq.com/cgi-bin/callbackprofile?type=info\u0026t=wxm-developer-ahead\u0026lang=zh_CN)\n\n### Reply Messages\n\nauto reply a message when your followers send a message to you. also text, image, videos, music, thumbnails posts are supported. details API goes [here (official documents)](http://mp.weixin.qq.com/wiki/index.php?title=发送被动响应消息)\n\n#### Reply with text\n```\nres.reply('Hello world!');\n// or\nres.reply({type: \"text\", content: 'Hello world!'});\n```\n#### Reply with Image\n```\nres.reply({\n  type: \"image\",\n  content: {\n    mediaId: 'mediaId'\n  }\n});\n```\n#### Reply with voice\n```\nres.reply({\n  type: \"voice\",\n  content: {\n    mediaId: 'mediaId'\n  }\n});\n```\n#### Reply with Video\n```\nres.reply({\n  type: \"video\",\n  content: {\n    mediaId: 'mediaId',\n    thumbMediaId: 'thumbMediaId'\n  }\n});\n```\n#### Reply with Music\n```\nres.reply({\n  title: \"Just some music\",\n  description: \"I have nothing to lose\",\n  musicUrl: \"http://mp3.com/xx.mp3\",\n  hqMusicUrl: \"http://mp3.com/xx.mp3\"\n});\n```\n#### Reply with Thumbnails posts\n```\nres.reply([\n  {\n    title: 'Come to fetch me',\n    description: 'or you want to play in another way ?',\n    picurl: 'http://nodeapi.cloudfoundry.com/qrcode.jpg',\n    url: 'http://nodeapi.cloudfoundry.com/'\n  }\n]);\n```\n#### Reply with social function messages\n```js\nres.reply({\n    type: 'hardware',\n    HardWare:{\n      MessageView: 'myrank',\n      MessageAction: 'ranklist'\n    }\n});\n```\n### transfer user message to wechat customer service\ntransfer the message sent from wechat users to the Wechat Multi Customer Service\n```js\nres.transfer2CustomerService();\n```\n\n### Reply with device messages\nSpecific responses will be made as the message type is device_text or device_event.\n```js\nvar wechat = require('wechat');\nvar config = {\n  token: 'token',\n  appid: 'appid',\n  encodingAESKey: 'encodinAESKey',\n  checkSignature: true // optional, default value is true. Because WeChat open platform debug tool does not send signature in plaintext mode, if you want to use this debug tool, please set to false\n};\n\napp.use(express.query());\napp.use('/wechat', wechat(config, function (req, res, next) {\n  // message is located in req.weixin\n  var message = req.weixin;\n  if (message.MsgType === 'device_text') {\n    // device text\n    res.reply('This message will be pushed onto the device.');\n  } else if (message.MsgType === 'device_event') {\n    if (message.Event === 'subscribe_status' ||\n      message.Event === 'unsubscribe_status') {\n    //subscribe or unsubscribe the WIFI device status,the reply should be 1 or 0\n      res.reply(1);\n    } else {\n      res.reply('This message will be pushed onto the device.')\n    }\n  }\n}));\n```\n### WXSession\n\nWechat messages are not communicate like traditional C/S model, therefore nothing Cookies will be store in Wechat client. this WXSession is designed to support access user's infomation via `req.wxsession`, with `connect.session` backed.\n\nIt's a simple demo:\n\n```\napp.use(connect.cookieParser());\napp.use(connect.session({secret: 'keyboard cat', cookie: {maxAge: 60000}}));\napp.use('/wechat', wechat('some token', wechat.text(function (info, req, res, next) {\n  if (info.Content === '=') {\n    var exp = req.wxsession.text.join('');\n    req.wxsession.text = '';\n    res.reply(exp);\n  } else {\n    req.wxsession.text = req.wxsession.text || [];\n    req.wxsession.text.push(info.Content);\n    res.reply('Message got ' + info.Content);\n  }\n})));\n```\n\n`req.wxsession` and `req.session` shares same store. width `redis` as persistence database, across processes sharing are supportd.\n\n### Reply Waiter\n\na reply waiter is seems like a telephone menu system. it must be setup before activation. this function is supported upon WXSession.\n\n```\nvar List = require('wechat').List;\nList.add('view', [\n  ['reply {a}', function (info, req, res) {\n    res.reply('Im Answer A');\n  }],\n  ['reply {b}', function (info, req, res) {\n    res.reply('Im Answer B');\n  }],\n  ['reply {c}', 'Im Answer C (the shorthand method)']\n]);\n```\n\nactive the reply waiter we setuped before:\n\n```\nvar app = connect();\napp.use(connect.query());\napp.use(connect.cookieParser());\napp.use(connect.session({secret: 'keyboard cat', cookie: {maxAge: 60000}}));\napp.use('/wechat', wechat('some token', wechat.text(function (info, req, res, next) {\n  if (info.Content === 'list') {\n    res.wait('view'); // view is the very waiter we setuped before.\n  } else {\n    res.reply('hehe');\n    // or stop the waiter and quit.\n    // res.nowait('hehe');\n  }\n})));\n```\n\nif waiter `view` actived, user will receive messages below:\n\n```\nreply a\nreply b\nreply c\n```\n\nreply waiter acquires both function and text as a `callback` action\n\n```\nList.add('view', [\n  ['reply {a}', function (info, req, res, next) {\n    // we callback as a function\n    res.reply('Answer A');\n  }],\n  // or text as shorthand\n  ['reply {c}', 'Answer C']\n]);\n```\n\nif user's message is not in waiter's trigger texts. this message will be processd in the `else` way and can be stoped by `res.nowait()`, `res.nowait` method actions like `reply` method.\n\n## Show cases\n### Auto-reply robot based on Node.js\n\n![Node.js API Auto-reply robot](http://nodeapi.diveintonode.org/assets/qrcode.jpg)\n\nCodes here \u003chttps://github.com/JacksonTian/api-doc-service\u003e\n\nrobots can be setup in PAASs like [CloudFoundry](http://www.cloudfoundry.com/), [appfog](https://www.appfog.com/) or [BAE](http://developer.baidu.com/wiki/index.php?title=docs/cplat/rt/node.js).\n\n## API details\nofficial document locates here [Messages API Guide (in Chinese)](http://mp.weixin.qq.com/wiki/index.php?title=消息接口指南)。\n\nwachat 0.6.x supports shorthand methods below:\n\n```\napp.use('/wechat', wechat('some token', wechat.text(function (message, req, res, next) {\n  // reply with text\n  // { ToUserName: 'gh_d3e07d51b513',\n  // FromUserName: 'oPKu7jgOibOA-De4u8J2RuNKpZRw',\n  // CreateTime: '1359125035',\n  // MsgType: 'text',\n  // Content: 'http',\n  // MsgId: '5837397576500011341' }\n}).image(function (message, req, res, next) {\n  // message为图片内容\n  // { ToUserName: 'gh_d3e07d51b513',\n  // FromUserName: 'oPKu7jgOibOA-De4u8J2RuNKpZRw',\n  // CreateTime: '1359124971',\n  // MsgType: 'image',\n  // PicUrl: 'http://mmsns.qpic.cn/mmsns/bfc815ygvIWcaaZlEXJV7NzhmA3Y2fc4eBOxLjpPI60Q1Q6ibYicwg/0',\n  // MediaId: 'media_id',\n  // MsgId: '5837397301622104395' }\n}).voice(function (message, req, res, next) {\n  // Reply with Voice\n  // { ToUserName: 'gh_d3e07d51b513',\n  // FromUserName: 'oPKu7jgOibOA-De4u8J2RuNKpZRw',\n  // CreateTime: '1359125022',\n  // MsgType: 'voice',\n  // MediaId: 'OMYnpghh8fRfzHL8obuboDN9rmLig4s0xdpoNT6a5BoFZWufbE6srbCKc_bxduzS',\n  // Format: 'amr',\n  // MsgId: '5837397520665436492' }\n}).video(function (message, req, res, next) {\n  // Reply with Video\n  // { ToUserName: 'gh_d3e07d51b513',\n  // FromUserName: 'oPKu7jgOibOA-De4u8J2RuNKpZRw',\n  // CreateTime: '1359125022',\n  // MsgType: 'video',\n  // MediaId: 'OMYnpghh8fRfzHL8obuboDN9rmLig4s0xdpoNT6a5BoFZWufbE6srbCKc_bxduzS',\n  // ThumbMediaId: 'media_id',\n  // MsgId: '5837397520665436492' }\n}).location(function (message, req, res, next) {\n  // Reply with Location (geo)\n  // { ToUserName: 'gh_d3e07d51b513',\n  // FromUserName: 'oPKu7jgOibOA-De4u8J2RuNKpZRw',\n  // CreateTime: '1359125311',\n  // MsgType: 'location',\n  // Location_X: '30.283950',\n  // Location_Y: '120.063139',\n  // Scale: '15',\n  // Label: {},\n  // MsgId: '5837398761910985062' }\n}).link(function (message, req, res, next) {\n  // Reply with Link\n  // { ToUserName: 'gh_d3e07d51b513',\n  // FromUserName: 'oPKu7jgOibOA-De4u8J2RuNKpZRw',\n  // CreateTime: '1359125022',\n  // MsgType: 'link',\n  // Title: 'A link',\n  // Description: 'A link has its desc',\n  // Url: 'http://1024.com/',\n  // MsgId: '5837397520665436492' }\n}).event(function (message, req, res, next) {\n  // Reply with Event\n  // { ToUserName: 'gh_d3e07d51b513',\n  // FromUserName: 'oPKu7jgOibOA-De4u8J2RuNKpZRw',\n  // CreateTime: '1359125022',\n  // MsgType: 'event',\n  // Event: 'LOCATION',\n  // Latitude: '23.137466',\n  // Longitude: '113.352425',\n  // Precision: '119.385040',\n  // MsgId: '5837397520665436492' }\n}).device_text(function (message, req, res, next) {\n  // Reply with device text.\n  // { ToUserName: 'gh_d3e07d51b513',\n  // FromUserName: 'oPKu7jgOibOA-De4u8J2RuNKpZRw',\n  // CreateTime: '1359125022',\n  // MsgType: 'device_text',\n  // DeviceType: 'gh_d3e07d51b513'\n  // DeviceID: 'dev1234abcd',\n  // Content: 'd2hvc3lvdXJkYWRkeQ==',\n  // SessionID: '9394',\n  // MsgId: '5837397520665436492',\n  // OpenID: 'oPKu7jgOibOA-De4u8J2RuNKpZRw' }\n}).device_event(function (message, req, res, next) {\n  // Reply with device event.\n  // { ToUserName: 'gh_d3e07d51b513',\n  // FromUserName: 'oPKu7jgOibOA-De4u8J2RuNKpZRw',\n  // CreateTime: '1359125022',\n  // MsgType: 'device_event',\n  // Event: 'bind'\n  // DeviceType: 'gh_d3e07d51b513'\n  // DeviceID: 'dev1234abcd',\n  // OpType : 0, //Available as Event is subscribe_status or unsubscribe_status.\n  // Content: 'd2hvc3lvdXJkYWRkeQ==', //Available as Event is not subscribe_status and unsubscribe_status.\n  // SessionID: '9394',\n  // MsgId: '5837397520665436492',\n  // OpenID: 'oPKu7jgOibOA-De4u8J2RuNKpZRw' }\n})));\n```\n\n*Tips*: `text`, `image`, `voice`, `video`, `location`, `link`, `event`, `device_text`, `device_event` must be set at least one.\n\n### More simple APIs\n\nSupported in 0.3.x and above.\n\n```\napp.use('/wechat', wechat('some token').text(function (message, req, res, next) {\n  // TODO\n}).image(function (message, req, res, next) {\n  // TODO\n}).voice(function (message, req, res, next) {\n  // TODO\n}).video(function (message, req, res, next) {\n  // TODO\n}).location(function (message, req, res, next) {\n  // TODO\n}).link(function (message, req, res, next) {\n  // TODO\n}).event(function (message, req, res, next) {\n  // TODO\n}).device_text(function (message, req, res, next) {\n  // TODO\n}).device_event(function (message, req, res, next) {\n  // TODO\n}).middlewarify());\n```\n\n### Functions Graph\n![graph](https://raw.github.com/node-webot/wechat/master/figures/wechat.png)\n\n*Tips*: Business logic in blue lines.\n\n## License\nThe MIT license.\n\n## Donation\nbuy me a cup of coffee please.\n\n[![donate wechat](https://img.alipay.com/sys/personalprod/style/mc/btn-index.png)](https://me.alipay.com/jacksontian)\n\n\nOr:\n\n[![](http://img.shields.io/gratipay/JacksonTian.svg)](https://www.gittip.com/JacksonTian/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnode-webot%2Fwechat","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnode-webot%2Fwechat","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnode-webot%2Fwechat/lists"}