├── .gitignore ├── README.md ├── features ├── feature1.feature ├── step_definitions │ ├── definitions1.js │ ├── model1.tmodel │ └── model1_files │ │ ├── Window.png │ │ └── 文本编辑器.png └── support │ └── env.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 微信发送自动暖信息 2 | 3 | 4 | ## 前言 5 | 6 | > 每个都是为公司的应用写自动化测试脚本,没曾把这些自动化应用到生活中,直到逛社区看到 [用Node+wechaty写一个爬虫脚本每天定时给女(男)朋友发微信暖心话](https://juejin.im/post/5c77c6bef265da2de6611cff) 发现还有如此好玩的事情。于是赏玩了一把,觉得还可以有更美好的实现方式。 7 | 8 | 9 | 上面的脚本还要进行扫码登录的操作,对于经常写自动化脚本的我们最好是一劳永逸的。所以这次我的脚本:只需一次登录。😁 10 | 11 | 下面言归正传 12 | 13 | 14 | ## 准备 15 | 16 | * 一台Windows操作系统。 17 | * 在Windows上安装微信客户端。 18 | * 在Windows上安装[CukeTest](http://cuketest.com) 19 | 20 | 这里说明一下为啥我要用CukeTest? 21 | 22 | > 因为CukeTest是一款专门用来做自动化测试的工具,我经常用它来开发Windows,Web,Mobile,Api等自动化测试脚本。 本次我主要是想用它来自动化操作Windows版微信,你在Windows上登录微信后,不用管它,它会一直在线,只要不管电脑,微信可以随时在线,再也不用担心掉线了。 23 | 24 | 25 | Pc上安装好微信客户端,我们手动登录一下,为了方便快速的让自动化脚本找到你的哪个她(或他),可以事先把她(或他)的微信置顶。 26 | 27 | 28 | ## 暖心内容来源 29 | 30 | 和上面的大佬一样也是每日一句来自[one](http://wufazhuce.com/) 31 | 32 | 天气信息来自[墨迹天气](http://tianqi.moji.com/) 33 | 34 | 35 | ## 使用库 36 | 37 | * [chromedriver](https://www.npmjs.com/package/chromedriver) 38 | * [selenium-webdriver](https://www.npmjs.com/package/selenium-webdriver) 39 | * [moment](https://www.npmjs.com/package/moment) 40 | 41 | 这些库大家如果经常写自动化测试,应该很熟悉。chromedriver chrome浏览器的驱动, selenium-webdriver web自动化库。
我脚本的原理比较简单,就是把平时我们手工操作的步骤转化为自动化脚本,自动打开chrome浏览器,去one和墨迹天气页面上提取信息。
因为CukeTest内置的有对Windows控件的操作,用CukeTest直接去自动操作微信,把上面提取出来的信息发送出去即可。 42 | 43 | 44 | ## 运行 45 | 46 | ``` 47 | git clone 48 | cd auto_wechat 49 | npm install 50 | ``` 51 | 52 | 使用CukeTest 打开项目,点击运行就可以看到运行效果。 53 | 54 | #### 55 | 56 | ## 主要代码片段 57 | 58 | ### 1.编辑一个故事场景 59 | 60 | ![image.png](https://cdn.nlark.com/yuque/0/2019/png/87080/1551931419013-238c46f0-cbbb-4afb-93d0-12e281f52d00.png#align=left&display=inline&height=479&name=image.png&originHeight=479&originWidth=569&size=33657&status=done&width=569) 61 | 62 | 63 | ### 2.获取信息 64 | 获取One  65 | 66 | ```javascript 67 | await driver.get(url); 68 | let css_selector = '.fp-one-cita-wrapper>.fp-one-cita > a'; //元素定位 69 | let text = await driver.findElement({css:css_selector}).getText(); 70 | this.oneText = text; 71 | ``` 72 | 获取天气 73 | 74 | ```javascript 75 | await driver.get(url); 76 | let current_tm = await driver.findElement({ css:'div.wea_weather.clearfix > em'}).getText(); 77 | let current_state = await driver.findElement({ css:'.wea_weather.clearfix > b'}).getText(); 78 | let wea_tips = await driver.findElement({ css:'.wea_tips.clearfix > em'}).getText(); 79 | let current_about = await driver.findElement({ css:'.wea_about.clearfix'}).getText(); 80 | this.today_wea = ` 81 | 温度:${current_tm}° 82 | 天气:${current_state} 83 | ${current_about} 84 | ${wea_tips} 85 | ` 86 | }); 87 | ``` 88 | 89 | ### 3.微信界面操作 90 | 操作微信界面需要在CukeTest中添加虚拟控件。
91 | ![addvirtualimage.gif](https://cdn.nlark.com/yuque/0/2019/gif/87080/1551946590971-e624c499-9876-4a36-9901-4a3c9cb70c0d.gif#align=left&display=inline&height=524&name=addvirtualimage.gif&originHeight=728&originWidth=1036&size=699587&status=done&width=746) 92 | 93 | 添加完虚拟控件,调用CukeTest提供的API即可。模型管理器中可以看到。
94 | ![image.png](https://cdn.nlark.com/yuque/0/2019/png/87080/1551946746043-99e24705-a30e-4901-a11b-93d45a4cb043.png#align=left&display=inline&height=604&name=image.png&originHeight=604&originWidth=749&size=43939&status=done&width=749) 95 | 96 | 复制或拖拽相关的方法到代码编辑器中即可。 97 | 98 | 99 | ### 4.运行 100 | 101 | ![runproject.gif](https://cdn.nlark.com/yuque/0/2019/gif/87080/1551947910373-82c51e42-e19c-4399-ade7-ed801acd6d4f.gif#align=left&display=inline&height=398&name=runproject.gif&originHeight=728&originWidth=1366&size=1595732&status=done&width=746) 102 | -------------------------------------------------------------------------------- /features/feature1.feature: -------------------------------------------------------------------------------- 1 | # language: zh-CN 2 | 功能: 自动发送微信消息 3 | 你可以选择给爱人,亲戚,朋友等等发送贴心的天气预报信息或者每日一言信息 4 | 5 | 场景: 微信贴心助手 6 | 假如打开浏览器从"http://wufazhuce.com/"找到当日一句 7 | 同时从"http://tianqi.moji.com/weather/china/shanghai/shanghai"获取当天的天气 8 | 那么保存这些信息并关闭浏览器 9 | 当自动打开微信 10 | 同时找到你特别关心的人 11 | 那么就让脚本把这些信息自动发送给你关心的人吧 -------------------------------------------------------------------------------- /features/step_definitions/definitions1.js: -------------------------------------------------------------------------------- 1 | const { Given, When, Then } = require('cucumber'); 2 | const { TestModel, Auto } = require('leanpro.win'); 3 | const { Util } = require('leanpro.common'); 4 | require('chromedriver') 5 | let {Builder} = require('selenium-webdriver'); 6 | let moment = require('moment') 7 | 8 | let model = TestModel.loadModel(__dirname + "/model1.tmodel"); 9 | let driver = new Builder().forBrowser('chrome').build(); 10 | 11 | //// 你的步骤定义 ///// 12 | 13 | 14 | 15 | Given(/^自动打开微信$/, async function () { 16 | 17 | await model.getVirtual("msg").click(0, 0, 1); 18 | await model.getWindow("Window").activate(); 19 | 20 | 21 | }); 22 | 23 | Given(/^找到你特别关心的人$/, async function () { 24 | 25 | await model.getVirtual("user").click(0, 0, 1); 26 | 27 | }); 28 | 29 | 30 | 31 | Given(/^从"([^"]*)"获取当天的天气$/, async function (url) { 32 | 33 | await driver.get(url); 34 | let current_tm = await driver.findElement({ css:'div.wea_weather.clearfix > em'}).getText(); 35 | let current_state = await driver.findElement({ css:'.wea_weather.clearfix > b'}).getText(); 36 | let wea_tips = await driver.findElement({ css:'.wea_tips.clearfix > em'}).getText(); 37 | let current_about = await driver.findElement({ css:'.wea_about.clearfix'}).getText(); 38 | this.today_wea = ` 39 | 温度:${current_tm}° 40 | 天气:${current_state} 41 | ${current_about} 42 | ${wea_tips} 43 | ` 44 | }); 45 | 46 | Then(/^就让脚本把这些信息自动发送给你关心的人吧$/, async function () { 47 | let today = moment().format('YYYY-MM-DD '); 48 | let current_time = moment().format('YYYY MM DD HH:mm:ss ') 49 | let d = moment().toArray() 50 | d = d.slice(0, 3) 51 | let d_arr = moment(d); 52 | let day = moment([2016,3,5]); 53 | let days = d_arr.diff(day,'days') 54 | let content = 55 | `今天是${today} 56 | ❤ 57 | ${this.oneText} 58 | ❤ 59 | 今天上海天气: 60 | ${this.today_wea} 61 | --:) 来自微信贴心助手 ${current_time} 62 | ` 63 | let proc = Util.launchProcess("notepad"); 64 | await model.getDocument("文本编辑器").set(content); 65 | await model.getDocument("文本编辑器").click(); 66 | await model.getDocument("文本编辑器").pressKeys("^a"); 67 | await Util.delay(1000); 68 | await model.getDocument("文本编辑器").pressKeys("^c"); 69 | Util.stopProcess(proc); 70 | 71 | await model.getWindow("Window").activate(); 72 | await model.getVirtual("editmsg").click(0, 0, 1); 73 | await model.getVirtual("editmsg").click(10, 10, 2); 74 | await Util.delay(1000); 75 | await model.getVirtual("editmsg").click(15, 15, 1); 76 | await Util.delay(1000); 77 | await model.getVirtual("send").click(0, 0, 1); 78 | 79 | }); 80 | 81 | Given(/^打开浏览器从"([^"]*)"找到当日一句$/, async function (url) { 82 | 83 | await driver.get(url); 84 | let css_selector = '.fp-one-cita-wrapper>.fp-one-cita > a'; 85 | let text = await driver.findElement({css:css_selector}).getText(); 86 | this.oneText = text; 87 | }); 88 | 89 | Then(/^保存这些信息并关闭浏览器$/, async function () { 90 | 91 | await driver.quit(); 92 | }); 93 | 94 | -------------------------------------------------------------------------------- /features/step_definitions/model1.tmodel: -------------------------------------------------------------------------------- 1 | { 2 | "ntype": "app", 3 | "process": null, 4 | "controls": [ 5 | { 6 | "ntype": "uia", 7 | "nname": "Window", 8 | "description": "", 9 | "identifyProperties": { 10 | "type": "Window", 11 | "className": "WeChatMainWndForPC", 12 | "title": "微信" 13 | }, 14 | "cachedProperties": { 15 | "type": "Window", 16 | "className": "WeChatMainWndForPC", 17 | "title": "微信", 18 | "boundingRectangle": "345,166,782,534" 19 | }, 20 | "image": { 21 | "imgPath": "Window.png", 22 | "rect": "1,1,782,534" 23 | }, 24 | "children": [ 25 | { 26 | "ntype": "virtual", 27 | "nname": "msg", 28 | "description": "", 29 | "identifyProperties": { 30 | "boundingRectangle": "2,64,55,48", 31 | "align": "0" 32 | } 33 | }, 34 | { 35 | "ntype": "virtual", 36 | "nname": "user", 37 | "description": "", 38 | "identifyProperties": { 39 | "boundingRectangle": "61,62,245,60", 40 | "align": "0" 41 | } 42 | }, 43 | { 44 | "ntype": "virtual", 45 | "nname": "editmsg", 46 | "description": "", 47 | "identifyProperties": { 48 | "boundingRectangle": "0,37,469,72", 49 | "align": "3" 50 | } 51 | }, 52 | { 53 | "ntype": "virtual", 54 | "nname": "send", 55 | "description": "", 56 | "identifyProperties": { 57 | "boundingRectangle": "678,497,76,27", 58 | "align": "0" 59 | } 60 | } 61 | ] 62 | }, 63 | { 64 | "ntype": "uia", 65 | "nname": "Window1", 66 | "description": "", 67 | "identifyProperties": { 68 | "type": "Window", 69 | "className": "Notepad" 70 | }, 71 | "cachedProperties": { 72 | "type": "Window", 73 | "className": "Notepad", 74 | "title": "无标题 - 记事本", 75 | "boundingRectangle": "378,192,658,524" 76 | }, 77 | "children": [ 78 | { 79 | "ntype": "uia", 80 | "nname": "文本编辑器", 81 | "description": "", 82 | "identifyProperties": { 83 | "type": "Document", 84 | "automationId": "15" 85 | }, 86 | "cachedProperties": { 87 | "automationId": "15", 88 | "type": "Document", 89 | "className": "Edit", 90 | "name": "文本编辑器", 91 | "boundingRectangle": "386,243,642,465" 92 | }, 93 | "image": { 94 | "imgPath": "文本编辑器.png", 95 | "rect": "1,1,642,465" 96 | } 97 | } 98 | ] 99 | } 100 | ] 101 | } -------------------------------------------------------------------------------- /features/step_definitions/model1_files/Window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/autonodejs/auto_wechat/6605afd1248b1c9a954002f3a18abdbbe1aea009/features/step_definitions/model1_files/Window.png -------------------------------------------------------------------------------- /features/step_definitions/model1_files/文本编辑器.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/autonodejs/auto_wechat/6605afd1248b1c9a954002f3a18abdbbe1aea009/features/step_definitions/model1_files/文本编辑器.png -------------------------------------------------------------------------------- /features/support/env.js: -------------------------------------------------------------------------------- 1 | let { setDefaultTimeout } = require('cucumber'); 2 | 3 | setDefaultTimeout(30 * 1000); //set step timeout to be 30 seconds -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wechat", 3 | "version": "1.0.0", 4 | "description": "generated by CukeTest, LeanPro Co., Ltd", 5 | "main": "", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "chromedriver": "^2.46.0", 13 | "moment": "^2.24.0", 14 | "selenium-webdriver": "^4.0.0-alpha.1" 15 | } 16 | } 17 | --------------------------------------------------------------------------------