-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.ts
271 lines (229 loc) · 7.46 KB
/
main.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
/**
WARNING: MESSY CODE BELOW.
TO-DO (June 2020):
- refactor according to informal-uml.png
- improve word difficulty scoring system
**/
import {
Contact,
Message,
ScanStatus,
Wechaty,
Friendship,
log,
FileBox
} from 'wechaty'
type WordInfo = {
word:string,
meaning:string,
pinyin:string
}
import { generate } from 'qrcode-terminal'
import * as cheerio from 'cheerio'
import request from "request"
require('dotenv').config()
import wordMetadata from "../word-data1.json";
var path = require('path');
const {exec, spawn} = require('child_process');
const _ = require("underscore");
const fs = require('fs');
const process = require('process');
const makePromiseSpawn = (params2Cmd) => {
return (params,stdoutCB=(a)=>{},stderrCB=(a)=>{},errCloseCB=(err)=>{}): Promise<string> => {
const cmds = params2Cmd(params).split(" ")
return new Promise((resolve, reject) => {
const [head, ...tail] = cmds;
console.log(`starting: ${cmds}`)
let childProcess = spawn(head, tail, { stdio: 'pipe' })
childProcess.stderr.pipe(process.stderr)
var dataToPassOn = ""
childProcess.stdout.on("data", data =>{
if(head == "python3" || head == "python"){
dataToPassOn += data.toString()
}
console.log("stdout: " + data)
stdoutCB(data.toString())
})
childProcess.stderr.on("data", data =>{
console.log('stderr: ' + data)
stderrCB(data.toString())
})
childProcess.on('exit', code => {
console.log('exited on ', code)
if(_.isEmpty(dataToPassOn)){
resolve(code)
}else{
resolve(dataToPassOn)
}
})
childProcess.on("error", err =>{
console.log('error: ' +err)
errCloseCB(err)
reject(err)
})
})
}
}
const mkDir = makePromiseSpawn((path)=>`mkdir -p ${path}`)
const sum =(a)=> _.reduce(a, function(a, b){ return a + b}, 0);
const fenci = makePromiseSpawn((fileName) => `python3 src/fenci.py ${fileName}`)
const cedict = async (word:string): Promise<WordInfo> => {
const jsonString = await makePromiseSpawn((word)=> `python3 src/CC-CE-DICT.py ${word}`)(word)
console.log(jsonString)
return JSON.parse(jsonString)
}
const makeAnki = makePromiseSpawn((fileName) => `python3 src/make-anki.py ${fileName}`)
const multiply =(a:Array<number>)=> _.reduce(a, function(a, b){ return a * b}, 1);
const getWordDifficulty = (word)=>{
var scores = _.map(word,(char)=>{
console.log(char,wordMetadata.data[0][0])
var r = _.find(wordMetadata.data,(a)=>a[0]==char)
if(_.isUndefined(r)){
console.log("not found: "+char)
return -10
}
const frequencyScore = r[1]
const isCommonWord = false
const stroke = r[2]
return (Math.sqrt(0.09 + stroke * (isCommonWord ? 0.024 : 0.042) ) + (isCommonWord ? 0.3 : 0.42 ) + frequencyScore ) * frequencyScore * (isCommonWord ? 5 : 8)
})
return multiply(scores)
}
// const getWordInfo = (word)=>{
// return new Promise((resolve,reject)=>{
// try{
// request('http://dict.cn/'+encodeURIComponent(word), function (error, response, body) {
// if (error) {
// reject({error})
// return;
// }
// if (response && response.statusCode) {
// if (response.statusCode == 200) {
// console.log(body)
// const $ = cheerio.load(body);
// const defintion =
// resolve(defintion);
// } else {
// reject({error:response.statusCode})
// }
// }else{
// reject({error:"no response"})
// }
// })
// }catch(error){
// reject({error})
// }
// })
// }
const getTop10Words = (words)=>{
return _.filter(_.uniq(_.map(_.first(words,10),a=>a.word)),a=>a.length>1)
}
//messy ah
function onScan (qrcode: string, status: ScanStatus) {
if (status === ScanStatus.Waiting || status === ScanStatus.Timeout) {
generate(qrcode, { small: true }) // show qrcode on console
const qrcodeImageUrl = [
'https://api.qrserver.com/v1/create-qr-code/?data=',
encodeURIComponent(qrcode),
].join('')
log.info('StarterBot', 'onScan: %s(%s) - %s', ScanStatus[status], status, qrcodeImageUrl)
} else {
log.info('StarterBot', 'onScan: %s(%s)', ScanStatus[status], status)
}
}
async function onLogin (user: Contact) {
log.info('StarterBot', '%s login', user)
const miao = await cedict("喵")
console.log("wordInfo",miao['pinyin'])
}
function onLogout (user: Contact) {
log.info('StarterBot', '%s logout', user)
}
async function onMessage (msg: Message) {
const t = msg.type()
if (t == bot.Message.Type.Url) {
const link = await msg.toUrlLink()
log.info('MeowStarterBot', link)
const uri = link.url()
await msg.say('ohh 收到了~')
await msg.say('分析文章中哦~')
request(
{ uri },
async function(error, response, body) {
console.log(body);
const $ = cheerio.load(body)
const t = $("#js_content").text()
console.log(t)
fs.writeFileSync('userDataFiles/test.txt',t)
await fenci('userDataFiles/test')
const words = JSON.parse(fs.readFileSync('userDataFiles/test.json', 'utf8'));
const chineseWords = _.filter(words,(a)=>a.flag !== '0')
console.log(chineseWords)
const sorted = _.sortBy(chineseWords, (a)=>getWordDifficulty(a.word))
const top10 = getTop10Words(sorted)
console.log(top10)
await msg.say("找到困难的词啦~")
await msg.say(_.foldr(top10,(r,v,i)=>(i+1)+". "+v+"\n"+r,""))
await msg.say("以下是词的意思与拼音~")
try{
const wordInfos = await Promise.all(_.map(top10, (w)=>{
return cedict(w)
}))
console.log(wordInfos)
await Promise.all(_.map(wordInfos,async (a)=>{
return msg.say(
a.word+" "+a.pinyin+"\n"+
(a.meaning || "unknown")
)
}))
await mkdir('userDataFiles')
fs.writeFileSync('userDataFiles/test-cards.json', JSON.stringify(wordInfos))
await msg.say("要生成Anki记忆卡片请输入「anki」~ ")
}catch(error){
console.log(error)
}
}
);
}else if(t == bot.Message.Type.Text ){
console.log("is text")
const text = msg.text()
console.log(text)
if(text == "anki"){
await msg.say("生成Anki记忆卡片中哦~")
await makeAnki("files/test")
const fileBox = FileBox.fromFile('userDataFiles/test.apkg')
await msg.say(fileBox)
}
}else{
console.log("message type ",t)
}
}
async function onNewFriendship(friendship: Friendship) {
try {
switch (friendship.type()) {
case Friendship.Type.Receive:
await friendship.accept()
break
case Friendship.Type.Confirm:
break
}
const contact = friendship.contact()
const name = contact.name()
await contact.say("您好呀,"+name+"!我是您的吖奇说神奇小助理!我的存在就是为了帮您自制Anki卡片学中文哦~ 这个是一个demo~ 把微信公众号文章发给我,我会帮您找文中的难词~ 加以释义,生成Anki卡片哦~")
} catch (e) {
console.error(e)
}
}
const bot = new Wechaty({
name: 'ding-dong-bot',
puppet: 'wechaty-puppet-padplus',
// puppet: 'wechaty-puppet-puppeteer'
})
bot.on('scan', onScan)
bot.on('login', onLogin)
bot.on('logout', onLogout)
bot.on('message', onMessage)
bot.on('friendship', onNewFriendship)
bot.start()
.then(() => log.info('StarterBot', 'Starter Bot Started.'))
.catch(e => log.error('StarterBot', e))